geo_labels 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +127 -6
  4. data/Rakefile +6 -5
  5. data/app/assets/javascripts/geo_labels/application.coffee +11 -2
  6. data/app/assets/javascripts/geo_labels/ratings.coffee +80 -0
  7. data/app/assets/stylesheets/geo_labels/application.sass +21 -0
  8. data/app/controllers/concerns/geo_labels/shared_template_and_instance_methods.rb +3 -0
  9. data/app/controllers/geo_labels/application_controller.rb +3 -0
  10. data/app/controllers/geo_labels/contacts_controller.rb +18 -0
  11. data/app/controllers/geo_labels/dashboard_controller.rb +14 -9
  12. data/app/controllers/geo_labels/labels_controller.rb +3 -1
  13. data/app/controllers/geo_labels/ratings_controller.rb +30 -0
  14. data/app/helpers/geo_labels/application_helper.rb +73 -44
  15. data/app/helpers/geo_labels/rating_helper.rb +15 -0
  16. data/app/jobs/geo_labels/application_job.rb +1 -0
  17. data/app/mailers/geo_labels/application_mailer.rb +4 -2
  18. data/app/models/concerns/geo_labels/ratings_support.rb +102 -0
  19. data/app/models/geo_labels/application_record.rb +22 -0
  20. data/app/models/geo_labels/contact.rb +62 -20
  21. data/app/models/geo_labels/contact_label.rb +43 -7
  22. data/app/models/geo_labels/label.rb +13 -0
  23. data/app/views/geo_labels/contacts/_form.html.slim +15 -6
  24. data/app/views/geo_labels/contacts/index.html.slim +19 -7
  25. data/app/views/geo_labels/contacts/show.html.slim +24 -2
  26. data/app/views/geo_labels/dashboard/main.html.slim +27 -5
  27. data/app/views/geo_labels/labels/index.html.slim +10 -6
  28. data/app/views/geo_labels/labels/show.html.slim +15 -1
  29. data/app/views/layouts/geo_labels/application.html.slim +2 -0
  30. data/config/environment.rb +2 -0
  31. data/config/initializers/human_plural.rb +8 -5
  32. data/config/locales/geo_labels.en.yml +1 -0
  33. data/config/locales/geo_labels.es.yml +9 -0
  34. data/config/routes.rb +12 -1
  35. data/db/migrate/20221019150722_create_geo_labels_contacts.rb +5 -3
  36. data/db/migrate/20221020180213_create_geo_labels_labels.rb +2 -0
  37. data/db/migrate/20221020195346_create_geo_labels_contact_labels.rb +4 -2
  38. data/db/migrate/20230602182522_geo_labels_contacts_complex_name_to_name.rb +9 -0
  39. data/db/migrate/20230726154822_add_state_to_geo_labels_contacts.rb +8 -0
  40. data/db/migrate/20230801145902_add_food_rating_to_geo_labels_contacts.rb +7 -0
  41. data/lib/geo_labels/engine.rb +17 -7
  42. data/lib/geo_labels/exporter.rb +24 -10
  43. data/lib/geo_labels/importer.rb +31 -27
  44. data/lib/geo_labels/version.rb +3 -1
  45. data/lib/geo_labels.rb +2 -0
  46. data/lib/tasks/geo_labels_tasks.rake +1 -0
  47. metadata +37 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bf986ec7c563cd8c2d4a80b0c47ba754995828ebdf137095504f0cd483cd31a
4
- data.tar.gz: b67e43e78fcfc7990d6e0517de6fe13e2911ff018544b33d5af3c24d90099578
3
+ metadata.gz: 31855a84c2be26b9082db844ad5a25b6a0dee36a79c9d9e19e1cbc9ebb5136c5
4
+ data.tar.gz: 0f7f8e4b520227a720051786c555ce85fc4360a7eea30d4a30dbcbc6c43022c0
5
5
  SHA512:
