apidae 0.7.3 → 0.7.4

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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -20
  3. data/README.rdoc +2 -2
  4. data/Rakefile +38 -38
  5. data/app/assets/images/apidae/logo-apidae-grey.svg +84 -84
  6. data/app/assets/javascripts/apidae/application.js +13 -13
  7. data/app/assets/stylesheets/apidae/application.css +15 -15
  8. data/app/controllers/apidae/api_controller.rb +25 -25
  9. data/app/controllers/apidae/application_controller.rb +6 -6
  10. data/app/controllers/apidae/dashboard_controller.rb +13 -13
  11. data/app/controllers/apidae/import_controller.rb +68 -68
  12. data/app/controllers/apidae/objects_controller.rb +59 -59
  13. data/app/controllers/apidae/projects_controller.rb +35 -35
  14. data/app/controllers/apidae/references_controller.rb +9 -9
  15. data/app/controllers/apidae/selections_controller.rb +53 -53
  16. data/app/helpers/apidae/api_helper.rb +4 -4
  17. data/app/helpers/apidae/application_helper.rb +4 -4
  18. data/app/helpers/apidae/dashboard_helper.rb +4 -4
  19. data/app/helpers/apidae/import_helper.rb +4 -4
  20. data/app/helpers/apidae/objects_helper.rb +4 -4
  21. data/app/helpers/apidae/references_helper.rb +4 -4
  22. data/app/helpers/apidae/selections_helper.rb +4 -4
  23. data/app/models/apidae/application_record.rb +5 -5
  24. data/app/models/apidae/export.rb +13 -13
  25. data/app/models/apidae/file_import.rb +161 -161
  26. data/app/models/apidae/obj.rb +351 -351
  27. data/app/models/apidae/project.rb +4 -4
  28. data/app/models/apidae/reference.rb +45 -45
  29. data/app/models/apidae/selection.rb +160 -160
  30. data/app/models/apidae/selection_object.rb +6 -6
  31. data/app/models/apidae/town.rb +28 -27
  32. data/app/views/apidae/dashboard/index.html.erb +41 -41
  33. data/app/views/apidae/import/callback.html.erb +2 -2
  34. data/app/views/apidae/objects/_form.html.erb +73 -73
  35. data/app/views/apidae/objects/edit.html.erb +6 -6
  36. data/app/views/apidae/objects/index.html.erb +34 -34
  37. data/app/views/apidae/objects/index.json.jbuilder +6 -6
  38. data/app/views/apidae/objects/new.html.erb +5 -5
  39. data/app/views/apidae/objects/show.html.erb +74 -74
  40. data/app/views/apidae/projects/edit.html.erb +32 -32
  41. data/app/views/apidae/projects/index.html.erb +34 -34
  42. data/app/views/apidae/references/index.html.erb +34 -34
  43. data/app/views/apidae/selections/_form.html.erb +29 -29
  44. data/app/views/apidae/selections/edit.html.erb +6 -6
  45. data/app/views/apidae/selections/index.html.erb +34 -34
  46. data/app/views/apidae/selections/new.html.erb +5 -5
  47. data/app/views/apidae/selections/show.html.erb +19 -19
  48. data/app/views/layouts/apidae/application.html.erb +14 -14
  49. data/config/locales/apidae.fr.yml +6 -6
  50. data/config/routes.rb +14 -14
  51. data/db/migrate/20170512212941_create_apidae_selections.rb +11 -11
  52. data/db/migrate/20170512214641_create_apidae_objects.rb +22 -22
  53. data/db/migrate/20170512221525_create_apidae_objects_apidae_selections.rb +8 -8
  54. data/db/migrate/20170513114002_create_apidae_towns.rb +15 -15
  55. data/db/migrate/20170513114409_add_town_insee_code_to_objects.rb +5 -5
  56. data/db/migrate/20170513115401_add_pictures_data_to_objects.rb +5 -5
  57. data/db/migrate/20170513121215_create_apidae_attached_files.rb +13 -13
  58. data/db/migrate/20170513205932_rename_objects_selections_table.rb +5 -5
  59. data/db/migrate/20170720161134_add_entity_data_to_objects.rb +5 -5
  60. data/db/migrate/20170730102424_create_apidae_file_imports.rb +13 -13
  61. data/db/migrate/20171025075304_create_apidae_exports.rb +15 -15
  62. data/db/migrate/20180217222410_create_apidae_selection_objects.rb +10 -10
  63. data/db/migrate/20180218172704_change_text_columns_to_json.rb +10 -10
  64. data/db/migrate/20180218231319_add_service_data_to_apidae_objects.rb +5 -5
  65. data/db/migrate/20180222104915_add_rates_data_to_apidae_objects.rb +6 -6
  66. data/db/migrate/20180222105302_rename_openings_to_openings_data.rb +5 -5
  67. data/db/migrate/20180307164936_add_attachments_data_to_apidae_objects.rb +5 -5
  68. data/db/migrate/20180307170349_create_apidae_references.rb +12 -12
  69. data/db/migrate/20180314093512_add_tags_data_to_apidae_objects.rb +5 -5
  70. data/db/migrate/20180314132631_add_meta_data_to_apidae_objects.rb +5 -5
  71. data/db/migrate/20180319143954_remove_apidae_id_unicity.rb +6 -6
  72. data/db/migrate/20180417164604_add_location_data_to_apidae_objects.rb +5 -5
  73. data/db/migrate/20180417165744_remove_address_from_apidae_objects.rb +5 -5
  74. data/db/migrate/20180417171344_remove_lat_lng_from_apidae_objects.rb +6 -6
  75. data/db/migrate/20180418141248_add_description_data_to_apidae_objects.rb +5 -5
  76. data/db/migrate/20180418141305_remove_desc_columns_from_apidae_objects.rb +6 -6
  77. data/db/migrate/20180424141656_add_meta_data_to_apidae_references.rb +5 -5
  78. data/db/migrate/20180519170210_remove_insee_code_unicity.rb +7 -7
  79. data/db/migrate/20180521211735_destroy_attached_files.rb +5 -5
  80. data/db/migrate/20180625050400_rename_objects_to_obj.rb +5 -5
  81. data/db/migrate/20181024072424_add_project_id_to_selections.rb +5 -5
  82. data/db/migrate/20181024072843_create_apidae_projects.rb +10 -10
  83. data/db/migrate/20190328122424_add_description_to_apidae_towns.rb +5 -0
  84. data/lib/apidae.rb +4 -4
  85. data/lib/apidae/engine.rb +10 -10
  86. data/lib/apidae/version.rb +3 -3
  87. data/lib/tasks/apidae_tasks.rake +4 -4
  88. data/test/apidae_test.rb +7 -7
  89. data/test/controllers/apidae/api_controller_test.rb +23 -23
  90. data/test/controllers/apidae/dashboard_controller_test.rb +15 -15
  91. data/test/controllers/apidae/import_controller_test.rb +15 -15
  92. data/test/controllers/apidae/objects_controller_test.rb +52 -52
  93. data/test/controllers/apidae/references_controller_test.rb +13 -13
  94. data/test/controllers/apidae/selections_controller_test.rb +52 -52
  95. data/test/data/selections.json +14 -14
  96. data/test/data/structure.json +64 -64
  97. data/test/data/update_selections.json +18 -18
  98. data/test/dummy/README.rdoc +28 -28
  99. data/test/dummy/Rakefile +6 -6
  100. data/test/dummy/app/assets/javascripts/application.js +13 -13
  101. data/test/dummy/app/assets/stylesheets/application.css +15 -15
  102. data/test/dummy/app/controllers/application_controller.rb +5 -5
  103. data/test/dummy/app/helpers/application_helper.rb +2 -2
  104. data/test/dummy/app/views/layouts/application.html.erb +14 -14
  105. data/test/dummy/bin/bundle +3 -3
  106. data/test/dummy/bin/rails +4 -4
  107. data/test/dummy/bin/rake +4 -4
  108. data/test/dummy/bin/setup +29 -29
  109. data/test/dummy/config.ru +4 -4
  110. data/test/dummy/config/application.rb +24 -24
  111. data/test/dummy/config/boot.rb +5 -5
  112. data/test/dummy/config/database.yml +20 -20
  113. data/test/dummy/config/environment.rb +5 -5
  114. data/test/dummy/config/environments/development.rb +41 -41
  115. data/test/dummy/config/environments/production.rb +79 -79
  116. data/test/dummy/config/environments/test.rb +42 -42
  117. data/test/dummy/config/initializers/apidae.rb +18 -18
  118. data/test/dummy/config/initializers/assets.rb +11 -11
  119. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -7
  120. data/test/dummy/config/initializers/cookies_serializer.rb +3 -3
  121. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -4
  122. data/test/dummy/config/initializers/inflections.rb +16 -16
  123. data/test/dummy/config/initializers/mime_types.rb +4 -4
  124. data/test/dummy/config/initializers/session_store.rb +3 -3
  125. data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -10
  126. data/test/dummy/config/initializers/wrap_parameters.rb +14 -14
  127. data/test/dummy/config/locales/en.yml +23 -23
  128. data/test/dummy/config/routes.rb +4 -4
  129. data/test/dummy/config/secrets.yml +22 -22
  130. data/test/dummy/db/schema.rb +104 -104
  131. data/test/dummy/public/404.html +67 -67
  132. data/test/dummy/public/422.html +67 -67
  133. data/test/dummy/public/500.html +66 -66
  134. data/test/fixtures/apidae/exports.yml +15 -15
  135. data/test/fixtures/apidae/objects.yml +33 -33
  136. data/test/fixtures/apidae/references.yml +11 -11
  137. data/test/fixtures/apidae/selection_objects.yml +9 -9
  138. data/test/fixtures/apidae/selections.yml +11 -11
  139. data/test/fixtures/apidae/towns.yml +15 -15
  140. data/test/integration/navigation_test.rb +8 -8
  141. data/test/models/apidae/export_test.rb +9 -9
  142. data/test/models/apidae/file_import_test.rb +88 -88
  143. data/test/models/apidae/object_test.rb +9 -9
  144. data/test/models/apidae/reference_test.rb +9 -9
  145. data/test/models/apidae/selection_object_test.rb +9 -9
  146. data/test/models/apidae/selection_test.rb +9 -9
  147. data/test/models/apidae/town_test.rb +9 -9
  148. data/test/test_helper.rb +22 -22
  149. metadata +49 -50
  150. data/test/dummy/log/test.log +0 -2109
