geo_labels 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +15 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +28 -0
  5. data/Rakefile +8 -0
  6. data/app/assets/config/geo_labels_manifest.js +2 -0
  7. data/app/assets/javascripts/geo_labels/application.coffee +29 -0
  8. data/app/assets/stylesheets/geo_labels/application.sass +10 -0
  9. data/app/controllers/concerns/geo_labels/shared_template_and_instance_methods.rb +21 -0
  10. data/app/controllers/geo_labels/application_controller.rb +14 -0
  11. data/app/controllers/geo_labels/contacts_controller.rb +56 -0
  12. data/app/controllers/geo_labels/dashboard_controller.rb +33 -0
  13. data/app/controllers/geo_labels/labels_controller.rb +56 -0
  14. data/app/helpers/geo_labels/application_helper.rb +197 -0
  15. data/app/jobs/geo_labels/application_job.rb +4 -0
  16. data/app/mailers/geo_labels/application_mailer.rb +6 -0
  17. data/app/models/geo_labels/application_record.rb +5 -0
  18. data/app/models/geo_labels/contact.rb +64 -0
  19. data/app/models/geo_labels/contact_label.rb +40 -0
  20. data/app/models/geo_labels/label.rb +15 -0
  21. data/app/views/geo_labels/application/_button-destroy-record.html.slim +1 -0
  22. data/app/views/geo_labels/application/_button-edit-record.html.slim +1 -0
  23. data/app/views/geo_labels/application/_button-new-record.html.slim +1 -0
  24. data/app/views/geo_labels/application/_messages.html.slim +4 -0
  25. data/app/views/geo_labels/application/_navigation.html.slim +29 -0
  26. data/app/views/geo_labels/contacts/_form.html.slim +46 -0
  27. data/app/views/geo_labels/contacts/edit.html.slim +3 -0
  28. data/app/views/geo_labels/contacts/index.html.slim +31 -0
  29. data/app/views/geo_labels/contacts/new.html.slim +3 -0
  30. data/app/views/geo_labels/contacts/show.html.slim +20 -0
  31. data/app/views/geo_labels/dashboard/export.html.slim +4 -0
  32. data/app/views/geo_labels/dashboard/import.html.slim +7 -0
  33. data/app/views/geo_labels/dashboard/main.html.slim +35 -0
  34. data/app/views/geo_labels/labels/_form.html.slim +13 -0
  35. data/app/views/geo_labels/labels/edit.html.slim +3 -0
  36. data/app/views/geo_labels/labels/index.html.slim +47 -0
  37. data/app/views/geo_labels/labels/new.html.slim +3 -0
  38. data/app/views/geo_labels/labels/show.html.slim +12 -0
  39. data/app/views/layouts/geo_labels/application.html.slim +22 -0
  40. data/config/environment.rb +2 -0
  41. data/config/initializers/human_plural.rb +16 -0
  42. data/config/locales/geo_labels.en.yml +5 -0
  43. data/config/locales/geo_labels.es.yml +5 -0
  44. data/config/routes.rb +13 -0
  45. data/db/migrate/20221019150722_create_geo_labels_contacts.rb +20 -0
  46. data/db/migrate/20221020180213_create_geo_labels_labels.rb +16 -0
  47. data/db/migrate/20221020195346_create_geo_labels_contact_labels.rb +12 -0
  48. data/lib/geo_labels/engine.rb +36 -0
  49. data/lib/geo_labels/exporter.rb +48 -0
  50. data/lib/geo_labels/importer.rb +124 -0
  51. data/lib/geo_labels/version.rb +3 -0
  52. data/lib/geo_labels.rb +8 -0
  53. data/lib/tasks/geo_labels_tasks.rake +4 -0
  54. metadata +267 -0