6
- metadata.gz: 44b8b346ca204d88afab5edeac9cd65e07490f49b8b394b873295df151182092573af0573ef290a2da67b31fa0be8d83cfcd78bb91da7adfe2fb1a70d4205250
7
- data.tar.gz: cc33f0f8be30c3baf48092763cb11b26d6b6d6429a4c944512e3b9a1d1e393a220230b8801c47eb942d089327eac5a5f657856448f237474664a2734bc6a465f
6
+ metadata.gz: 8039e382b800fb007608a7ee76067244d323494c20e5b7cc5c9f428ab13bbf04cc77a93fd0d613bf0a9907d6d19d2bbfacfd9570770d2700876f3e56bcdb9b47
7
+ data.tar.gz: 689de4b0c2a1540ed8f63f16744f975cf0a9e563449f5d43493c9664b3648ffe5626b48af5c121e662e31790387b51b2dc209a73911c41fd92b2aef278c16bc8
data/CHANGELOG.md CHANGED
@@ -1,6 +1,26 @@
1
1
  CHANGELOG of geo\_labels
2
2
  ============================
3
3
 
4
+ 2023-09-04 v0.3.1
5
+ -------------------------
6
+ * Better navigation connections
7
+ * Fix the contact show map feature
8
+ * Show label's connected contacts
9
+
10
+ 2023-09-04 v0.3.0
11
+ -------------------------
12
+ * Better README
13
+ * Fuzzy search for all dropdowns
14
+ * Change complex name to just name for contacts
15
+ * Make main page contacts list first, map optional
16
+ * Better label management GUI
17
+ * Spanish translation for models
18
+ * Comply with some rubocop suggestions
19
+
20
+ 2023-02-14 v0.2.1
21
+ -------------------------
22
+ * Default to ENV variable for google api key
23
+
4
24
  2023-02-10 v0.2.0
5
25
  -------------------------
6
26
  * Add importer link to menu
data/README.md CHANGED
@@ -1,10 +1,21 @@
1
1
  # GeoLabels
2
- Short description and motivation.
2
+ GeoLabels is a `rails` `engine` that allows you to create entries that will be resolved to
3
+ their geographic location so that they can be shown on a map.
3
4
 
4
- ## Usage
5
- How to use my plugin.
5
+ The idea is to also create a hierarchical structure of labels and assign the lowest level applicable
6
+ label to the contact entry.
7
+
8
+ Now interesting queries can be made based on this organization. Examples:
9
+
10
+ * Give me the good coffee places in the state of Texas
11
+ * Give me the Vietnamese food places in Miami
12
+
13
+ The geo-contacts and their labels can be managed in the engine's GUI, but also using a text format. This can be a useful,
14
+ but also dangarous feature.
6
15
 
7
16
  ## Installation
17
+
18
+ ### Add the engine to your `rails` app
8
19
  Add this line to your application's Gemfile:
9
20
 
10
21
  ```ruby
@@ -14,15 +25,125 @@ gem "geo_labels"
14
25
  And then execute:
15
26
  ```bash
16
27
  $ bundle
28
+ $ bundle exec rails db:migrate
29
+ ```
30
+
31
+ ### Mount the engine to your routes
32
+ In your `config/routes.rb` file add:
33
+ ```ruby
34
+ mount GeoLabels::Engine => '/geo-labels'
17
35
  ```
18
36
 
19
- Or install it yourself as:
37
+ ### Add the authorizations
38
+ In your `app/models/ability.rb` file add the authorizations.
39
+ This is a custom operation that you have to adjust to your needs.
40
+ To allow all users full controll to the contracts add:
41
+ ```ruby
42
+ can :manage, GeoLabels::Contact
43
+ can :manage, GeoLabels::ContactLabel
44
+ ```
45
+
46
+ If the `Ability` file does not yet exist, generate it using:
20
47
  ```bash
