geo_labels 0.2.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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(',')
|