app/controllers/tours_controller.rb
# frozen_string_literal: true
class ToursController < ApplicationController
before_action :verify_current_user
before_action { verify_current_user_role("role_tour") }
before_action :init_graphql_client
before_action :load_category_list, only: [:edit, :new, :create]
def index
results = @smart_village.query <<~GRAPHQL
query {
tours {
id
name
categories {
name
}
visible
dataProvider {
id
name
}
updatedAt
createdAt
}
}
GRAPHQL
@tours = results.data.tours
end
def show
redirect_to edit_tour_path(params[:id])
end
def new
@tour = new_tour
end
def edit
results = @smart_village.query <<~GRAPHQL
query {
tour(
id: #{params[:id]}
) {
visible
categories {
id
name
}
id
name
lengthKm
description
mediaContents {
id
captionText
contentType
copyright
height
width
sourceUrl {
url
description
}
}
addresses {
addition
street
zip
city
kind
geoLocation {
latitude
longitude
}
}
contact {
id
email
fax
lastName
firstName
phone
webUrls{
url
description
}
}
webUrls {
id
url
description
}
operatingCompany {
name
contact {
firstName
lastName
phone
fax
email
webUrls {
url
description
}
}
address {
addition
street
zip
city
kind
geoLocation {
latitude
longitude
}
}
}
dataProvider {
name
}
geometryTourData {
latitude
longitude
}
tourStops {
id
name
description
payload
location {
geoLocation {
latitude
longitude
}
}
}
}
}
GRAPHQL
@tour = results.data.tour
rescue Graphlient::Errors::GraphQLError
flash[:error] = "Die angeforderte Ressource ist leider nicht verfügbar"
redirect_to tours_path
end
def create
query = create_or_update_mutation
begin
results = @smart_village.query query
rescue Graphlient::Errors::GraphQLError => e
flash[:error] = e.errors.messages["data"].to_s
@tour = new_tour
render :new
return
end
new_id = results.data.create_tour.id
flash[:notice] = "Tour wurde erstellt"
redirect_to edit_tour_path(new_id)
end
def update
tour_id = params[:id]
query = create_or_update_mutation(true)
# logger.warn(query)
begin
@smart_village.query query
rescue Graphlient::Errors::GraphQLError => e
flash[:error] = e.errors.messages["data"].to_s
end
redirect_to edit_tour_path(tour_id)
end
def destroy
results = @smart_village.query <<~GRAPHQL
mutation {
destroyRecord(
id: #{params["id"]},
recordType: "Tour"
) {
id
status
statusCode
}
}
GRAPHQL
flash["notice"] = if results.try(:data).try(:destroy_record).try(:status_code) == 200
"Eintrag wurde gelöscht"
else
"Fehler: #{results.errors.inspect}"
end
redirect_to tours_path
end
private
def tour_params
params.require(:tour).permit!
end
def new_tour
OpenStruct.new(
addresses: [OpenStruct.new(
geo_location: OpenStruct.new
)],
contact: OpenStruct.new(web_urls: [OpenStruct.new]),
web_urls: [OpenStruct.new],
operating_company: OpenStruct.new(
web_urls: [OpenStruct.new],
contact: OpenStruct.new(web_urls: [OpenStruct.new]),
address: OpenStruct.new
),
media_contents: [OpenStruct.new(source_url: OpenStruct.new)]
)
end
def create_or_update_mutation(update = false)
@tour_params = tour_params
convert_params_for_graphql
Converter::Base.new.build_mutation("createTour", @tour_params, update)
end
def convert_params_for_graphql
# Check recursively if any addresses data is given.
# If not, we do not want to submit the params, because the name is required by the model,
# which will result in a validation error.
if @tour_params["addresses"].present?
unless nested_values?(@tour_params["addresses"].to_h).include?(true)
@tour_params.delete :addresses
end
end
# Convert has_many categories
if @tour_params["categories"].present?
categories = []
@tour_params["categories"].each do |_key, category|
next if category.blank?
categories << category
end
@tour_params["categories"] = categories
end
# Convert length_km to Integer
@tour_params["length_km"] = if @tour_params["length_km"].present?
@tour_params["length_km"].to_i
else
0
end
# Convert has_many media_contents
if @tour_params["media_contents"].present?
media_contents = []
@tour_params["media_contents"].each do |_key, media_content|
next if media_content.blank?
media_contents << media_content if media_content.dig("source_url", "url").present?
end
@tour_params["media_contents"] = media_contents
end
# Convert has_many urls
if @tour_params["web_urls"].present?
web_urls = []
@tour_params["web_urls"].each do |_key, url|
next if url.blank?
web_urls << url
end
@tour_params["web_urls"] = web_urls
end
# Check recursively if any contact data is given.
# If not, we do not want to submit the params, because the name is required by the model,
# which will result in a validation error.
if @tour_params["contact"].present?
unless nested_values?(@tour_params["contact"].to_h).include?(true)
@tour_params.delete :contact
end
end
# Check recursively if any operating_company data is given.
# If not, we do not want to submit the params, because the name is required by the model,
# which will result in a validation error.
if @tour_params["operating_company"].present?
unless nested_values?(@tour_params["operating_company"].to_h).include?(true)
@tour_params.delete :operating_company
end
end
# Convert has_many tour_stops
if @tour_params["tour_stops"].present?
tour_stops = []
@tour_params["tour_stops"].each do |_key, tour_stop|
next if tour_stop.blank?
next if tour_stop["name"].blank?
if tour_stop["payload"].present?
if tour_stop["payload"]["scenes"].present?
scenes = []
total_size_calculated_from_downloadable_uris = 0
tour_stop["payload"]["scenes"].each do |_key, scene|
next if scene.blank?
if scene["downloadable_uris"].present?
downloadable_uris = []
scene["downloadable_uris"].each do |_key, downloadable_uri|
next if downloadable_uri.blank?
# skip entries with empty url but set an empty object to keep indexes in order
if downloadable_uri.keys.include?("uri") && downloadable_uri["uri"].blank?
downloadable_uris << {}
next
end
set_defaults_and_types(downloadable_uri)
# calculate total size
if downloadable_uri["size"].present?
total_size_calculated_from_downloadable_uris += downloadable_uri["size"]
end
downloadable_uris << downloadable_uri
end
scene["downloadable_uris"] = downloadable_uris
end
scene["local_uris"] = []
scenes << scene
end
if scenes.count.positive?
# add target, mp3, mp4, image, light, quad and spot to the first scene
# if there is a start date and time period in days, otherwise add them to the last
# scene in order to have them at the correct place in the object for the mobile app
scene = scenes.last
if tour_stop["payload"]["start_date"].present? && tour_stop["payload"]["time_period_in_days"].present?
scene = scenes.first
end
scene_downloadable_uris = scene["downloadable_uris"]
if tour_stop["payload"]["target"].present? && tour_stop["payload"]["target"]["uri"].present?
target = set_defaults_and_types(tour_stop["payload"]["target"])
target["id"] = "-1"
scene_downloadable_uris.unshift(target)
total_size_calculated_from_downloadable_uris += target["size"].to_i
end
if tour_stop["payload"]["mp3"].present? && tour_stop["payload"]["mp3"]["uri"].present?
mp3 = set_defaults_and_types(tour_stop["payload"]["mp3"])
mp3["id"] = "-2"
scene_downloadable_uris.unshift(mp3)
total_size_calculated_from_downloadable_uris += mp3["size"].to_i
end
if tour_stop["payload"]["mp4"].present? && tour_stop["payload"]["mp4"]["uri"].present?
mp4 = set_defaults_and_types(tour_stop["payload"]["mp4"])
mp4["id"] = "-3"
scene_downloadable_uris.unshift(mp4)
total_size_calculated_from_downloadable_uris += mp4["size"].to_i
end
if tour_stop["payload"]["image"].present? && tour_stop["payload"]["image"]["uri"].present?
image = set_defaults_and_types(tour_stop["payload"]["image"])
image["id"] = "-4"
scene_downloadable_uris.unshift(image)
total_size_calculated_from_downloadable_uris += image["size"].to_i
end
if tour_stop["payload"]["light"].present?
light = set_defaults_and_types(tour_stop["payload"]["light"])
light["id"] = "-5"
scene_downloadable_uris.unshift(light)
end
if tour_stop["payload"]["quad"].present?
quad = set_defaults_and_types(tour_stop["payload"]["quad"])
quad["id"] = "-6"
quad["height"] = 1000
quad["width"] = 1000
scene_downloadable_uris.unshift(quad)
end
if tour_stop["payload"]["spot"].present?
spot = tour_stop["payload"]["spot"]
spot["id"] = "-7"
spot["shadow_opacity"] = spot["shadow_opacity"].presence || 0.6
spot["inner_angle"] = spot["inner_angle"].presence || 5
spot["outer_angle"] = spot["outer_angle"].presence || 45
spot["shadow_map_size"] = 2048
spot["position"] = if spot["position"].present?
JSON.parse(spot["position"])
else
[0, 3, 1]
end
spot["direction"] = if spot["direction"].present?
JSON.parse(spot["direction"])
else
[0, -1, -0.2]
end
scene_downloadable_uris.unshift(spot)
end
end
tour_stop["payload"]["scenes"] = scenes
# converts to integer
if tour_stop["payload"]["time_period_in_days"].present?
tour_stop["payload"]["time_period_in_days"] = tour_stop["payload"]["time_period_in_days"].to_i
end
end
# set total size
tour_stop["payload"]["total_size"] = total_size_calculated_from_downloadable_uris
# set other defaults in payload
tour_stop["payload"]["progress"] = 0
tour_stop["payload"]["progress_size"] = 0
tour_stop["payload"]["size"] = 0
tour_stop["payload"]["type_format"] = "VRX"
tour_stop["payload"]["download_type"] = "downloadable"
end
tour_stops << tour_stop
end
@tour_params["tour_stops"] = tour_stops
end
end
def set_defaults_and_types(entry)
# set default values
entry["color"] = entry["color"].presence || "#ffffff" if entry.keys.include?("color")
if entry.keys.include?("temperature")
entry["temperature"] = entry["temperature"].presence || "6500"
end
entry["intensity"] = entry["intensity"].presence || "1000" if entry.keys.include?("intensity")
# converts to float
entry["min_distance"] = entry["min_distance"].to_f if entry["min_distance"].present?
entry["max_distance"] = entry["max_distance"].to_f if entry["max_distance"].present?
# converts to array or set default value
if entry.keys.include?("position")
entry["position"] = if entry["position"].present?
JSON.parse(entry["position"])
else
[0, 0, 0]
end
end
if entry.keys.include?("scale")
entry["scale"] = if entry["scale"].present?
JSON.parse(entry["scale"])
else
[1, 1, 1]
end
end
if entry.keys.include?("rotation")
entry["rotation"] = if entry["rotation"].present?
JSON.parse(entry["rotation"])
else
[0, 0, 0]
end
end
if entry.keys.include?("direction")
entry["direction"] = if entry["direction"].present?
JSON.parse(entry["direction"])
else
[0, 0, 0]
end
end
# converts to boolean
if entry.keys.include?("is_spatial_sound")
entry["is_spatial_sound"] = entry["is_spatial_sound"].to_s == "true"
end
# converts to integer
entry["size"] = entry["size"].to_i if entry["size"].present?
entry["temperature"] = entry["temperature"].to_i if entry["temperature"].present?
entry["intensity"] = entry["intensity"].to_i if entry["intensity"].present?
entry
end
end