#!/usr/bin/ruby
#
# This file is part of centurio.work/ing/commands.
#
# centurio.work/ing/commands is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# centurio.work/ing/commands is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# centurio.work/ing/commands (file COPYING in the main directory). If not, see
# .
require 'rubygems'
require 'json'
require 'riddl/server'
require 'riddl/client'
require 'fileutils'
require 'nokogiri'
require 'sqlite3'
require 'net/http'
$db = SQLite3::Database.open 'database/stations.db'
class GetStations < Riddl::Implementation
def response
result = $db.execute "SELECT * FROM stations"
builder = Nokogiri::XML::Builder.new do |xml|
xml.stations {
result.each do |row|
xml.station(:id => row[0]){
resultstation = $db.execute "SELECT * FROM station WHERE station = #{row[0]}"
resultstation.each do |row|
xml.pattern(:id => row[1], :value => row[2], :description => row[3], :changed => row[4])
end
}
end
}
end
#puts builder.to_xml
Riddl::Parameter::Complex.new('stations','application/xml',builder.to_xml)
end
end
class CreateStation < Riddl::Implementation
def self::createDB(n)
#result = $db.execute "SELECT MAX(station) FROM stations"
#if(result[0][0] == nil)
# $db.execute "INSERT INTO stations (station) VALUES (?)", 0
#else
# $db.execute "INSERT INTO stations (station) VALUES (?)", result[0][0]+1
#end
#$db.execute ("INSERT INTO stations (station, name) VALUES (?,?)", [n,n])
$db.execute("INSERT OR IGNORE INTO stations (station, name) VALUES (?,?)", [n,n])
end
def response
GetStation::createDB
end
end
class GetStation < Riddl::Implementation
def self::prepare(id)
result = $db.execute "SELECT * FROM station WHERE station = #{id}"
Nokogiri::XML::Builder.new do |xml|
xml.station(:id => id){
result.each do |row|
xml.pattern(:id => row[1], :value => row[2], :description => row[3], :changed => row[4])
end
}
end
end
def response
builder = GetStation::prepare(@r.last)
Riddl::Parameter::Complex.new('station','application/xml',builder.to_xml)
end
end
class CreatePattern < Riddl::Implementation
def response
doc = Nokogiri::XML(@p[0].value)
result = $db.execute "SELECT MAX(patternID) FROM station"
if(result[0][0] == nil)
id = 0
else
id = result[0][0] +1
end
values = doc.xpath("/*/@value")[0].value.split(".")
$db.execute("INSERT INTO station (station, patternID, pattern, description, date, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", [@r.last, id, doc.xpath("/*/@value")[0].value, doc.xpath("/*/@description")[0].value, doc.xpath("/*/@changed")[0].value, values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9], values[10]])
ret = {:id => id}
Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(ret))
end
end
class GetPattern < Riddl::Implementation
def response
result = $db.execute "SELECT * FROM station WHERE station = #{@r[0]} and patternID = #{@r[1]}"
builder = Nokogiri::XML::Builder.new do |xml|
xml.pattern(:id => result[0][1], :value => result[0][2], :description => result[0][3], :changed => result[0][4])
end
Riddl::Parameter::Complex.new('pattern','application/xml',builder.to_xml)
end
end
class UpdatePattern < Riddl::Implementation
def response
doc = Nokogiri::XML(@p[0].value)
#puts "UPdate Pattern" + @r[0] + @r[1] + " value " + doc.xpath("/*/@value")[0].value
#$db.execute("DELETE FROM station WHERE station = ? AND patternID = ?", [@r[0], @r[1]])
values = doc.xpath("/*/@value")[0].value.split(".")
#$db.execute("INSERT INTO station (station, patternID, pattern, description, date, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", [@r[0], @r[1], doc.xpath("/*/@value")[0].value, doc.xpath("/*/@description")[0].value, doc.xpath("/*/@changed")[0].value, values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9], values[10]])
#$db.execute("UPDATE image SET imageID = ? WHERE station = ? AND patternID = ? AND imageID = ? AND language = ?", [999999, @r[0], @r[1], i, @r[4]])
$db.execute("Update station SET pattern = ?, description = ?, date = ?, P0 = ?, P1 = ?, P2 = ?, P3 = ?, P4 = ?, P5 = ?, P6 = ?, P7 = ?, P8 = ?, P9 = ?, P10 = ? WHERE station = ? AND patternID = ?", [doc.xpath("/*/@value")[0].value, doc.xpath("/*/@description")[0].value, doc.xpath("/*/@changed")[0].value, values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9], values[10], @r[0], @r[1]])
#$db.execute("INSERT INTO station (station, patternID, pattern, description, date) VALUES (?,?,?,?,?)", [@r[0], @r[1]], doc.xpath("/*/@value")[0].value, doc.xpath("/*/@description")[0].value, doc.xpath("/*/@changed")[0].value)
#$db.execute("UPDATE station SET pattern = ? and description = ? and date = ? WHERE station = ? and patternID = ?", [doc.xpath("/*/@value")[0].value, doc.xpath("/*/@description")[0].value, doc.xpath("/*/@changed")[0].value, @r[0], @r[1]])
end
end
class DeletePattern < Riddl::Implementation
def response
$db.execute("DELETE FROM station WHERE station = ? AND patternID = ?", [@r[0], @r[1]])
end
end
class DuplicatePattern < Riddl::Implementation
def response
#duplicate db etwas mühsam da der eintrag kopiert werden muss und dabei die unique ID verändert werden muss daher geht insert into select nicht so ganz.
result = $db.execute "SELECT MAX(patternID) FROM station"
if(result[0][0] == nil)
id = 0
else
id = result[0][0] +1
end
result = $db.execute "SELECT * FROM station WHERE station = #{@r[0]} and patternID = #{@r[1]}"
result[0][1] = id;
questionmarks = ""
result[0].length().times{questionmarks = questionmarks + ",?"}
questionmarks[0] = ""
$db.execute( "INSERT INTO station values (#{questionmarks})", result[0])
#duplicate error
result = $db.execute "SELECT * FROM error WHERE station = #{@r[0]} and patternID = #{@r[1]}"
if(result[0] != nil)
result.each do |row|
row[1] = id;
questionmarks = ""
row.length().times{questionmarks = questionmarks + ",?"}
questionmarks[0] = ""
$db.execute( "INSERT INTO error values (#{questionmarks})", row)
end
end
#duplicate image db
result = $db.execute "SELECT * FROM image WHERE station = #{@r[0]} and patternID = #{@r[1]}"
if(result[0] != nil)
result.each do |row|
row[1] = id;
questionmarks = ""
row.length().times{questionmarks = questionmarks + ",?"}
questionmarks[0] = ""
$db.execute( "INSERT INTO image values (#{questionmarks})", row)
end
end
#duplicate images
pathorigin = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1])
pathdestination = File.join(File.dirname(__dir__),'images/uploads', @r[0], id.to_s)
FileUtils.mkdir_p(pathdestination)
FileUtils.copy_entry pathorigin, pathdestination
#duplicate Replacement db
result = $db.execute "SELECT * FROM replacements WHERE station = #{@r[0]} and patternID = #{@r[1]}"
if(result[0] != nil)
result.each do |row|
row[1] = id;
questionmarks = ""
row.length().times{questionmarks = questionmarks + ",?"}
questionmarks[0] = ""
$db.execute( "INSERT INTO replacements values (#{questionmarks})", row)
end
end
ret = {:id => id}
Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(ret))
end
end
class SaveError < Riddl::Implementation
def response
$db.execute "CREATE TABLE IF NOT EXISTS error(station INT, patternID INT, error TEXT, FOREIGN KEY(station) REFERENCES station(station), FOREIGN KEY(patternID) REFERENCES station(patternID))"
#Delete all, afterwards insert
$db.execute("DELETE FROM error WHERE station = ? AND patternID = ?", [@r[0], @r[1]])
doc = Nokogiri::XML(@p[0].value)
doc.xpath(".//reason").each do |node|
$db.execute("INSERT INTO error (station, patternID, error) VALUES (?,?,?)", [@r[0], @r[1], node.text])
end
end
end
class GetError < Riddl::Implementation
def response
$db.execute "CREATE TABLE IF NOT EXISTS error(station INT, patternID INT, error TEXT, FOREIGN KEY(station) REFERENCES station(station), FOREIGN KEY(patternID) REFERENCES station(patternID))"
result = $db.execute "SELECT * FROM error WHERE station = #{@r[0]} and patternID = #{@r[1]}"
builder = Nokogiri::XML::Builder.new do |xml|
xml.error(){
result.each do |row|
xml.reason row[2]
end
}
end
Riddl::Parameter::Complex.new('error','application/xml',builder.to_xml)
end
end
class SaveReplacement < Riddl::Implementation
def response
#Delete all, afterwards insert
$db.execute("DELETE FROM replacements WHERE station = ? AND patternID = ?", [@r[0], @r[1]])
order = 0;
doc = Nokogiri::XML(@p[0].value)
doc.xpath(".//item").each do |node|
$db.execute("INSERT INTO replacements (station, patternID, abbreviation, url, ordering) VALUES (?,?,?,?,?)", [@r[0], @r[1], node.xpath(".//abbreviation").text, node.xpath(".//url").text, order])
order += 1
end
end
end
class GetReplacement < Riddl::Implementation
def response
result = $db.execute "SELECT * FROM replacements WHERE station = #{@r[0]} and patternID = #{@r[1]} ORDER BY ordering ASC"
builder = Nokogiri::XML::Builder.new do |xml|
xml.replacement(){
result.each do |row|
xml.item(){
xml.abbreviation row[2]
xml.url row[3]
}
end
}
end
Riddl::Parameter::Complex.new('replacement','application/xml',builder.to_xml)
end
end
class GetImages < Riddl::Implementation
def response
formatted_no_decl = Nokogiri::XML::Node::SaveOptions::FORMAT +
Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
result = $db.execute "SELECT DISTINCT station, patternID, imageID FROM image WHERE station = #{@r[0]} and patternID = #{@r[1]}"
builder = Nokogiri::XML::Builder.new do |xml|
xml.images(){
result.each do |row|
xml<< GetImage::prepare(@r[0], @r[1], row[2]).to_xml( save_with:formatted_no_decl )
end
}
end
Riddl::Parameter::Complex.new('images','application/xml',builder.to_xml)
end
end
class UploadImage < Riddl::Implementation
def response
lang = @p[@p.length-1].value
result = $db.execute "SELECT MAX(imageID) FROM image WHERE station = #{@r[0]} and patternID = #{@r[1]} and language = '#{lang}'"
if(result[0][0] == nil)
id = 0
else
id = result[0][0] +1
end
#puts JSON.pretty_generate(@p)
i = 0
while i < @p.length-1 do
item = @p[i]
if(item != nil && item.name == "files[]")
path = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], id.to_s)
FileUtils.mkdir_p(path)
#juergen nach alternative fragen
readFile = File.read(item.value.inspect.to_s[/Tempfile:(.*?)>/m, 1])
File.open(File.join(path, lang + ".svg"), 'wb') do |file|
file.write(readFile.to_s)
end
$db.execute("INSERT INTO image (station, patternID, imageID, language, label) VALUES (?,?,?,?,?)", [@r[0], @r[1], id, lang, "Label"])
id += 1
end
i +=1
end
end
end
class AddExternalImage < Riddl::Implementation
def response
doc = Nokogiri::XML(@p[0].value)
url = doc.xpath("/externalImage/url").text
lang = doc.xpath("/externalImage/lang").text
result = $db.execute "SELECT MAX(imageID) FROM image WHERE station = #{@r[0]} and patternID = #{@r[1]} and language = '#{lang}'"
if(result[0][0] == nil)
id = 0
else
id = result[0][0] +1
end
#Create link file (we create a file so moving/deleting works always the same and the database does not have to be changed for external files) only thing that had to be changed was imgReplacement.php
image = ''
path = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], id.to_s)
File.open(File.join(path, lang + ".svg"), 'wb') do |file|
file.write(image)
end
#Create DB entry as usual
$db.execute("INSERT INTO image (station, patternID, imageID, language, label) VALUES (?,?,?,?,?)", [@r[0], @r[1], id, lang, url])
end
end
class ReorderImages < Riddl::Implementation
def response
#remove brackets
iter = @p[0].value.read.chop
iter[0] =""
iter = iter.split(",").map(&:to_i)
i = 0
while i < iter.length do
if(i != iter[i])
#swap
path = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], i.to_s)
path2 = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], iter[i].to_s)
tmp = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1])
FileUtils.mv(path + "/" + @r[4] + ".svg", tmp)
FileUtils.mv(path2 + "/" + @r[4] + ".svg", path)
FileUtils.mv(tmp + "/" + @r[4] + ".svg", path2)
#DB Swap
result = $db.execute "SELECT MAX(imageID) FROM image"
if(result[0][0] == nil)
maxImgId = 0
else
maxImgId = result[0][0] +1
end
#Wegen 999999 fragen
#nutze max ID+1 zum tauschen
$db.execute("UPDATE image SET imageID = ? WHERE station = ? AND patternID = ? AND imageID = ? AND language = ?", [maxImgId, @r[0], @r[1], i, @r[4]])
$db.execute("UPDATE image SET imageID = ? WHERE station = ? AND patternID = ? AND imageID = ? AND language = ?", [i, @r[0], @r[1], iter[i], @r[4]])
$db.execute("UPDATE image SET imageID = ? WHERE station = ? AND patternID = ? AND imageID = ? AND language = ?", [iter[i], @r[0], @r[1], maxImgId, @r[4]])
iter.map! do |item|
if(item == i)
iter[i]
else
item
end
end
end
i +=1
end
end
end
class GetImage < Riddl::Implementation
def self::prepare(station, pattern, imageID)
result = $db.execute "SELECT * FROM image WHERE station = #{station} and patternID = #{pattern} and imageID = #{imageID}"
Nokogiri::XML::Builder.new do |xml|
xml.image(:id => imageID){
result.each do |row|
xml.variant(:lang => row[3], :label => row[4]){
xml.text("https://centurio.work/customers/evva/was/images/uploads/#{station}/#{pattern}/#{imageID}/#{row[3]}.svg")
}
end
}
end
end
def response
builder = GetImage::prepare(@r[0], @r[1], @r[3])
Riddl::Parameter::Complex.new('image','application/xml',builder.to_xml)
end
end
class UpdateImageLabel < Riddl::Implementation
def response
$db.execute("UPDATE image SET label = ? WHERE station = ? AND patternID = ? AND imageID = ? AND language = ?", [@p[0].value, @r[0], @r[1], @r[3], @r[4]])
end
end
class GetRealImage < Riddl::Implementation
def response
#split on "." and tacke [0] to allow for e.g. de-at.svg
#would lead to error on de.at.svg //should this be fixed? would also lead to error if everything after last "." would be removed in case of only having de.at
#https://centurio.work/customers/evva/was/server/0/0/images/3/de-at?video=xyz
img = File.read(File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], @r[3], @r[4].split(".")[0] + ".svg"))
if false #Currently replacements are done on the client side
if(@p[0].nil?)
puts "Undefined p"
else
if(@p[0].name == "video" && @p[0].value)
xml = Nokogiri.parse img
puts "Width " + xml.xpath("string(//xmlns:image/@width)")
puts "Height " + xml.xpath("string(//xmlns:image/@height)")
#puts "Posi " + xml.xpath("string(//xmlns:clipPath/path/@height)")
puts "Posi " + xml.xpath("string(//xmlns:image/following-sibling::clipPath/@id)")
puts xml.xpath("string(//xmlns:text[starts-with(text(), 'url')])").sub("url=", "")
# text
img = img.sub! "", '
'
else
@p.each do |item|
unless item.name.nil? || item.value.nil?
img.sub! item.name, item.value
end
end
end
end
end
Riddl::Parameter::Complex.new('theRealImage','image/svg+xml',img)
end
end
class DeleteImage < Riddl::Implementation
def response
#puts "Delete Image"
#puts File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], @r[3] , @r[4] + ".svg")
File.delete(File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], @r[3] , @r[4] + ".svg")) if File.exist?(File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], @r[3] , @r[4] + ".svg"))
$db.execute("DELETE FROM image WHERE station = ? AND patternID = ? AND imageID = ? AND language = ?", [@r[0], @r[1], @r[3], @r[4]])
result = $db.execute "SELECT MAX(imageID) FROM image WHERE station = #{@r[0]} and patternID = #{@r[1]} and language = '#{@r[4]}'"
if(result[0][0] == nil)
id = 0
else
id = result[0][0] +1
end
#reorder if !end gets deleted
if(id == @r[3])
return
else
cur = @r[3].to_i + 1
prev = @r[3].to_i
while cur < id.to_i do
$db.execute("UPDATE image SET imageID = ? WHERE station = ? AND patternID = ? AND imageID = ? AND language = ?", [prev, @r[0], @r[1], cur, @r[4]])
src = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], cur.to_s, @r[4] + ".svg")
target = File.join(File.dirname(__dir__),'images/uploads', @r[0], @r[1], prev.to_s, @r[4] + ".svg")
FileUtils.mv(src, target)
cur+=1
prev += 1
end
end
end
end
class ListSearch < Riddl::Implementation
def response
ret = {
:patternID => "/patternID",
:multi => "/multi",
:juergen => "/juergen"
}
Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(ret))
end
end
class SearchPattern < Riddl::Implementation
def response
iter = @p[0].value.split(".")
searchstring = ""
count = 0
iter.each do |item|
if !item.nil?
if item.to_s != "*"
searchstring = searchstring + " and (P" + count.to_s + "='*' or P" + count.to_s + " = '" + item.to_s + "')"
end
count += 1
end
end
result = $db.execute "SELECT patternID FROM station WHERE station = " + @r[0] + searchstring
if result.length > 0
ret = {
:amount => result.length.to_s,
:ids => result
}
end
Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(ret))
#puts the first found string
#result = $db.execute "SELECT P0,P1,P2,P3,P4,P5,P6,P7,P8,P9,P10 FROM station WHERE station = 0" + searchstring
#puts "found" + result.length.to_s
#result[0].each do |item|
# if !item.nil?
# puts item + "."
# end
#end
end
end
class SearchImages < Riddl::Implementation
def response
iter = @p[0].value.split(".")
searchstring = ""
count = 0
#search = K.*.9.*
#match: = K.5.9.7
#match: = K.5.9.*
#match: = K.*.*.*
#no match: = K.*.8.*
iter.each do |item|
if !item.nil?
if item.to_s != "*"
searchstring = searchstring + " and (P" + count.to_s + "='*' or P" + count.to_s + " = '" + item.to_s + "')"
else
searchstring = searchstring + " and (P" + count.to_s + "!=''" + ")"
end
count += 1
end
end
result = $db.execute "SELECT patternID FROM station WHERE station = " + @r[0] + searchstring
ret = []
if result.length > 0
result.each do |item|
result2 = $db.execute "SELECT DISTINCT station, patternID, imageID FROM image WHERE station = #{@r[0]} and patternID = #{item[0]}"
result2.each do |item2|
ret << item2[0].to_s + "/" + item2[1].to_s + "/" + item2[2].to_s
end
end
end
Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(ret))
end
end
class SearchImagesSingle < Riddl::Implementation
def response
iter = @p[0].value.split(".")
searchstring = ""
count = 0
iter.each do |item|
if !item.nil?
if item.to_s != "*"
searchstring = searchstring + " and (P" + count.to_s + "='*' or P" + count.to_s + " = '" + item.to_s + "')"
else
searchstring = searchstring + " and (P" + count.to_s + "!=''" + ")"
end
count += 1
end
end
result = $db.execute "SELECT patternID FROM station WHERE station = " + @r[0] + searchstring
count = 0
pattern = 0
image = 0
if result.length > 0
result.each do |item|
result2 = $db.execute "SELECT DISTINCT station, patternID, imageID FROM image WHERE station = #{@r[0]} and patternID = #{item[0]}"
result2.each do |item2|
if(count == @r[3].to_i)
puts "Found "
pattern = item2[1]
image = item2[2]
end
count += 1
end
end
end
img = File.read(File.join(File.dirname(__dir__),'images/uploads', @r[0].to_s, pattern.to_s, image.to_s, "de-at.svg"))
Riddl::Parameter::Complex.new('theRealImage','image/svg+xml',img)
end
end
class SearchImages2 < Riddl::Implementation
def response
iter = @p[0].value.split(".")
searchstring = ""
count = 0
#search = K.*.9.*
#match: = K.5.9.7
#match: = K.5.9.*
#match: = K.*.*.*
#no match: = K.*.8.*
iter.each do |item|
if !item.nil?
if item.to_s != "*"
searchstring = searchstring + " and (P" + count.to_s + "='*' or P" + count.to_s + " = '" + item.to_s + "')"
else
searchstring = searchstring + " and (P" + count.to_s + "!=''" + ")"
end
count += 1
end
end
result = $db.execute "SELECT patternID FROM station WHERE station = " + @r[0] + searchstring
ret = []
if result.length > 0
result.each do |item|
result2 = $db.execute "SELECT DISTINCT station, patternID, imageID FROM image WHERE station = #{@r[0]} and patternID = #{item[0]}"
result2.each do |item2|
ret << item2[0].to_s + "/" + item2[1].to_s + "/" + item2[2].to_s
end
end
end
Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(ret))
end
end
class SearchImages2Single < Riddl::Implementation
def response
iter = @p[0].value.split(".")
searchstring = ""
count = 0
iter.each do |item|
if !item.nil?
if item.to_s != "*"
searchstring = searchstring + " and (P" + count.to_s + "='*' or P" + count.to_s + " = '" + item.to_s + "')"
else
searchstring = searchstring + " and (P" + count.to_s + "!=''" + ")"
end
count += 1
end
end
result = $db.execute "SELECT patternID FROM station WHERE station = " + @r[0] + searchstring
count = 0
pattern = 0
image = 0
if result.length > 0
result.each do |item|
result2 = $db.execute "SELECT DISTINCT station, patternID, imageID FROM image WHERE station = #{@r[0]} and patternID = #{item[0]}"
result2.each do |item2|
if(count == @r[3].to_i)
puts "Found "
pattern = item2[1]
image = item2[2]
end
count += 1
end
end
end
#https://centurio.work/customers/evva/was/ui/imageReplacement.php?___image___=8/23/0/de-at.svg
img = File.read(File.join(File.dirname(__dir__),'images/uploads', @r[0].to_s, pattern.to_s, image.to_s, "de-at.svg"))
uri = "https://centurio.work/customers/evva/was/ui/imageReplacement.php?___image___=" + @r[0].to_s + "/" + pattern.to_s + "/" + image.to_s + "/de-at.svg"
string = '