geo_labels 0.2.0 → 0.3.2
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.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +24 -0
 - data/README.md +127 -6
 - data/Rakefile +6 -5
 - data/app/assets/javascripts/geo_labels/application.coffee +13 -2
 - data/app/assets/javascripts/geo_labels/ratings.coffee +80 -0
 - data/app/assets/stylesheets/geo_labels/application.sass +21 -0
 - data/app/controllers/concerns/geo_labels/shared_template_and_instance_methods.rb +3 -0
 - data/app/controllers/geo_labels/application_controller.rb +3 -0
 - data/app/controllers/geo_labels/contacts_controller.rb +18 -0
 - data/app/controllers/geo_labels/dashboard_controller.rb +14 -9
 - data/app/controllers/geo_labels/labels_controller.rb +3 -1
 - data/app/controllers/geo_labels/ratings_controller.rb +30 -0
 - data/app/helpers/geo_labels/application_helper.rb +73 -44
 - data/app/helpers/geo_labels/rating_helper.rb +15 -0
 - data/app/jobs/geo_labels/application_job.rb +1 -0
 - data/app/mailers/geo_labels/application_mailer.rb +4 -2
 - data/app/models/concerns/geo_labels/ratings_support.rb +102 -0
 - data/app/models/geo_labels/application_record.rb +22 -0
 - data/app/models/geo_labels/contact.rb +62 -20
 - data/app/models/geo_labels/contact_label.rb +43 -7
 - data/app/models/geo_labels/label.rb +13 -0
 - data/app/views/geo_labels/contacts/_form.html.slim +22 -6
 - data/app/views/geo_labels/contacts/index.html.slim +19 -7
 - data/app/views/geo_labels/contacts/show.html.slim +24 -2
 - data/app/views/geo_labels/dashboard/main.html.slim +27 -5
 - data/app/views/geo_labels/labels/index.html.slim +10 -6
 - data/app/views/geo_labels/labels/show.html.slim +15 -1
 - data/app/views/layouts/geo_labels/application.html.slim +2 -0
 - data/config/environment.rb +2 -0
 - data/config/initializers/human_plural.rb +8 -5
 - data/config/locales/geo_labels.en.yml +8 -0
 - data/config/locales/geo_labels.es.yml +17 -0
 - data/config/routes.rb +12 -1
 - data/db/migrate/20221019150722_create_geo_labels_contacts.rb +5 -3
 - data/db/migrate/20221020180213_create_geo_labels_labels.rb +2 -0
 - data/db/migrate/20221020195346_create_geo_labels_contact_labels.rb +4 -2
 - data/db/migrate/20230602182522_geo_labels_contacts_complex_name_to_name.rb +9 -0
 - data/db/migrate/20230726154822_add_state_to_geo_labels_contacts.rb +8 -0
 - data/db/migrate/20230801145902_add_food_rating_to_geo_labels_contacts.rb +7 -0
 - data/lib/geo_labels/engine.rb +17 -7
 - data/lib/geo_labels/exporter.rb +24 -10
 - data/lib/geo_labels/importer.rb +31 -27
 - data/lib/geo_labels/version.rb +3 -1
 - data/lib/geo_labels.rb +2 -0
 - data/lib/tasks/geo_labels_tasks.rake +1 -0
 - metadata +37 -15
 
| 
         @@ -1,32 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module GeoLabels
         
     | 
| 
       2 
4 
     | 
    
         
             
              module ApplicationHelper
         
     | 
| 
      
 5 
     | 
    
         
            +
                include RatingHelper
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       3 
7 
     | 
    
         
             
                def link_to_labels_tree
         
     | 
| 
       4 
     | 
    
         
            -
                  #output = "<ul class='ui list'>"
         
     | 
| 
       5 
     | 
    
         
            -
                  output = tag.ul(class: 'ui list')[0..-6]
         
     | 
| 
       6 
     | 
    
         
            -
                  add_punctuation = '*'
         
     | 
| 
       7 
     | 
    
         
            -
                  tree_formatter =  
     | 
| 
      
 8 
     | 
    
         
            +
                  # output = "<ul class='ui list'>"
         
     | 
| 
      
 9 
     | 
    
         
            +
                  output = tag.ul(class: 'ui list labels-tree')[0..-6]
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #add_punctuation = '*'
         
     | 
| 
      
 11 
     | 
    
         
            +
                  tree_formatter = proc do |items, lead_space|
         
     | 
| 
       8 
12 
     | 
    
         
             
                    items.each do |item|
         
     | 
| 
       9 
     | 
    
         
            -
                       
     | 
| 
       10 
     | 
    
         
            -
                       
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 13 
     | 
    
         
            +
                      label_name = link_to(item[:name], label_path(item[:id]))
         
     | 
| 
      
 14 
     | 
    
         
            +
                      edit_link  = table_edit_link(item, geo_labels.edit_label_path(item[:id]))
         
     | 
| 
      
 15 
     | 
    
         
            +
                      add_child_link = link_to(tag.i(class: 'plus icon'), geo_labels.new_label_path(parent_id: item[:id]))
         
     | 
| 
      
 16 
     | 
    
         
            +
                      output += "#{lead_space}#{tag.li(label_name + add_child_link + edit_link)}\n".html_safe
         
     | 
| 
      
 17 
     | 
    
         
            +
                      next unless item[:children].present?
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                      output += "#{lead_space}<ul>".html_safe
         
     | 
| 
      
 20 
     | 
    
         
            +
                      tree_formatter.call(item[:children], "#{lead_space}  ")
         
     | 
| 
      
 21 
     | 
    
         
            +
                      output += "#{lead_space}</ul>".html_safe
         
     | 
| 
       15 
22 
     | 
    
         
             
                    end
         
     | 
| 
       16 
23 
     | 
    
         
             
                  end
         
     | 
| 
       17 
24 
     | 
    
         
             
                  tree_formatter.call(GeoLabels::Exporter.labels_tree_h, '')
         
     | 
