apidae 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
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