@@ -1,4 +1,4 @@
1
- module Apidae
2
- module ApiHelper
3
- end
4
- end
1
+ module Apidae
2
+ module ApiHelper
3
+ end
4
+ end
@@ -1,4 +1,4 @@
1
- module Apidae
2
- module ApplicationHelper
3
- end
4
- end
1
+ module Apidae
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -1,4 +1,4 @@
1
- module Apidae
2
- module DashboardHelper
3
- end
4
- end
1
+ module Apidae
2
+ module DashboardHelper
3
+ end
4
+ end
@@ -1,4 +1,4 @@
1
- module Apidae
2
- module ImportHelper
3
- end
4
- end
1
+ module Apidae
2
+ module ImportHelper
3
+ end
4
+ end
@@ -1,4 +1,4 @@
1
- module Apidae
2
- module ObjectsHelper
3
- end
4
- end
1
+ module Apidae
2
+ module ObjectsHelper
3
+ end
4
+ end
@@ -1,4 +1,4 @@
1
- module Apidae
2
- module ReferencesHelper
3
- end
4
- end
1
+ module Apidae
2
+ module ReferencesHelper
3
+ end
4
+ end
@@ -1,4 +1,4 @@
1
- module Apidae
2
- module SelectionsHelper
3
- end
4
- end
1
+ module Apidae
2
+ module SelectionsHelper
3
+ end
4
+ end
@@ -1,5 +1,5 @@
1
- module Apidae
2
- class ApplicationRecord < ActiveRecord::Base
3
- self.abstract_class = true
4
- end
5
- end
1
+ module Apidae
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -1,13 +1,13 @@
1
- module Apidae
2
- class Export < ActiveRecord::Base
3
-
4
- PENDING = 'pending'
5
- COMPLETE = 'complete'
6
- CANCELLED = 'cancelled'
7
-
8
- # Note : handle reset case
9
- def self.pending
10
- where(remote_status: 'SUCCESS', status: PENDING).order(:id)
11
- end
12
- end
13
- end
1
+ module Apidae
2
+ class Export < ActiveRecord::Base
3
+
4
+ PENDING = 'pending'
5
+ COMPLETE = 'complete'
6
+ CANCELLED = 'cancelled'
7
+
8
+ # Note : handle reset case
9
+ def self.pending
10
+ where(remote_status: 'SUCCESS', status: PENDING).order(:id)
11
+ end
12
+ end
13
+ end
@@ -1,161 +1,161 @@
1
- require 'zip'
2
-
3
- module Apidae
4
- class FileImport < ActiveRecord::Base
5
-
6
- STATUS_PENDING = 'pending'
7
- STATUS_COMPLETE = 'complete'
8
- STATUS_CANCELLED = 'cancelled'
9
-
10
- TOWNS_FILE = 'communes.json'
11
- INTERNAL_FILE = 'criteres_internes.json'
12
- REFERENCES_FILE = 'elements_reference.json'
13
- MODIFIED_DIR = 'objets_modifies'
14
- DELETED_FILE = 'objets_supprimes.json'
15
- SELECTIONS_FILE = 'selections.json'
16
-
17
- def self.import(zip_file, project_id)
18
- Zip::File.open(zip_file) do |zfile|
19
- result = {created: 0, updated: 0, deleted: 0, selections: []}
20
- Reference.import(zfile.read(REFERENCES_FILE))
21
- Reference.import_internal(zfile.read(INTERNAL_FILE))
22
- logger.info "Completed #{Reference.count} references update"
23
- Town.import(zfile.read(TOWNS_FILE))
24
- logger.info "Completed #{Town.count} towns update"
25
- zfile.each do |file|
26
- if file.file? && file.name.end_with?('.json')
27
- logger.info "Processing file : #{file.name}"
28
- if file.name.include?(MODIFIED_DIR)
29
- add_or_update_objects(zfile.read(file.name), result)
30
- elsif file.name.include?(DELETED_FILE)
31
- delete_objects(zfile.read(file.name), result)
32
- elsif file.name.include?(SELECTIONS_FILE)
33
- add_or_update_selections(project_id, zfile.read(file.name), result)
34
- end
35
- end
36
- end
37
- create(result.except(:selections).merge({remote_file: (zip_file.is_a?(File) ? zip_file.path : zip_file), status: STATUS_COMPLETE}))
38
- logger.info "Import results : #{result}"
39
- result
40
- end
41
- end
42
-
43
- def self.import_dir(project_id, dir)
44
- result = {created: 0, updated: 0, deleted: 0, selections: []}
45
- import_updates(File.join(dir, MODIFIED_DIR), result)
46
- import_deletions(File.join(dir, DELETED_FILE), result)
47
- import_selections(project_id, File.join(dir, SELECTIONS_FILE), result)
48
- logger.info "Import results : #{result}"
49
- result
50
- end
51
-
52
- def self.import_updates(json_dir, result)
53
- if Dir.exist?(json_dir)
54
- Dir.foreach(json_dir) do |f|
55
- if f.end_with?('.json')
56
- json_file = File.join(json_dir, f)
57
- objects_json = File.read(json_file)
58
- add_or_update_objects(objects_json, result)
59
- end
60
- end
61
- end
62
- end
63
-
64
- def self.add_or_update_objects(objects_json, result)
65
- objects_hashes = JSON.parse(objects_json, symbolize_names: true)
66
- objects_hashes.each do |object_data|
67
- begin
68
- existing = Obj.find_by_apidae_id(object_data[:id])
69
- if existing
70
- Obj.update_object(existing, object_data)
71
- result[:updated] += 1
72
- else
73
- Obj.add_object(object_data)
74
- result[:created] += 1
75
- end
76
- rescue Exception => e
77
- puts "Failed to import object #{object_data[:id]}"
78
- puts e.message
79
- puts e.backtrace[0..5].join("\n")
80
- raise e
81
- end
82
- end
83
- end
84
-
85
- def self.import_deletions(json_file, result)
86
- if File.exist?(json_file)
87
- deleted_json = File.read(json_file)
88
- delete_objects(deleted_json, result)
89
- end
90
- end
91
-
92
- def self.delete_objects(deleted_json, result)
93
- deleted_ids = JSON.parse(deleted_json)
94
- deleted_ids.each do |id|
95
- obj = Obj.find_by_apidae_id(id)
96
- if obj
97
- obj.destroy!
98
- result[:deleted] += 1
99
- else
100
- logger.info "skipping object deletion : #{id}"
101
- end
102
- end
103
- end
104
-
105
- def self.update_fields(json_dir)
106
- result = false
107
- if Dir.exist?(json_dir)
108
- Dir.foreach(json_dir) do |f|
109
- if f.end_with?('.json')
110
- json_file = File.join(json_dir, f)
111
- objects_json = File.read(json_file)
112
- objects_hashes = JSON.parse(objects_json, symbolize_names: true)
113
- objects_hashes.each do |object_data|
114
- obj = Obj.find_by_apidae_id(object_data[:id])
115
- if obj
116
- yield(obj, object_data)
117
- obj.save!
118
- end
119
- end
120
- end
121
- result = true
122
- end
123
- result
124
- end
125
- end
126
-
127
- # def self.load_pictures
128
- # Apidae::Object.all.each do |obj|
129
- # if obj.apidae_attached_files.blank? && obj.pictures.any?
130
- # obj.pictures.each do |pic|
131
- # begin
132
- # attached = Apidae::AttachedFile.new(apidae_object_id: id, name: pic[:name], picture: URI.parse(pic[:url]),
133
- # description: pic[:description], credits: pic[:credits])
134
- # attached.save
135
- # rescue OpenURI::HTTPError => e
136
- # logger.error "Could not retrieve attached picture for object #{title} - Error is #{e.message}"
137
- # end
138
- # end
139
- # end
140
- # end
141
- # end
142
-
143
- def self.import_selections(project_id, json_file, result)
144
- selections_json = File.read(json_file)
145
- add_or_update_selections(project_id, selections_json, result)
146
- end
147
-
148
- def self.add_or_update_selections(project_id, selections_json, result)
149
- selections_hashes = JSON.parse(selections_json, symbolize_names: true)
150
- project = Project.find_or_create_by(apidae_id: project_id)
151
- deleted_ids = Selection.where(apidae_project_id: project.id).collect {|sel| sel.apidae_id}.uniq - selections_hashes.collect {|sel| sel[:id]}
152
- Selection.where(apidae_id: deleted_ids).delete_all
153
- selections_hashes.each do |selection_data|
154
- logger.info "Updating selection #{selection_data[:id]}"
155
- Selection.add_or_update(selection_data, project.id)
156
- end
157
- result[:selections] = Selection.where(apidae_project_id: project_id)
158
- .collect {|sel| {apidae_id: sel.apidae_id, reference: sel.reference, objects: sel.objects.count}}
159
- end
160
- end
161
- end
1
+ require 'zip'
2
+
3
+ module Apidae
4
+ class FileImport < ActiveRecord::Base
5
+
6
+ STATUS_PENDING = 'pending'
7
+ STATUS_COMPLETE = 'complete'
8
+ STATUS_CANCELLED = 'cancelled'
9
+
10
+ TOWNS_FILE = 'communes.json'
11
+ INTERNAL_FILE = 'criteres_internes.json'
12
+ REFERENCES_FILE = 'elements_reference.json'
13
+ MODIFIED_DIR = 'objets_modifies'
14
+ DELETED_FILE = 'objets_supprimes.json'
15
+ SELECTIONS_FILE = 'selections.json'
16
+
17
+ def self.import(zip_file, project_id)
18
+ Zip::File.open(zip_file) do |zfile|
19
+ result = {created: 0, updated: 0, deleted: 0, selections: []}
20
+ Reference.import(zfile.read(REFERENCES_FILE))
21
+ Reference.import_internal(zfile.read(INTERNAL_FILE))
22
+ logger.info "Completed #{Reference.count} references update"
23
+ Town.import(zfile.read(TOWNS_FILE))
24
+ logger.info "Completed #{Town.count} towns update"
25
+ zfile.each do |file|
26
+ if file.file? && file.name.end_with?('.json')
27
+ logger.info "Processing file : #{file.name}"
28
+ if file.name.include?(MODIFIED_DIR)
29
+ add_or_update_objects(zfile.read(file.name), result)
30
+ elsif file.name.include?(DELETED_FILE)
31
+ delete_objects(zfile.read(file.name), result)
32
+ elsif file.name.include?(SELECTIONS_FILE)
33
+ add_or_update_selections(project_id, zfile.read(file.name), result)
34
+ end
35
+ end
36
+ end
37
+ create(result.except(:selections).merge({remote_file: (zip_file.is_a?(File) ? zip_file.path : zip_file), status: STATUS_COMPLETE}))
38
+ logger.info "Import results : #{result}"
39
+ result
40
+ end
41
+ end
42
+
43
+ def self.import_dir(project_id, dir)
44
+ result = {created: 0, updated: 0, deleted: 0, selections: []}
45
+ import_updates(File.join(dir, MODIFIED_DIR), result)
46
+ import_deletions(File.join(dir, DELETED_FILE), result)
47
+ import_selections(project_id, File.join(dir, SELECTIONS_FILE), result)
48
+ logger.info "Import results : #{result}"
49
+ result
50
+ end
51
+
52
+ def self.import_updates(json_dir, result)
53
+ if Dir.exist?(json_dir)
54
+ Dir.foreach(json_dir) do |f|
55
+ if f.end_with?('.json')
56
+ json_file = File.join(json_dir, f)
57
+ objects_json = File.read(json_file)
58
+ add_or_update_objects(objects_json, result)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.add_or_update_objects(objects_json, result)
65
+ objects_hashes = JSON.parse(objects_json, symbolize_names: true)
66
+ objects_hashes.each do |object_data|
67
+ begin
68
+ existing = Obj.find_by_apidae_id(object_data[:id])
69
+ if existing
70
+ Obj.update_object(existing, object_data)
71
+ result[:updated] += 1
72
+ else
73
+ Obj.add_object(object_data)
74
+ result[:created] += 1
75
+ end
76
+ rescue Exception => e
77
+ puts "Failed to import object #{object_data[:id]}"
78
+ puts e.message
79
+ puts e.backtrace[0..5].join("\n")
80
+ raise e
81
+ end
82
+ end
83
+ end
84
+
85
+ def self.import_deletions(json_file, result)
86
+ if File.exist?(json_file)
87
+ deleted_json = File.read(json_file)
88
+ delete_objects(deleted_json, result)
89
+ end
90
+ end
91
+
92
+ def self.delete_objects(deleted_json, result)
93
+ deleted_ids = JSON.parse(deleted_json)
94
+ deleted_ids.each do |id|
95
+ obj = Obj.find_by_apidae_id(id)
96
+ if obj
97
+ obj.destroy!
98
+ result[:deleted] += 1
99
+ else
100
+ logger.info "skipping object deletion : #{id}"
101
+ end
102
+ end
103
+ end
104
+
105
+ def self.update_fields(json_dir)
106
+ result = false
107
+ if Dir.exist?(json_dir)
108
+ Dir.foreach(json_dir) do |f|
109
+ if f.end_with?('.json')
110
+ json_file = File.join(json_dir, f)
111
+ objects_json = File.read(json_file)
112
+ objects_hashes = JSON.parse(objects_json, symbolize_names: true)
113
+ objects_hashes.each do |object_data|
114
+ obj = Obj.find_by_apidae_id(object_data[:id])
115
+ if obj
116
+ yield(obj, object_data)
117
+ obj.save!
118
+ end
119
+ end
120
+ end
121
+ result = true
122
+ end
123
+ result
124
+ end
125
+ end
126
+
127
+ # def self.load_pictures
128
+ # Apidae::Object.all.each do |obj|
129
+ # if obj.apidae_attached_files.blank? && obj.pictures.any?
130
+ # obj.pictures.each do |pic|
131
+ # begin
132
+ # attached = Apidae::AttachedFile.new(apidae_object_id: id, name: pic[:name], picture: URI.parse(pic[:url]),
133
+ # description: pic[:description], credits: pic[:credits])
134
+ # attached.save
135
+ # rescue OpenURI::HTTPError => e
136
+ # logger.error "Could not retrieve attached picture for object #{title} - Error is #{e.message}"
137
+ # end
138
+ # end
139
+ # end
140
+ # end
141
+ # end
142
+
143
+ def self.import_selections(project_id, json_file, result)
144
+ selections_json = File.read(json_file)
145
+ add_or_update_selections(project_id, selections_json, result)
146
+ end
147
+
148
+ def self.add_or_update_selections(project_id, selections_json, result)
149
+ selections_hashes = JSON.parse(selections_json, symbolize_names: true)
150
+ project = Project.find_or_create_by(apidae_id: project_id)
151
+ deleted_ids = Selection.where(apidae_project_id: project.id).collect {|sel| sel.apidae_id}.uniq - selections_hashes.collect {|sel| sel[:id]}
152
+ Selection.where(apidae_id: deleted_ids).delete_all
153
+ selections_hashes.each do |selection_data|
154
+ logger.info "Updating selection #{selection_data[:id]}"
155
+ Selection.add_or_update(selection_data, project.id)
156
+ end
157
+ result[:selections] = Selection.where(apidae_project_id: project_id)
158
+ .collect {|sel| {apidae_id: sel.apidae_id, reference: sel.reference, objects: sel.objects.count}}
159
+ end
160
+ end
161
+ end
@@ -1,351 +1,351 @@
1
- module Apidae
2
- class Obj < ActiveRecord::Base
3
-
4
- belongs_to :town, class_name: 'Apidae::Town', foreign_key: :town_insee_code, primary_key: :insee_code
5
- has_many :apidae_selection_objects, class_name: 'Apidae::SelectionObject', foreign_key: :apidae_object_id
6
- has_many :selections, class_name: 'Apidae::Selection', source: :apidae_selection, through: :apidae_selection_objects
7
-
8
- store_accessor :description_data, :short_desc, :long_desc, :theme_desc, :private_desc
9
- store_accessor :pictures_data, :pictures
10
- store_accessor :attachments_data, :attachments
11
- store_accessor :type_data, :categories, :themes, :capacity, :classification, :labels, :chains, :area, :track,
12
- :products, :audience, :animals, :extra, :duration, :certifications
13
- store_accessor :entity_data, :entity_id, :entity_name, :service_provider_id
14
- store_accessor :contact, :telephone, :email, :website
15
- store_accessor :location_data, :address, :place, :latitude, :longitude, :access, :territories, :environments
16
- store_accessor :openings_data, :openings_desc, :openings, :time_periods
17
- store_accessor :rates_data, :rates_desc, :rates, :payment_methods, :includes, :excludes
18
- store_accessor :service_data, :services, :equipments, :comfort, :activities, :challenged, :languages
19
- store_accessor :tags_data, :promo, :internal, :linked
20
-
21
- ACT = 'ACTIVITE'
22
- COS = 'COMMERCE_ET_SERVICE'
23
- DEG = 'DEGUSTATION'
24
- DOS = 'DOMAINE_SKIABLE'
25
- EQU = 'EQUIPEMENT'
26
- FEM = 'FETE_ET_MANIFESTATION'
27
- HCO = 'HEBERGEMENT_COLLECTIF'
28
- HLO = 'HEBERGEMENT_LOCATIF'
29
- HOT = 'HOTELLERIE'
30
- HPA = 'HOTELLERIE_PLEIN_AIR'
31
- PCU = 'PATRIMOINE_CULTUREL'
32
- PNA = 'PATRIMOINE_NATUREL'
33
- RES = 'RESTAURATION'
34
- SPA = 'SEJOUR_PACKAGE'
35
- STR = 'STRUCTURE'
36
- TER = 'TERRITOIRE'
37
-
38
- TYPES_DATA = {
39
- ACT => {node: :informationsActivite, subtype: :activiteType},
40
- COS => {node: :informationsCommerceEtService, subtype: :commerceEtServiceType},
41
- DEG => {node: :informationsDegustation, subtype: :degustationType},
42
- DOS => {node: :informationsDomaineSkiable, subtype: :domaineSkiableType},
43
- EQU => {node: :informationsEquipement, subtype: :equipementType},
44
- FEM => {node: :informationsFeteEtManifestation, subtype: :feteEtManifestationType},
45
- HCO => {node: :informationsHebergementCollectif, subtype: :hebergementCollectifType},
46
- HLO => {node: :informationsHebergementLocatif, subtype: :hebergementLocatifType},
47
- HOT => {node: :informationsHotellerie, subtype: :hotellerieType},
48
- HPA => {node: :informationsHotelleriePleinAir, subtype: :hotelleriePleinAirType},
49
- PCU => {node: :informationsPatrimoineCulturel, subtype: :patrimoineCulturelType},
50
- PNA => {node: :informationsPatrimoineNaturel, subtype: :patrimoineNaturelType},
51
- RES => {node: :informationsRestauration, subtype: :restaurationType},
52
- SPA => {node: :informationsSejourPackage, subtype: :sejourPackageType},
53
- STR => {node: :informationsStructure, subtype: :structureType},
54
- TER => {node: :informationsTerritoire, subtype: :territoireType}
55
- }
56
-
57
- PHONE = 201
58
- EMAIL = 204
59
- WEBSITE = 205
60
-
61
- def self.add_object(object_data)
62
- apidae_obj = Obj.new(apidae_id: object_data[:id])
63
- update_object(apidae_obj, object_data)
64
- end
65
-
66
- def self.update_object(apidae_obj, object_data)
67
- populate_fields(apidae_obj, object_data)
68
-
69
- if Rails.application.config.respond_to?(:apidae_aspect) && !object_data[:aspects].blank?
70
- apidae_aspect = object_data[:aspects].find {|a| a[:aspect] == Rails.application.config.apidae_aspect}
71
- if apidae_aspect
72
- apidae_aspect[:type] = apidae_obj.apidae_type
73
- aspect_obj = Obj.new
74
- populate_fields(aspect_obj, apidae_aspect)
75
- merge_fields(apidae_obj, aspect_obj)
76
- end
77
- end
78
-
79
- apidae_obj.save!
80
- end
81
-
82
- def self.populate_fields(apidae_obj, object_data)
83
- type_fields = TYPES_DATA[object_data[:type]]
84
- apidae_obj.apidae_type = object_data[:type]
85
- apidae_obj.apidae_subtype = node_id(object_data[type_fields[:node]], type_fields[:subtype])
86
- apidae_obj.title = node_value(object_data, :nom)
87
- apidae_obj.description_data = parse_desc_data(object_data[:presentation], object_data[:donneesPrivees])
88
- apidae_obj.contact = contact(object_data[:informations])
89
- apidae_obj.location_data = parse_location_data(object_data[:localisation], object_data[type_fields[:node]],
90
- object_data[:territoires])
91
- apidae_obj.town = town(object_data[:localisation])
92
- apidae_obj.openings_data = parse_openings(object_data[:ouverture])
93
- apidae_obj.rates_data = parse_rates(object_data[:descriptionTarif])
94
- apidae_obj.reservation = parse_reservation(object_data[:reservation])
95
- apidae_obj.type_data = parse_type_data(apidae_obj, object_data[type_fields[:node]], object_data[:prestations])
96
- apidae_obj.pictures_data = pictures_urls(object_data[:illustrations])
97
- apidae_obj.attachments_data = attachments_urls(object_data[:multimedias])
98
- apidae_obj.entity_data = entity_fields(object_data[:informations], object_data[type_fields[:node]])
99
- apidae_obj.service_data = parse_service_data(object_data[:prestations], object_data[type_fields[:node]])
100
- apidae_obj.tags_data = parse_tags_data(object_data[:presentation], object_data[:criteresInternes], object_data[:liens])
101
- apidae_obj.meta_data = object_data[:metadonnees]
102
- end
103
-
104
- def self.merge_fields(apidae_obj, aspect_obj)
105
- apidae_obj.description_data.merge!(non_empty(aspect_obj.description_data)) if aspect_obj.description_data
106
- apidae_obj.contact.merge!(non_empty(aspect_obj.contact)) if aspect_obj.contact
107
- apidae_obj.location_data.merge!(non_empty(aspect_obj.location_data)) if aspect_obj.location_data
108
- apidae_obj.openings_data.merge!(non_empty(aspect_obj.openings_data)) if aspect_obj.openings_data
109
- apidae_obj.rates_data.merge!(non_empty(aspect_obj.rates_data)) if aspect_obj.rates_data
110
- apidae_obj.reservation = aspect_obj.reservation unless aspect_obj.reservation.blank?
111
- apidae_obj.type_data.merge!(non_empty(aspect_obj.type_data)) if aspect_obj.type_data
112
- apidae_obj.pictures_data.merge!(non_empty(aspect_obj.pictures_data)) if aspect_obj.pictures_data
113
- apidae_obj.attachments_data.merge!(non_empty(aspect_obj.attachments_data)) if aspect_obj.attachments_data
114
- apidae_obj.service_data.merge!(non_empty(aspect_obj.service_data)) if aspect_obj.service_data
115
- apidae_obj.tags_data.merge!(non_empty(aspect_obj.tags_data)) if aspect_obj.tags_data
116
- apidae_obj.meta_data.merge!(non_empty(aspect_obj.meta_data)) if aspect_obj.meta_data
117
- end
118
-
119
- def self.non_empty(data_hash)
120
- data_hash.keep_if {|k, v| !v.blank?}
121
- end
122
-
123
- def self.parse_desc_data(data_hash, private_data)
124
- unless data_hash.blank?
125
- {
126
- short_desc: node_value(data_hash, :descriptifCourt),
127
- long_desc: node_value(data_hash, :descriptifDetaille),
128
- theme_desc: data_hash[:descriptifsThematises].blank? ? {} : Hash[data_hash[:descriptifsThematises].map {|th| [node_id(th, :theme), node_value(th, :description)]}],
129
- private_desc: private_data.blank? ? {} : Hash[private_data.map {|d| [d[:nomTechnique], node_value(d, :descriptif)]}]
130
- }
131
- end
132
- end
133
-
134
- def self.pictures_urls(pictures_array)
135
- pics_data = []
136
- unless pictures_array.blank?
137
- pictures_array.select { |p| p.is_a?(Hash) && !p[:traductionFichiers].blank? }.each do |pic|
138
- pics_data << {
139
- name: node_value(pic, :nom),
140
- url: pic[:traductionFichiers][0][:url],
141
- description: node_value(pic, :legende),
142
- credits: node_value(pic, :copyright)
143
- }
144
- end
145
- end
146
- {pictures: pics_data}
147
- end
148
-
149
- def self.attachments_urls(attachments_array)
150
- atts_data = []
151
- unless attachments_array.blank?
152
- attachments_array.select { |att| att.is_a?(Hash) && !att[:traductionFichiers].blank? }.each do |att|
153
- atts_data << {
154
- name: node_value(att, :nom),
155
- url: att[:traductionFichiers][0][:url]
156
- }
157
- end
158
- end
159
- {attachments: atts_data}
160
- end
161
-
162
- def self.contact(information_hash)
163
- contact_details = {}
164
- unless information_hash.blank?
165
- contact_entries = information_hash[:moyensCommunication] || []
166
- contact_entries.each do |c|
167
- case c[:type][:id]
168
- when PHONE
169
- contact_details[:telephone] ||= []
170
- contact_details[:telephone] << c[:coordonnees][:fr]
171
- when EMAIL
172
- contact_details[:email] ||= []
173
- contact_details[:email] << c[:coordonnees][:fr]
174
- when WEBSITE
175
- contact_details[:website] ||= []
176
- contact_details[:website] << c[:coordonnees][:fr]
177
- else
178
- end
179
- end
180
- end
181
- contact_details
182
- end
183
-
184
- def self.parse_location_data(location_hash, type_data_hash, territories)
185
- loc_data = {}
186
- unless location_hash.blank?
187
- address_hash = location_hash[:adresse]
188
- computed_address = []
189
- unless address_hash.blank?
190
- computed_address << address_hash[:adresse1] unless address_hash[:adresse1].blank?
191
- computed_address << address_hash[:adresse2] unless address_hash[:adresse2].blank?
192
- computed_address << address_hash[:adresse3] unless address_hash[:adresse3].blank?
193
- end
194
- loc_data.merge!({address: computed_address})
195
- loc_data.merge!({place: type_data_hash[:nomLieu]}) if type_data_hash
196
- geoloc_details = location_hash[:geolocalisation]
197
- if geoloc_details && geoloc_details[:valide] && geoloc_details[:geoJson]
198
- loc_data[:latitude] = geoloc_details[:geoJson][:coordinates][1]
199
- loc_data[:longitude] = geoloc_details[:geoJson][:coordinates][0]
200
- end
201
- loc_data[:access] = node_value(geoloc_details, :complement) if geoloc_details
202
- loc_data[:environments] = location_hash[:environnements].map {|e| e[:id]} if location_hash[:environnements]
203
- end
204
- loc_data[:territories] = territories.map {|t| t[:id]} unless territories.blank?
205
- loc_data
206
- end
207
-
208
- def self.town(location_hash)
209
- if location_hash
210
- address_hash = location_hash[:adresse]
211
- (!address_hash.blank? && address_hash[:commune]) ? Town.find_by_apidae_id(address_hash[:commune][:id]) : nil
212
- else
213
- nil
214
- end
215
- end
216
-
217
- def self.parse_openings(openings_hash)
218
- if openings_hash && openings_hash[:periodeEnClair]
219
- {
220
- openings_desc: openings_hash[:periodeEnClair][:libelleFr],
221
- openings: openings_hash[:periodesOuvertures],
222
- time_periods: lists_ids(openings_hash[:indicationsPeriode])
223
- }
224
- end
225
- end
226
-
227
- def self.parse_rates(rates_hash)
228
- if rates_hash
229
- desc = rates_hash[:gratuit] ? 'gratuit' : node_value(rates_hash, :tarifsEnClair)
230
- values = rates_hash[:periodes].blank? ? [] : rates_hash[:periodes].map {|p| build_rate(p)}
231
- methods = rates_hash[:modesPaiement].blank? ? [] : rates_hash[:modesPaiement].map {|p| p[:id]}
232
- {rates_desc: desc, rates: values, payment_methods: methods, includes: node_value(rates_hash, :leTarifComprend), excludes: node_value(rates_hash, :leTarifNeComprendPas)}
233
- end
234
- end
235
-
236
- def self.parse_type_data(apidae_obj, type_hash, presta_hash)
237
- data_hash = type_hash || {}
238
- prestations_hash = presta_hash || {}
239
- apidae_obj.apidae_subtype = lists_ids(data_hash[:typesManifestation]).first if apidae_obj.apidae_type == FEM
240
- apidae_obj.apidae_subtype = node_id(data_hash, :rubrique) if apidae_obj.apidae_type == EQU
241
- apidae_obj.apidae_subtype = lists_ids(data_hash[:typesHebergement]).first if apidae_obj.apidae_type == SPA
242
- {
243
- categories: lists_ids(data_hash[:categories], data_hash[:typesDetailles], data_hash[:activiteCategories]),
244
- themes: lists_ids(data_hash[:themes]),
245
- capacity: (data_hash[:capacite] || {})
246
- .merge(presta_hash ? {group_min: presta_hash[:tailleGroupeMin], group_max: presta_hash[:tailleGroupeMax],
247
- age_min: presta_hash[:ageMin], age_max: presta_hash[:ageMax]} : {}),
248
- classification: nodes_ids(data_hash[:classement], data_hash[:classementPrefectoral], data_hash[:classification]) +
249
- lists_ids(data_hash[:classementsGuides]) + lists_ids(data_hash[:classements]),
250
- labels: lists_ids(data_hash[:labels], prestations_hash[:labelsTourismeHandicap]) +
251
- (node_id(data_hash, :typeLabel) ? [node_id(data_hash, :typeLabel)] : []),
252
- chains: lists_ids(data_hash[:chaines]) + nodes_ids(data_hash[:chaineEtLabel]),
253
- area: apidae_obj.apidae_type == DOS ? data_hash.except(:classification) : node_value(data_hash, :lieuDePratique),
254
- track: apidae_obj.apidae_type == EQU ? data_hash[:itineraire] : nil,
255
- products: lists_ids(data_hash[:typesProduit], data_hash[:aopAocIgps], data_hash[:specialites]),
256
- audience: lists_ids(prestations_hash[:typesClientele]),
257
- animals: prestations_hash[:animauxAcceptes] == 'ACCEPTES',
258
- extra: apidae_obj.apidae_type == SPA ? node_value(data_hash, :formuleHebergement) : node_value(prestations_hash, :complementAccueil),
259
- duration: apidae_obj.apidae_type == SPA ? {days: data_hash[:nombreJours], nights: data_hash[:nombreNuits]} : data_hash[:dureeSeance],
260
- certifications: data_hash[:agrements].blank? ? [] : data_hash[:agrements].map {|a| {id: a[:type][:id], identifier: a[:numero]}}
261
- }
262
- end
263
-
264
- def self.parse_service_data(data_hash, type_data_hash)
265
- if data_hash
266
- {
267
- services: lists_ids(data_hash[:services]),
268
- equipments: lists_ids(data_hash[:equipements]),
269
- comfort: lists_ids(data_hash[:conforts]),
270
- activities: lists_ids(data_hash[:activites], type_data_hash ? type_data_hash[:activites] : [],
271
- type_data_hash ? type_data_hash[:activitesSportives] : [],
272
- type_data_hash ? type_data_hash[:activitesCulturelles] : []),
273
- challenged: lists_ids(data_hash[:tourismesAdaptes]),
274
- languages: lists_ids(data_hash[:languesParlees])
275
- }
276
- end
277
- end
278
-
279
- def self.parse_tags_data(pres_data_hash, crit_data_hash, linked_data_hash)
280
- tags = {}
281
- if pres_data_hash
282
- tags[:promo] = lists_ids(pres_data_hash[:typologiesPromoSitra])
283
- end
284
- unless crit_data_hash.blank?
285
- tags[:internal] = crit_data_hash.map {|c| c[:id]}
286
- end
287
- unless linked_data_hash.blank? || linked_data_hash[:liensObjetsTouristiquesTypes].blank?
288
- tags[:linked] = linked_data_hash[:liensObjetsTouristiquesTypes]
289
- .map {|l| {apidae_id: l[:objetTouristique][:id], apidae_type: l[:objetTouristique][:type], category: l[:type]}}
290
- end
291
- tags
292
- end
293
-
294
- def self.parse_reservation(reservation_hash)
295
- if reservation_hash
296
- if reservation_hash[:complement]
297
- reservation_hash[:complement][:libelleFr]
298
- else
299
- reservation_hash[:organismes]
300
- end
301
- end
302
- end
303
-
304
- def self.entity_fields(information_hash, type_data_hash)
305
- if information_hash && information_hash[:structureGestion]
306
- {entity_id: information_hash[:structureGestion][:id], service_provider_id: node_id(type_data_hash, :prestataireActivites)}
307
- end
308
- end
309
-
310
- def contact_text
311
- entries = []
312
- JSON.parse(contact).each_pair do |k, v|
313
- entries << "#{k}: #{v}"
314
- end
315
- entries.join("\n")
316
- end
317
-
318
- def main_picture
319
- pictures.any? ? pictures[0]["url"] : "#{Rails.application.config.apidae_pictures_path}/default/logo.png"
320
- end
321
-
322
- def self.build_rate(rate_period)
323
- {
324
- id: rate_period[:identifiant], from: rate_period[:dateDebut], to: rate_period[:dateFin],
325
- values: rate_period[:tarifs].blank? ? [] : rate_period[:tarifs].map {|t| {min: t[:minimum], max: t[:maximum], type: t[:type][:id], details: node_value(t, :precisionTarif)}}
326
- }
327
- end
328
-
329
- private
330
-
331
- def self.node_value(node, key)
332
- if node && node[key]
333
- node[key][:libelleFr]
334
- else
335
- ''
336
- end
337
- end
338
-
339
- def self.node_id(node, key)
340
- node[key][:id] if node && node[key]
341
- end
342
-
343
- def self.lists_ids(*lists)
344
- lists.blank? ? [] : lists.map {|l| l.blank? ? [] : l.map {|elt| elt[:id]}}.flatten.uniq
345
- end
346
-
347
- def self.nodes_ids(*nodes)
348
- nodes.blank? ? [] : nodes.select {|n| !n.blank?}.map {|n| n[:id]}
349
- end
350
- end
351
- end
1
+ module Apidae
2
+ class Obj < ActiveRecord::Base
3
+
4
+ belongs_to :town, class_name: 'Apidae::Town', foreign_key: :town_insee_code, primary_key: :insee_code
5
+ has_many :apidae_selection_objects, class_name: 'Apidae::SelectionObject', foreign_key: :apidae_object_id
6
+ has_many :selections, class_name: 'Apidae::Selection', source: :apidae_selection, through: :apidae_selection_objects
7
+
8
+ store_accessor :description_data, :short_desc, :long_desc, :theme_desc, :private_desc
9
+ store_accessor :pictures_data, :pictures
10
+ store_accessor :attachments_data, :attachments
11
+ store_accessor :type_data, :categories, :themes, :capacity, :classification, :labels, :chains, :area, :track,
12
+ :products, :audience, :animals, :extra, :duration, :certifications
13
+ store_accessor :entity_data, :entity_id, :entity_name, :service_provider_id
14
+ store_accessor :contact, :telephone, :email, :website
15
+ store_accessor :location_data, :address, :place, :latitude, :longitude, :access, :territories, :environments
16
+ store_accessor :openings_data, :openings_desc, :openings, :time_periods
17
+ store_accessor :rates_data, :rates_desc, :rates, :payment_methods, :includes, :excludes
18
+ store_accessor :service_data, :services, :equipments, :comfort, :activities, :challenged, :languages
19
+ store_accessor :tags_data, :promo, :internal, :linked
20
+
21
+ ACT = 'ACTIVITE'
22
+ COS = 'COMMERCE_ET_SERVICE'
23
+ DEG = 'DEGUSTATION'
24
+ DOS = 'DOMAINE_SKIABLE'
25
+ EQU = 'EQUIPEMENT'
26
+ FEM = 'FETE_ET_MANIFESTATION'
27
+ HCO = 'HEBERGEMENT_COLLECTIF'
28
+ HLO = 'HEBERGEMENT_LOCATIF'
29
+ HOT = 'HOTELLERIE'
30
+ HPA = 'HOTELLERIE_PLEIN_AIR'
31
+ PCU = 'PATRIMOINE_CULTUREL'
32
+ PNA = 'PATRIMOINE_NATUREL'
33
+ RES = 'RESTAURATION'
34
+ SPA = 'SEJOUR_PACKAGE'
35
+ STR = 'STRUCTURE'
36
+ TER = 'TERRITOIRE'
37
+
38
+ TYPES_DATA = {
39
+ ACT => {node: :informationsActivite, subtype: :activiteType},
40
+ COS => {node: :informationsCommerceEtService, subtype: :commerceEtServiceType},
41
+ DEG => {node: :informationsDegustation, subtype: :degustationType},
42
+ DOS => {node: :informationsDomaineSkiable, subtype: :domaineSkiableType},
43
+ EQU => {node: :informationsEquipement, subtype: :equipementType},
44
+ FEM => {node: :informationsFeteEtManifestation, subtype: :feteEtManifestationType},
45
+ HCO => {node: :informationsHebergementCollectif, subtype: :hebergementCollectifType},
46
+ HLO => {node: :informationsHebergementLocatif, subtype: :hebergementLocatifType},
47
+ HOT => {node: :informationsHotellerie, subtype: :hotellerieType},
48
+ HPA => {node: :informationsHotelleriePleinAir, subtype: :hotelleriePleinAirType},
49
+ PCU => {node: :informationsPatrimoineCulturel, subtype: :patrimoineCulturelType},
50
+ PNA => {node: :informationsPatrimoineNaturel, subtype: :patrimoineNaturelType},
51
+ RES => {node: :informationsRestauration, subtype: :restaurationType},
52
+ SPA => {node: :informationsSejourPackage, subtype: :sejourPackageType},
53
+ STR => {node: :informationsStructure, subtype: :structureType},
54
+ TER => {node: :informationsTerritoire, subtype: :territoireType}
55
+ }
56
+
57
+ PHONE = 201
58
+ EMAIL = 204
59
+ WEBSITE = 205
60
+
61
+ def self.add_object(object_data)
62
+ apidae_obj = Obj.new(apidae_id: object_data[:id])
63
+ update_object(apidae_obj, object_data)
64
+ end
65
+
66
+ def self.update_object(apidae_obj, object_data)
67
+ populate_fields(apidae_obj, object_data)
68
+
69
+ if Rails.application.config.respond_to?(:apidae_aspect) && !object_data[:aspects].blank?
70
+ apidae_aspect = object_data[:aspects].find {|a| a[:aspect] == Rails.application.config.apidae_aspect}
71
+ if apidae_aspect
72
+ apidae_aspect[:type] = apidae_obj.apidae_type
73
+ aspect_obj = Obj.new
74
+ populate_fields(aspect_obj, apidae_aspect)
75
+ merge_fields(apidae_obj, aspect_obj)
76
+ end
77
+ end
78
+
79
+ apidae_obj.save!
80
+ end
81
+
82
+ def self.populate_fields(apidae_obj, object_data)
83
+ type_fields = TYPES_DATA[object_data[:type]]
84
+ apidae_obj.apidae_type = object_data[:type]
85
+ apidae_obj.apidae_subtype = node_id(object_data[type_fields[:node]], type_fields[:subtype])
86
+ apidae_obj.title = node_value(object_data, :nom)
87
+ apidae_obj.description_data = parse_desc_data(object_data[:presentation], object_data[:donneesPrivees])
88
+ apidae_obj.contact = contact(object_data[:informations])
89
+ apidae_obj.location_data = parse_location_data(object_data[:localisation], object_data[type_fields[:node]],
90
+ object_data[:territoires])
91
+ apidae_obj.town = town(object_data[:localisation])
92
+ apidae_obj.openings_data = parse_openings(object_data[:ouverture])
93
+ apidae_obj.rates_data = parse_rates(object_data[:descriptionTarif])
94
+ apidae_obj.reservation = parse_reservation(object_data[:reservation])
95
+ apidae_obj.type_data = parse_type_data(apidae_obj, object_data[type_fields[:node]], object_data[:prestations])
96
+ apidae_obj.pictures_data = pictures_urls(object_data[:illustrations])
97
+ apidae_obj.attachments_data = attachments_urls(object_data[:multimedias])
98
+ apidae_obj.entity_data = entity_fields(object_data[:informations], object_data[type_fields[:node]])
99
+ apidae_obj.service_data = parse_service_data(object_data[:prestations], object_data[type_fields[:node]])
100
+ apidae_obj.tags_data = parse_tags_data(object_data[:presentation], object_data[:criteresInternes], object_data[:liens])
101
+ apidae_obj.meta_data = object_data[:metadonnees]
102
+ end
103
+
104
+ def self.merge_fields(apidae_obj, aspect_obj)
105
+ apidae_obj.description_data.merge!(non_empty(aspect_obj.description_data)) if aspect_obj.description_data
106
+ apidae_obj.contact.merge!(non_empty(aspect_obj.contact)) if aspect_obj.contact
107
+ apidae_obj.location_data.merge!(non_empty(aspect_obj.location_data)) if aspect_obj.location_data
108
+ apidae_obj.openings_data.merge!(non_empty(aspect_obj.openings_data)) if aspect_obj.openings_data
109
+ apidae_obj.rates_data.merge!(non_empty(aspect_obj.rates_data)) if aspect_obj.rates_data
110
+ apidae_obj.reservation = aspect_obj.reservation unless aspect_obj.reservation.blank?
111
+ apidae_obj.type_data.merge!(non_empty(aspect_obj.type_data)) if aspect_obj.type_data
112
+ apidae_obj.pictures_data.merge!(non_empty(aspect_obj.pictures_data)) if aspect_obj.pictures_data
113
+ apidae_obj.attachments_data.merge!(non_empty(aspect_obj.attachments_data)) if aspect_obj.attachments_data
114
+ apidae_obj.service_data.merge!(non_empty(aspect_obj.service_data)) if aspect_obj.service_data
115
+ apidae_obj.tags_data.merge!(non_empty(aspect_obj.tags_data)) if aspect_obj.tags_data
116
+ apidae_obj.meta_data.merge!(non_empty(aspect_obj.meta_data)) if aspect_obj.meta_data
117
+ end
118
+
119
+ def self.non_empty(data_hash)
120
+ data_hash.keep_if {|k, v| !v.blank?}
121
+ end
122
+
123
+ def self.parse_desc_data(data_hash, private_data)
124
+ unless data_hash.blank?
125
+ {
126
+ short_desc: node_value(data_hash, :descriptifCourt),
127
+ long_desc: node_value(data_hash, :descriptifDetaille),
128
+ theme_desc: data_hash[:descriptifsThematises].blank? ? {} : Hash[data_hash[:descriptifsThematises].map {|th| [node_id(th, :theme), node_value(th, :description)]}],
129
+ private_desc: private_data.blank? ? {} : Hash[private_data.map {|d| [d[:nomTechnique], node_value(d, :descriptif)]}]
130
+ }
131
+ end
132
+ end
133
+
134
+ def self.pictures_urls(pictures_array)
135
+ pics_data = []
136
+ unless pictures_array.blank?
137
+ pictures_array.select { |p| p.is_a?(Hash) && !p[:traductionFichiers].blank? }.each do |pic|
138
+ pics_data << {
139
+ name: node_value(pic, :nom),
140
+ url: pic[:traductionFichiers][0][:url],
141
+ description: node_value(pic, :legende),
142
+ credits: node_value(pic, :copyright)
143
+ }
144
+ end
145
+ end
146
+ {pictures: pics_data}
147
+ end
148
+
149
+ def self.attachments_urls(attachments_array)
150
+ atts_data = []
151
+ unless attachments_array.blank?
152
+ attachments_array.select { |att| att.is_a?(Hash) && !att[:traductionFichiers].blank? }.each do |att|
153
+ atts_data << {
154
+ name: node_value(att, :nom),
155
+ url: att[:traductionFichiers][0][:url]
156
+ }
157
+ end
158
+ end
159
+ {attachments: atts_data}
160
+ end
161
+
162
+ def self.contact(information_hash)
163
+ contact_details = {}
164
+ unless information_hash.blank?
165
+ contact_entries = information_hash[:moyensCommunication] || []
166
+ contact_entries.each do |c|
167
+ case c[:type][:id]
168
+ when PHONE
169
+ contact_details[:telephone] ||= []
170
+ contact_details[:telephone] << c[:coordonnees][:fr]
171
+ when EMAIL
172
+ contact_details[:email] ||= []
173
+ contact_details[:email] << c[:coordonnees][:fr]
174
+ when WEBSITE
175
+ contact_details[:website] ||= []
176
+ contact_details[:website] << c[:coordonnees][:fr]
177
+ else
178
+ end
179
+ end
180
+ end
181
+ contact_details
182
+ end
183
+
184
+ def self.parse_location_data(location_hash, type_data_hash, territories)
185
+ loc_data = {}
186
+ unless location_hash.blank?
187
+ address_hash = location_hash[:adresse]
188
+ computed_address = []
189
+ unless address_hash.blank?
190
+ computed_address << address_hash[:adresse1] unless address_hash[:adresse1].blank?
191
+ computed_address << address_hash[:adresse2] unless address_hash[:adresse2].blank?
192
+ computed_address << address_hash[:adresse3] unless address_hash[:adresse3].blank?
193
+ end
194
+ loc_data.merge!({address: computed_address})
195
+ loc_data.merge!({place: type_data_hash[:nomLieu]}) if type_data_hash
196
+ geoloc_details = location_hash[:geolocalisation]
197
+ if geoloc_details && geoloc_details[:valide] && geoloc_details[:geoJson]
198
+ loc_data[:latitude] = geoloc_details[:geoJson][:coordinates][1]
199
+ loc_data[:longitude] = geoloc_details[:geoJson][:coordinates][0]
200
+ end
201
+ loc_data[:access] = node_value(geoloc_details, :complement) if geoloc_details
202
+ loc_data[:environments] = location_hash[:environnements].map {|e| e[:id]} if location_hash[:environnements]
203
+ end
204
+ loc_data[:territories] = territories.map {|t| t[:id]} unless territories.blank?
205
+ loc_data
206
+ end
207
+
208
+ def self.town(location_hash)
209
+ if location_hash
210
+ address_hash = location_hash[:adresse]
211
+ (!address_hash.blank? && address_hash[:commune]) ? Town.find_by_apidae_id(address_hash[:commune][:id]) : nil
212
+ else
213
+ nil
214
+ end
215
+ end
216
+
217
+ def self.parse_openings(openings_hash)
218
+ if openings_hash && openings_hash[:periodeEnClair]
219
+ {
220
+ openings_desc: openings_hash[:periodeEnClair][:libelleFr],
221
+ openings: openings_hash[:periodesOuvertures],
222
+ time_periods: lists_ids(openings_hash[:indicationsPeriode])
223
+ }
224
+ end
225
+ end
226
+
227
+ def self.parse_rates(rates_hash)
228
+ if rates_hash
229
+ desc = rates_hash[:gratuit] ? 'gratuit' : node_value(rates_hash, :tarifsEnClair)
230
+ values = rates_hash[:periodes].blank? ? [] : rates_hash[:periodes].map {|p| build_rate(p)}
231
+ methods = rates_hash[:modesPaiement].blank? ? [] : rates_hash[:modesPaiement].map {|p| p[:id]}
232
+ {rates_desc: desc, rates: values, payment_methods: methods, includes: node_value(rates_hash, :leTarifComprend), excludes: node_value(rates_hash, :leTarifNeComprendPas)}
233
+ end
234
+ end
235
+
236
+ def self.parse_type_data(apidae_obj, type_hash, presta_hash)
237
+ data_hash = type_hash || {}
238
+ prestations_hash = presta_hash || {}
239
+ apidae_obj.apidae_subtype = lists_ids(data_hash[:typesManifestation]).first if apidae_obj.apidae_type == FEM
240
+ apidae_obj.apidae_subtype = node_id(data_hash, :rubrique) if apidae_obj.apidae_type == EQU
241
+ apidae_obj.apidae_subtype = lists_ids(data_hash[:typesHebergement]).first if apidae_obj.apidae_type == SPA
242
+ {
243
+ categories: lists_ids(data_hash[:categories], data_hash[:typesDetailles], data_hash[:activiteCategories]),
244
+ themes: lists_ids(data_hash[:themes]),
245
+ capacity: (data_hash[:capacite] || {})
246
+ .merge(presta_hash ? {group_min: presta_hash[:tailleGroupeMin], group_max: presta_hash[:tailleGroupeMax],
247
+ age_min: presta_hash[:ageMin], age_max: presta_hash[:ageMax]} : {}),
248
+ classification: nodes_ids(data_hash[:classement], data_hash[:classementPrefectoral], data_hash[:classification]) +
249
+ lists_ids(data_hash[:classementsGuides]) + lists_ids(data_hash[:classements]),
250
+ labels: lists_ids(data_hash[:labels], prestations_hash[:labelsTourismeHandicap]) +
251
+ (node_id(data_hash, :typeLabel) ? [node_id(data_hash, :typeLabel)] : []),
252
+ chains: lists_ids(data_hash[:chaines]) + nodes_ids(data_hash[:chaineEtLabel]),
253
+ area: apidae_obj.apidae_type == DOS ? data_hash.except(:classification) : node_value(data_hash, :lieuDePratique),
254
+ track: apidae_obj.apidae_type == EQU ? data_hash[:itineraire] : nil,
255
+ products: lists_ids(data_hash[:typesProduit], data_hash[:aopAocIgps], data_hash[:specialites]),
256
+ audience: lists_ids(prestations_hash[:typesClientele]),
257
+ animals: prestations_hash[:animauxAcceptes] == 'ACCEPTES',
258
+ extra: apidae_obj.apidae_type == SPA ? node_value(data_hash, :formuleHebergement) : node_value(prestations_hash, :complementAccueil),
259
+ duration: apidae_obj.apidae_type == SPA ? {days: data_hash[:nombreJours], nights: data_hash[:nombreNuits]} : data_hash[:dureeSeance],
260
+ certifications: data_hash[:agrements].blank? ? [] : data_hash[:agrements].map {|a| {id: a[:type][:id], identifier: a[:numero]}}
261
+ }
262
+ end
263
+
264
+ def self.parse_service_data(data_hash, type_data_hash)
265
+ if data_hash
266
+ {
267
+ services: lists_ids(data_hash[:services]),
268
+ equipments: lists_ids(data_hash[:equipements]),
269
+ comfort: lists_ids(data_hash[:conforts]),
270
+ activities: lists_ids(data_hash[:activites], type_data_hash ? type_data_hash[:activites] : [],
271
+ type_data_hash ? type_data_hash[:activitesSportives] : [],
272
+ type_data_hash ? type_data_hash[:activitesCulturelles] : []),
273
+ challenged: lists_ids(data_hash[:tourismesAdaptes]),
274
+ languages: lists_ids(data_hash[:languesParlees])
275
+ }
276
+ end
277
+ end
278
+
279
+ def self.parse_tags_data(pres_data_hash, crit_data_hash, linked_data_hash)
280
+ tags = {}
281
+ if pres_data_hash
282
+ tags[:promo] = lists_ids(pres_data_hash[:typologiesPromoSitra])
283
+ end
284
+ unless crit_data_hash.blank?
285
+ tags[:internal] = crit_data_hash.map {|c| c[:id]}
286
+ end
287
+ unless linked_data_hash.blank? || linked_data_hash[:liensObjetsTouristiquesTypes].blank?
288
+ tags[:linked] = linked_data_hash[:liensObjetsTouristiquesTypes]
289
+ .map {|l| {apidae_id: l[:objetTouristique][:id], apidae_type: l[:objetTouristique][:type], category: l[:type]}}
290
+ end
291
+ tags
292
+ end
293
+
294
+ def self.parse_reservation(reservation_hash)
295
+ if reservation_hash
296
+ if reservation_hash[:complement]
297
+ reservation_hash[:complement][:libelleFr]
298
+ else
299
+ reservation_hash[:organismes]
300
+ end
301
+ end
302
+ end
303
+
304
+ def self.entity_fields(information_hash, type_data_hash)
305
+ if information_hash && information_hash[:structureGestion]
306
+ {entity_id: information_hash[:structureGestion][:id], service_provider_id: node_id(type_data_hash, :prestataireActivites)}
307
+ end
308
+ end
309
+
310
+ def contact_text
311
+ entries = []
312
+ JSON.parse(contact).each_pair do |k, v|
313
+ entries << "#{k}: #{v}"
314
+ end
315
+ entries.join("\n")
316
+ end
317
+
318
+ def main_picture
319
+ pictures.any? ? pictures[0]["url"] : "#{Rails.application.config.apidae_pictures_path}/default/logo.png"
320
+ end
321
+
322
+ def self.build_rate(rate_period)
323
+ {
324
+ id: rate_period[:identifiant], from: rate_period[:dateDebut], to: rate_period[:dateFin],
325
+ values: rate_period[:tarifs].blank? ? [] : rate_period[:tarifs].map {|t| {min: t[:minimum], max: t[:maximum], type: t[:type][:id], details: node_value(t, :precisionTarif)}}
326
+ }
327
+ end
328
+
329
+ private
330
+
331
+ def self.node_value(node, key)
332
+ if node && node[key]
333
+ node[key][:libelleFr]
334
+ else
335
+ ''
336
+ end
337
+ end
338
+
339
+ def self.node_id(node, key)
340
+ node[key][:id] if node && node[key]
341
+ end
342
+
343
+ def self.lists_ids(*lists)
344
+ lists.blank? ? [] : lists.map {|l| l.blank? ? [] : l.map {|elt| elt[:id]}}.flatten.uniq
345
+ end
346
+
347
+ def self.nodes_ids(*nodes)
348
+ nodes.blank? ? [] : nodes.select {|n| !n.blank?}.map {|n| n[:id]}
349
+ end
350
+ end
351
+ end