how to select from the children of selected node using xpath?i - ruby-on-rails

I am having problem to get children of selected node that match the format. It somehow return me a global selection of the format instead from the selected one.
doc = Nokogiri::XML(f)
group_with_obj_and_unit = doc.xpath("/x:svg/x:g[x:text[matches(text(),'^#[0123456789]+\-[0123456789][\/[0123456789]+]*$')]][x:text[not(matches(text(),'^#[0123456789]+\-[0123456789][\/[0123456789]+]*$'))]][x:path or x:rect or x:circle or x:ellipse or x:polyline or x:polygon]", FindWithRegex, "x" => "http://www.w3.org/2000/svg")
group_with_obj_and_unit.each do |matched_group|
text_object = matched_group.xpath("//x:g/x:text[matches(text(),'^#[0123456789]+\-[0123456789][\/[0123456789]+]*$')]", FindWithRegex, "x" => "http://www.w3.org/2000/svg")
object = matched_group.xpath("//x:g/x:path | /x:svg/x:g/x:rect | /x:svg/x:g/x:circle | /x:svg/x:g/x:ellipse | /x:svg/x:g/x:polyline | /x:svg/x:g/x:polygon", "x" => "http://www.w3.org/2000/svg")
#object['id'] = text_object.text
end
edited
after suggested by #pguardiario
def run!
fileContent = File.new #file_path, "r"
file = File.open #file_path, "r+"
doc = Nokogiri::XML(fileContent)
group_with_obj_and_unit = doc.xpath("/x:svg/x:g[x:text[matches(text(),'^#[0123456789]+\-[0123456789][\/[0123456789]+]*$')]][x:text[not(matches(text(),'^#[0123456789]+\-[0123456789][\/[0123456789]+]*$'))]][x:path or x:rect or x:circle or x:ellipse or x:polyline or x:polygon]", FindWithRegex, "x" => "http://www.w3.org/2000/svg")
group_with_obj_and_unit.each do |matched_group|
#find the text object and the object of the selected node
text_object = matched_group.at_xpath("./x:text[matches(text(),'^#[0123456789]+\-[0123456789][\/[0123456789]+]*$')]", FindWithRegex, "x" => "http://www.w3.org/2000/svg")
object = matched_group.at_xpath("./x:path | ./x:rect | ./x:circle | ./x:ellipse | ./x:polyline | ./x:polygon", "x" => "http://www.w3.org/2000/svg")
object['id'] = text_object.text
end
file.write doc.to_xml
file.close
end

// means match anywhere following root
.// means match anywhere following the current node.

Related

Remove characters after any tags

I am working in rails. I have one doubt.
1. a = "ABCD123"
I want to print ABCD123
2. b = "ABCDE<123>"
I want to print ABCDE
For that I am using this
a.scan(/\b[A-Za-z]+\b/).join and
b.scan(/\b[A-Za-z]+\b/).join.
First one is giving nil but I want to print it as ABCD123 and second one is showing correct what I want.
Could anyone please help me. Thanks.
code below can remove all tags in the string
a = "ABCD123"
b = "ABCDE<123>"
a.gsub /<.*?>/, '' # => "ABCD123"
b.gsub /<.*?>/, '' # => "ABCDE"
def conversion(str)
index_number = str.index(/[\W_]+/)
if index_number.present?
main_str = str.gsub(str[index_number..],'')
else
main_str = str
end
return main_str
end
or you can use
b = "ABCD-123"
b.match(/(^[A-Za-z0-9]+)/)[1]
#=> "ABCD"
You can try following,
b = "ABCDE<123>"
b[/[^<>]+/]
# => "ABCDE"
Since comments are a bit limited:
Here is a small snippet to test different inputs.
strings = %w[ABCD123 ABCD<123> ABCD <123>ABCDE]
strings.each do |string|
match = string.match(/(^[A-Za-z0-9]+)/)
if match
puts "'#{string}' => #{match[1]}"
else
puts "'#{string}' does not match pattern"
end
end
Is this the desired behaviour?
'ABCD123' => ABCD123
'ABCD<123>' => ABCD
'ABCD' => ABCD
'<123>ABCDE' does not match pattern