| 
       18 
     | 
    
         
            -
                  #output.gsub!(/^(\s*)  /, "\1#{add_punctuation} ") if add_punctuation.present?
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # output.gsub!(/^(\s*)  /, "\1#{add_punctuation} ") if add_punctuation.present?
         
     | 
| 
       19 
26 
     | 
    
         
             
                  output += '</ul>'.html_safe
         
     | 
| 
       20 
27 
     | 
    
         
             
                  output.html_safe
         
     | 
| 
       21 
28 
     | 
    
         
             
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       22 
30 
     | 
    
         
             
                # Return active or nil based on the given route spec
         
     | 
| 
       23 
31 
     | 
    
         
             
                #   %li{ class: active_class('users') # true if controller is users, false otherwise
         
     | 
| 
       24 
32 
     | 
    
         
             
                #   %li{ class: active_class('pages#about') # true if controller is pages and action is about
         
     | 
| 
       25 
     | 
    
         
            -
                #NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
      
 33 
     | 
    
         
            +
                # NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
       26 
34 
     | 
    
         
             
                def active_class(*route_specs)
         
     | 
| 
       27 
35 
     | 
    
         
             
                  options = route_specs.extract_options!
         
     | 
| 
       28 
     | 
    
         
            -
                  return nil if Array.wrap(options[:except]).any?{|exception| current_route_spec?(exception) }
         
     | 
| 
       29 
     | 
    
         
            -
                  return 'active' if route_specs.any?{|rs| current_route_spec?(rs, options) }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  return nil if Array.wrap(options[:except]).any?{ |exception| current_route_spec?(exception) }
         
     | 
| 
      
 37 
     | 
    
         
            +
                  return 'active' if route_specs.any?{ |rs| current_route_spec?(rs, options) }
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       30 
39 
     | 
    
         
             
                  nil
         
     | 
| 
       31 
40 
     | 
    
         
             
                end
         
     | 
| 
       32 
41 
     | 
    
         | 
| 
         @@ -36,19 +45,23 @@ module GeoLabels 
     | 
|
| 
       36 
45 
     | 
    
         
             
                #   current_route_spec?('products') #=> true if controller name is products, false otherwise
         
     | 
| 
       37 
46 
     | 
    
         
             
                #   current_route_spec?('products#show') #=> true if controller_name is products AND action_name is show
         
     | 
| 
       38 
47 
     | 
    
         
             
                #   current_route_spec?('#show') #=> true if action_name is show, false otherwise
         
     | 
| 
       39 
     | 
    
         
            -
                #NOTE: this helper is tested through the active_class helper
         
     | 