21
- $ gem install geo_labels
48
+ rails generate cancan:ability
49
+ ```
50
+
51
+ ### Other languages (i18n)
52
+ To use for a lets say Spanish based website add [rails-i18n](https://github.com/svenfuchs/rails-i18n) to your `Gemfile`
53
+
54
+ ```ruby
55
+ gem 'rails-i18n'
22
56
  ```
23
57
 
58
+ And configure your application in `config/application.rb` to handle the languages:
59
+
60
+ ```ruby
61
+ config.i18n.available_locales = %i[en es]
62
+ config.i18n.default_locale = :en
63
+ ```
64
+
65
+ ## Customization
66
+
67
+ ### The main page
68
+ The main page of this engine is the query page where the created structure can be queried.
69
+
70
+ ### The link home content
71
+ The default value for `config/application.rb` is:
72
+
73
+ ```ruby
74
+ config.x.geo_labels.link_home_content = -> { '<i class="arrow left icon"></i> Back' }
75
+ ```
76
+ To change for example the icon, see the options at the [fomantic-ui](https://fomantic-ui.com/elements/icon.html) site.
77
+ Note that the value is a `lambda` to allow the use of for example `I18n`.
78
+
24
79
  ## Contributing
25
- Contribution directions go here.
80
+ There are many ways to contribute. Here some example steps that should work.
81
+
82
+ ### 1. Fork the repository
83
+ Go to the original repository at https://gitlab.com/benja-2/geo\_labels and [fork](https://gitlab.com/benja-2/geo_labels/-/forks/new) the project.
84
+ Then `git clone` your code on your local computer.
85
+
86
+ If you are in the git repository directory you can tell your system to use the local code when actually the
87
+ gitlab repository is specified for faster debugging. To achieve this type:
88
+
89
+ ```bash
90
+ bundle config local.geo_labels .
91
+ ```
92
+
93
+ ### 2. Add your forked codebase to a project
94
+ To start from zero, create a new rails (> 7) project and add the `geo_labels` gem configured to use `gitlab` as a base:
95
+
96
+ ```bash
97
+ rails new my_geo_labels_project
98
+ cd my_geo_labels_project
99
+ ```
100
+
101
+ Then in the `Gemfile`
102
+ ```ruby
103
+ git_source(:gitlab) do |repo_name|
104
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
105
+ "git@gitlab.com:#{repo_name}.git"
106
+ end
107
+
108
+ gem 'geo_labels', gitlab: '<your gitlab name>/geo_labels', branch: :master
109
+ ```
110
+
111
+ ## TESTING
112
+ ### Testing against other databases
113
+ Since there are some more advanced queries in this gem and was discovered that `mysql` does not support
114
+ the `IN (SELECT ...)` syntax, support for testing against multiple databases was added.
115
+
116
+ The default test (`rspec spec`) database is `sqlite`.
117
+
118
+ #### Test against a mysql database
119
+ To setup the mysql environment type (optional new window after the `docker-compose` command):
120
+ ```bash
121
+ docker-compose test_mysql up
122
+ DATABASE_URL=mysql2://root:password@127.0.0.1:33062/test rails db:create
123
+ DATABASE_URL=mysql2://root:password@127.0.0.1:33062/test rails db:migrate
124
+ ```
125
+
126
+ then run the specs against the mysql database:
127
+ ```bash
128
+ DATABASE_URL=mysql2://root:password@127.0.0.1:33062/test rspec spec
129
+ ```
130
+
131
+ #### Test against postgresql database
132
+ To setup the postgresql environment type (optional new window after the `docker-compose` command):
133
+ ```bash
134
+ docker-compose test_postgresql up
135
+ DATABASE_URL=postgres://pguser:pgpassword@127.0.0.1:54321/test rails db:create
136
+ DATABASE_URL=postgres://pguser:pgpassword@127.0.0.1:54321/test rails db:migrate
137
+ ```
138
+
139
+ then run the specs against the mysql database:
140
+ ```bash
141
+ DATABASE_URL=postgres://pguser:pgpassword@127.0.0.1:54321/test rspec spec
142
+ ```
143
+
144
+ ## CHANGELOG
145
+ The CHANGELOG can be found using
146
+ [CHANGELOG.md](/CHANGELOG.md)
26
147
 
27
148
  ## License
28
149
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,8 +1,9 @@
1
- require "bundler/setup"
1
+ # frozen_string_literal: true
2
+ require 'bundler/setup'
2
3
 
3
- APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
- load "rails/tasks/engine.rake"
4
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
5
+ load 'rails/tasks/engine.rake'
5
6
 
6
- load "rails/tasks/statistics.rake"
7
+ load 'rails/tasks/statistics.rake'
7
8
 
8
- require "bundler/gem_tasks"
9
+ require 'bundler/gem_tasks'
@@ -1,6 +1,7 @@
1
1
  #= require jquery
2
2
  #= require jquery_ujs
3
3
  #= require semantic-ui
4
+ #= require ./ratings
4
5
 
5
6
  window.initMap = ->
6
7
  if map_canvas = document.getElementById('map-canvas')
@@ -15,10 +16,18 @@ window.initMap = ->
15
16
  title: map_point.title
16
17
  false
17
18
  $ ->
18
- $('.ui.dropdown').dropdown() # assume fomantic that has the clearable class option, a lot better than the below custom code
19
+ $('.ui.dropdown').dropdown
20
+ fullTextSearch: true
21
+
22
+ $('.tabular.menu .item').tab
23
+ onFirstLoad: (name) ->
24
+ initMap() if name is 'map'
25
+ true
26
+ $('.accordion').accordion()
27
+
19
28
  $('.message .close').on 'click', ->
20
29
  $(@).closest('.message').transition('fade')
21
- $('.accordion').accordion()
30
+
22
31
  $('.and-or-switch-buttons .button').on 'click', ->
23
32
  #debugger
24
33
  container = @parentNode
@@ -0,0 +1,80 @@
1
+ # Supply a global initialization access point to load for custom actions
2
+ window.divmod = (x, y) -> [Math.floor(x / y), x % y]
3
+ window.setupRatings = ->
4
+ # For now jQuery based, but setup for easy future change to non jQuery systems
5
+ setClassForRatingDivmod = (star, whole_stars, half_star) ->
6
+ star_number = Number(star.dataset.starNumber)
7
+ if star_number + 1 > whole_stars + half_star
8
+ star.classList.add 'outline'
9
+ else
10
+ star.classList.remove 'outline'
11
+ star.classList.toggle 'half', star_number is whole_stars and half_star
12
+ #star.classList.toggle 'half', half_star
13
+ #
14
+ getStarEventRating = (star, event) ->
15
+ rect = event.target.getBoundingClientRect()
16
+ leftHalf = event.clientX < rect.x + rect.width / 2
17
+ hoverRating = event.target.dataset.starNumber * 2
18
+ hoverRating += if leftHalf then 1 else 2
19
+ hoverRating
20
+
21
+ mousemove = (event) ->
22
+ [whole_stars, half_star] = divmod(getStarEventRating(event.target, event), 2)
23
+ Array.from(event.target.parentElement.childNodes).forEach (star) ->
24
+ setClassForRatingDivmod star, whole_stars, half_star
25
+ star.classList.remove 'yellow'
26
+ star.classList.add 'orange'
27
+
28
+ mouseleave = (event) ->
29
+ rating = Number(event.target.parentElement.dataset.rating)
30
+ [whole_stars, half_star] = divmod(rating, 2)
31
+ Array.from(event.target.parentElement.childNodes).forEach (star) ->
32
+ setClassForRatingDivmod star, whole_stars, half_star
33
+ star.classList.remove 'orange'
34
+ star.classList.remove 'green'
35
+ star.classList.add 'yellow'
36
+
37
+ mouseclick = (event) ->
38
+ clickValue = getStarEventRating(event.target, event)
39
+ [whole_stars, half_star] = divmod(clickValue, 2)
40
+ Array.from(event.target.parentElement.childNodes).forEach (star) ->
41
+ star.classList.remove 'yellow'
42
+ star.classList.remove 'orange'
43
+ cdata = event.target.parentNode.dataset
44
+ post_data =
45
+ record: cdata.record
46
+ record_id: cdata.recordId
47
+ topic: cdata.topic
48
+ value: clickValue
49
+ jQuery.post geo_labels.paths.set_rating, post_data, (response, status, jqXHR) ->
50
+ event.target.parentNode.dataset.rating = clickValue
51
+ Array.from(event.target.parentElement.childNodes).forEach (star) ->
52
+ star.classList.add 'green'
53
+ setTimeout ->
54
+ mouseleave(event)
55
+ , 1000
56
+ .fail (jqXHR) ->
57
+ # jqXHR.status is probably 422 or 403
58
+ alert "The rating cannot be updated"
59
+ mouseleave(event)
60
+
61
+
62
+ jQuery(document).ready ->
63
+ containers = document.getElementsByClassName 'rating-container'
64
+ for container in containers
65
+ container.innerHTML = ''
66
+ data = container.dataset
67
+ [whole_stars, half_star] = divmod(Number(data.rating), 2)
68
+ for star_index in [0...5]
69
+ icon = document.createElement 'i'
70
+ icon.className = 'yellow star icon'
71
+ #icon.classList.add 'half' if star_index is whole_stars and half_star
72
+ #if star_index > whole_stars
73
+ # icon.classList.add 'outline'
74
+ icon.dataset.starNumber = star_index
75
+ setClassForRatingDivmod icon, whole_stars, half_star
76
+ icon.onmousemove = (event) -> mousemove(event)
77
+ icon.onmouseleave = (event) -> mouseleave(event)
78
+ icon.addEventListener 'click', (event) -> mouseclick(event)
79
+ container.appendChild icon
80
+ false
@@ -6,5 +6,26 @@ main
6
6
  padding-top: 12px
7
7
  > .container .page-title
8
8
  padding-top: 12px
9
+ .page-title
10
+ .primary.button
11
+ float: right
12
+ .rating-container i.icon.hidden
13
+ display: none
14
+
15
+ .labels-tree
16
+ li
17
+ &:hover
18
+ background-color: #ddd
19
+ .icon
20
+ display: inline-block
21
+ .icon
22
+ float: right
23
+ margin-left: 10px
24
+ display: none
25
+ .table-link.edit
26
+ font-size: 0.5rem !important
27
+ @media (hover: none)
28
+ .labels-tree li .icon
29
+ display: inline-block
9
30
  #map-canvas
10
31
  height: 600px
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoLabels
2
4
  module SharedTemplateAndInstanceMethods
3
5
  extend ActiveSupport::Concern
@@ -11,6 +13,7 @@ module GeoLabels
11
13
  def record_class
12
14
  result = controller_path.classify.safe_constantize
13
15
  raise "Cannot determine record_class from controller_path: #{controller_path}" unless result
16
+
14
17
  result
15
18
  end
16
19
 
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module GeoLabels
2
3
  class ApplicationController < ::ApplicationController
4
+
3
5
  helper_method :query
4
6
 
5
7
  def main
8
+ # root landing action
6
9
  end
7
10
 
8
11
  private
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  module GeoLabels
2
3
  class ContactsController < ApplicationController
4
+
3
5
  include SharedTemplateAndInstanceMethods
4
6
  respond_to :html
5
7
 
@@ -52,5 +54,21 @@ module GeoLabels
52
54
  @record.destroy
53
55
  redirect_to record_class, status: :see_other
54
56
  end
57
+
58
+ # PUT /contacts/:id/reject
59
+ def reject
60
+ # redirect_to record_class, alert: "Only contacts of state recomm"
61
+ @record = record_class.find params[:id]
62
+ @record.update state: 'rejected'
63
+ redirect_to @record, notice: 'Rejected'
64
+ end
65
+
66
+ # PUT /contacts/:id/reject
67
+ def approve
68
+ # redirect_to record_class, alert: "Only contacts of state recomm"
69
+ @record = record_class.find params[:id]
70
+ @record.update state: 'approved'
71
+ redirect_to @record, notice: 'Approved'
72
+ end
55
73
  end
56
74
  end
@@ -1,26 +1,31 @@
1
+ # frozen_string_literal: true
1
2
  module GeoLabels
2
3
  class DashboardController < ApplicationController
4
+
3
5
  def main
4
- if params[:label_ids].present?
5
- label_ids = params[:label_ids]
6
- # fomantic-ui uses comma separated id values
7
- label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
8
- @contacts = Contact.geocoded.for_label_ids(label_ids, predication: params[:predication])
9
- end
6
+ return unless params[:label_ids].present?
7
+
8
+ label_ids = params[:label_ids]
9
+ # fomantic-ui uses comma separated id values
10
+ label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
11
+ states_array = (params[:states].presence || 'approved').split(',')
12
+ @contacts = Contact.geocoded.for_label_ids(label_ids, predication: params[:predication], states: states_array)
10
13
  end
11
14
 
12
15
  def export
16
+ # SHOW action for the view to call the export_file action
13
17
  end
14
18
 
15
19
  def export_file
16
20
  timestamp = Time.now.utc.iso8601.first(19).gsub(/-|:/, '')
17
21
  send_data GeoLabels::Exporter.export_str,
18
- filename: "GeoLabels-export-#{timestamp}.yml",
19
- type: :text,
20
- disposition: :attachment
22
+ filename: "GeoLabels-export-#{timestamp}.yml",
23
+ type: :text,
24
+ disposition: :attachment
21
25
  end
22
26
 
23
27
  def import
28
+ # SHOW action for the view to call the import_file action
24
29
  end
25
30
 
26
31
  def import_file
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  module GeoLabels
2
3
  class LabelsController < ApplicationController
4
+
3
5
  include SharedTemplateAndInstanceMethods
4
6
  respond_to :html
5
7
 
@@ -12,7 +14,7 @@ module GeoLabels
12
14
  end
13
15
 
14
16
  def new
15
- @record = record_class.new
17
+ @record = record_class.new(parent_id: params[:parent_id])
16
18
  authorize! :create, @record
17
19
  end
18
20
 
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Separate ratings controller for easyer future extraction of functionality
4
+ # posibly gem extraction if used sufficiently
5
+ module GeoLabels
6
+ class RatingsController < ApplicationController
7
+
8
+ # Still has no application
9
+ def get
10
+ render plain: 'TODO app/controllers/geo_labels/ratings_controller.rb'
11
+ end
12
+
13
+ # PUT /geo-labels/ratings/set?record=GeoLabels::Contact&record_id=3&topic=food&value=7
14
+ def set
15
+ model = params[:record].safe_constantize
16
+ return render plain: '', status: :unprocessable_entity unless model&.include?(GeoLabels::RatingsSupport) # 422
17
+ return render plain: '', status: :unprocessable_entity unless model.rating_topics.include? params[:topic].to_sym
18
+
19
+ record = model.find(params[:record_id])
20
+ #TODO: add more sophisticated safeguard
21
+ record.rating[params[:topic]] = params[:value]
22
+
23
+ if record.save
24
+ render plain: ''
25
+ else
26
+ render plain: '', status: :unprocessable_entity
27
+ end
28
+ end
29
+ end
30
+ end