# HG changeset patch # User cin # Date 1441579031 -10800 # Node ID 14a9663692787fb734cec3af07c6aa09969dbcfa # Parent cc7244ab1b9f7dcfdaa79a3da92e4d36b6f04721 working version of exporting bugs from bugzilla in tj3 format (without bookings) diff -r cc7244ab1b9f -r 14a966369278 bug-list.xsl --- a/bug-list.xsl Sat Sep 05 22:01:12 2015 +0300 +++ b/bug-list.xsl Mon Sep 07 01:37:11 2015 +0300 @@ -22,9 +22,9 @@ - + - + @@ -33,11 +33,11 @@ - + - + @@ -49,7 +49,7 @@ - + - + - + - @@ -128,7 +128,7 @@ + select="depends_on[not(text() = exsl:node-set($parents)/bug/parent[@id=$id]/@bugid)][key('bugid', .)]" /> @@ -147,7 +147,7 @@ - + diff -r cc7244ab1b9f -r 14a966369278 lib/Benzin/Bugzilla/Bug.pm --- a/lib/Benzin/Bugzilla/Bug.pm Sat Sep 05 22:01:12 2015 +0300 +++ b/lib/Benzin/Bugzilla/Bug.pm Mon Sep 07 01:37:11 2015 +0300 @@ -46,22 +46,10 @@ use constant { BUG_FIELDS => \@fields }; use IMPL::declare { - require => { - Strptime => 'DateTime::Format::Strptime' - }, - base => [ - 'IMPL::Object::Fields' => undef - ] + base => [ 'IMPL::Object::Fields' => undef ] }; - use fields @fields; -my $dtparser = Strptime->new( - pattern => '%Y%m%dT%H:%M:%S', - time_zone => 'UTC', - on_error => 'croak' -); - sub CTOR { my SELF $this = shift; my $data = shift; @@ -71,7 +59,7 @@ # returns { # reports => [ -# { who => email:string, when => report-date-time:DateTime, work_time => hours:double } +# { who => email:string, start => work-start-date-time:DateTime, end => report-date-time:DateTime, work_time => hours:double } # ], # actual => hours # remaining => hours @@ -79,16 +67,13 @@ sub GetTimeReports { my SELF $this = shift; my $resolution = shift || 0.25; - - warn "Processing: $this->{id}"; my @bookings; my $actual = 0; for my $history ( @{ $this->{history} || [] } ) { - my $who = $history->{who}; - warn $history->{when}; - my $when = $dtparser->parse_datetime( $history->{when} ); + my $who = $history->{who}; + my $when = $history->{when}; my $changes = $history->{changes}; for my $change ( @{ $changes || [] } ) { @@ -101,10 +86,11 @@ if ($dt) { push @bookings, { - who => $who, + end => $who, when => $when->iso8601(), work_time => $dt, - start => $when->clone()->subtract( hours => $dt )->iso8601() + start => $when->clone()->subtract( hours => $dt ) + ->iso8601() }; $actual += $dt; } @@ -125,4 +111,4 @@ return $resolution ? ceil( $value / $resolution ) * $resolution : $value; } -1; \ No newline at end of file +1; diff -r cc7244ab1b9f -r 14a966369278 lib/Benzin/Bugzilla/XmlRpcClient.pm --- a/lib/Benzin/Bugzilla/XmlRpcClient.pm Sat Sep 05 22:01:12 2015 +0300 +++ b/lib/Benzin/Bugzilla/XmlRpcClient.pm Mon Sep 07 01:37:11 2015 +0300 @@ -1,29 +1,20 @@ package Benzin::Bugzilla::XmlRpcClient; use strict; -use LWP::UserAgent; -use XMLRPC::Lite; use YAML::XS qw(Dump); +use URI; use IMPL::declare { require => { - Bug => 'Benzin::Bugzilla::Bug', - BugComment => 'Benzin::Bugzilla::BugComment' + Bug => 'Benzin::Bugzilla::Bug', + BugComment => 'Benzin::Bugzilla::BugComment', + Deserializer => 'Benzin::Bugzilla::XmlRpcDeserializer', + XmlRpc => 'XMLRPC::Lite' }, base => { 'IMPL::Object::Fields' => undef } }; -use fields qw(url apikey); - -sub new { - my $class = shift; - $class = ref $class || $class; - - my $inst = fields::new($class); - $inst->CTOR(@_); - - return $inst; -} +use fields qw(url apikey _client); sub CTOR { my SELF $this = shift; @@ -31,6 +22,13 @@ $this->{url} = $params{url} or die "An url is required"; $this->{apikey} = $params{apikey} if $params{apikey}; + + #create slightly modified client to parse datetime values + my $url = URI->new_abs( 'xmlrpc.cgi', $this->{url} ); + + $this->{_client} = + XmlRpc->new( deserializer => Deserializer->new() )->proxy($url); + } sub GetBugs { @@ -42,7 +40,35 @@ ]; } -sub PopulateBugsWithComments { +sub GetBugsHierarchy { + my SELF $this = shift; + my $args = shift || {}; + + my @queue = @{$args->{ids}}; + my %visited; + + my @fetched; + + while (@queue) { + @queue = grep not( $visited{$_}++ ), @queue; + + last unless @queue; + + my $bugs = $this->GetBugs( { ids => \@queue } ); + @queue = (); + + foreach my Bug $bug (@$bugs) { + + push @queue, @{ $bug->{depends_on} } + if ( $bug->{depends_on} ); + push @fetched, $bug; + } + } + + return \@fetched; +} + +sub PopulateBugsComments { my SELF $this = shift; my $bugs = shift || []; @@ -68,11 +94,12 @@ if ( keys %bugs ) { my $resp = - $this->_CallService( 'Bug.history', { ids => [ keys %bugs ] } )->{bugs}; + $this->_CallService( 'Bug.history', { ids => [ keys %bugs ] } ) + ->{bugs}; for my $data (@$resp) { - my Bug $bug = $bugs{$data->{id}}; - + my Bug $bug = $bugs{ $data->{id} }; + $bug->{history} = $data->{history}; } } @@ -87,9 +114,9 @@ $params ||= {}; $params->{api_key} = $this->{apikey}; - my $url = URI->new_abs( 'xmlrpc.cgi', $this->{url} ); + - my $result = XMLRPC::Lite->proxy($url)->call( $method, $params ); + my $result = $this->{_client}->call( $method, $params ); die $result->fault if $result->fault; return $result->result; diff -r cc7244ab1b9f -r 14a966369278 lib/Benzin/Bugzilla/XmlRpcDeserializer.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/Benzin/Bugzilla/XmlRpcDeserializer.pm Mon Sep 07 01:37:11 2015 +0300 @@ -0,0 +1,51 @@ +package Benzin::Bugzilla::XmlRpcDeserializer; +use strict; +use mro; +use parent qw(-norequire XMLRPC::Deserializer); +use IMPL::require { Strptime => 'DateTime::Format::Strptime' }; + +my $xmlRpcDateFormat = Strptime->new( + pattern => '%Y%m%dT%H:%M:%S', + time_zone => 'UTC', + on_error => 'croak' +); + +sub decode_value { + my $this = shift; + my ( $name, $attrs, $children, $value ) = @{ $_[0] }; + + if ( $name eq 'dateTime.iso8601' ) { + return $xmlRpcDateFormat->parse_datetime($value); + } + else { + $this->next::method(@_); + } + +} + +1; + +__END__ + +=pod + +=head1 NAME + +C - data deserializer for L. + +=head1 SYNOPSIS + +=begin code perl + +# override the default deserializer +my $client = XMLRPC::Lite->new( + deserializer => Benzin::Bugzilla::XmlRpcDeserializer->new() +); + +=end code perl + +=head1 DESCRIPTION + +Slightly tuned C to parse C values to L objects. + +=cut diff -r cc7244ab1b9f -r 14a966369278 lib/Benzin/Bugzilla/XmlSerializer.pm --- a/lib/Benzin/Bugzilla/XmlSerializer.pm Sat Sep 05 22:01:12 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -package Benzin::Bugzilla::XmlSerializer; - -use IMPL::declare { - require => { - XMLWriter => 'XML::Writer' - }, - base => [ - 'IMPL::Object::Fields' => undef - ] -}; - -use fields qw(_writer); - -sub CTOR { - my SELF $this = shift; - - $this->{_writer} = XMLWriter->new(@_); -} - -sub WriteBugList { - my SELF $this = shift; - my $bugs = shift || []; - my $writer = $this->{_writer}; - - - $writer->xmlDecl("UTF-8"); - $writer->startTag("bugzilla"); - - $writer->startElement("bugs"); - - $this->WriteBug($_) foreach @$bugs; - - $writer->endTag(); - $writer->endTag(); - -} - -sub WriteBug { - my SELF $this = shift; - my $bug = shift; - my $writer = $this->{_writer}; - - $writer->startTag("bug"); - foreach my $field ( @{ Bug->BUG_FIELDS } ) { - next unless $bug->{$field}; - $this->WriteElement( $field, $bug->{$field} ); - } - $writer->endTag(); -} - -sub WriteElement { - my SELF $this = shift; - my ( $name, $data ) = @_; - my $writer = $this->{_writer}; - - my @values = - ref($data) - && ref($data) eq 'ARRAY' - ? @{$data} - : $data; - - foreach my $v (@values) { - $writer->dataElement( $name, $v ); - } -} - -1; diff -r cc7244ab1b9f -r 14a966369278 lib/Benzin/Bugzilla/XmlWriter.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/Benzin/Bugzilla/XmlWriter.pm Mon Sep 07 01:37:11 2015 +0300 @@ -0,0 +1,110 @@ +package Benzin::Bugzilla::XmlWriter; + +our %Transform = ( + 'Benzin::Bugzilla::Bug' => 'WriteBug', + 'Benzin::Bugzilla::BugComment' => 'WriteBugComment', + 'HASH' => 'WriteHash', + -plain => 'WriteValue', + -default => 'WriteValue' +); + +use IMPL::Const qw(:prop); +use IMPL::declare { + require => { + XMLWriter => 'XML::Writer', + Bug => 'Benzin::Bugzilla::Bug', + BugComment => 'Benzin::Bugzilla::BugComment' + }, + base => [ + 'IMPL::Transform' => sub { %Transform } + ], + props => [ + _writer => PROP_RW + ] +}; + +use fields qw(_writer); + +sub CTOR { + my SELF $this = shift; + + $this->_writer(XMLWriter->new(@_)); +} + +sub WriteBugList { + my SELF $this = shift; + my $bugs = shift || []; + my $writer = $this->_writer; + + + $writer->xmlDecl("UTF-8"); + $writer->startTag("bugzilla"); + + + foreach my $bug (@$bugs) { + $writer->startTag("bug"); + $this->WriteBug($bug); + $writer->endTag(); + } + + $writer->endTag(); + +} + +sub WriteBug { + my SELF $this = shift; + my $value = shift; + my $writer = $this->_writer; + + foreach my $field ( @{ Bug->BUG_FIELDS } ) { + next unless $value->{$field}; + $this->WriteElement( $field, $value->{$field} ); + } +} + +sub WriteBugComment { + my SELF $this = shift; + my $value = shift; + my $writer = $this->_writer; + + foreach my $field ( @{ BugComment->COMMENT_FIELDS } ) { + next unless $value->{$field}; + $this->WriteElement( $field, $value->{$field} ); + } +} + +sub WriteHash { + my SELF $this = shift; + my $value = shift; + + while(my ($k,$v) = each %$value) { + $this->WriteElement($k,$v); + } +} + +sub WriteElement { + my SELF $this = shift; + my ( $name, $data ) = @_; + my $writer = $this->_writer; + + my @values = + ref($data) + && ref($data) eq 'ARRAY' + ? @{$data} + : $data; + + foreach my $v (@values) { + $writer->startTag($name); + $this->Transform( $v ); + $writer->endTag; + } +} + +sub WriteValue { + my SELF $this = shift; + my $value = shift; + + $this->_writer->characters($value) if defined $value; +} + +1; diff -r cc7244ab1b9f -r 14a966369278 translate.pl --- a/translate.pl Sat Sep 05 22:01:12 2015 +0300 +++ b/translate.pl Mon Sep 07 01:37:11 2015 +0300 @@ -1,21 +1,13 @@ #!/usr/bin/perl -w use IMPL::require { - BzClient => 'Benzin::Bugzilla::XmlRpcClient', - Bug => 'Benzin::Bugzilla::Bug' + BzClient => 'Benzin::Bugzilla::XmlRpcClient', + BugWriter => 'Benzin::Bugzilla::XmlWriter' }; use YAML::XS qw(LoadFile Dump); use XML::Writer; use IPC::Run qw(start finish); -our @ClassPath = qw( - /usr/share/java/xalan-j2-serializer.jar - /usr/share/java/xalan-j2.jar - /usr/share/java/xerces-j2.jar - /usr/share/java/xml-commons-resolver.jar - . -); - my $config = LoadFile("config.yaml"); if ( !( $config->{bugzilla}{url} =~ /\/$/ ) ) { @@ -27,40 +19,30 @@ apikey => $config->{bugzilla}{apikey} ); +my $bugs = $config->{bugzilla}{bugs} or die "No bugs specified"; +$bugs = [$bugs] unless ref $bugs eq 'ARRAY'; + local (*HIN); -my $proc = start( [ 'saxon8', '-novw', '-', 'bug-list.xsl' ], - '', \*STDOUT ) - or die "failed to create pipe: $!"; +my $proc = start( + [ 'saxon8', '-novw', '-', 'bug-list.xsl' ], + #[ 'cat' ], + '', \*STDOUT +) or die "failed to create pipe: $!"; + +binmode *HIN, ":encoding(utf-8)"; +my $writer = + BugWriter->new( OUTPUT => *HIN, DATA_INDENT => 2, DATA_MODE => 'on' ); eval { my %visited; - my @queue = (283); - my @fetched; - while (@queue) { - @queue = grep not( $visited{$_}++ ), @queue; - - last unless @queue; - - print "#Fetching: ", join( ', ', @queue ), "\n"; - - my $bugs = $bz->GetBugs( { ids => \@queue } ); - @queue = (); + my $fetched = $bz->GetBugsHierarchy( { ids => [283] } ); - foreach my $bug (@$bugs) { + $bz->PopulateBugsComments($fetched); + $bz->PopulateBugsHistory($fetched); - push @queue, @{ $bug->{depends_on} } - if ( $bug->{depends_on} ); - push @fetched, $bug; - } - } - print Dump( \@fetched ); - - $bz->PopulateBugsWithComments( \@fetched ); - $bz->PopulateBugsHistory( \@fetched ); - - print Dump( [ map $_->GetTimeReports(0.25), @fetched ] ); + $writer->WriteBugList($fetched); }; warn Dump($@) and die $@ if $@;