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(',')
|