@@ -0,0 +1,29 @@
1
+ .ui.inverted.menu.fixed
2
+ = link_to main_app.root_path, class: 'header item'
3
+ = Rails.application.config.x.geo_labels.link_home_content.call.to_s.html_safe
4
+ = link_to geo_labels.root_path, class: 'header item'
5
+ .ui.icon
6
+ i.map.icon
7
+ = Rails.application.config.x.geo_labels.application_title.call.to_s.html_safe
8
+ - if current_user.present?
9
+ - if can? :read, GeoLabels::Contact
10
+ = link_to GeoLabels::Contact.model_name.human_plural, [GeoLabels::Contact], class: ['item', active_class('geo_labels/contacts')]
11
+ - if can? :read, GeoLabels::Label
12
+ = link_to GeoLabels::Label.model_name.human_plural, [GeoLabels::Label], class: ['item', active_class('geo_labels/labels')]
13
+ .right.menu
14
+ - if current_user.present?
15
+ .ui.dropdown.item
16
+ span= current_user.respond_to?(:nickname) ? current_user.nickname : current_user.email
17
+ i.dropdown.icon
18
+ .menu
19
+ = link_to geo_labels.export_path, class: 'item'
20
+ i.file.export.icon
21
+ span.text= t('geo_labels.export')
22
+ = link_to geo_labels.import_path, class: 'item'
23
+ i.file.import.icon
24
+ span.text= t('geo_labels.import')
25
+ = link_to main_app.destroy_user_session_path, method: :delete, class: 'item'
26
+ i.sign.out.icon
27
+ span.text= t 'user.sign_out'
28
+ - else
29
+ = link_to t('devise.sessions.new.sign_in'), main_app.new_user_session_path, class: 'item'
@@ -0,0 +1,46 @@
1
+ /table.ui.attached.compact.definition.table
2
+ = form_with model: @record, class: 'ui form' do |f|
3
+ .ui.top.attached.segment
4
+ .field
5
+ label= t 'form.name'
6
+ .three.fields
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)
10
+ .field
11
+ label= t 'form.address'
12
+ .two.fields
13
+ .field= f.text_field :street, placeholder: at(:street)
14
+ .field= f.text_field :subsection, placeholder: at(:subsection)
15
+ .three.fields
16
+ .field
17
+ label= at :city
18
+ = f.text_field :city, placeholder: at(:city)
19
+ .field
20
+ label= at :state
21
+ = f.text_field :state, placeholder: at(:state)
22
+ .field
23
+ label= at :country
24
+ .ui.fluid.search.selection.dropdown
25
+ = f.hidden_field :country
26
+ i.dropdown.icon
27
+ .default.text= at(:country)
28
+ .menu
29
+ - ISO3166::Country.all.each do |country|
30
+ .item data-value=country.iso_short_name
31
+ i.flag class=country.alpha2.downcase
32
+ = country.iso_short_name
33
+ .ui.attached.segment
34
+ .ui.fluid.multiple.search.selection.dropdown
35
+ = f.hidden_field :label_ids, value: @record.label_ids.join(',')
36
+ /= hidden_field_tag 'label_ids', params[:label_ids]
37
+ i.dropdown.icon
38
+ .default.text= GeoLabels::Label.model_name.human_plural
39
+ .menu
40
+ - nested_set_options(GeoLabels::Label) {|i| "#{'-' * i.level} #{i.name}" }.each do |(label_name, label_id)|
41
+ .item data-value=label_id
42
+ = label_name
43
+ .ui.bottom.attached.segment
44
+ = f.submit class: 'ui primary button'
45
+ - unless @record.new_record?
46
+ = render 'button-destroy-record', record: @record
@@ -0,0 +1,3 @@
1
+ .ui.container
2
+ = page_title :edit, @record, back: @record
3
+ = render 'form'
@@ -0,0 +1,31 @@
1
+ .ui.container
2
+ = page_title :index, record_class
3
+ - if @records.any? or @q.conditions.present?
4
+ = search_form_for(@q, html: {class: 'ui form'}) do |f|
5
+ = hidden_field_tag :per_page, params[:per_page], id: 'q_per_page'
6
+ table.ui.compact.top.attached.striped.table
7
+ thead
8
+ tr
9
+ th= sort_link @q, :full_name, at(:full_name), default_order: :asc
10
+ th.column-filter.actions
11
+ = search_result_info @records
12
+ = search_row class: 'ui mini form' do
13
+ th= f.text_field :full_name_cont, placeholder: at(:full_name)
14
+ th.column-filter.actions
15
+ button.ui.mini.primary.icon.button
16
+ i.filter.icon
17
+ = link_to polymorphic_path(record_class, reset_q: true), class: 'clear-filters-button ui mini basic yellow icon button' do
18
+ i.close.icon
19
+ tbody
20
+ - @records.each do |record|
21
+ tr
22
+ td= link_to record.full_name, record
23
+ td.actions
24
+ = table_show_link record
25
+ = table_edit_link record
26
+ .ui.bottom.attached.segment
27
+ = render 'button-new-record'
28
+ = paginate @records
29
+ - else
30
+ p= t 'collection.empty', models: record_class.model_name.human_plural
31
+ = render 'button-new-record'
@@ -0,0 +1,3 @@
1
+ .ui.container
2
+ = page_title :new, record_class, back: [record_class]
3
+ = render 'form'
@@ -0,0 +1,20 @@
1
+ .ui.container
2
+ = page_title :show, @record, back: [record_class]
3
+ table.ui.top.attached.compact.definition.table
4
+ tbody
5
+ tr
6
+ td= t 'form.name'
7
+ td= @record.full_name
8
+ tr
9
+ td= GeoLabels::Label.model_name.human_plural
10
+ td= @record.labels.map(&:name).join(', ')
11
+ - if @record.geocoded?
12
+ tr
13
+ td Location
14
+ td
15
+ #map-canvas
16
+ javascript:
17
+ window.map_points = #{[@record.map_attributes].to_json.html_safe};
18
+ script src="https://maps.googleapis.com/maps/api/js?key=#{Rails.application.config.x.geo_labels.google_api_key}&callback=initMap&v=weekly" defer
19
+ .ui.bottom.attached.segment
20
+ = render 'button-edit-record', record: @record
@@ -0,0 +1,4 @@
1
+ .ui.grid.container
2
+ = page_title t('geo_labels.export')
3
+ .ui.segment
4
+ p= link_to t('geo_labels.export'), geo_labels.export_file_path, class: 'ui primary button'
@@ -0,0 +1,7 @@
1
+ /.ui.grid.container
2
+ = form_tag geo_labels.import_file_path, method: :post, multipart: true, class: 'ui grid container'
3
+ = page_title t('geo_labels.import')
4
+ .ui.top.attached.segment
5
+ = file_field_tag :file
6
+ .ui.bottom.attached.segment
7
+ = submit_tag t('geo_labels.import'), class: 'ui primary button'
@@ -0,0 +1,35 @@
1
+ .ui.segment
2
+ /= select_tag 'label_ids[]', options_for_select(nested_set_options(GeoLabels::Label) {|i| "#{'-' * i.level} #{i.name}" } )
3
+ form.ui.form method='GET'
4
+ .field
5
+ /label Blabla
6
+ .fields
7
+ .eleven.wide.field
8
+ .ui.fluid.multiple.search.selection.dropdown
9
+ = hidden_field_tag 'label_ids', params[:label_ids]
10
+ i.dropdown.icon
11
+ .default.text= GeoLabels::Label.model_name.human_plural
12
+ .menu
13
+ /- ISO3166::Country.all.each do |country|
14
+ - nested_set_options(GeoLabels::Label) {|i| "#{'-' * i.level} #{i.name}" }.each do |(label_name, label_id)|
15
+ .item data-value=label_id
16
+ = label_name
17
+ .four.wide.field
18
+ .ui.buttons.fluid.and-or-switch-buttons
19
+ = hidden_field_tag 'predication', params[:predication].presence || 'and'
20
+ /= text_field_tag 'predication', 'and'
21
+ .ui.button class=(params[:predication] == 'or' ? '' : 'secondary') data-predicate='and' AND
22
+ .or
23
+ .ui.button class=(params[:predication] == 'or' ? 'secondary' : '') data-predicate='or' OR
24
+ .one.wide.field
25
+ button.ui.blue.icon.button
26
+ i.search.icon
27
+
28
+
29
+ - if @contacts.present?
30
+ .ui.horizontal.divider= GeoLabels::Contact.model_name.human_plural
31
+ .ui.segment
32
+ #map-canvas
33
+ javascript:
34
+ window.map_points = #{@contacts.map(&:map_attributes).to_json.html_safe};
35
+ script src="https://maps.googleapis.com/maps/api/js?key=#{Rails.application.config.x.geo_labels.google_api_key}&callback=initMap&v=weekly" defer
@@ -0,0 +1,13 @@
1
+ /table.ui.attached.compact.definition.table
2
+ = form_with model: @record, class: 'ui form' do |f|
3
+ .ui.top.attached.segment
4
+ .field
5
+ label= at :name
6
+ = f.text_field :name, placeholder: at(:name)
7
+ .field
8
+ label= t 'form.parent'
9
+ = f.select :parent_id, nested_set_options(record_class, @record) {|i| "#{'-' * i.level} #{i.name}" }, {include_blank: true}, class: 'ui clearable selection search dropdown'
10
+ .ui.bottom.attached.segment
11
+ = f.submit class: 'ui primary button'
12
+ - unless @record.new_record?
13
+ = render 'button-destroy-record', record: @record
@@ -0,0 +1,3 @@
1
+ .ui.container
2
+ = page_title :edit, @record, back: @record
3
+ = render 'form'
@@ -0,0 +1,47 @@
1
+ .ui.container
2
+ = page_title :index, record_class
3
+ - if @records.any? or @q.conditions.present?
4
+ = search_form_for(@q, html: {class: 'ui form'}) do |f|
5
+ = hidden_field_tag :per_page, params[:per_page], id: 'q_per_page'
6
+ table.ui.compact.top.attached.striped.table
7
+ thead
8
+ tr
9
+ th= sort_link @q, :name, at(:name), default_order: :asc
10
+ th.column-filter.actions
11
+ = search_result_info @records
12
+ = search_row class: 'ui mini form' do
13
+ th= f.text_field :name_cont, placeholder: at(:name)
14
+ th.column-filter.actions
15
+ button.ui.mini.primary.icon.button
16
+ i.filter.icon
17
+ = link_to polymorphic_path(record_class, reset_q: true), class: 'clear-filters-button ui mini basic yellow icon button' do
18
+ i.close.icon
19
+ tbody
20
+ - @records.each do |record|
21
+ tr
22
+ td= link_to record.name, record
23
+ td.actions
24
+ = table_show_link record
25
+ = table_edit_link record
26
+ .ui.bottom.attached.segment
27
+ = render 'button-new-record'
28
+ = paginate @records
29
+ - else
30
+ p= t 'collection.empty', models: record_class.model_name.human_plural
31
+ = render 'button-new-record'
32
+
33
+ .ui.styled.accordion
34
+ .title.active
35
+ i.dropdown.icon
36
+ = t 'geo_labels.tree_title'
37
+ .content.active= link_to_labels_tree
38
+ /
39
+ .ui.dividing.header= t 'geo_labels.tree_title'
40
+ .ui.segment
41
+ - record_class.nested_set_scope.each do |record|
42
+ .record-in-tree= link_to "#{'  ' * record.level + '-'} #{record.name}".html_safe, record
43
+ - nested_set_options(record_class){|i| "#{'     ' * i.level + '-'} #{i.name}".html_safe }.each do |option|
44
+ .record-in-tree= option.first
45
+ .ui.segment= link_to_labels_tree
46
+
47
+
@@ -0,0 +1,3 @@
1
+ .ui.container
2
+ = page_title :new, record_class, back: [record_class]
3
+ = render 'form'
@@ -0,0 +1,12 @@
1
+ .ui.container
2
+ = page_title :show, @record, back: [record_class]
3
+ table.ui.top.attached.compact.definition.table
4
+ tbody
5
+ tr
6
+ td= at :name
7
+ td= @record.name
8
+ tr
9
+ td Parents
10
+ td= @record.ancestors.map(&:name).join(' -> ')
11
+ .ui.bottom.attached.segment
12
+ = render 'button-edit-record', record: @record
@@ -0,0 +1,22 @@
1
+ doctype html
2
+ html lang="en"
3
+ head
4
+ title= content_for?(:title) ? yield(:title) : 'GeoLabels Engine'
5
+ meta name="description" content="#{content_for?(:description) ? yield(:description) : 'Application'}"
6
+ = csrf_meta_tags
7
+ = csp_meta_tag
8
+
9
+ meta charset="utf-8"
10
+ meta content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" name="viewport"
11
+
12
+ = stylesheet_link_tag 'geo_labels/application', media: 'all'
13
+ = javascript_include_tag 'geo_labels/application'
14
+ = content_for :head
15
+ body
16
+ header= render 'navigation'
17
+ span= Rails.env
18
+ main role="main"
19
+ = render 'messages'
20
+ = yield
21
+ hr
22
+ p= Rails.application.config.x.geo_labels.application_title.call.to_s.html_safe
@@ -0,0 +1,2 @@
1
+ ### NOTE:
2
+ # This is a dummy file to let the vim-rails vim plugin work with the rails engine
@@ -0,0 +1,16 @@
1
+ module ActiveModel
2
+ class Name
3
+ def human_plural
4
+ # Try to find the plural name of a model. If none can be found try to find a non namespaced version and return that one
5
+ default = Proc.new do |path|
6
+ if path.present? and path['/'] # non namespaced model a/b/c => a
7
+ unnamespaced_path = path.sub(/\.(\w+)\/[\w\\]+/, '.\1')
8
+ I18n.t(unnamespaced_path, default: Proc.new{ human.pluralize })
9
+ else
10
+ human.pluralize
11
+ end
12
+ end
13
+ I18n.t("#{@klass.i18n_scope}.models.plural.#{i18n_key}", default: default )
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ en:
2
+ geo_labels:
3
+ export: Export
4
+ import: Import
5
+ tree_title: Tree
@@ -0,0 +1,5 @@
1
+ es:
2
+ geo_labels:
3
+ export: Exportar
4
+ import: Importar
5
+ tree_title: Árbol
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ GeoLabels::Engine.routes.draw do
2
+ root 'dashboard#main'
3
+
4
+ controller :dashboard do
5
+ get :export
6
+ get :export_file
7
+ get :import
8
+ post :import_file
9
+ end
10
+
11
+ resources :contacts
12
+ resources :labels
13
+ end
@@ -0,0 +1,20 @@
1
+ class CreateGeoLabelsContacts < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :geo_labels_contacts do |t|
4
+ t.string :first_name
5
+ t.string :middle_name
6
+ t.string :last_name
7
+ t.string :street
8
+ t.string :subsection
9
+ t.string :city
10
+ t.string :state
11
+ t.string :country
12
+ t.text :description
13
+ t.float :latitude
14
+ t.float :longitude
15
+
16
+ t.timestamps
17
+ t.index [:latitude, :longitude]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ class CreateGeoLabelsLabels < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :geo_labels_labels do |t|
4
+ t.string :name
5
+ t.integer :parent_id, null: true, index: true
6
+ t.integer :lft, null: false, index: true
7
+ t.integer :rgt, null: false, index: true
8
+
9
+ # optional fields
10
+ t.integer :depth, null: false, default: 0
11
+ t.integer :children_count, null: false, default: 0
12
+
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ class CreateGeoLabelsContactLabels < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :geo_labels_contact_labels, id: false do |t|
4
+ #t.belongs_to :contact, null: false, foreign_key: true
5
+ #t.belongs_to :label, null: false, foreign_key: true
6
+ t.integer :contact_id, index: true
7
+ t.integer :label_id, index: true
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ require 'geocoder'
2
+ require 'slim-rails'
3
+ require 'coffee-rails'
4
+ require 'sassc-rails'
5
+ require 'jquery-rails'
6
+ require 'cancancan'
7
+ require 'ransack'
8
+ require 'kaminari'
9
+ require 'countries/global'
10
+ require 'awesome_nested_set'
11
+
12
+ module GeoLabels
13
+ class Engine < ::Rails::Engine
14
+ isolate_namespace GeoLabels
15
+
16
+ initializer "geo_labels.assets.precompile" do |main_app|
17
+ #app.config.assets.precompile << "config/engine_name_manifest.js"
18
+ main_app.config.assets.precompile << "geo_labels/application.css"
19
+ main_app.config.assets.precompile << "geo_labels/application.js"
20
+
21
+ # Engine specific config
22
+ main_app.config.x.geo_labels.application_title ||= -> { 'GEO-labels' }
23
+ main_app.config.x.geo_labels.link_home_content ||= -> { '<i class="arrow left icon"></i> Back' }
24
+ main_app.config.x.geo_labels.google_api_key ||= "GOOGLE_API_KEY"
25
+ end
26
+
27
+ # add migrations to containing application
28
+ initializer 'geo_labels.append_migrations' do |app|
29
+ unless app.root.to_s.match root.to_s
30
+ config.paths["db/migrate"].expanded.each do |expanded_path|
31
+ app.config.paths["db/migrate"] << expanded_path
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+ module GeoLabels
3
+ class Exporter
4
+ def self.export_h
5
+ obj = {
6
+ 'labels' => labels_tree,
7
+ 'contacts' => GeoLabels::Contact.includes(:labels).map{ |contact|
8
+ contact.attributes.merge('labels' => contact.labels.map(&:name)).select{ |attr, val|
9
+ val.present? and not %w[id created_at updated_at].include?(attr)
10
+ }
11
+ }
12
+ }
13
+ obj
14
+ end
15
+
16
+ def self.export_str
17
+ export_h.to_yaml
18
+ end
19
+
20
+ # Formats the tree structure from labels_tree_h to a string output
21
+ # can be called with add_punctuation: '-' to add dashes
22
+ def self.labels_tree(add_punctuation: nil)
23
+ output = ""
24
+ tree_formatter = Proc.new do |items, lead_space|
25
+ items.each do |item|
26
+ output += "#{lead_space}#{item[:name]}\n"
27
+ tree_formatter.call(item[:children], lead_space + ' ') if item[:children].present?
28
+ end
29
+ end
30
+ tree_formatter.call(labels_tree_h, '')
31
+ output.gsub!(/^(\s*) /, "\1#{add_punctuation} ") if add_punctuation.present?
32
+ output
33
+ end
34
+
35
+ # Returns an array having the tree structure as ruby objects
36
+ def self.labels_tree_h
37
+ labels = GeoLabels::Label.order(:name).to_a
38
+ tree = Hash.new { |h, k| h[k] = {name: nil, id: nil, children: []} }
39
+ labels.each do |label|
40
+ id, name, parent_id = label.attributes.values_at('id', 'name', 'parent_id')
41
+ tree[id][:name] = name
42
+ tree[id][:id] = id
43
+ tree[parent_id][:children].push tree[id]
44
+ end
45
+ tree[nil][:children]
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,124 @@
1
+ module GeoLabels
2
+ module Importer
3
+ def self.import(subject)
4
+ yaml_h = case subject
5
+ when String
6
+ if subject.start_with?('/') or subject.start_with?('./')
7
+ YAML.load_file(subject)
8
+ else
9
+ YAML.load(subject)
10
+ end
11
+ when File then YAML.load(subject.read)
12
+ when Pathname then YAML.load(subject.read)
13
+ when ActionDispatch::Http::UploadedFile then YAML.load(subject.read)
14
+ else
15
+ raise "Argument not recognized"
16
+ end
17
+ GeoLabels::Contact.delete_all
18
+ GeoLabels::Label.delete_all
19
+ GeoLabels::ContactLabel.delete_all
20
+ labels_dict = persist_labels_tree yaml_h['labels']
21
+ yaml_h['contacts'].each do |contact_attributes|
22
+ label_names = Array(contact_attributes.delete 'labels')
23
+ contact = GeoLabels::Contact.new(contact_attributes)
24
+ contact.labels = label_names.map{labels_dict[_1]}.compact
25
+ contact.save
26
+ end
27
+ GeoLabels::Contact.count
28
+ end
29
+
30
+ def self.labels_text_to_tree(tree_text)
31
+ txt = tree_text.gsub("\n\u0001", "\n") # remove START OF HEADING TRASH
32
+ txt.gsub! /^(\s*)[\-\*] /, '\1 ' # remove list punctuations and interpret just the spaces
33
+ lines = txt.split("\n")
34
+ root_label, current_indentation = ImportLabel.new('root'), -1
35
+ working_object = root_label
36
+ lines.each do |line|
37
+ line_indentation = (line.match(/\s+/).try(:[], 0).try(:size) || 0) / 2
38
+ #name = line[(2*line_indentation)..] # strip the leading spaces
39
+ name = line.strip
40
+ label = ImportLabel.new(name)
41
+ if line_indentation > current_indentation # level deeper
42
+ working_object = working_object.add_child(label)
43
+ elsif line_indentation < current_indentation
44
+ (current_indentation - line_indentation).times do
45
+ working_object = working_object.parent unless working_object.parent.is_root?
46
+ end
47
+ working_object = working_object.add_sibling(label)
48
+ else
49
+ working_object = working_object.add_sibling label
50
+ end
51
+ current_indentation = line_indentation
52
+ end
53
+ root_label
54
+ end
55
+
56
+ def self.persist_labels_tree(tree_or_text = nil)
57
+ tree = case tree_or_text
58
+ when ImportLabel then tree_or_text
59
+ when String then labels_text_to_tree(tree_or_text)
60
+ else
61
+ raise "Must provide a tree label object or tree text"
62
+ end
63
+ record_lookup_dict = {}
64
+ persister = Proc.new do |items, parent|
65
+ items.each do |item|
66
+ record = Label.create name: item.name, parent: parent
67
+ record_lookup_dict[item.name] = record
68
+ persister.call item.children, record if item.children.present?
69
+ end
70
+ end
71
+ persister.call(tree.children, nil)
72
+ record_lookup_dict
73
+ end
74
+
75
+ class LabelList < Array
76
+ attr_accessor :parent
77
+ end
78
+
79
+ class ImportLabel
80
+ attr_accessor :name, :children, :parent
81
+ def initialize(name)
82
+ @name = name
83
+ end
84
+
85
+ def is_root?
86
+ name == 'root'
87
+ end
88
+
89
+ def children
90
+ @children ||= LabelList.new
91
+ end
92
+
93
+ def add_sibling(label)
94
+ label.parent = parent
95
+ parent.children.push label
96
+ label
97
+ end
98
+
99
+ def add_child(label)
100
+ children.push label
101
+ label.parent = self
102
+ label
103
+ end
104
+
105
+ def inspect
106
+ #"#{name} => #{children.inspect}"
107
+ #children.any? ? "<#{name}> => #{children.inpsect}" : "#{name}"
108
+ base = "#{name}"
109
+ base = "#{base} => #{children.inspect}" if children.any?
110
+ base
111
+ end
112
+
113
+ def to_h
114
+ if is_root?
115
+ {labels: children.map(&:to_h)}
116
+ else
117
+ r = {name: name}
118
+ r[:children] = children.map(&:to_h) if children.any?
119
+ r
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,3 @@
1
+ module GeoLabels
2
+ VERSION = "0.2.0"
3
+ end
data/lib/geo_labels.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'geo_labels/version'
2
+ require 'geo_labels/engine'
3
+ require 'geo_labels/importer'
4
+ require 'geo_labels/exporter'
5
+
6
+ module GeoLabels
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :geo_labels do
3
+ # # Task goes here
4
+ # end