7
|
1 package Benzin::Bugzilla::Bug;
|
|
2 use strict;
|
9
|
3 use POSIX;
|
|
4 use Scalar::Util qw(looks_like_number);
|
|
5 use DateTime;
|
7
|
6
|
9
|
7 my @fields;
|
7
|
8
|
|
9 BEGIN {
|
9
|
10 @fields = qw(
|
7
|
11 id
|
|
12 summary
|
|
13 creation_time
|
|
14 last_change_time
|
|
15 creator
|
|
16 assigned_to
|
|
17 qa_contact
|
|
18 cc
|
|
19
|
9
|
20 is_open
|
7
|
21 status
|
|
22 resolution
|
|
23
|
|
24 priority
|
|
25 severity
|
|
26 url
|
|
27
|
|
28 blocks
|
|
29 depends_on
|
|
30
|
|
31 component
|
|
32 product
|
|
33 classification
|
|
34 version
|
|
35
|
|
36 actual_time
|
|
37 estimated_time
|
|
38 remaining_time
|
|
39 deadline
|
9
|
40
|
7
|
41 comments
|
9
|
42 history
|
7
|
43 );
|
|
44 }
|
|
45
|
9
|
46 use constant { BUG_FIELDS => \@fields };
|
7
|
47
|
|
48 use IMPL::declare {
|
9
|
49 require => {
|
|
50 Strptime => 'DateTime::Format::Strptime'
|
|
51 },
|
7
|
52 base => [
|
9
|
53 'IMPL::Object::Fields' => undef
|
7
|
54 ]
|
|
55 };
|
|
56
|
9
|
57 use fields @fields;
|
|
58
|
|
59 my $dtparser = Strptime->new(
|
|
60 pattern => '%Y%m%dT%H:%M:%S',
|
|
61 time_zone => 'UTC',
|
|
62 on_error => 'croak'
|
|
63 );
|
7
|
64
|
|
65 sub CTOR {
|
|
66 my SELF $this = shift;
|
9
|
67 my $data = shift;
|
|
68 $this->{$_} = $data->{$_}
|
|
69 foreach grep exists $data->{$_}, @{ SELF->BUG_FIELDS };
|
7
|
70 }
|
|
71
|
9
|
72 # returns {
|
|
73 # reports => [
|
|
74 # { who => email:string, when => report-date-time:DateTime, work_time => hours:double }
|
|
75 # ],
|
|
76 # actual => hours
|
|
77 # remaining => hours
|
|
78 # }
|
|
79 sub GetTimeReports {
|
7
|
80 my SELF $this = shift;
|
9
|
81 my $resolution = shift || 0.25;
|
7
|
82
|
9
|
83 warn "Processing: $this->{id}";
|
|
84
|
|
85 my @bookings;
|
|
86 my $actual = 0;
|
|
87
|
|
88 for my $history ( @{ $this->{history} || [] } ) {
|
|
89 my $who = $history->{who};
|
|
90 warn $history->{when};
|
|
91 my $when = $dtparser->parse_datetime( $history->{when} );
|
|
92 my $changes = $history->{changes};
|
|
93
|
|
94 for my $change ( @{ $changes || [] } ) {
|
|
95 if ( $change->{field_name} eq 'work_time' ) {
|
|
96 my $prev = $change->{removed} || 0;
|
|
97 my $value = $change->{added} || 0;
|
|
98 if ( looks_like_number($prev) and looks_like_number($value) ) {
|
|
99 my $dt = coarsen( $value - $prev, $resolution );
|
|
100
|
|
101 if ($dt) {
|
|
102 push @bookings,
|
|
103 {
|
|
104 who => $who,
|
|
105 when => $when->iso8601(),
|
|
106 work_time => $dt,
|
|
107 start => $when->clone()->subtract( hours => $dt )->iso8601()
|
|
108 };
|
|
109 $actual += $dt;
|
|
110 }
|
|
111 }
|
|
112 }
|
|
113 }
|
|
114 }
|
|
115
|
|
116 return {
|
|
117 reports => \@bookings,
|
|
118 actual => $actual,
|
|
119 remaining => coarsen( $this->{remaining_time}, $resolution )
|
|
120 };
|
7
|
121 }
|
|
122
|
9
|
123 sub coarsen {
|
|
124 my ( $value, $resolution ) = @_;
|
|
125 return $resolution ? ceil( $value / $resolution ) * $resolution : $value;
|
|
126 }
|
|
127
|
|
128 1; |