#!/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 'xml/smart'
require 'riddl/server'
require 'fileutils'
require 'typhoeus'
require 'sqlite3'
class ConfigSites < Riddl::Implementation
def response
Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','index.html')))
end
end
class GetAllConfigs < Riddl::Implementation
def response
Dir.chdir( __dir__ + '/data')
databack = JSON::pretty_generate(Dir.glob('*/').sort_by{|x| x.downcase});
Dir.chdir( __dir__)
Riddl::Parameter::Complex.new('list','application/json',databack)
end
end
class GetAllVisus < Riddl::Implementation
def response
Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r[-2],'visus.html')))
end
end
class Get < Riddl::Implementation
def response
#Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'data', @r.last,'index.html')))
end
end
class GetAllData < Riddl::Implementation
def response
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
#db.execute "CREATE TABLE IF NOT EXISTS Entries(seriennummer TEXT PRIMARY KEY, produktcode TEXT, activity TEXT, cpeeInstance INTEGER)"
db.results_as_hash = true
alldata = {};
alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC")
Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
else
Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
end
end
end
class AddColumn < Riddl::Implementation
def response
colname = @p[0].value
colvalue = @p[1].value
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
searchstring = "SELECT DISTINCT __orderID__, alldata FROM Entries"
result = db.execute searchstring;
result.each do |item|
key = item[0]
newalldata = item[1]
thaData = JSON.parse(newalldata)
thaData[colname] = colvalue;
db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", thaData.to_json, key )
end
Riddl::Parameter::Complex.new('value','application/json',JSON.dump({status: "looks good?"}))
end
end
class DeleteColumn < Riddl::Implementation
def response
colname = @p[0].value
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
searchstring = "SELECT DISTINCT __orderID__, alldata FROM Entries"
result = db.execute searchstring;
result.each do |item|
key = item[0]
newalldata = item[1]
thaData = JSON.parse(newalldata)
thaData.delete(colname)
db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", thaData.to_json, key )
end
Riddl::Parameter::Complex.new('value','application/json',JSON.dump({status: "looks good?"}))
end
end
class GetNData < Riddl::Implementation
def response
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
db.results_as_hash = true
alldata = {};
alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
else
Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
end
end
end
class DeleteNData < Riddl::Implementation
def response
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
db.results_as_hash = true
alldata = {};
alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1 OFFSET " + @r[-1])
if alldata["data"] != []
id = alldata["data"][0]["__orderID__"].to_s
db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id + "'")
end
else
nil
end
@a[0].send("reset")
end
end
# used when executed from e.g. website, in such a case no cpee instance is given
class AddEntry < Riddl::Implementation
def response
newEntry = JSON.parse(@p[0].value.read)
fname = File.join('data',@r.last,'data.db')
db = SQLite3::Database.open fname
# in case primary key is given
# PRIMARY KEY Momentan noch nicht implementiert
if newEntry.has_key?("pkvalue")
db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT, cpeeInstance INTEGER)"
db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",JSON.dump(newEntry["alldata"]), newEntry["pkvalue"], "Direkt")
hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), pkvalue: newEntry["pkvalue"], cpeeInstance: "Direkt"};
else
db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",JSON.dump(newEntry["alldata"]), "Direkt")
hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: JSON.dump(newEntry["alldata"]), cpeeInstance: "Direkt"};
end
@a[0].send("reset")
nil
end
end
class Put < Riddl::Implementation
def self::putentry(r, p, h)
Dir.mkdir(File.join('data',r.last)) rescue nil
Dir.mkdir(File.join('data',r.last, "js")) rescue nil
Dir.mkdir(File.join('data',r.last, "visus")) rescue nil
#prevent overriding ui and index in case user changed something there
if !File.file?(File.join('data',r.last,'js/ui.js'))
File.write(File.join('data',r.last,'js/ui.js'),File.open("defaultContent/js/ui.js").read)
end
if !File.file?(File.join('data',r.last,'index.html'))
file = File.open("defaultContent/index.html").read
newfile = file.gsub("!replaceThisString!", r.last)
File.write(File.join('data',r.last,'index.html'), newfile)
end
#prevent overriding visualization
if !File.file?(File.join('data',r.last,'visus.html'))
File.write(File.join('data',r.last,'visus.html'),File.open("defaultContent/visus.html").read)
end
fname = File.join('data',r.last,'data.db')
db = SQLite3::Database.open fname
#in case primary key is given
if p[1] != nil
keyfunction = ""
if p[2] != nil
keyfunction = p[2].value
end
db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, pkvalue TEXT " + keyfunction + ", cpeeInstance INTEGER)"
db.execute("INSERT OR REPLACE INTO Entries (alldata, pkvalue, cpeeInstance) VALUES (?,?,?)",p[0].value, p[1].value, h['CPEE_INSTANCE'])
hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, pkvalue: p[1].value, cpeeInstance: h['CPEE_INSTANCE']};
else
db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)",p[0].value, h['CPEE_INSTANCE'])
hash = {__orderID__: db.execute("select last_insert_rowid()")[0], alldata: p[0].value, cpeeInstance: h['CPEE_INSTANCE']};
end
hash;
end
def response
hash = Put::putentry(@r, @p, @h)
@a[0].send("reset")
nil
end
end
class PostHtmlVals < Riddl::Implementation
def response
Dir.mkdir(File.join('data',@r[-2])) rescue nil
Dir.mkdir(File.join('data',@r[-2], "js")) rescue nil
Dir.mkdir(File.join('data',@r[-2], "visus")) rescue nil
#prevent overriding ui and index in case user changed something there
if !File.file?(File.join('data',@r[-2],'js/ui.js'))
File.write(File.join('data',@r[-2],'js/ui.js'),File.open("defaultContent/js/ui.js").read)
end
if !File.file?(File.join('data',@r[-2],'index.html'))
file = File.open("defaultContent/index.html").read
newfile = file.gsub("!replaceThisString!", @r[-2])
File.write(File.join('data',@r[-2],'index.html'), newfile)
end
#prevent overriding visualization
if !File.file?(File.join('data',@r[-2],'visus.html'))
File.write(File.join('data',@r[-2],'visus.html'),File.open("defaultContent/visus.html").read)
end
file = JSON['{}']
@p.each_with_index do |child, idx|
file[child.name] = child.value
end
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
db.execute "CREATE TABLE IF NOT EXISTS Entries(__orderID__ INTEGER PRIMARY KEY AUTOINCREMENT, alldata TEXT, cpeeInstance INTEGER)"
db.execute("INSERT OR REPLACE INTO Entries (alldata, cpeeInstance) VALUES (?,?)", JSON[file], "none")
@a[0].send("reset")
Riddl::Parameter::Complex.new('ui','text/html', '
')
end
end
class PutPrio < Riddl::Implementation
def response
hash = Put::putentry(@r, @p, @h) #add entry
hash = Move::moveentry(hash[:__orderID__][0], 0,@r) #move to front
@a[0].send("reset")
nil
end
end
class PutPrioN < Riddl::Implementation
def response
#move all back
fname = File.join('data',@r.last,'data.db')
db = SQLite3::Database.open fname
#Updating entries directly + X would result in unique constraint failed
#Therefore updates first to negative values then *-1
db.execute "UPDATE Entries SET __orderID__= 0 - (__orderID__ + " + @p[1].value + ")"
db.execute "UPDATE Entries SET __orderID__= __orderID__ *-1"
#Add entries
i = 0
while i < @p[1].value.to_i do
#db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
#in case primary key is given
if @p[2] != nil
db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, pkvalue, cpeeInstance) VALUES (?,?,?,?)",i, @p[0].value, @p[2].value, @h['CPEE_INSTANCE'])
else
db.execute("INSERT OR REPLACE INTO Entries (__orderID__, alldata, cpeeInstance) VALUES (?,?,?)",i, @p[0].value, @h['CPEE_INSTANCE'])
end
i +=1
end
@a[0].send("reset")
nil
end
end
class Move < Riddl::Implementation
def self::moveentry(from, to,r)
fname = File.join('data',r.last,'data.db')
db = SQLite3::Database.open fname
#getMaxID
result = db.execute "SELECT MAX(__orderID__) FROM Entries"
if(result[0][0] == nil)
maxImgId = 0
else
maxImgId = result[0][0] +1
end
i = 0
if to < from
while to < from do
#nutze max ID zum tauschen
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from - 1])
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from - 1, from])
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
from -=1
end
elsif from < to
while from < to do
#nutze max ID zum tauschen
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxImgId, from + 1])
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from + 1, from])
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [from, maxImgId])
from +=1
end
end
nil
end
def response
movement = JSON.parse(@p[0].value.read)
from = movement["From"]
to = movement["To"]
hash = Move::moveentry(from, to,@r)
nil
end
end
class Update < Riddl::Implementation
def response
fname = File.join('data',@r.last,'data.db')
db = SQLite3::Database.open fname
updateData = JSON.parse(@p[0].value.read)
orderID = updateData["ID"]
alldata = JSON.generate(updateData["alldata"])
db.execute("UPDATE Entries SET alldata = ? WHERE __orderID__ = ?", [alldata, orderID])
nil
end
end
class Storesortedtable < Riddl::Implementation
def response
fname = File.join('data',@r.last,'data.db')
db = SQLite3::Database.open fname
#invert keys from + to - to prevent duplicate primary keys when reordering
db.execute("UPDATE Entries SET __orderID__ = __orderID__ *-1 -1")
updateData = JSON.parse(@p[0].value.read)
updateData.each_with_index do |child, idx|
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [idx, child.to_i*-1 -1])
end
#security
#in case there are negative entries remaining add them with positive value to the end
result = db.execute "SELECT MAX(__orderID__) FROM Entries"
if(result[0][0] == nil)
maxorderID = 0
else
maxorderID = result[0][0] +1
end
resultpattern = db.execute "SELECT * FROM Entries WHERE __orderID__ < 0"
resultpattern.each do |row2|
db.execute("UPDATE Entries SET __orderID__ = ? WHERE __orderID__ = ?", [maxorderID, row2[0]]);
maxorderID = maxorderID +1;
end
@a[0].send("reset")
nil
end
end
class Delete < Riddl::Implementation
def response
fname = File.join('data',@r.last,'data.db')
db = SQLite3::Database.open fname
alldata = @p[0].value.read
db.execute("DELETE FROM Entries WHERE alldata = '" + alldata + "'")
@a[0].send("reset")
nil
end
end
class DeleteID < Riddl::Implementation
def self::remove(dbfolder, id)
fname = File.join('data',dbfolder,'data.db')
db = SQLite3::Database.open fname
db.execute("DELETE FROM Entries WHERE __orderID__ = '" + id.to_s + "'")
end
def response
DeleteID::remove(@r.last, @p[0].value.read)
@a[0].send("reset")
nil
end
end
class DeleteIDDirect < Riddl::Implementation
def response
DeleteID::remove(@r.last, @p[0].value)
@a[0].send("reset")
nil
end
end
class DeletePK < Riddl::Implementation
def response
fname = File.join('data',@r.last,'data.db')
db = SQLite3::Database.open fname
pkvalue = @p[0].value
db.execute("DELETE FROM Entries WHERE pkvalue = '" + pkvalue + "'")
@a[0].send("reset")
nil
end
end
class SearchPK < Riddl::Implementation
def response
fname = File.join('data',@r[-4],'data.db')
db = SQLite3::Database.open fname
pkvalue = @r.last
if !db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Entries';").empty?
db.results_as_hash = true
alldata = {};
alldata["data"] = (db.execute "Select * from Entries WHERE pkvalue = '" + pkvalue + "'")
Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
else
Riddl::Parameter::Complex.new('value','application/json',JSON.dump({}))
end
end
end
class GetNext < Riddl::Implementation
def response
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
#get value
db.results_as_hash = true
alldata = {};
alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
end
end
class DeleteNext < Riddl::Implementation
def response
fname = File.join('data',@r[-2],'data.db')
db = SQLite3::Database.open fname
#get value
db.results_as_hash = true
alldata = {};
alldata["data"] = (db.execute "Select * from Entries ORDER BY __orderID__ ASC LIMIT 1")[0]
#remove from DB
DeleteID::remove(@r[-2], alldata["data"]["__orderID__"])
@a[0].send("reset")
Riddl::Parameter::Complex.new('value','application/json',JSON.dump(alldata))
end
end
class DeleteAll < Riddl::Implementation
def response
File.unlink(File.join('data',@r.last,'data.db')) rescue nil
nil
end
end
class SSE < Riddl::SSEImplementation #{{{
def onopen
signals = @a[0]
signals.add self
send 'started'
true
end
def onclose
signals = @a[0]
signals.remove self
nil
end
end #}}}
class Signaling # {{{
def initialize
@binding = []
end
def add(binding)
@binding << binding
end
def remove(binding)
@binding.delete(binding)
end
def length
@binding.length
end
def send(value)
@binding.each do |b|
b.send(value)
end
end
end #}}}
server = Riddl::Server.new(File.join(__dir__,'/dashboard.xml'), :host => 'localhost') do |opts|
accessible_description true
cross_site_xhr true
opts[:signals] = {}
parallel do
loop do
opts[:signals].each do |k,v|
v.send('keepalive')
end
sleep 5
end
end
on resource do
run ConfigSites if get
on resource 'getconfigs' do
run GetAllConfigs if get
end
on resource do |r|
idx = r[:r][0]
opts[:signals][idx] ||= Signaling.new
run Get if get
run Put, opts[:signals][idx] if put 'input'
run PutPrio, opts[:signals][idx] if put 'inputprio'
run PutPrioN, opts[:signals][idx] if put 'inputNprio'
run Move if put 'move'
run Update if put 'update'
run Storesortedtable, opts[:signals][idx] if put 'storesortedtable'
run AddEntry, opts[:signals][idx] if post 'directADD'
run Delete, opts[:signals][idx] if delete 'deleteMsg'
run DeleteID, opts[:signals][idx] if delete 'deleteByID'
run DeletePK, opts[:signals][idx] if delete 'deletePK'
run DeleteAll, opts[:signals][idx] if delete 'deleteAll'
run DeleteIDDirect, opts[:signals][idx] if delete
on resource '\d+' do
run GetNData, opts if get
run DeleteNData, opts[:signals][idx] if delete
end
on resource 'sse' do
run SSE, opts[:signals][idx] if sse
end
on resource 'data.db' do
run GetAllData if get
end
on resource 'Column' do
run AddColumn if post 'addColumn'
run DeleteColumn if post 'removeColumn'
end
on resource 'htmlform' do
run PostHtmlVals, opts[:signals][idx] if post
end
on resource 'search' do
on resource 'PK' do
on resource '.*' do
run SearchPK if get
end
end
end
on resource 'visus' do
run GetAllVisus if get
end
on resource 'next' do
run GetNext, opts[:signals][idx] if get
run DeleteNext, opts[:signals][idx] if delete
end
end
end
end.loop!