geo_labels 0.2.0
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 +7 -0
- data/CHANGELOG.md +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +8 -0
- data/app/assets/config/geo_labels_manifest.js +2 -0
- data/app/assets/javascripts/geo_labels/application.coffee +29 -0
- data/app/assets/stylesheets/geo_labels/application.sass +10 -0
- data/app/controllers/concerns/geo_labels/shared_template_and_instance_methods.rb +21 -0
- data/app/controllers/geo_labels/application_controller.rb +14 -0
- data/app/controllers/geo_labels/contacts_controller.rb +56 -0
- data/app/controllers/geo_labels/dashboard_controller.rb +33 -0
- data/app/controllers/geo_labels/labels_controller.rb +56 -0
- data/app/helpers/geo_labels/application_helper.rb +197 -0
- data/app/jobs/geo_labels/application_job.rb +4 -0
- data/app/mailers/geo_labels/application_mailer.rb +6 -0
- data/app/models/geo_labels/application_record.rb +5 -0
- data/app/models/geo_labels/contact.rb +64 -0
- data/app/models/geo_labels/contact_label.rb +40 -0
- data/app/models/geo_labels/label.rb +15 -0
- data/app/views/geo_labels/application/_button-destroy-record.html.slim +1 -0
- data/app/views/geo_labels/application/_button-edit-record.html.slim +1 -0
- data/app/views/geo_labels/application/_button-new-record.html.slim +1 -0
- data/app/views/geo_labels/application/_messages.html.slim +4 -0
- data/app/views/geo_labels/application/_navigation.html.slim +29 -0
- data/app/views/geo_labels/contacts/_form.html.slim +46 -0
- data/app/views/geo_labels/contacts/edit.html.slim +3 -0
- data/app/views/geo_labels/contacts/index.html.slim +31 -0
- data/app/views/geo_labels/contacts/new.html.slim +3 -0
- data/app/views/geo_labels/contacts/show.html.slim +20 -0
- data/app/views/geo_labels/dashboard/export.html.slim +4 -0
- data/app/views/geo_labels/dashboard/import.html.slim +7 -0
- data/app/views/geo_labels/dashboard/main.html.slim +35 -0
- data/app/views/geo_labels/labels/_form.html.slim +13 -0
- data/app/views/geo_labels/labels/edit.html.slim +3 -0
- data/app/views/geo_labels/labels/index.html.slim +47 -0
- data/app/views/geo_labels/labels/new.html.slim +3 -0
- data/app/views/geo_labels/labels/show.html.slim +12 -0
- data/app/views/layouts/geo_labels/application.html.slim +22 -0
- data/config/environment.rb +2 -0
- data/config/initializers/human_plural.rb +16 -0
- data/config/locales/geo_labels.en.yml +5 -0
- data/config/locales/geo_labels.es.yml +5 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20221019150722_create_geo_labels_contacts.rb +20 -0
- data/db/migrate/20221020180213_create_geo_labels_labels.rb +16 -0
- data/db/migrate/20221020195346_create_geo_labels_contact_labels.rb +12 -0
- data/lib/geo_labels/engine.rb +36 -0
- data/lib/geo_labels/exporter.rb +48 -0
- data/lib/geo_labels/importer.rb +124 -0
- data/lib/geo_labels/version.rb +3 -0
- data/lib/geo_labels.rb +8 -0
- data/lib/tasks/geo_labels_tasks.rake +4 -0
- 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,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,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,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,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,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,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
|
data/config/routes.rb
ADDED
@@ -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
|
data/lib/geo_labels.rb
ADDED