Ruby: transform a data structure into an excel sheet

I have processed a log file and created a data structure of below format
values = [ {:VM=>"VM_US_OLA_1"}
{:id=>"OLFTB51", :datum=>{"LAP"=>"6.93817", "YCC_OWER"=>"1.0391"}}
{:id=>"OLFTB10", :datum=>{"LAP_2"=>"2.72646", "CFG_ON"=>"15.9489746", "YCC_ON"=>".401794"}}
{:VM=>"VM_ASIA_FLO_1"}
{:id=>"LOPMLAP", :datum=>{"LAP"=>"1.81048584", "FM_ON"=>".00"} ]
values is an array.
I'm trying to create a spreadsheet of below format where only the VM column gets highlighted in green and for every VM set, a blank line highlighted in 'yellow' should be inserted. I tried multiple approaches and went thru the axlsx documentation too but unable to get the desired format.
My excel snippet below:
require 'axlsx'
p = Axlsx::Package.new
p.workbook.add_worksheet(:name => "Statistics") do |sheet|
style1 = sheet.styles.add_style(:bg_color => "EF0920", :fg_color => "FFFFFF", b:true)
style2 = sheet.styles.add_style(:bg_color => "00FF00", :fg_color => "FFFFFF", b:true)
sheet.add_row ["VM", "NAME", "DETAILS", "OCC"], :style => style1
values.each do |val|
sheet.add_row [ val[:VM], val[:id] ], :style =>[style2, nil]
val[:datum].each do |k, v|
sheet << ["", "", k, v]
end
end
sheet.add_row
end
p.serialize 'Stat.xlsx'
Any suggestions should be really helpful here, many thanks.
Expected Output
Current Output
Adding Log file and my code
Log
---- vm name ----
VM_US_OLA_1
OLFTB51
OWNER IN_GB
------------------------------ ----------
LAP 6.93817
YCC_OWER 1.0391
=========================================
---- vm name ----
OLFTB10
OWNER IN_GB
------------------------------ ----------
LAP_2 2.7266846
CFG_ON 15.9489746
YCC_ON .401794
=========================================
---- vm name ----
VM_ASIA_FLO_1
LOPMLAP
OWNER IN_GB
------------------------------ ----------
LAP 1.81048584
FM_ON .00
=========================================
---- vm name ----
INGTY_2
OWNER IN_GB
------------------------------ ----------
=========================================
so on of the same format
Code to process the logs
require 'csv'
values = []
total = File.read("final.log")
total.each_line do |line|
line.strip!
next if line.empty?
next if line.include?('selected') || line.include?('IN_GB')
next if ['-','='].include? line[0]
parts = line.split ' '
if parts.size == 1 and line.start_with?('size')
values[current += 1] = {vm: line.strip}
next
elsif parts.size == 1 and parts = /^(?!.*size_).*$/
values[current += 1] = {id: line, datum: {}}
next
end
parts.each_cons(2) do |key, value|
values[current][:datum][key] = value
end
end
puts values
The problem is with the structure of your data. Try to change to have the values of your array in this structure
{:VM=>"VM_US_OLA_1", :id=>"OLFTB51", :datum=>{"LAP"=>"6.93817", "YCC_OWER"=>"1.0391"}
What's happening is VM is one record in your array, and the other data is another record in the array, you need to merge them so you access them in the same loop iteration
EDIT
I guess how we parse the log file is a bit tricky, so I am going to leave it but I am sure that this part I am going to introduce can be done in the log file parsing
First, we can change the structure of the array like that (before p.workbook.add_worksheet)
dataset = []
values.each_with_index do |value|
if value[:VM].present? # or :vm, not sure about the key
dataset << value
dataset.last[:data] = []
else
dataset.last[:data] << value
end
end
Then this loop: values.each do |val|
Can be changed to dataset.each do |val|
Then you can continue with the rest of your logic:
sheet.add_row [ val[:VM], val[:data][0][:id] ], :style =>[style2, nil]
val[data].each do |record|
record[:datanum].each do |k, v|
The problem with your previous code is, you used to add a new row sheet.add_row at the end of each iteration, so this was messing things up as you were expecting the data you need to be just right after your VM but a new line has already been inserted

Pulling XML tags with Lua

I'm currently working on an XML parser and I'm trying to use Lua's pattern matching tools but I'm not getting the desired result. Let's say I have this XML snippet:
<Parent>
<Child>
<Details>Text in Parent tag and Details child tag</Details>
<Division>Text in Parent tag and Division child tag</Division>
</Child>
</Parent>
I need to pull the Parent tag out into a table, followed by any child tags, and their corresponding text data. I already have the pattern for pulling the data figured out:
DATA = "<.->(.-)<"
Likewise for pulling tags individually:
TAGS ="<(%w+)>"
However like I mentioned, I need to differentiate between tags that are nested and tags that aren't. Currently the pattern that's getting the closest result I need is:
CHILDTAG= "<%w->.-<(%w-)>"
Which should print only "Child" but it prints "Division" as well for a reason I can't comprehend. The idea behind the CHILDTAG pattern is it captures a tag IFF it had an enclosing tag, i.e , the ".-" is there to signify that it may/may not have a new line between it, however I think that's completely wrong because \n- doesn't work and that signifies a new line. I referred to the documentation and to:
https://www.fhug.org.uk/wiki/wiki/doku.php?id=plugins:understanding_lua_patterns
I use Lua 5.1. I want to parse an XML file of the following pattern. How should I go about it?
Lua XML extract from pattern
Simple XML parser (Named entities in XML are not supported)
local symbols = {lt = '<', gt = '>', amp = '&', quot = '"', apos = "'", nbsp = ' ', euro = '€', copy = '©', reg = '®'}
local function unicode_to_utf8(codepoint)
-- converts numeric unicode to string containing single UTF-8 character
local t, h = {}, 127
while codepoint > h do
local low6 = codepoint % 64
codepoint = (codepoint - low6) / 64
t[#t+1] = 128 + low6
h = 288067 % h
end
t[#t+1] = 254 - 2*h + codepoint
return string.char((table.unpack or unpack)(t)):reverse()
end
local function unescape(text)
return (
(text..'<![CDATA[]]>'):gsub('(.-)<!%[CDATA%[(.-)]]>',
function(not_cdata, cdata)
return
not_cdata
:gsub('%s', ' ')
--:gsub(' +', ' ') -- only for html
:gsub('^ +', '')
:gsub(' +$', '')
:gsub('&(%w+);', symbols)
:gsub('&#(%d+);', function(u) return unicode_to_utf8(to_number(u)) end)
:gsub('&#[xX](%x+);', function(u) return unicode_to_utf8(to_number(u, 16)) end)
..cdata
end
)
)
end
function parse_xml(xml)
local tag_stack = {}
local result = {find_child_by_tag = {}}
for text_before_tag, closer, tag, attrs, self_closer in xml
:gsub('^%s*<?xml.-?>', '') -- remove prolog
:gsub('^%s*<!DOCTYPE[^[>]+%[.-]>', '')
:gsub('^%s*<!DOCTYPE.->', '')
:gsub('<!%-%-.-%-%->', '') -- remove comments
:gmatch'([^<]*)<(/?)([%w_]+)(.-)(/?)>'
do
table.insert(result, unescape(text_before_tag))
if result[#result] == '' then
result[#result] = nil
end
if closer ~= '' then
local parent_pos, parent
repeat
parent_pos = table.remove(tag_stack)
if not parent_pos then
error("Closing unopened tag: "..tag)
end
parent = result[parent_pos]
until parent.tag == tag
local elems = parent.elems
for pos = parent_pos + 1, #result do
local child = result[pos]
table.insert(elems, child)
if type(child) == 'table' then
--child.find_parent = parent
parent.find_child_by_tag[child.tag] = child
end
result[pos] = nil
end
else
local attrs_dict = {}
for names, value in ('\0'..attrs:gsub('%s*=%s*([\'"])(.-)%1', '\0%2\0')..'\0')
:gsub('%z%Z*%z', function(unquoted) return unquoted:gsub('%s*=%s*([%w_]+)', '\0%1\0') end)
:gmatch'%z(%Z*)%z(%Z*)'
do
local last_attr_name
for name in names:gmatch'[%w_]+' do
name = unescape(name)
if last_attr_name then
attrs_dict[last_attr_name] = '' -- boolean attributes (such as "disabled" in html) are converted to empty strings
end
last_attr_name = name
end
if last_attr_name then
attrs_dict[last_attr_name] = unescape(value)
end
end
table.insert(result, {tag = tag, attrs = attrs_dict, elems = {}, find_child_by_tag = {}})
if self_closer == '' then
table.insert(tag_stack, #result)
end
end
end
for _, child in ipairs(result) do
if type(child) == 'table' then
result.find_child_by_tag[child.tag] = child
end
end
-- Now result is a sequence of upper-level tags
-- each tag is a table containing fields: tag (string), attrs (dictionary, may be empty), elems (array, may be empty) and find_child_by_tag (dictionary, may be empty)
-- attrs is a dictionary of attributes
-- elems is a sequence of elements (with preserving their order): tables (nested tags) or strings (text between <tag> and </tag>)
return result
end
Usage example:
local xml= [[
<Parent>
<Child>
<Details>Text in Parent tag and Details child tag</Details>
<Division>Text in Parent tag and Division child tag</Division>
</Child>
</Parent>
]]
xml = parse_xml(xml)
--> both these lines print "Text in Parent tag and Division child tag"
print(xml[1].elems[1].elems[2].elems[1])
print(xml.find_child_by_tag.Parent.find_child_by_tag.Child.find_child_by_tag.Division.elems[1])
What parsed xml looks like:
xml = {
find_child_by_tag = {Parent = ...},
[1] = {
tag = "Parent",
attrs = {},
find_child_by_tag = {Child = ...},
elems = {
[1] = {
tag = "Child",
attrs = {},
find_child_by_tag = {Details = ..., Division = ...},
elems = {
[1] = {
tag = "Details",
attrs = {},
find_child_by_tag = {},
elems = {[1] = "Text in Parent tag and Details child tag"}
},
[2] = {
tag = "Division",
attrs = {},
find_child_by_tag = {},
elems = {[1] = "Text in Parent tag and Division child tag"}
}
}
}
}
}
}

Elasticsearch result to table

I am using the gem elasticsearch-rails to retrieve data from elasticsearch in a dynamic way, meaning that the result can have none or multiple aggregations depending on users choices.
Imagine a response like this:
(...)
"aggregations"=>
{"agg_insignia_id"=>
{"buckets"=>
[{"key"=>1,
"key_as_string"=>"1",
"doc_count"=>32156,
"agg_chain_id"=>
{"buckets"=>
[{"key"=>9,
"key_as_string"=>"9",
"doc_count"=>23079,
"agg_store_id"=>
{"buckets"=>
[{"key"=>450,
"key_as_string"=>"450",
"doc_count"=>145,
"agg_value"=>{"value"=>1785.13}},
{"key"=>349,
"key_as_string"=>"349",
"doc_count"=>143,
"agg_value"=>{"value"=>1690.37}},
How can I transform that data in a tabular data? like
| insignia_id | chain_id | store_id | value |
| 1 | 9 | 450 | 1785.13 |
| 1 | 9 | 349 | 1690.37 |
(...)
EDIT :: Being clear on the response I am looking for, two choices here: Array (simple) or Array of hashes.
Array style: [[insignia_id, chain_id, store_id, value], [1,9,450,1785.13], [1,9,349,1690.37],...]
Array of Hashes style: [{insignia_id => 1, chain_id => 9, store_id => 450, value => 1785.13}, {insignia_id => 1, chain_id => 9, store_id => 450, value => 1690.37 }]
The later is more like an activerecord style...
ok, so I came up with a solution for an array response.
Firstly added a helper for what comes ahead...
class Hash
def deep_find(key, object=self, found=nil)
if object.respond_to?(:key?) && object.key?(key)
return object[key]
elsif object.is_a? Enumerable
object.find { |*a| found = deep_find(key, a.last) }
return found
end
end
end
now for the array algorithm (added in a concern):
def self.to_table_array(data, aggs, final_table = nil, row = [])
final_table = [aggs.keys] if final_table.nil?
hash_tree = data.deep_find(aggs.keys.first)
if aggs.values.uniq.length == 1 && aggs.values.uniq == [:data]
aggs.keys.each do |agg|
row << data[agg]["value"]
end
final_table << row
else
hash_tree["buckets"].each_with_index do |h, index|
row.pop if index > 0
aggs.shift if index == 0
row << h["key_as_string"]
final_table = to_table_array(h, aggs.clone, final_table, row.clone)
end
end
final_table
end
The call for this method could be made like this:
#_fields = { "insignia_id" => :row, "chain_id" => :row, "store_id"=> :row, "value" => : data }
#res.response => Elasticsearch response
result = to_table_array(res.response, _fields)
There are some things quite specific to this case like you can see on this _fields variable. Also I'm assuming each aggregation has the name of the term itself. The rest is quite the same for every possible case.
A result of an array of hashes is pretty simple from here just by replacing few lines.
I put a lot of efford in this. Hope this helps someone else other than me.

Ruby - Passing binding variables in SQL execution

I'm new to Ruby and I want to try to access a MySQL database:
require 'rubygems'
require "dbi"
class DBConnection
attr_accessor :dbh
#Connect to db
def connect?(driver_url,user,pass)
begin
#dbh = DBI.connect(driver_url, user,pass);
return true
rescue DBI::DatabaseError => e
puts "Error message: #{e.errstr}"
#dbh.rollback
return false
ensure
#dbh.disconnect if !dbh
end
end
def execute_customize(query,params)
stm = #dbh.prepare(query)
if( (params != nil) && !(params.empty?) )
stm.execute(params)
else
stm.execute
end
header = false
stm.fetch do |row|
if (!header)
puts("ID Name")
header = true
end
puts("#{row[0]} #{row[1]}")
end
end
end
db = DBConnection.new
db.connect?("DBI:Mysql:test:localhost", "root", "123456")
db.execute_customize("SELECT * FROM test.employee WHERE name = ? OR name = ? ",*["John","Terry"])
But the above returns the following error:
in `execute_customize': wrong number of arguments (3 for 2) (ArgumentError)
But the execution is successful with:
dbh.execute_customize("SELECT * FROM test.employee WHERE name = ?",*["John"])
What am I doing wrong?
Demo data from employee table :
+------+-------+
| id | name |
+------+-------+
| 1 | John |
| 2 | Terry |
| 3 | Vidal |
| 4 | CR7 |
| 5 | M10 |
| 6 | R10 |
| 7 | F4 |
+------+-------+
// Update : Your comment almost told me using IN in query, but if with other query like :
SELECT * FROM test.employee WHERE name = ? and id > ?
I still need a way to passing seperate paramer to every "?" character
You're passing three arguments instead of two.
The splat operator * expands the array, so its elements are treated as separate arguments.
Try
dbh.execute("SELECT * FROM test.employee WHERE name IN (?)", names)
where names is a comma-separated list of strings.
That should work, but you may not need to use execute for this.
If you're using Rails, you can just use
Employee.where(name: ["John","Terry"])
and ActiveRecord will understand what you mean.
See http://guides.rubyonrails.org/active_record_querying.html

Resources