Errors while debugging code using HTML::TreeBuilder::XPath - apache

I am getting some errors when I try to debug the following code.
Note that it fetches the data from approx 6,000 fields from the http://europa.eu/youth/volunteering/evs-organisation#open
After parsing each page, check for the existence of the next › link at the bottom.
View-source is a browser based command. It tells the browser to output the response in plain text rather than render it based on its actual content type, HTML in this case. You should not need to include view-source in your URL.
Here we have a script that extracts the data out of each block and cleans it up a little. The browse function is generic. It takes an input reference which contains the URL and XPaths of the parent and children in order to construct the output ref. It is just an approach: it does not yet navigate across each page,
In a rough script I tested, I fetched the total results using //span[#class="ey_badge"] then the max page using
my $page_max = $results / 21;
$page_max = int( $page_max ) == $page_max ? $page_max-- : int( $page_max ) ;
See the errors
martin#linux-3645:~/dev/perl> perl eu.pl
syntax error at eu.pl line 81, near "our "
Global symbol "$iterator_organizations" requires explicit package name at eu.pl line 81.
Can't use global #_ in "my" at eu.pl line 84, near "= #_"
Missing right curly or square bracket at eu.pl line 197, at end of line
Execution of eu.pl aborted due to compilation errors.
martin#linux-3645:~/dev/perl> ^C
martin#linux-3645:~/dev/perl>
It fetches the data from approx 6,000 fields from http://europa.eu/youth/volunteering/evs-organisation#open
See the code
use strict;
use warnings FATAL => qw#all#;
use LWP::UserAgent;
use HTML::TreeBuilder::XPath;
use Data::Dumper;
my $handler_relurl = sub { q#https://europa.eu# . $_[0] };
my $handler_trim = sub { $_[0] =~ s#^\s*(.+?)\s*$#$1#r };
my $handler_val = sub { $_[0] =~ s#^[^:]+:\s*##r };
my $handler_split = sub { [ split $_[0], $_[1] ] };
my $handler_split_colon = sub { $handler_split->( qr#; #, $_[0] ) };
my $handler_split_comma = sub { $handler_split->( qr#, #, $_[0] ) };
my $conf = {
url => q#https://europa.eu/youth/volunteering/evs-organisation_en#,
parent => q#//div[#class="vp ey_block block-is-flex"]#,
children => {
internal_url => [ q#//a/#href#, [ $handler_relurl ] ],
external_url => [ q#//i[#class="fa fa-external-link fa-lg"]/parent::p//a/#href#, [ $handler_trim ] ],
title => [ q#//h4# ],
topics => [ q#//div[#class="org_cord"]#, [ $handler_val, $handler_split_colon ] ],
location => [ q#//i[#class="fa fa-location-arrow fa-lg"]/parent::p#, [ $handler_trim ] ],
hand => [ q#//i[#class="fa fa-hand-o-right fa-lg"]/parent::p#, [ $handler_trim, $handler_split_comma ] ],
pic_number => [ q#//p[contains(.,'PIC no')]#, [ $handler_val ] ],
}
};
print Dumper browse( $conf );
sub browse {
my $conf = shift;
my $ref = [ ];
my $lwp_useragent = LWP::UserAgent->new( agent => q#IE 6#, timeout => 10 );
my $response = $lwp_useragent->get( $conf->{url} );
die $response->status_line unless $response->is_success;
my $content = $response->decoded_content;
my $html_treebuilder_xpath = HTML::TreeBuilder::XPath->new_from_content( $content );
my #nodes = $html_treebuilder_xpath->findnodes( $conf->{parent} );
for my $node ( #nodes ) {
push #$ref, { };
while ( my ( $key, $val ) = each %{ $conf->{children} } ) {
my $xpath = $val->[0];
my $handlers = $val->[1] // [ ];
$val = ( $node->findvalues( qq#.$xpath# ) )[0] // next;
$val = $_->( $val ) for #$handlers;
$ref->[-1]->{$key} = $val;
}
}
return $ref;
}
{
'internal_url' => 'https://europa.eu/youth/volunteering/organisation/948417016_en',
'external_url' => 'http://www.apd.ge',
'location' => 'Tbilisi, Georgia',
'title' => '"Academy for Peace and Development" Union',
'topics' => [
'Access for disadvantaged',
'Youth (Participation, Youth Work, Youth Policy)',
'Intercultural/intergenerational education and (lifelong)learning'
],
'pic_number' => '948417016',
'hand' => [
'Receiving',
'Sending'
]
}
our $iterator_organizations = sub {
my ( $browser, $parent ) = #_;
my $url = q#https://europa.eu/youth/volunteering/evs-organisation_en#;
my $nodes = $browser->nodes( url => $url );
my $iterator = sub {
return shift #$nodes;
};
return ( $iterator, 1 );
our $iterator_organizations_b = sub {
my ( $browser, $parent ) = #_;
my $url = q#https://europa.eu/youth/volunteering/evs-organisation_en#;
my $uri = URI->new( $url );
my $xpath = q#//div[#class="vp ey_block block-is-flex"]#;
my $nodes = [ ];
my $page = 0;
my $results = $parent->{results};
my $page_max = $results / 21;
$page_max = int($page_max) == $page_max ? $page_max-- : int($page_max);
my $iterator_uri = sub {
$uri->query_form( page => $page++ );
return $page > 2 ? undef : $uri ; # $page_max;
};
my $iterator_node = sub {
unless ( #$nodes ) {
my $uri = $iterator_uri->( ) // return undef;
my $options = $page == 1 ? { tree => $parent->{_node} } : { url => $uri->as_string };
$nodes = $browser->nodes( %$options, xpath => $xpath );
}
return shift #$nodes;
};
return ( $iterator_node, 0 );
};
our $iterator_organization = sub {
my ( $browser, $parent ) = #_;
my $url = $parent->{internal_url};
my $nodes = $browser->nodes( url => $url );
my $iterator = sub {
return shift #$nodes;
};
return ( $iterator, 1 );
};
sub organizations {
my ( $self, $options ) = ( shift, { #_ } );
my $map = [
$Massweb::Browser::Europa::iterator_organizations,
results => q#.//span[#class="ey_badge"]#,
organizations => [
$Massweb::Browser::Europa::iterator_organizations_b,
internal_url => [ q#.//a/#href#, $Massweb::Browser::Europa::handler_url ],
external_url => [ q#.//i[#class="fa fa-external-link fa-lg"]/parent::p//a/#href#, $Massweb::Browser::handler_trim ],
title => q#.//h4#,
topics => [ q#.//div[#class="org_cord"]#, $Massweb::Browser::handler_val, $Massweb::Browser::handler_list_colon ],
location => [ q#.//i[#class="fa fa-location-arrow fa-lg"]/parent::p#, $Massweb::Browser::handler_trim ],
hand => [ q#.//i[#class="fa fa-hand-o-right fa-lg"]/parent::p#, $Massweb::Browser::handler_trim, $Massweb::Browser::handler_list_comma ],
pic_number => [ q#.//p[contains(.,'PIC no')]#, $Massweb::Browser::handler_val ],
recruiting => [ q#boolean(.//i[#class="fa fa-user-times fa-lg"])#, $Massweb::Browser::handler_bool_rev ],
_ => \&organization,
],
];
my $organizations = $self->browse( map => $map );
return $organizations;
}
sub organization {
my ( $self, $options ) = ( shift, { #_ } );
my $map = [
sub { $Massweb::Browser::Europa::iterator_organization->( $_[0], $options ) },
#title => q#.//h1#,
description => q#.//div[#class="ey_vp_detail_page"]/p#,
];
my $organization = $self->browse( map => $map );
return $organization;
}

The problem appears to be the block/anonymous hash starting 'internal_url'. I can't imagine what you intend there but it is a syntax error and would have no effect if you fixed it
Why are you declaring so many subroutine references like our $iterator_organizations = sub { ... } instead of using standard subroutines? It is a very strange approach

Related

Laravel get specific Route parameters

I am from Taiwan, Sorry for my poor English first.
I try to get Laravel specific Route parameters, here are what i have done:
Route :
Route::get('/a', function () {
//
})->name('test.a');
Route::get('/b/{b1}/{b2}', function () {
//
})->name('test.b');
Route::get('/c/{c1}/{c2?}/{c3}', function () {
//
})->name('test.c');
PHP :
function getRouteParameters(?string $routeName)
{
$routeCheck = Illuminate\Support\Facades\Route::getRoutes()->hasNamedRoute($routeName);
if ($routeCheck) {
$routeParameters = [];
$route = Illuminate\Support\Facades\Route::getRoutes()->getByName($routeName);
$uri = $route->uri();
$uriArray = explode('/', $uri);
$uriParameters = array_filter($uriArray, function ($value) {
return str_contains($value, '{');
});
foreach ($uriParameters as $uriParameter) {
$uriParameterName = str_replace(['{', '?', '}'], '', $uriParameter);
$uriParameterStatus = str_contains($uriParameter, '?') ? 'optional' : 'required';
$routeParameters[$uriParameterName] = $uriParameterStatus;
}
return $routeParameters;
}
return false;
}
$a = getRouteParameters('test.a');
$b = getRouteParameters('test.b');
$c = getRouteParameters('test.c');
$d = getRouteParameters('test.d');
dump($a, $b, $c, $d);
Result :
// $a
[]
// $b
array:2 [▼
"b1" => "required"
"b2" => "required"
]
// $c
array:3 [▼
"c1" => "required"
"c2" => "optional"
"c3" => "required"
]
// $d
false
It's there a better way to do this ( without write the function by myself )?

what is returned by Engine_Api::_()->getTable

I wonder if it is an array that is returned by calling getTable('tbl', 'module') or an object. I am facing problems when I try to insert the returned value into another array and then encode it as JSON. Passing it per se to $this->_helper->json() generates JSON output successfully, but using it as part of another array just does not work and is a null array. See below for the controller code:
//put your code here
public function indexAction () {
}
public function browseAction () {
$resultArray = $this->prepareArray();
$this->sendResponse($resultArray);
}
/**
* HTTP Response Codes
*/
const HTTP_OK = 200;
public function sendResponse($data) {
ob_clean();
$this->_helper->json($data);
}
public function prepareArray() {
//Here we will prepare the array to be later encoded as JSON
$responseArray = [
'status_code' => '',
'body' => [
'itemsCount' => '',
'foods' => [
'food_id' => '',
'food_title' => '',
'image' => '',
],
],
];
$foodTable = Engine_Api::_()->getDbTable('foods', 'restapi');
$foods = $foodTable->getFoods($this->view);
$foodArray = [];
print_r($foods);
while ($row = mysqli_fetch_row($foods)) {
$foodArray['food_id'] = $row['food_id'];
$foodArray['food_title'] = $row['title'];
$foodArray['image'] = $row['image'];
}
error_log( $row ['food_id'] . ',' . $row['title']);
$responseArray['status_code'] = '200';
$responseArray['body']['itemsCount'] = count($foods);
$responseArray['body']['foods']= $foodsArray;
return $responseArray;
}
If everything is set properly, getTable('tbl', 'module') returns object(Module_Model_DbTable_Tbl). This is the model of your database table.
Your particular error is on the line where you're assigning foodsArray variable that isn't defined anywhere. You should have assign foodArray here instead.
$responseArray['body']['foods']= $foodsArray; // should be foodArray

Customize error message in yii2 REST API

I want to customize this following error message
Error 404: Not Found
{
"name": "Not Found",
"message": "Object not found: 6",
"code": 0,
"status": 404,
"type": "yii\\web\\NotFoundHttpException"
}
to:
Error 404: Not Found
{
"name": "Not Found",
"message": "Country not found for: 6",
"code": 404,
"status": Error
}
Where is need to write this customization code?
try this (e.g. config/web.php):
return [
// ...
'components' => [
// ...
'response' => [
// ...
'on beforeSend' => function (yii\base\Event $event) {
$response = $event->sender;
if (404 === $response->statusCode && is_array($response->data)) {
$response->data['code'] = $response->data['status'];
$response->data['status'] = 'Error';
unset($response->data['type']);
}
},
],
You need to override errorHandler which extends yii\web\ErrorHandler, just convertExceptionToArray method
/**
* Converts an exception into an array.
* #param \Exception|\Error $exception the exception being converted
* #return array the array representation of the exception.
*/
protected function convertExceptionToArray($exception)
{
if (!YII_DEBUG && !$exception instanceof UserException && !$exception instanceof HttpException) {
$exception = new HttpException(500, Yii::t('yii', 'An internal server error occurred.'));
}
$array = [
'name' => ($exception instanceof Exception || $exception instanceof ErrorException) ? $exception->getName() : 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
];
if ($exception instanceof HttpException) {
$array['status'] = $exception->statusCode;
}
if (YII_DEBUG) {
$array['type'] = get_class($exception);
if (!$exception instanceof UserException) {
$array['file'] = $exception->getFile();
$array['line'] = $exception->getLine();
$array['stack-trace'] = explode("\n", $exception->getTraceAsString());
if ($exception instanceof \yii\db\Exception) {
$array['error-info'] = $exception->errorInfo;
}
}
}
if (($prev = $exception->getPrevious()) !== null) {
$array['previous'] = $this->convertExceptionToArray($prev);
}
return $array;
}
The code above is from Yii, you just need to tweak it a little.
Then add it to config : ( usually web.php)
'errorHandler' => [
'class' => 'your\namespace\YourErrHandler',
],
Im not sure what I do is right or wrong but it works.
Just create a new custom php file called ErrorMsg.php
<?php
use Yii;
use yii\web\HttpException;
class ErrorMsg extends \Exception
{
public static function customErrorMsg($error_code,$message = null, $code = 0, \Exception $previous = null,$extra_content=NULL)
{
$httpException = new HttpException($error_code,$message,$code,$previous);
Yii::$app->response->statusCode = $error_code;
$custom_err = array(
'name'=> $httpException->getName(),
'message' => $message,
'code' => $code,
'extraContent' => $content,
'status' => $error_code
);
return $custom_err;
}
and call the functions wherever you want. Example
return ErrorMsg::customErrorMsg(400,"Message Here",11,NULL,"Extra Content Here");

Customizing Joomla PDF output

In Joomla 1.5 constructor of JDocumentPDF class has an array parameter to setup some parameter of generated PDF.
function __construct($options = array()) {
parent::__construct($options);
if (isset($options['margin-header'])) {
$this->_margin_header = $options['margin-header'];
}
if (isset($options['margin-footer'])) {
$this->_margin_footer = $options['margin-footer'];
}
if (isset($options['margin-top'])) {
$this->_margin_top = $options['margin-top'];
}
...
}
_createDocument() function of JFactory class instantiates JDocumentPDF object, but doesn't pass any options that useful for PDF generation:
function &_createDocument() {
...
$attributes = array (
'charset' => 'utf-8',
'lineend' => 'unix',
'tab' => ' ',
'language' => $lang->getTag(),
'direction' => $lang->isRTL() ? 'rtl' : 'ltr'
);
$doc =& JDocument::getInstance($type, $attributes);
return $doc;
}
So I don't understand how it works and where can I set this options (margin-header, margin-footer etc)?
To set and get any properties of JDocumentPDF
you can call set and get function on object. For example
$obj = JFactory::getDocument();
$marginHeader = $obj->get('_margin_header');
$obj->set('_margin_header', $value);

Generate JSON from nested sets (perl, sql, jquery)

I have content pages in the database (using nested sets) and I need to show it by jQuery jsTree plugin. It's need to return JSON with data like this:
[
{
data: 'node1Title',
children: [
{
data: 'subNode1Title',
children: [...]
},
{
data: 'subNode2Title',
children: [...]
}
]
},
{
data: 'node2Title',
children: [...]
}
]
What I need for do it?
I can transform an array of hashes to JSON but I don't understand how to generate an array.
Sample data:
**'pages'table**
id parent_id level lkey rkey name
1 0 1 1 14 index
2 1 2 2 7 info
3 1 2 8 13 test
4 2 3 3 4 about
5 2 3 5 6 help
6 3 3 9 10 test1
7 3 3 11 12 test2
I need to get:
[
{
data: 'index',
children: [
{
data: 'info',
children: [
{
data: 'about'
},
{
data: 'help',
}
]
},
{
data: 'test',
children: [
{
data: 'test1'
},
{
data: 'test2'
}
]
}
]
}
]
I had exactly the same problem and here is what I wrote in Perl to convert my nested set tree into a JSON object for jsTree plugin (I'm using DBIx::Tree::NestedSet to access the MySQL database tree). I know my code is ugly from a Perl perspective, but it works for me.
sub get_json_tree {
my $json = '[';
my $first = 1;
my $last_level = 1;
my $level = 1;
my $tree = DBIx::Tree::NestedSet->new(dbh => $dbh);
my $ancestors = $tree->get_self_and_children_flat(id => $tree->get_root);
foreach (#{$ancestors}) {
my $name = $_->{'name'};
$last_level = $level;
$level = $_->{'level'};
if ($level > $last_level) {
$json .= ',' if ($json =~ /}$/);
} elsif ($level < $last_level) {
$json .= ']}';
for (my $i = 0; $i < $last_level - $level; $i++) {
$json .= ']}';
}
$json .= ',';
} elsif ($level == $last_level && !$first) {
$json .= ']},';
}
$json .= '{"attr":{"id":'.$_->{'id'}.',"rel":"folder"},"data":"'.$name.'","children":[';
$first = 0;
}
$json .= ']}';
for (my $i = 1; $i < $level; $i++) {
$json .= ']}';
}
$json .= ']';
return $json;
}
I'm looking for it. Perhaps DataTable plugin examples offer a solution. I'm looking on the plugin directory /examples/server_side/scripts/ssp.class.php. You can download it here.
Take a look about simplest way of using it at "Server-side script" label in this documentation.
This is very simple. You need to write a recursive function. I wrote it in Perl. $list - this is your array sorted by 'left_key'.
sub make_tree {
my $list = shift;
my #nodes;
while (my $node = shift #$list) {
if (#$list and $node->{level} < $list->[0]{level}) {
$node->{data} = make_tree($list);
push #nodes, $node;
}
last if #$list and $node->{level} > $list->[0]{level};
}
return \#nodes;
}
my $hash = make_tree($list);
Recently I was looking for a similar solution. I didn't find this until after posting my own question. The final code I posted on question I think would answers your question nicely.
I am using the following code with a modified version of DBIx::Tree::NestedSet. I use this code to create a JSON output of the nested sets tree.
Convert a flat datastructure into a tree
sub get_jsonTree {
my ($array_of_hashes_ref) = #_;
my $roots;
my %recs_by_name;
my %children_by_parent_name;
my %count;
for my $row (#$array_of_hashes_ref) {
my $name = $row->{position_id};
my $parent_name = $row->{placement_id};
my $rec = {
name => $name,
};
## Added to loop through all key,value pairs and add them to $rec
while ( my ($key, $value) = each(%$row) ) {
$rec->{$key} = $value;
}
##Added To Count Child Nodes
$count{$parent_name} = 0 if (!$count{$parent_name});
$rec->{'child_count'} = $count{$parent_name};
$count{$parent_name}++;
push #{ $children_by_parent_name{$parent_name // 'root'} }, $rec;
$recs_by_name{$name} = $rec;
}
$roots = delete($children_by_parent_name{root}) || [];
for my $name (keys(%children_by_parent_name)) {
my $children = $children_by_parent_name{$name};
if ( my $rec = $recs_by_name{$name} ) {
$rec->{children} = $children;
} else {
$util{'test'} .= "Parent $name doesn't exist.\n<BR>";
push #$roots, #$children;
}
}
use JSON;
my $json_str = encode_json(#{$roots}[0]);
return $json_str;
}
my $array_of_hashes_ref = [
{ position_id => 123, placement_id => undef },
{ position_id => 456, placement_id => 123 },
{ position_id => 789, placement_id => 123 },
# ...
];
my $json_str = &get_jsonTree($array_of_hashes_ref);