Foreach hash with Perl template toolkit - perl

I am using Perl template toolkit for rendering my data.
This is the hash that I am passing to the template
'location' => {
'1' => {
'nmi' => 'QB13557343'
},
'2' => {
'nmi' => 'QB13559843'
},
},
and in the template I am looping this hash to get the result but its not displaying
The code in the template is:
[% FOREACH loc IN location %]
<p>NMI: [% loc.nmi %][% location.loc.nmi %]
[% END %]
I tried both loc.nmi and location.loc.nmi but not getting any result.
Any help would be greatly appreciated.

It's a hash and not an array. Try to iterate over the keys.
[% FOREACH key IN location.keys %]
<p>NMI: [% location.$key.nmi %]</p>
[% END %]

Related

Extract email from string using Template Tookit

I'm guessing this is relatively simple, but I can't find the answer.
From a string such as '"John Doe" <email#example.com>' - how can I extract the email portion from it using Template Tookit?
An example string to parse is this:
$VAR1 = {
'date' => '2021-03-25',
'time' => '03:58:18',
'href' => 'https://example.com',
'from' => 'fezius#evrostroyserov.ru on behalf of Caroline <fezius#evrostroyserov.ru>',
'bytes' => 13620,
'pmail' => 'user#example.com',
'sender' => 'sender#example.com',
'subject' => 'Some Email Subject'
};
My code, based on #dave-cross help below where $VAR1 is the output of dumper.dump(item.from)
[% text = item.from -%]
[% IF (matches = text.match('(.*?)(\s)?+<(.*?)>')) -%]
<td>[% matches.1 %]</td>
[% ELSE -%]
<td>[% text %]</td>
[% END %]
However, it's still not matching against $VAR1
This does what you want, but it's pretty fragile and this really isn't the kind of thing that you should be doing in TT code. You should either get the data parsed outside of the template and passed into variables, or you should pass in a parsing subroutine that can be called from inside the template.
But, having given you the caveats, if you still insist this is what you want to do, then this is how you might do it:
In test.tt:
[% text = '"John Doe" <email#example.com>';
matches = text.match('"(.*?)"\s+<(.*?)>');
IF matches -%]
Name: [% matches.0 %]
Email: [% matches.1 %]
[% ELSE -%]
No match found
[% END -%]
Then, testing using tpage:
$ tpage test.tt
Name: John Doe
Email: email#example.com
But I cannot emphasise enough that you should not be doing it like this.
Update: I've used this test template to investigate your further problem.
[% item = { from => '"John Doe" <email#example.com>' };
text = item.from -%]
[% IF (matches = text.match('(.*?)(\s)?+<(.*?)>')) -%]
<td>[% matches.1 %]</td>
[% ELSE -%]
<td>[% text %]</td>
[% END %]
And running it, I get this:
$ tpage test2.tt
<td> </td>
That's what I'd expect to see for a match. You're printing matches.1. That's the second item from the matches array. And the second match group is (\s). So I'm getting the space between the name and the opening angle bracket.
You probably don't want that whitespace match in your matches array, so I'd remove the parentheses around it, to make the regex (.*?)\s*<(.*?)> (note that \s* is a simpler way to say "zero or more whitespace characters").
You can now use matches.0 to get the name and matches.1 to get the email address.
Oh, and there's no need to copy items.from into text. You can call the matches vmethod on any scalar variable, so it's probably simpler to just use:
[% matches = item.from.match(...) -%]
Did I mention that this is all a really terrible idea? :-)
Update2:
This is all going to be far easier if you give me complete, runnable code examples in the same way that I am doing for you. Any time I have to edit something in order to get an example running, we run the risk that I'm guessing incorrectly how your code works.
But, bearing that in mind, here's my latest test template:
[% item = {
'date' => '2021-03-25',
'time' => '03:58:18',
'href' => 'https://example.com',
'from' => 'fezius#evrostroyserov.ru on behalf of Caroline <fezius#evrostroyserov.ru>',
'bytes' => 13620,
'pmail' => 'user#example.com',
'sender' => 'sender#example.com',
'subject' => 'Some Email Subject'
};
text = item.from -%]
[% IF (matches = text.match('(.*?)(\s)?<(.*?)>')) -%]
<td>[% matches.2 %]</td>
[% ELSE -%]
<td>[% text %]</td>
[% END %]
I've changed the definition of item to have your full example. I've left the regex as it was before my suggestions. And (because I haven't changed the regex) I've changed the output to print matches.2 instead of matches.1.
And here's what happens:
$ tpage test3.tt
<td>fezius#evrostroyserov.ru</td>
So it works.
If yours doesn't work, then you need to identify the differences between my (working) code and your (non-working) code. I'm happy to help you identify those differences, but you have to give my your non-working example in order for me to do that.
Update3:
Again I've tried to incorporate the changes that you're talking about. But again, I've had to guess at stuff because you're not sharing complete runnable examples. And again, my code works as expected.
[% USE dumper -%]
[% item = {
'date' => '2021-03-25',
'time' => '03:58:18',
'href' => 'https://example.com',
'from' => 'fezius#evrostroyserov.ru on behalf of Caroline <fezius#evrostroyserov.ru>',
'bytes' => 13620,
'pmail' => 'user#example.com',
'sender' => 'sender#example.com',
'subject' => 'Some Email Subject'
};
-%]
[% matches = item.from.match('(.*?)(\s)?<(.*?)>') -%]
[% dumper.dump(matches) %]
And testing it:
$ tpage test4.tt
$VAR1 = [
'fezius#evrostroyserov.ru on behalf of Caroline',
' ',
'fezius#evrostroyserov.ru'
];
So that works. If you want any more help, then send a complete runnable example. If you don't do that, I won't be able to help you any more.
There's a very old (and unmaintained) module, Template::Extract, that let's you define a template, then work backward from a string that might have been produced by that template:
use Template::Extract;
use Data::Dumper;
my $obj = Template::Extract->new;
my $template = qq("[% name %]" <[% email %]>);
my $string = '"John Doe" <email#example.com>';
my $extracted = $obj->extract($template, $string);
print Dumper( $extracted );
The output is:
$VAR1 = {
'email' => 'email#example.com',
'name' => 'John Doe'
};
However, there are modules that already do this job for you and will handle many more situations
I have no idea how Template Toolkit can help you. Use Email::Address or Email::Address::XS to parse an e-mail address.

getting results from sql join using perl

this works
TABLE users
userid firstname lastname
1 JOHN DEO
2 JANE DEO
TABLE msg
msg_id msg_from msg_to received
1 userid(1) userid(2) null
$janedeo_id = 2;
my $data = $DBH->prepare("SELECT SND.userid, SND.firstname, SND.lastname
FROM msg as M
JOIN users as SND
ON SND.userid = M.msg_from
WHERE M.msg_to = ?
AND M.received IS NULL");
$data->execute($janedeo_id);
while (my $row = $data->fetchrow_hashref) {
foreach $row ( #$data) {
($userid, $snd_firstname, $snd_lastname) = #$data;
}
}
my $templ = <<START_HTML;
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1
+" />
<title>Untitled Document</title>
</head>
<body>
[% FOREACH name IN list %]
<p>userid [% name.0 %] </p>
<p>firstname [% name.1 %] </p>
<p>lastname [% name.2 %] </p>
[% END %]
</body>
</html>
START_HTML
$template->process (\$templ, { list => \#$data })
or die $template->error;
this doesn't work. when i try to add age city country and results it fails cant works
TABLE users
userid firstname lastname
1 JOHN DEO
2 JANE DEO
TABLE msg
msg_id msg_from msg_to received age city country
1 userid(1) userid(2) null 26 any any
$janedeo_id = 2;
my $data = $DBH->prepare("SELECT SND.userid, SND.firstname, SND.lastname, SND.age, SND.city, SND.country
FROM msg as M
JOIN users as SND
ON SND.userid = M.msg_from
WHERE M.msg_to = ?
AND M.received IS NULL");
$data->execute($janedeo_id);
while (my $row = $data->fetchrow_hashref) {
foreach $row ( #$data) {
($userid, $snd_firstname, $snd_lastname, $snd.age, $snd.city, $snd.country) = #$data;
}
}
my $templ = <<START_HTML;
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>Untitled Document</title>
</head>
<body>
[% FOREACH name IN list %]
<p>userid [% name.0 %] </p>
<p>firstname [% name.1 %] </p>
<p>lastname [% name.2 %] </p>
<p>city [% name.3 %] </p>
<p>age [% name.4 %] </p>
<p>country [% name.5 %] </p>
[% END %]
</body>
</html>
START_HTML
$template->process (\$templ, { list => \#$data })
or die $template->error;
am failing to get results when i try to add city age country to table msg and getting them printed out. am just getting blank response. even the script just printing error. cant find anything in database. so am just confused i don't know the problem
This is really weird:
while (my $row = $data->fetchrow_hashref) {
foreach $row ( #$data) {
($userid, $snd_firstname, $snd_lastname, $snd.age, $snd.city, $snd.country) = #$data;
}
}
You fetch one row from the result as a hash reference. You than iterate over #$data which is a statement handle - this can't work. Use the fetched $row in the loop.
Also, . is the concatenation operator. What do you mean by $snd.city?
You can use Data::Dumper to see what structure is returned:
use Data::Dumper;
...
while (my $row = $data->fetchrow_hashref) {
print Dumper $row;
}
Here's a standalone example:
#!/usr/bin/perl
use warnings;
use strict;
use DBI;
use Template;
# Fake the database
my $dbh = 'DBI'->connect('dbi:SQLite:dbname=:memory:', "", "");
$dbh->do('CREATE TABLE USERS (id INT, firstname VARCHAR(20), lastname VARCHAR(20), age INT, city VARCHAR(20), country VARCHAR(20))');
$dbh->do('INSERT INTO USERS VALUES(1, "John", "Doe", 30, "Mumbai", "India")');
$dbh->do('INSERT INTO USERS VALUES(2, "Jane", "Doe", 20, "New York", "USA")');
$dbh->do('CREATE TABLE msg (id INT, msg_from VARCHAR(20), msg_to VARCHAR(20), received BOOL)');
$dbh->do('INSERT INTO msg VALUES (1, 2, 1, NULL)');
# Template.
my $templ = << '__HTML__';
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>Untitled Document</title>
</head>
<body>
[% FOREACH user IN list %]
<p>userid [% user.id %] </p>
<p>firstname [% user.firstname %] </p>
<p>lastname [% user.lastname %] </p>
<p>city [% user.city %] </p>
<p>age [% user.age %] </p>
<p>country [% user.country %] </p>
[% END %]
</body>
</html>
__HTML__
my $data = $dbh->prepare(<< '__SQL__');
SELECT u.id, u.firstname, u.lastname, age, u.city, u.country
FROM msg as m
JOIN users as u
ON u.id = m.msg_from
WHERE m.msg_to = ?
AND m.received IS NULL
__SQL__
$data->execute(1);
my #output;
while (my $row = $data->fetchrow_hashref) {
push #output, $row;
}
'Template'->new->process (\$templ, { list => \#output })
or die $templ->error;
You seem very, very confused. I don't think you are actually showing us the code that you are running. Look at this code:
my $data = $DBH->prepare("SELECT SND.userid, SND.firstname,
SND.lastname
FROM msg as M
JOIN users as SND
ON SND.userid = M.msg_from
WHERE M.msg_to = ?
AND M.received IS NULL");
$data->execute($janedeo_id);
while (my $row = $data->fetchrow_hashref) {
foreach $row ( #$data) {
($userid, $snd_firstname, $snd_lastname,
$snd.age, $snd.city, $snd.country) = #$data;
}
}
This is your first code example - the code that you claim works. But it can't possibly work.
You have a variable called $data which contains your DBI statement handle (I know that because it's returned from a $DBH->prepare() call). It would be more usual to call this variable $sth.
You, correctly, call execute() on this object and then start to call fetchrow_hashref() in a loop. You store the hash reference you get in a variable called $row. So far so good.
But then it all goes wrong.
You ignore the hash reference you have and overwrite it with the data that is stored in $data. But there's no data in $data - it's a statement handle. You treat it as an array reference (#$data) but it's not an array reference, so this would throw a fatal runtime error.
You then ignore the second value you have put in $row and switch to using individual variables to, presumably, store the data that you're getting back from the database. Once again, you treat $data as an array reference and once again, it won't work and will throw a fatal runtime error. Oh, and you're using variable names (for example, $snd.age) which are invalid in Perl and which would almost certainly give you a syntax error.
By claiming that this code works, you are wasting our time. It doesn't work. It doesn't even compile. We are very happy to help people here, but you need to show us actual code that we can run.
I don't know what your programming background is. But in order to be successful in this career, you are going to have to pay a lot more attention to detail.
i got this fixed by doing
SELECT SND.userid, SND.firstname, SND.lastname, M.age, M.city, M.country
FROM msg as M
JOIN users as SND
ON SND.userid = M.msg_from
WHERE M.msg_to = 'userid -1'
AND M.received IS NULL

How to print recursive data structure with Template Toolkit?

I am trying to print a recursive data structure using Template Toolkit. How can I describe this in my template file?
I have a data structure (Hash Array) with some elements like this
ELEMENT
-> Description: XYXY
-> Childs: [Array of Child ELEMENTS of same type]
Where childs can contain an array of child elements. I want now recursively print all contents including the elements childs and those childs and so on.
How can I achieve this?
You can use PROCESS to pass data into a BLOCK which you can do recursively. For example:
[%
SET element = {
description = "A",
children= [
{
description= "AA",
children= [
{ description = "AAA" }
]
},
{
description= "AB",
children= [
{
description = "ABA",
children = [
{
description = "ABAA"
}
]
},
{ description = "ABB" }
]
}
]
};
%]
[% BLOCK show_element %]
[% my_element.description | html %]
[% IF my_element.children %]
<ul>
[% FOR child_element IN my_element.children %]
<li>[% PROCESS show_element my_element=child_element %]</li>
[% END %]
</ul>
[% END %]
[% END %]
[% PROCESS show_element my_element=element %]

access variable member "*" in template

i have a variable
var1 = {
'*' = > {
'counter' = > {
'default' = > '0',
'description' = > 'test'
}
}
}
in perl template toolkit.
How can i access the content of '*' in the template.
[% var1.*.counter %]
does not work because of the symbol is no valid name.
You can define a variable to be equal to * within the template, and use that instead.
[% star = '*' %]
[% var1.$star.counter.description %]
But I wonder why you have to have an asterisk as a key in the first place? It would be far better to provide a sensible key at the Perl level, perhaps by writing
$vars->{var1}{star} = $vars->{var1}{'*'}
before you call the Template Toolkit.

Define default value in OS field when creating new task in bugzilla

I installed bugzilla 4.4 on linux server, and finished to define all groups and products.
I also defined kinds of OS and Hardware platforms. I am wondering if it is possible do define default OS or default hardware platform for specific Product.
I coudn't find any such option in the administartion page.
Thanks
You should do the next steps:
cd ..../bugzilla-4.4/template/en/custom/bug/create/
then you have to edit this file:
vi create.html.tmpl
inside the file you have to find this peace of code:
<tr>
[% INCLUDE bug/field.html.tmpl
bug = default, field = bug_fields.op_sys, editable = 1,
value = default.op_sys %]
</tr>
replace it with this code:
<tr>
[% IF product.name == "ProductA" %]
[% INCLUDE bug/field.html.tmpl
bug = default, field = bug_fields.op_sys, editable = 1,
value = "Android" %]
[% ELSIF product.name == "ProductB" %]
[% INCLUDE bug/field.html.tmpl
bug = default, field = bug_fields.op_sys, editable = 1,
value = "iOS" %]
[% ELSE %]
[% INCLUDE bug/field.html.tmpl
bug = default, field = bug_fields.op_sys, editable = 1,
value = default.op_sys %]
[% END %]
</tr>