| 
       40 
     | 
    
         
            -
                #NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
       41 
     | 
    
         
            -
                def current_route_spec?(route_spec,  
     | 
| 
      
 48 
     | 
    
         
            +
                # NOTE: this helper is tested through the active_class helper
         
     | 
| 
      
 49 
     | 
    
         
            +
                # NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
      
 50 
     | 
    
         
            +
                def current_route_spec?(route_spec, _options = {})
         
     | 
| 
       42 
51 
     | 
    
         
             
                  return route_spec.match([controller_path, action_name].join('#')) if route_spec.is_a?(Regexp)
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
       43 
53 
     | 
    
         
             
                  controller, action = route_spec.split('#')
         
     | 
| 
       44 
54 
     | 
    
         
             
                  return action == params[:id] if controller_path == 'high_voltage/pages'
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
       45 
56 
     | 
    
         
             
                  actual_controller_parts = controller_path.split('/')
         
     | 
| 
       46 
     | 
    
         
            -
                  if controller #and controller_path == controller
         
     | 
| 
      
 57 
     | 
    
         
            +
                  if controller # and controller_path == controller
         
     | 
| 
       47 
58 
     | 
    
         
             
                    tested_controller_parts = controller.split('/')
         
     | 
| 
       48 
59 
     | 
    
         
             
                    return if tested_controller_parts.size > actual_controller_parts.size
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
       49 
61 
     | 
    
         
             
                    if actual_controller_parts[0...tested_controller_parts.size] == tested_controller_parts
         
     | 
| 
       50 
62 
     | 
    
         
             
                      # controller spec matches
         
     | 
| 
       51 
63 
     | 
    
         
             
                      return true unless action
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
       52 
65 
     | 
    
         
             
                      action_name == action
         
     | 
| 
       53 
66 
     | 
    
         
             
                    end
         
     | 
| 
       54 
67 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -56,7 +69,7 @@ module GeoLabels 
     | 
|
| 
       56 
69 
     | 
    
         
             
                  end
         
     | 
| 
       57 
70 
     | 
    
         
             
                end
         
     | 
| 
       58 
71 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
                #NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
      
 72 
     | 
    
         
            +
                # NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
       60 
73 
     | 
    
         
             
                def page_title(*args, &blk)
         
     | 
| 
       61 
74 
     | 
    
         
             
                  extra_content = capture(&blk) if blk.present?
         
     | 
| 
       62 
75 
     | 
    
         
             
                  if resource_title?(args)
         
     | 
| 
         @@ -80,16 +93,13 @@ module GeoLabels 
     | 
|
| 
       80 
93 
     | 
    
         
             
                  title = html_escape(args.first)
         
     | 
| 
       81 
94 
     | 
    
         
             
                  options = args.last.is_a?(Hash) ? args.last : {}
         
     | 
| 
       82 
95 
     | 
    
         
             
                  if back_options = options[:back]
         
     | 
| 
       83 
     | 
    
         
            -
                    url =
         
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                     
     | 
| 
       90 
     | 
    
         
            -
                      back_link = link_to content_tag(:i, nil, class: 'left arrow icon'), url, class: 'title-back-link'
         
     | 
| 
       91 
     | 
    
         
            -
                      title = back_link.safe_concat(title)
         
     | 
| 
       92 
     | 
    
         
            -
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
                    url = case back_options
         
     | 
| 
      
 97 
     | 
    
         
            +
                          when Array then polymorphic_path(back_options)
         
     | 
| 
      
 98 
     | 
    
         
            +
                          when true then :back
         
     | 
| 
      
 99 
     | 
    
         
            +
                          else back_options
         
     | 
| 
      
 100 
     | 
    
         
            +
                          end
         
     | 
| 
      
 101 
     | 
    
         
            +
                    back_link = link_to content_tag(:i, nil, class: 'left arrow icon'), url, class: 'title-back-link'
         
     | 
| 
      
 102 
     | 
    
         
            +
                    title = back_link.safe_concat(title)
         
     | 
| 
       93 
103 
     | 
    
         
             
                  end
         
     | 
| 
       94 
104 
     | 
    
         
             
                  title
         
     | 
| 
       95 
105 
     | 
    
         
             
                end
         
     | 
| 
         @@ -97,11 +107,11 @@ module GeoLabels 
     | 
|
| 
       97 
107 
     | 
    
         
             
                def page_title_for_resource(args)
         
     | 
| 
       98 
108 
     | 
    
         
             
                  options = args.extract_options!
         
     | 
| 
       99 
109 
     | 
    
         
             
                  model = args[1].respond_to?(:model_name) ? args[1] : args[1].class
         
     | 
| 
       100 
     | 
    
         
            -
                  if args.first == :index
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
      
 110 
     | 
    
         
            +
                  title = if args.first == :index
         
     | 
| 
      
 111 
     | 
    
         
            +
                            t('action.index.label', models: model.model_name.human_plural).html_safe
         
     | 
| 
      
 112 
     | 
    
         
            +
                          else
         
     | 
| 
      
 113 
     | 
    
         
            +
                            t("action.#{args.first}.label", model: model.model_name.human).html_safe
         
     | 
| 
      
 114 
     | 
    
         
            +
                          end
         
     | 
| 
       105 
115 
     | 
    
         
             
                  if back_options = options[:back]
         
     | 
| 
       106 
116 
     | 
    
         
             
                    url =
         
     | 
| 
       107 
117 
     | 
    
         
             
                      case back_options
         
     | 
| 
         @@ -124,7 +134,7 @@ module GeoLabels 
     | 
|
| 
       124 
134 
     | 
    
         
             
                  "#{from_item} - #{to_item} / #{records.total_count}"
         
     | 
| 
       125 
135 
     | 
    
         
             
                end
         
     | 
| 
       126 
136 
     | 
    
         | 
| 
       127 
     | 
    
         
            -
                def at(attribute_name, scope_model=nil)
         
     | 
| 
      
 137 
     | 
    
         
            +
                def at(attribute_name, scope_model = nil)
         
     | 
| 
       128 
138 
     | 
    
         
             
                  scope_model ||= @scope_model
         
     | 
| 
       129 
139 
     | 
    
         
             
                  scope_model.human_attribute_name(attribute_name)
         
     | 
| 
       130 
140 
     | 
    
         
             
                end
         
     | 
| 
         @@ -133,9 +143,9 @@ module GeoLabels 
     | 
|
| 
       133 
143 
     | 
    
         
             
                def search_row(options = {}, &blk)
         
     | 
| 
       134 
144 
     | 
    
         
             
                  classes = Array.wrap(options[:class])
         
     | 
| 
       135 
145 
     | 
    
         
             
                  classes |= ['search']
         
     | 
| 
       136 
     | 
    
         
            -
                  classes << 'conditions-present' if @q.conditions. 
     | 
| 
      
 146 
     | 
    
         
            +
                  classes << 'conditions-present' if @q.base.conditions.any?
         
     | 
| 
       137 
147 
     | 
    
         
             
                  content = capture(&blk)
         
     | 
| 
       138 
     | 
    
         
            -
                  content_tag(:tr, content, 
     | 
| 
      
 148 
     | 
    
         
            +
                  content_tag(:tr, content, class: classes)
         
     | 
| 
       139 
149 
     | 
    
         
             
                end
         
     | 
| 
       140 
150 
     | 
    
         | 
| 
       141 
151 
     | 
    
         
             
                # This helper returns the link for showing a record inside a table
         
     | 
| 
         @@ -145,7 +155,11 @@ module GeoLabels 
     | 
|
| 
       145 
155 
     | 
    
         
             
                  else
         
     | 
| 
       146 
156 
     | 
    
         
             
                    return unless can? :show, record
         
     | 
| 
       147 
157 
     | 
    
         
             
                  end
         
     | 
| 
       148 
     | 
    
         
            -
                  link_to( 
     | 
| 
      
 158 
     | 
    
         
            +
                  link_to(
         
     | 
| 
      
 159 
     | 
    
         
            +
                    content_tag(:i, nil, class: 'folder open icon'),
         
     | 
| 
      
 160 
     | 
    
         
            +
                    path || record,
         
     | 
| 
      
 161 
     | 
    
         
            +
                    class: 'table-link show ui mini basic primary icon button'
         
     | 
| 
      
 162 
     | 
    
         
            +
                  )
         
     | 
| 
       149 
163 
     | 
    
         
             
                end
         
     | 
| 
       150 
164 
     | 
    
         | 
| 
       151 
165 
     | 
    
         
             
                # This helper returns the link for showing a record inside a table
         
     | 
| 
         @@ -155,7 +169,11 @@ module GeoLabels 
     | 
|
| 
       155 
169 
     | 
    
         
             
                  else
         
     | 
| 
       156 
170 
     | 
    
         
             
                    return unless can? :download, record
         
     | 
| 
       157 
171 
     | 
    
         
             
                  end
         
     | 
| 
       158 
     | 
    
         
            -
                  link_to( 
     | 
| 
      
 172 
     | 
    
         
            +
                  link_to(
         
     | 
| 
      
 173 
     | 
    
         
            +
                    content_tag(:i, nil, class: 'download icon'),
         
     | 
| 
      
 174 
     | 
    
         
            +
                    path || [:download, record],
         
     | 
| 
      
 175 
     | 
    
         
            +
                    class: 'table-link download ui mini violet icon button'
         
     | 
| 
      
 176 
     | 
    
         
            +
                  )
         
     | 
| 
       159 
177 
     | 
    
         
             
                end
         
     | 
| 
       160 
178 
     | 
    
         | 
| 
       161 
179 
     | 
    
         
             
                # This helper returns the link for editing a record inside a table
         
     | 
| 
         @@ -180,16 +198,27 @@ module GeoLabels 
     | 
|
| 
       180 
198 
     | 
    
         
             
                  record_name ||= record.name if record.respond_to?(:name)
         
     | 
| 
       181 
199 
     | 
    
         
             
                  record_name ||= record.title if record.respond_to?(:title)
         
     | 
| 
       182 
200 
     | 
    
         
             
                  confirm_text << " #{record_name}" if record_name.present?
         
     | 
| 
       183 
     | 
    
         
            -
                  confirm_text <<  
     | 
| 
       184 
     | 
    
         
            -
                  link_to( 
     | 
| 
      
 201 
     | 
    
         
            +
                  confirm_text << '?'
         
     | 
| 
      
 202 
     | 
    
         
            +
                  link_to(
         
     | 
| 
      
 203 
     | 
    
         
            +
                    content_tag(:i, nil, class: 'trash icon'),
         
     | 
| 
      
 204 
     | 
    
         
            +
                    path || record,
         
     | 
| 
      
 205 
     | 
    
         
            +
                    method: :delete,
         
     | 
| 
      
 206 
     | 
    
         
            +
                    data: { confirm: confirm_text },
         
     | 
| 
      
 207 
     | 
    
         
            +
                    class: 'table-link destroy ui mini negative icon button'
         
     | 
| 
      
 208 
     | 
    
         
            +
                  )
         
     | 
| 
      
 209 
     | 
    
         
            +
                end
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                def address_tag(address, title: nil)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  tag.span(' '.html_safe * 10) +
         
     | 
| 
      
 213 
     | 
    
         
            +
                    tag.div(address, class: 'ui basic tag label', title: title)
         
     | 
| 
       185 
214 
     | 
    
         
             
                end
         
     | 
| 
       186 
215 
     | 
    
         | 
| 
       187 
216 
     | 
    
         
             
                # https://coderwall.com/p/7gqmog/display-flash-messages-with-semantic-ui-in-rails
         
     | 
| 
       188 
217 
     | 
    
         
             
                def flash_class(level)
         
     | 
| 
       189 
218 
     | 
    
         
             
                  case level.to_sym
         
     | 
| 
       190 
     | 
    
         
            -
                  when :success       then  
     | 
| 
       191 
     | 
    
         
            -
                  when :error, :alert then  
     | 
| 
       192 
     | 
    
         
            -
                  when :notice        then  
     | 
| 
      
 219 
     | 
    
         
            +
                  when :success       then 'ui positive message'
         
     | 
| 
      
 220 
     | 
    
         
            +
                  when :error, :alert then 'ui negative message'
         
     | 
| 
      
 221 
     | 
    
         
            +
                  when :notice        then 'ui info message'
         
     | 
| 
       193 
222 
     | 
    
         
             
                  else "ui #{level} message"
         
     | 
| 
       194 
223 
     | 
    
         
             
                  end
         
     | 
| 
       195 
224 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 3 
     | 
    
         
            +
              module RatingHelper
         
     | 
| 
      
 4 
     | 
    
         
            +
                def show_rating_for(record, helper_topic)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  tag.div class: 'rating-container item', data: {
         
     | 
| 
      
 6 
     | 
    
         
            +
                    topic: helper_topic,
         
     | 
| 
      
 7 
     | 
    
         
            +
                    record: record.class.name,
         
     | 
| 
      
 8 
     | 
    
         
            +
                    record_id: record.id,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    rating: record.rating[helper_topic].value
         
     | 
| 
      
 10 
     | 
    
         
            +
                  } do
         
     | 
| 
      
 11 
     | 
    
         
            +
                    tag.span 'Hi theree'
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,102 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'active_support/concern'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Represent the rating of one topic as an object
         
     | 
| 
      
 6 
     | 
    
         
            +
              class RatingObject
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :record, :topic
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(record, topic)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @record, @topic = record, topic
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def value
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # expect active_record column attribute
         
     | 
| 
      
 16 
     | 
    
         
            +
                  record.public_send("#{topic}_rating")
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def value=(value)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  record.public_send("#{topic}_rating=", value)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              # Represent the possible rating objects for the topics as a list
         
     | 
| 
      
 25 
     | 
    
         
            +
              class RatingList
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                class OutOfBoundIndex < StandardError
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 31 
     | 
    
         
            +
                attr_reader :record
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def initialize(record)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @record = record
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def each(&block)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  if block_given?
         
     | 
| 
      
 39 
     | 
    
         
            +
                    object_list.each(&block)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  else
         
     | 
| 
      
 41 
     | 
    
         
            +
                    to_enum(:each)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def object_list
         
     | 
| 
      
 46 
     | 
    
         
            +
                  record.class.rating_topics.map { |topic| RatingObject.new(record, topic) }
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def [](index)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  case index
         
     | 
| 
      
 51 
     | 
    
         
            +
                  when String then RatingObject.new(record, index.to_sym)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  when Symbol then RatingObject.new(record, index)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  when Integer
         
     | 
| 
      
 54 
     | 
    
         
            +
                    topic = record.class.rating_topics[index]
         
     | 
| 
      
 55 
     | 
    
         
            +
                    raise OutOfBoundIndex, 'Rating list index provided is out of bounds' unless topic
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    RatingObject.new(record, topic)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  else
         
     | 
| 
      
 59 
     | 
    
         
            +
                    raise OutOfBoundIndex, 'Rating list index provided is out of bounds'
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                # allow short hand assign
         
     | 
| 
      
 64 
     | 
    
         
            +
                #   record.rating[:food] = 3
         
     | 
| 
      
 65 
     | 
    
         
            +
                def []=(topic, value)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  self[topic].value = value
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
              # Extend an ActiveRecord model with rating support
         
     | 
| 
      
 71 
     | 
    
         
            +
              module RatingsSupport
         
     | 
| 
      
 72 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                included do
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @rating_topics ||= {}
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def rating(topic = nil)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  if topic.present?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    RatingObject.new(self, topic)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  else
         
     | 
| 
      
 82 
     | 
    
         
            +
                    RatingList.new(self)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                # nodoc
         
     | 
| 
      
 87 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 88 
     | 
    
         
            +
                  def has_rating_with_topic(topic, topic_options = {})
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @rating_topics[topic] = topic_options
         
     | 
| 
      
 90 
     | 
    
         
            +
                    # TODO: check existance of integer field of "topic_#{rating}, default: 0"
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  def has_rating_with_topic?(topic)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    @rating_topics.has_key?(topic)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  def rating_topics
         
     | 
| 
      
 98 
     | 
    
         
            +
                    @rating_topics.keys
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,5 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module GeoLabels
         
     | 
| 
       2 
4 
     | 
    
         
             
              class ApplicationRecord < ActiveRecord::Base
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       3 
6 
     | 
    
         
             
                self.abstract_class = true
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                RANSACKABLE_ATTRIBUTES = [].freeze
         
     | 
| 
      
 9 
     | 
    
         
            +
                RANSACKABLE_ASSOCIATIONS = [].freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def self.ransackable_attributes(_auth_object = nil)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  if const_defined? :RANSACKABLE_ATTRIBUTES
         
     | 
| 
      
 13 
     | 
    
         
            +
                    const_get :RANSACKABLE_ATTRIBUTES
         
     | 
| 
      
 14 
     | 
    
         
            +
                  else
         
     | 
| 
      
 15 
     | 
    
         
            +
                    RANSACKABLE_ATTRIBUTES
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def self.ransackable_associations(_auth_object = nil)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  if const_defined? :RANSACKABLE_ASSOCIATIONS
         
     | 
| 
      
 21 
     | 
    
         
            +
                    const_get :RANSACKABLE_ASSOCIATIONS
         
     | 
| 
      
 22 
     | 
    
         
            +
                  else
         
     | 
| 
      
 23 
     | 
    
         
            +
                    RANSACKABLE_ASSOCIATIONS
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
       4 
26 
     | 
    
         
             
              end
         
     | 
| 
       5 
27 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,19 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module GeoLabels
         
     | 
| 
       2 
4 
     | 
    
         
             
              class Contact < ApplicationRecord
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       3 
6 
     | 
    
         
             
                extend Geocoder::Model::ActiveRecord
         
     | 
| 
      
 7 
     | 
    
         
            +
                include RatingsSupport
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                RANSACKABLE_ATTRIBUTES = %w[
         
     | 
| 
      
 10 
     | 
    
         
            +
                  city country created_at department description
         
     | 
| 
      
 11 
     | 
    
         
            +
                  id latitude longitude name state street
         
     | 
| 
      
 12 
     | 
    
         
            +
                  subsection updated_at
         
     | 
| 
      
 13 
     | 
    
         
            +
                ].freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
                RANSACKABLE_ASSOCIATIONS = %w[contact_labels labels].freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                STATE_OPTIONS = %w[approved recommended rejected].freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       4 
18 
     | 
    
         
             
                has_many :contact_labels, dependent: :delete_all
         
     | 
| 
       5 
19 
     | 
    
         
             
                has_many :labels, through: :contact_labels
         
     | 
| 
       6 
20 
     | 
    
         | 
| 
       7 
21 
     | 
    
         
             
                geocoded_by :address
         
     | 
| 
       8 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
                has_rating_with_topic :food
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                validates :name, presence: true
         
     | 
| 
      
 26 
     | 
    
         
            +
                validates :state, inclusion: {in: STATE_OPTIONS}
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       9 
28 
     | 
    
         
             
                after_validation :geocode_if_necessary
         
     | 
| 
       10 
29 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                def self.for_label_ids(label_ids, predication: 'and')
         
     | 
| 
       12 
     | 
    
         
            -
                  #predication = %w[and or].include?(predication) ? predication : 'and' #whitlelisting
         
     | 
| 
      
 30 
     | 
    
         
            +
                def self.for_label_ids(label_ids, predication: 'and', states: ['approved'])
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # predication = %w[and or].include?(predication) ? predication : 'and' #whitlelisting
         
     | 
| 
       13 
32 
     | 
    
         
             
                  if predication == 'or'
         
     | 
| 
       14 
     | 
    
         
            -
                     
     | 
| 
      
 33 
     | 
    
         
            +
                    labels = Label.find(label_ids)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    labels_scope = labels.shift.self_and_descendants
         
     | 
| 
      
 35 
     | 
    
         
            +
                    labels.each do |label|
         
     | 
| 
      
 36 
     | 
    
         
            +
                      labels_scope = labels_scope.or(label.self_and_descendants)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    # joins(:labels).merge(Label.descendants_for_ids(label_ids)).distinct
         
     | 
| 
      
 39 
     | 
    
         
            +
                    joins(:labels).merge(labels_scope).where(state: states).reorder('').distinct
         
     | 
| 
       15 
40 
     | 
    
         
             
                  else
         
     | 
| 
       16 
     | 
    
         
            -
                    where 
     | 
| 
      
 41 
     | 
    
         
            +
                    where(id: ContactLabel.contact_ids_for_label_ids(label_ids), state: states)
         
     | 
| 
       17 
42 
     | 
    
         
             
                  end
         
     | 
| 
       18 
43 
     | 
    
         
             
                end
         
     | 
| 
       19 
44 
     | 
    
         | 
| 
         @@ -22,43 +47,60 @@ module GeoLabels 
     | 
|
| 
       22 
47 
     | 
    
         
             
                end
         
     | 
| 
       23 
48 
     | 
    
         | 
| 
       24 
49 
     | 
    
         
             
                def address
         
     | 
| 
       25 
     | 
    
         
            -
                  [street, city,  
     | 
| 
      
 50 
     | 
    
         
            +
                  [street, city, department, country].map(&:presence).compact.join(', ')
         
     | 
| 
       26 
51 
     | 
    
         
             
                end
         
     | 
| 
       27 
52 
     | 
    
         | 
| 
       28 
53 
     | 
    
         
             
                def address_changed?
         
     | 
| 
       29 
     | 
    
         
            -
                  street_changed? or city_changed? or  
     | 
| 
      
 54 
     | 
    
         
            +
                  street_changed? or city_changed? or department_changed? or country_changed?
         
     | 
| 
       30 
55 
     | 
    
         
             
                end
         
     | 
| 
       31 
56 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                 
     | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
      
 57 
     | 
    
         
            +
                STATE_OPTIONS.each do |option|
         
     | 
| 
      
 58 
     | 
    
         
            +
                  define_method "#{option}?" do
         
     | 
| 
      
 59 
     | 
    
         
            +
                    state == option
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
       34 
61 
     | 
    
         
             
                end
         
     | 
| 
       35 
62 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
                 
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
                #def full_name
         
     | 
| 
      
 64 
     | 
    
         
            +
                #  [first_name, middle_name, last_name].map(&:presence).compact.join(' ')
         
     | 
| 
      
 65 
     | 
    
         
            +
                #end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                #ransacker :full_name do |parent|
         
     | 
| 
      
 68 
     | 
    
         
            +
                #  Arel::Nodes::InfixOperation.new(
         
     | 
| 
      
 69 
     | 
    
         
            +
                #    '||',
         
     | 
| 
      
 70 
     | 
    
         
            +
                #    Arel::Nodes::InfixOperation.new('||', parent.table[:first_name], parent.table[:middle_name]),
         
     | 
| 
      
 71 
     | 
    
         
            +
                #    parent.table[:last_name]
         
     | 
| 
      
 72 
     | 
    
         
            +
                #  )
         
     | 
| 
      
 73 
     | 
    
         
            +
                #end
         
     | 
| 
       39 
74 
     | 
    
         | 
| 
       40 
75 
     | 
    
         
             
                def map_title
         
     | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
      
 76 
     | 
    
         
            +
                  name
         
     | 
| 
       42 
77 
     | 
    
         
             
                end
         
     | 
| 
       43 
78 
     | 
    
         | 
| 
       44 
79 
     | 
    
         
             
                def map_attributes
         
     | 
| 
       45 
80 
     | 
    
         
             
                  return {} unless geocoded?
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
       46 
82 
     | 
    
         
             
                  {
         
     | 
| 
       47 
     | 
    
         
            -
                    lat: latitude,
         
     | 
| 
       48 
     | 
    
         
            -
                    lng: longitude,
         
     | 
| 
      
 83 
     | 
    
         
            +
                    lat: latitude.try(:to_f),
         
     | 
| 
      
 84 
     | 
    
         
            +
                    lng: longitude.try(:to_f),
         
     | 
| 
       49 
85 
     | 
    
         
             
                    title: map_title
         
     | 
| 
       50 
86 
     | 
    
         
             
                  }
         
     | 
| 
       51 
87 
     | 
    
         
             
                end
         
     | 
| 
       52 
88 
     | 
    
         | 
| 
       53 
89 
     | 
    
         
             
                def geocode_if_necessary
         
     | 
| 
       54 
90 
     | 
    
         
             
                  return if new_record? and latitude.present? and longitude.present?
         
     | 
| 
       55 
     | 
    
         
            -
                   
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                   
     | 
| 
      
 91 
     | 
    
         
            +
                  return unless address_changed?
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  self.latitude = nil
         
     | 
| 
      
 94 
     | 
    
         
            +
                  self.longitude = nil
         
     | 
| 
      
 95 
     | 
    
         
            +
                  # Geocode addresses with at least 2 commas
         
     | 
| 
      
 96 
     | 
    
         
            +
                  geocode if address.count(',') > 1
         
     | 
| 
       61 
97 
     | 
    
         
             
                end
         
     | 
| 
       62 
98 
     | 
    
         | 
| 
      
 99 
     | 
    
         
            +
                def lat_lng=(value)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  case value
         
     | 
| 
      
 101 
     | 
    
         
            +
                  when String then self.latitude, self.longitude = value.split(/,\s?/)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  when Array then self.latitude, self.longitude = value
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
       63 
105 
     | 
    
         
             
              end
         
     | 
| 
       64 
106 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       1 
2 
     | 
    
         
             
            module GeoLabels
         
     | 
| 
       2 
3 
     | 
    
         
             
              class ContactLabel < ApplicationRecord
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
                belongs_to :contact
         
     | 
| 
       4 
6 
     | 
    
         
             
                belongs_to :label
         
     | 
| 
       5 
7 
     | 
    
         | 
| 
         @@ -9,32 +11,66 @@ module GeoLabels 
     | 
|
| 
       9 
11 
     | 
    
         
             
                # Sadly this stragegy fails when a contact has to labels assigned (Indonesian AND Vietnamese food), and
         
     | 
| 
       10 
12 
     | 
    
         
             
                # the query is for all food categories (parent).
         
     | 
| 
       11 
13 
     | 
    
         
             
                # Works for simple and concice situations. Not for multi label definitions
         
     | 
| 
       12 
     | 
    
         
            -
                def self. 
     | 
| 
      
 14 
     | 
    
         
            +
                def self.contact_ids_for_label_ids_old1(label_ids)
         
     | 
| 
       13 
15 
     | 
    
         
             
                  label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
         
     | 
| 
       14 
     | 
    
         
            -
                   
     | 
| 
       15 
     | 
    
         
            -
                  scope =  
     | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
      
 16 
     | 
    
         
            +
                  label_ids_with_descendants = Label.descendants_for_ids(label_ids)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # scope = joins(:label).merge(label_ids_with_descendants).select(:contact_id).group(:contact_id)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  joins(:label)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    .merge(Label.where(id: label_ids_with_descendants.pluck(:id)))
         
     | 
| 
      
 20 
     | 
    
         
            +
                    .select(:contact_id)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    .group(:contact_id)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    .having(arel_table[:contact_id].count.eq label_ids.size)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    .reorder('')
         
     | 
| 
       17 
24 
     | 
    
         
             
                end
         
     | 
| 
       18 
25 
     | 
    
         | 
| 
       19 
26 
     | 
    
         
             
                ### The strategy:
         
     | 
| 
       20 
27 
     | 
    
         
             
                # Create a subquery for each label collection possible situation
         
     | 
| 
       21 
28 
     | 
    
         
             
                # Then join all these subqueries on the contact_id to remain only
         
     | 
| 
       22 
29 
     | 
    
         
             
                # with the records having the contact_id in each of these subqueries
         
     | 
| 
       23 
     | 
    
         
            -
                def self. 
     | 
| 
      
 30 
     | 
    
         
            +
                def self.contact_ids_for_label_ids_old2(label_ids)
         
     | 
| 
       24 
31 
     | 
    
         
             
                  label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  return none unless label_ids.any?
         
     | 
| 
       25 
33 
     | 
    
         | 
| 
       26 
34 
     | 
    
         
             
                  records = Label.find(label_ids)
         
     | 
| 
       27 
35 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                  scope = joins(%|INNER JOIN "geo_labels_labels" ON "geo_labels_labels"."id" = "geo_labels_contact_labels"."label_id"|) 
     | 
| 
      
 36 
     | 
    
         
            +
                  scope = joins(%|INNER JOIN "geo_labels_labels" ON "geo_labels_labels"."id" = "geo_labels_contact_labels"."label_id"|)
         
     | 
| 
      
 37 
     | 
    
         
            +
                          .merge(records.shift.self_and_descendants)
         
     | 
| 
      
 38 
     | 
    
         
            +
                          .reorder('')
         
     | 
| 
      
 39 
     | 
    
         
            +
                          .select(:contact_id)
         
     | 
| 
       29 
40 
     | 
    
         
             
                  records.each do |record|
         
     | 
| 
       30 
41 
     | 
    
         
             
                    record_scope = joins(:label).merge(record.self_and_descendants).reorder('').select(:contact_id)
         
     | 
| 
       31 
42 
     | 
    
         
             
                    join_table_name = "labeltable#{record.id}"
         
     | 
| 
       32 
43 
     | 
    
         
             
                    join_table_contact_id = Arel::Nodes::SqlLiteral.new("#{join_table_name}.contact_id")
         
     | 
| 
       33 
44 
     | 
    
         
             
                    query_str = %|INNER JOIN (#{record_scope.to_sql}) #{join_table_name} ON #{arel_table[:contact_id].eq(join_table_contact_id).to_sql}|
         
     | 
| 
       34 
45 
     | 
    
         
             
                    scope = scope.joins(query_str)
         
     | 
| 
       35 
     | 
    
         
            -
                    #descendants_scope = descendants_scope.or(record.self_and_descendants)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    # descendants_scope = descendants_scope.or(record.self_and_descendants)
         
     | 
| 
       36 
47 
     | 
    
         
             
                  end
         
     | 
| 
       37 
48 
     | 
    
         
             
                  scope
         
     | 
| 
       38 
49 
     | 
    
         
             
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                ### The strategy predication AND:
         
     | 
| 
      
 52 
     | 
    
         
            +
                # Create a query for all labels with descendents getting the contact ids to ruby
         
     | 
| 
      
 53 
     | 
    
         
            +
                # Then find the overlapping ids [] & []
         
     | 
| 
      
 54 
     | 
    
         
            +
                # Then find the resulting contacts
         
     | 
| 
      
 55 
     | 
    
         
            +
                def self.contact_ids_for_label_ids(label_ids)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  return [] unless label_ids.any?
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  found_contact_ids = nil
         
     | 
| 
      
 60 
     | 
    
         
            +
                  Label.find(label_ids).each do |label|
         
     | 
| 
      
 61 
     | 
    
         
            +
                    label_contact_ids = ContactLabel.where(label_id: label.self_and_descendants.pluck(:id)).pluck(:contact_id)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    next found_contact_ids = label_contact_ids unless found_contact_ids
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    found_contact_ids = found_contact_ids.intersection(label_contact_ids)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    #if found_contact_ids
         
     | 
| 
      
 66 
     | 
    
         
            +
                    #  # merge the overlapping ids
         
     | 
| 
      
 67 
     | 
    
         
            +
                    #  found_contact_ids = found_contact_ids.intersection(label_contact_ids)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    #else
         
     | 
| 
      
 69 
     | 
    
         
            +
                    #  # initialize with found results
         
     | 
| 
      
 70 
     | 
    
         
            +
                    #  found_contact_ids = label_contact_ids
         
     | 
| 
      
 71 
     | 
    
         
            +
                    #end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
                  found_contact_ids
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
       39 
75 
     | 
    
         
             
              end
         
     | 
| 
       40 
76 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,8 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module GeoLabels
         
     | 
| 
       2 
4 
     | 
    
         
             
              class Label < ApplicationRecord
         
     | 
| 
      
 5 
     | 
    
         
            +
                RANSACKABLE_ATTRIBUTES = %w[name].freeze
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       3 
7 
     | 
    
         
             
                has_many :contact_labels, dependent: :delete_all
         
     | 
| 
      
 8 
     | 
    
         
            +
                has_many :contacts, through: :contact_labels
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       4 
10 
     | 
    
         
             
                acts_as_nested_set
         
     | 
| 
       5 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
                validates :name, presence: true
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
       6 
14 
     | 
    
         
             
                def self.descendants_for_ids(ids)
         
     | 
| 
       7 
15 
     | 
    
         
             
                  records = find(ids)
         
     | 
| 
       8 
16 
     | 
    
         
             
                  descendants_scope = records.shift.self_and_descendants
         
     | 
| 
         @@ -11,5 +19,10 @@ module GeoLabels 
     | 
|
| 
       11 
19 
     | 
    
         
             
                  end
         
     | 
| 
       12 
20 
     | 
    
         
             
                  descendants_scope
         
     | 
| 
       13 
21 
     | 
    
         
             
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def associated_contacts
         
     | 
| 
      
 24 
     | 
    
         
            +
                  ids = ContactLabel.joins(:label).merge(self_and_descendants).pluck(:contact_id).uniq
         
     | 
| 
      
 25 
     | 
    
         
            +
                  Contact.order(:name).find(ids)
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
       14 
27 
     | 
    
         
             
              end
         
     | 
| 
       15 
28 
     | 
    
         
             
            end
         
     | 
| 
         @@ -3,10 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
              .ui.top.attached.segment
         
     | 
| 
       4 
4 
     | 
    
         
             
                .field
         
     | 
| 
       5 
5 
     | 
    
         
             
                  label= t 'form.name'
         
     | 
| 
       6 
     | 
    
         
            -
                  . 
     | 
| 
       7 
     | 
    
         
            -
                    .field= f.text_field :first_name, placeholder: at(:first_name)
         
     | 
| 
       8 
     | 
    
         
            -
                    .field= f.text_field :middle_name, placeholder: at(:middle_name)
         
     | 
| 
       9 
     | 
    
         
            -
                    .field= f.text_field :last_name, placeholder: at(:last_name)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  .field= f.text_field :name, placeholder: at(:name)
         
     | 
| 
       10 
7 
     | 
    
         
             
                .field
         
     | 
| 
       11 
8 
     | 
    
         
             
                  label= t 'form.address'
         
     | 
| 
       12 
9 
     | 
    
         
             
                  .two.fields
         
     | 
| 
         @@ -17,8 +14,8 @@ 
     | 
|
| 
       17 
14 
     | 
    
         
             
                      label= at :city
         
     | 
| 
       18 
15 
     | 
    
         
             
                      = f.text_field :city, placeholder: at(:city)
         
     | 
| 
       19 
16 
     | 
    
         
             
                    .field
         
     | 
| 
       20 
     | 
    
         
            -
                      label= at : 
     | 
| 
       21 
     | 
    
         
            -
                      = f.text_field : 
     | 
| 
      
 17 
     | 
    
         
            +
                      label= at :department
         
     | 
| 
      
 18 
     | 
    
         
            +
                      = f.text_field :department, placeholder: at(:department)
         
     | 
| 
       22 
19 
     | 
    
         
             
                    .field
         
     | 
| 
       23 
20 
     | 
    
         
             
                      label= at :country
         
     | 
| 
       24 
21 
     | 
    
         
             
                      .ui.fluid.search.selection.dropdown
         
     | 
| 
         @@ -30,6 +27,25 @@ 
     | 
|
| 
       30 
27 
     | 
    
         
             
                            .item data-value=country.iso_short_name
         
     | 
| 
       31 
28 
     | 
    
         
             
                              i.flag class=country.alpha2.downcase
         
     | 
| 
       32 
29 
     | 
    
         
             
                              = country.iso_short_name
         
     | 
| 
      
 30 
     | 
    
         
            +
                .field
         
     | 
| 
      
 31 
     | 
    
         
            +
                  label= at :state
         
     | 
| 
      
 32 
     | 
    
         
            +
                  .ui.fluid.selection.dropdown
         
     | 
| 
      
 33 
     | 
    
         
            +
                    = f.hidden_field :state
         
     | 
| 
      
 34 
     | 
    
         
            +
                    i.dropdown.icon
         
     | 
| 
      
 35 
     | 
    
         
            +
                    .default.text= at(:state)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    .menu
         
     | 
| 
      
 37 
     | 
    
         
            +
                      - GeoLabels::Contact::STATE_OPTIONS.each do |option|
         
     | 
| 
      
 38 
     | 
    
         
            +
                        .item data-value=option = option
         
     | 
| 
      
 39 
     | 
    
         
            +
                .field
         
     | 
| 
      
 40 
     | 
    
         
            +
                  label= at :description
         
     | 
| 
      
 41 
     | 
    
         
            +
                  = f.text_field :description
         
     | 
| 
      
 42 
     | 
    
         
            +
                .field
         
     | 
| 
      
 43 
     | 
    
         
            +
                  .ui.compact.accordion
         
     | 
| 
      
 44 
     | 
    
         
            +
                    .title
         
     | 
| 
      
 45 
     | 
    
         
            +
                      i.dropdown.icon
         
     | 
| 
      
 46 
     | 
    
         
            +
                      => t('geo_labels.manual_lat_lng.title')
         
     | 
| 
      
 47 
     | 
    
         
            +
                      i.info.icon data-html=t('geo_labels.manual_lat_lng.explanation', default: 'missing translation geo_labels.manual_lat_lng.explanation') data-variation='wide'
         
     | 
| 
      
 48 
     | 
    
         
            +
                    .content= f.text_field :lat_lng
         
     | 
| 
       33 
49 
     | 
    
         
             
              .ui.attached.segment
         
     | 
| 
       34 
50 
     | 
    
         
             
                .ui.fluid.multiple.search.selection.dropdown
         
     | 
| 
       35 
51 
     | 
    
         
             
                  = f.hidden_field :label_ids, value: @record.label_ids.join(',')
         
     |