Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
bugzilla
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
etersoft
bugzilla
Commits
8b0a6508
Commit
8b0a6508
authored
Mar 01, 2005
by
travis%sedsystems.ca
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bug 250410 : Time tracking summaries
Patch by Christian Reis <kiko@async.com.br> r=jpeshkin a=justdave
parent
ca2f67d8
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
883 additions
and
1 deletion
+883
-1
buglist.cgi
buglist.cgi
+1
-0
summarize-time.css
skins/standard/summarize-time.css
+46
-0
summarize_time.cgi
summarize_time.cgi
+484
-0
edit.html.tmpl
template/en/default/bug/edit.html.tmpl
+7
-0
summarize-time.html.tmpl
template/en/default/bug/summarize-time.html.tmpl
+329
-0
filterexceptions.pl
template/en/default/filterexceptions.pl
+8
-0
list.html.tmpl
template/en/default/list/list.html.tmpl
+8
-1
No files found.
buglist.cgi
View file @
8b0a6508
...
...
@@ -894,6 +894,7 @@ if (@bugidlist) {
$vars
->
{
'bugs'
}
=
\
@bugs
;
$vars
->
{
'buglist'
}
=
\
@bugidlist
;
$vars
->
{
'buglist_joined'
}
=
join
(
','
,
@bugidlist
);
$vars
->
{
'columns'
}
=
$columns
;
$vars
->
{
'displaycolumns'
}
=
\
@displaycolumns
;
...
...
skins/standard/summarize-time.css
0 → 100644
View file @
8b0a6508
/* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Bugzilla Bug Tracking System.
*
* Contributor(s): Christian Reis <kiko@async.com.br>
*/
td
{
vertical-align
:
top
}
table
.zeroitems
,
table
.realitems
{
margin-left
:
2.0em
;
margin-top
:
2px
;
border
:
1px
solid
black
;
border
:
1px
solid
black
;
}
tr
.section_total
{
background
:
#000000
;
color
:
#ffffff
;
}
td
.subtotal
{
background
:
#B0C0D9
;
}
.zeroitems
.bug_header
{
background
:
#d0e0f0
}
.zeroitems
.bug_header2
{
background
:
#f9f9f9
}
/* the fixed headers -- .number uses bug_header so hack it here */
.number
.bug_header
,
.number
.bug_header2
{
background
:
#d0e0f0
}
.owner_header
{
background
:
#d0e0f0
}
/* the details headers */
.number
.owner_header
,
.owner
.bug_header
{
background
:
#ffffff
}
.number
.owner_header2
,
.owner
.bug_header2
{
background
:
#EFEFEF
}
summarize_time.cgi
0 → 100755
View file @
8b0a6508
#!/usr/bin/perl -wT
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Christian Reis <kiko@async.com.br>
# Shane H. W. Travis <travis@sedsystems.ca>
#
use
strict
;
use
lib
qw(.)
;
use
Date::
Parse
;
# strptime
use
Date::
Format
;
# strftime
use
Bugzilla::
Bug
;
# EmitDependList
use
Bugzilla::
Util
;
# trim
use
Bugzilla::
Constants
;
# LOGIN_*
require
"CGI.pl"
;
GetVersionTable
();
# Use global template variables.
use
vars
qw($template $vars)
;
#
# Date handling
#
sub
date_adjust
{
my
(
$year
,
$month
,
$day
)
=
@_
;
if
(
$month
==
13
)
{
$month
=
1
;
$year
+=
1
;
}
if
(
$month
==
2
&&
(
$day
==
31
||
$day
==
30
||
$day
==
29
))
{
if
(
$year
%
4
==
0
)
{
$day
=
29
;
}
else
{
$day
=
28
;
}
}
if
((
$month
==
4
||
$month
==
6
||
$month
==
9
||
$month
==
11
)
&&
(
$day
==
31
)
)
{
$day
=
30
;
}
return
(
$year
,
$month
,
$day
);
}
sub
check_dates
{
my
(
$start_date
,
$end_date
)
=
@_
;
if
(
$start_date
)
{
if
(
!
str2time
(
$start_date
))
{
ThrowUserError
(
"illegal_date"
,
{
'date'
=>
$start_date
});
}
# This code may strike you as funny. It's actually a workaround
# for an "issue" in str2time. If you enter the date 2004-06-31,
# even though it's a bogus date (there *are* only 30 days in
# June), it will parse and return 2004-07-01. To make this
# less painful to the end-user, I do the "normalization" here,
# but it might be "surprising" and warrant a warning in the end.
$start_date
=
time2str
(
"%Y-%m-%d"
,
str2time
(
$start_date
));
}
if
(
$end_date
)
{
if
(
!
str2time
(
$end_date
))
{
ThrowUserError
(
"illegal_date"
,
{
'date'
=>
$end_date
});
}
# see related comment above.
$end_date
=
time2str
(
"%Y-%m-%d"
,
str2time
(
$end_date
));
}
return
(
$start_date
,
$end_date
);
}
sub
split_by_month
{
# Takes start and end dates and splits them into a list of
# monthly-spaced 2-lists of dates.
my
(
$start_date
,
$end_date
)
=
@_
;
# We assume at this point that the dates are provided and sane
my
(
undef
,
undef
,
undef
,
$sd
,
$sm
,
$sy
,
undef
)
=
strptime
(
$start_date
);
my
(
undef
,
undef
,
undef
,
$ed
,
$em
,
$ey
,
undef
)
=
strptime
(
$end_date
);
# Find out how many months fit between the two dates so we know many
# many times we loop.
my
$yd
=
$ey
-
$sy
;
my
$md
=
12
*
$yd
+
$em
-
$sm
;
my
(
@months
,
$sub_start
,
$sub_end
);
# This +1 and +1900 are a result of strptime's bizarre semantics
my
$year
=
$sy
+
1900
;
my
$month
=
$sm
+
1
;
# If both years and months were equals.
if
(
$md
==
0
)
{
push
@months
,
[
sprintf
(
"%04d-%02d-%02d"
,
$year
,
$month
,
$sd
),
sprintf
(
"%04d-%02d-%02d"
,
$year
,
$month
,
$ed
)];
return
@months
;
}
# Keep the original $sd, when the day will be changed in the adjust_date.
# Case day > 28 and month = 2, for instance.
my
$sd_tmp
=
$sd
;
for
(
my
$i
=
0
;
$i
<
$md
;
$i
++
)
{
(
$year
,
$month
,
$sd_tmp
)
=
date_adjust
(
$year
,
$month
,
$sd
);
$sub_start
=
sprintf
(
"%04d-%02d-%02d"
,
$year
,
$month
,
$sd_tmp
);
(
$year
,
$month
,
$sd_tmp
)
=
date_adjust
(
$year
,
$month
+
1
,
$sd
);
$sub_end
=
sprintf
(
"%04d-%02d-%02d"
,
$year
,
$month
,
$sd_tmp
);
push
@months
,
[
$sub_start
,
$sub_end
];
}
# This section handles the last month for cases where the starting
# day and ending day aren't identical; in this case we need to fudge
# the last entry -- either add an extra one (for the extra days) or
# swap the last one for a shorter one (for the fewer days).
my
$fixup
=
sprintf
(
"%04d-%02d-%02d"
,
$ey
+
1900
,
$em
+
1
,
$ed
);
if
(
$sd
<
$ed
)
{
push
@months
,
[
$sub_end
,
$fixup
];
}
elsif
(
$sd
>
$ed
)
{
pop
@months
;
push
@months
,
[
$sub_start
,
$fixup
];
}
return
@months
;
}
sub
include_tt_details
{
my
(
$res
,
$bugids
,
$start_date
,
$end_date
)
=
@_
;
my
$dbh
=
Bugzilla
->
dbh
;
my
(
$date_bits
,
$date_values
)
=
sqlize_dates
(
$start_date
,
$end_date
);
my
$buglist
=
join
", "
,
@
{
$bugids
};
my
$q
=
qq{SELECT bugs.bug_id, profiles.login_name, bugs.deadline,
bugs.estimated_time, bugs.remaining_time
FROM longdescs, bugs, profiles
WHERE longdescs.bug_id in ($buglist) AND
longdescs.bug_id = bugs.bug_id AND
longdescs.who = profiles.userid
$date_bits}
;
my
%
res
=
%
{
$res
};
my
$sth
=
$dbh
->
prepare
(
$q
);
$sth
->
execute
(
@
{
$date_values
});
while
(
my
$row
=
$sth
->
fetch
)
{
$res
{
$row
->
[
0
]}{
"deadline"
}
=
$row
->
[
2
];
$res
{
$row
->
[
0
]}{
"estimated_time"
}
=
$row
->
[
3
];
$res
{
$row
->
[
0
]}{
"remaining_time"
}
=
$row
->
[
4
];
}
return
\%
res
;
}
sub
sqlize_dates
{
my
(
$start_date
,
$end_date
)
=
@_
;
my
$date_bits
;
my
@date_values
;
if
(
$start_date
)
{
# we've checked, trick_taint is fine
trick_taint
(
$start_date
);
$date_bits
=
" AND longdescs.bug_when > ?"
;
push
@date_values
,
$start_date
;
}
if
(
$end_date
)
{
# we need to add one day to end_date to catch stuff done today
my
(
undef
,
undef
,
undef
,
$ed
,
$em
,
$ey
,
undef
)
=
strptime
(
$end_date
);
$end_date
=
sprintf
(
"%04d-%02d-%02d"
,
$ey
+
1900
,
$em
+
1
,
$ed
+
1
);
$date_bits
.=
" AND longdescs.bug_when < ?"
;
push
@date_values
,
$end_date
;
}
return
(
$date_bits
,
\
@date_values
);
}
#
# Dependencies
#
sub
get_blocker_ids_unique
{
my
$bug_id
=
shift
;
my
@ret
=
(
$bug_id
);
get_blocker_ids_deep
(
$bug_id
,
\
@ret
);
my
%
unique
;
foreach
my
$blocker
(
@ret
)
{
$unique
{
$blocker
}
=
$blocker
}
return
keys
%
unique
;
}
sub
get_blocker_ids_deep
{
my
(
$bug_id
,
$ret
)
=
@_
;
my
@deps
=
Bugzilla::Bug::
EmitDependList
(
"blocked"
,
"dependson"
,
$bug_id
);
push
@
{
$ret
},
@deps
;
foreach
$bug_id
(
@deps
)
{
get_blocker_ids_deep
(
$bug_id
,
$ret
);
}
}
#
# Queries and data structure assembly
#
sub
query_work_by_buglist
{
my
(
$bugids
,
$start_date
,
$end_date
)
=
@_
;
my
$dbh
=
Bugzilla
->
dbh
;
my
(
$date_bits
,
$date_values
)
=
sqlize_dates
(
$start_date
,
$end_date
);
# $bugids is guaranteed to be non-empty because at least one bug is
# always provided to this page.
my
$buglist
=
join
", "
,
@
{
$bugids
};
# Returns the total time worked on each bug *per developer*, with
# bug descriptions and developer address
my
$q
=
qq{SELECT sum(longdescs.work_time) as total_time,
profiles.login_name,
longdescs.bug_id,
bugs.short_desc,
bugs.bug_status
FROM longdescs, profiles, bugs
WHERE longdescs.bug_id IN ($buglist) AND
longdescs.who = profiles.userid AND
bugs.bug_id = longdescs.bug_id
$date_bits
GROUP BY longdescs.bug_id, profiles.login_name
ORDER BY longdescs.bug_when}
;
my
$sth
=
$dbh
->
prepare
(
$q
);
$sth
->
execute
(
@
{
$date_values
});
return
$sth
;
}
sub
get_work_by_owners
{
my
$sth
=
query_work_by_buglist
(
@_
);
my
%
res
;
while
(
my
$row
=
$sth
->
fetch
)
{
# XXX: Why do we need to check if the total time is positive
# instead of using SQL to do that? Simply because MySQL 3.x's
# GROUP BY doesn't work correctly with aggregates. This is
# really annoying, but I've spent a long time trying to wrestle
# with it and it just doesn't seem to work. Should work OK in
# 4.x, though.
if
(
$row
->
[
0
]
>
0
)
{
my
$login_name
=
$row
->
[
1
];
push
@
{
$res
{
$login_name
}},
{
total_time
=>
$row
->
[
0
],
bug_id
=>
$row
->
[
2
],
short_desc
=>
$row
->
[
3
],
bug_status
=>
$row
->
[
4
]
};
}
}
return
\%
res
;
}
sub
get_work_by_bugs
{
my
$sth
=
query_work_by_buglist
(
@_
);
my
%
res
;
while
(
my
$row
=
$sth
->
fetch
)
{
# Perl doesn't let me use arrays as keys :-(
# merge in ID, status and summary
my
$bug
=
join
";"
,
(
$row
->
[
2
],
$row
->
[
4
],
$row
->
[
3
]);
# XXX: see comment in get_work_by_owners
if
(
$row
->
[
0
]
>
0
)
{
push
@
{
$res
{
$bug
}},
{
total_time
=>
$row
->
[
0
],
login_name
=>
$row
->
[
1
],
};
}
}
return
\%
res
;
}
sub
get_inactive_bugs
{
my
(
$bugids
,
$start_date
,
$end_date
)
=
@_
;
my
$dbh
=
Bugzilla
->
dbh
;
my
(
$date_bits
,
$date_values
)
=
sqlize_dates
(
$start_date
,
$end_date
);
my
$buglist
=
join
", "
,
@
{
$bugids
};
my
%
res
;
# This sucks. I need to make sure that even bugs that *don't* show
# up in the longdescs query (because no comments were filed during
# the specified period) but *are* dependent on the parent bug show
# up in the results if they have no work done; that's why I prefill
# them in %res here and then remove them below.
my
$q
=
qq{SELECT DISTINCT bugs.bug_id, bugs.short_desc ,
bugs.bug_status
FROM longdescs, bugs
WHERE longdescs.bug_id in ($buglist) AND
longdescs.bug_id = bugs.bug_id}
;
my
$sth
=
$dbh
->
prepare
(
$q
);
$sth
->
execute
();
while
(
my
$row
=
$sth
->
fetch
)
{
$res
{
$row
->
[
0
]}
=
[
$row
->
[
1
],
$row
->
[
2
]];
}
# Returns the total time worked on each bug, with description. This
# query differs a bit from one in the query_work_by_buglist and I
# avoided complicating that one just to make it more general.
$q
=
qq{SELECT sum(longdescs.work_time) as total_time,
longdescs.bug_id,
bugs.short_desc,
bugs.bug_status
FROM longdescs, bugs
WHERE longdescs.bug_id IN ($buglist) AND
bugs.bug_id = longdescs.bug_id
$date_bits
GROUP BY longdescs.bug_id
ORDER BY longdescs.bug_when}
;
$sth
=
$dbh
->
prepare
(
$q
);
$sth
->
execute
(
@
{
$date_values
});
while
(
my
$row
=
$sth
->
fetch
)
{
# XXX: see comment in get_work_by_owners
if
(
$row
->
[
0
]
==
0
)
{
$res
{
$row
->
[
1
]}
=
[
$row
->
[
2
],
$row
->
[
3
]];
}
else
{
delete
$res
{
$row
->
[
1
]};
}
}
return
\%
res
;
}
#
# Misc
#
sub
sort_bug_keys
{
# XXX a hack is the mother of all evils. The fact that we store keys
# joined by semi-colons in the workdata-by-bug structure forces us to
# write this evil comparison function to ensure we can process the
# data timely -- just pushing it through a numerical sort makes TT
# hang while generating output :-(
my
$list
=
shift
;
my
@a
;
my
@b
;
return
sort
{
@a
=
split
(
";"
,
$a
);
@b
=
split
(
";"
,
$b
);
$a
[
0
]
<=>
$b
[
0
]
}
@
{
$list
};
}
#
# Template code starts here
#
Bugzilla
->
login
(
LOGIN_REQUIRED
);
my
$cgi
=
Bugzilla
->
cgi
;
Bugzilla
->
switch_to_shadow_db
();
ThrowUserError
(
"timetracking_access_denied"
)
unless
UserInGroup
(
Param
(
"timetrackinggroup"
));
my
@ids
=
split
(
","
,
$cgi
->
param
(
'id'
));
map
{
ValidateBugID
(
$_
)
}
@ids
;
@ids
=
map
{
detaint_natural
(
$_
)
&&
$_
}
@ids
;
@ids
=
grep
{
Bugzilla
->
user
->
can_see_bug
(
$_
)
}
@ids
;
my
$group_by
=
$cgi
->
param
(
'group_by'
)
||
"number"
;
my
$monthly
=
$cgi
->
param
(
'monthly'
);
my
$detailed
=
$cgi
->
param
(
'detailed'
);
my
$do_report
=
$cgi
->
param
(
'do_report'
);
my
$inactive
=
$cgi
->
param
(
'inactive'
);
my
$do_depends
=
$cgi
->
param
(
'do_depends'
);
my
$ctype
=
scalar
(
$cgi
->
param
(
"ctype"
));
my
(
$start_date
,
$end_date
);
if
(
$do_report
&&
@ids
)
{
my
@bugs
=
@ids
;
# Dependency mode requires a single bug and grabs dependents.
if
(
$do_depends
)
{
if
(
scalar
(
@bugs
)
!=
1
)
{
ThrowCodeError
(
"bad_arg"
,
{
argument
=>
"id"
,
function
=>
"summarize_time"
});
}
@bugs
=
get_blocker_ids_unique
(
$bugs
[
0
]);
@bugs
=
grep
{
Bugzilla
->
user
->
can_see_bug
(
$_
)
}
@bugs
;
}
$start_date
=
trim
$cgi
->
param
(
'start_date'
);
$end_date
=
trim
$cgi
->
param
(
'end_date'
);
# Swap dates in case the user put an end_date before the start_date
if
(
$start_date
&&
$end_date
&&
str2time
(
$start_date
)
>
str2time
(
$end_date
))
{
$vars
->
{
'warn_swap_dates'
}
=
1
;
(
$start_date
,
$end_date
)
=
(
$end_date
,
$start_date
);
}
(
$start_date
,
$end_date
)
=
check_dates
(
$start_date
,
$end_date
);
if
(
$detailed
)
{
my
%
detail_data
;
my
$res
=
include_tt_details
(
\%
detail_data
,
\
@bugs
,
$start_date
,
$end_date
);
$vars
->
{
'detail_data'
}
=
$res
;
}
# Store dates ia session cookie the dates so re-visiting the page
# for other bugs keeps them around.
$cgi
->
send_cookie
(
-
name
=>
'time-summary-dates'
,
-
value
=>
join
";"
,
(
$start_date
,
$end_date
));
my
(
@parts
,
$part_data
,
@part_list
);
# Break dates apart into months if necessary; if not, we use the
# same @parts list to allow us to use a common codepath.
if
(
$monthly
)
{
# unfortunately it's not too easy to guess a start date, since
# it depends on what bugs we're looking at. We risk bothering
# the user here. XXX: perhaps run a query to see what the
# earliest activity in longdescs for all bugs and use that as a
# start date.
$start_date
||
ThrowUserError
(
"illegal_date"
,
{
'date'
=>
$start_date
});
# we can, however, provide a default end date. Note that this
# differs in semantics from the open-ended queries we use when
# start/end_date aren't provided -- and clock skews will make
# this evident!
@parts
=
split_by_month
(
$start_date
,
$end_date
||
time2str
(
"%Y-%m-%d"
,
time
()));
}
else
{
@parts
=
([
$start_date
,
$end_date
]);
}
my
%
empty_hash
;
# For each of the separate divisions, grab the relevant summaries
foreach
my
$part
(
@parts
)
{
my
(
$sub_start
,
$sub_end
)
=
@
{
$part
};
if
(
@bugs
)
{
if
(
$group_by
eq
"owner"
)
{
$part_data
=
get_work_by_owners
(
\
@bugs
,
$sub_start
,
$sub_end
);
}
else
{
$part_data
=
get_work_by_bugs
(
\
@bugs
,
$sub_start
,
$sub_end
);
}
}
else
{
# $part_data must be a reference to a hash
$part_data
=
\%
empty_hash
;
}
push
@part_list
,
$part_data
;
}
if
(
$inactive
&&
@bugs
)
{
$vars
->
{
'null'
}
=
get_inactive_bugs
(
\
@bugs
,
$start_date
,
$end_date
);
}
else
{
$vars
->
{
'null'
}
=
\%
empty_hash
;
}
$vars
->
{
'part_list'
}
=
\
@part_list
;
$vars
->
{
'parts'
}
=
\
@parts
;
}
elsif
(
$cgi
->
cookie
(
"time-summary-dates"
))
{
(
$start_date
,
$end_date
)
=
split
";"
,
$cgi
->
cookie
(
'time-summary-dates'
);
}
$vars
->
{
'ids'
}
=
\
@ids
;
$vars
->
{
'start_date'
}
=
$start_date
;
$vars
->
{
'end_date'
}
=
$end_date
;
$vars
->
{
'group_by'
}
=
$group_by
;
$vars
->
{
'monthly'
}
=
$monthly
;
$vars
->
{
'detailed'
}
=
$detailed
;
$vars
->
{
'inactive'
}
=
$inactive
;
$vars
->
{
'do_report'
}
=
$do_report
;
$vars
->
{
'do_depends'
}
=
$do_depends
;
$vars
->
{
'check_time'
}
=
\&
check_time
;
$vars
->
{
'sort_bug_keys'
}
=
\&
sort_bug_keys
;
$vars
->
{
'GetBugLink'
}
=
\&
GetBugLink
;
$ctype
=
"html"
if
!
$ctype
;
my
$format
=
GetFormat
(
"bug/summarize-time"
,
undef
,
$ctype
);
# Get the proper content-type
print
$cgi
->
header
(
-
type
=>
Bugzilla::Constants::
contenttypes
->
{
$ctype
});
$template
->
process
(
"$format->{'template'}"
,
$vars
)
||
ThrowTemplateError
(
$template
->
error
());
template/en/default/bug/edit.html.tmpl
View file @
8b0a6508
...
...
@@ -419,6 +419,13 @@
size="10" maxlength="10">
</td>
</tr>
<tr>
<td colspan="6" align="right">
<a href="summarize_time.cgi?id=[% bug.bug_id %]&do_depends=1">
Summarize time (including time for [% terms.bugs %]
blocking this [% terms.bug %])</a>
</td>
</tr>
</table>
[% END %]
...
...
template/en/default/bug/summarize-time.html.tmpl
0 → 100644
View file @
8b0a6508
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Christian Reis <kiko@async.com.br>
#%]
[% USE date %]
[% PROCESS global/variables.none.tmpl %]
[% title = "Time Summary " %]
[% IF do_depends %]
[% title = title _ "for " %]
[% h1 = title _ GetBugLink(ids.0, "$terms.Bug $ids.0") %]
[% title = title _ "$terms.Bug $ids.0: " %]
[% h1 = (h1 _ " (and $terms.bugs blocking it)") IF do_depends %]
[% ELSE %]
[% title = title _ "($ids.size $terms.bugs selected)" %]
[% h1 = title %]
[% END %]
[% PROCESS global/header.html.tmpl
title = title
h1 = h1
style_urls = ["skins/standard/summarize-time.css"]
%]
<p>
[% IF ids.size == 0 %]
No [% terms.bugs %] specified or visible.
[% ELSE %]
[% INCLUDE query_form %]
[% IF do_report %]
[% global.grand_total = 0 %]
<p>
[% FOREACH workdata = part_list %]
[% part = parts.shift %]
<div align="right">
<h4 style="padding-right: 2em; margin: 0;">
[% IF part.0 or part.1 %]
[% part.0 OR "Up" FILTER html %] to [% part.1 OR "now" FILTER html %]
[% ELSE %]
Full summary (no period specified)
[% END %]
</h4>
</div>
[% IF group_by == "number" %]
[% INCLUDE number_report %]
[% ELSE %]
[% INCLUDE owner_report %]
[% END %]
<p>
[% END %]
[% IF monthly %]
<h4 style="margin: 0">Total of [% global.grand_total FILTER format("%.2f") %] hours worked</h4>
<hr noshade size="1">
[% END %]
[% IF null.keys.size > 0 %]
[% INCLUDE inactive_report %]
<p>
<h4 style="margin: 0">Total of [% null.keys.size %]
inactive [% terms.bugs %]</h4>
[% END %]
[% END %]
[% END %]
<p>
[% PROCESS global/footer.html.tmpl %]
[%#
#
# Developer reporting
#
#%]
[% BLOCK owner_report %]
[% global.total = 0 global.bug_count = {} global.owner_count = {}%]
<table cellpadding="4" cellspacing="0" width="90%" class="realitems owner">
[% FOREACH owner = workdata.keys.sort %]
[% INCLUDE do_one_owner owner=owner ownerdata=workdata.$owner
detailed=detailed %]
[% END %]
[% additional = "$global.owner_count.size developers @
$global.bug_count.size $terms.bugs" %]
[% INCLUDE section_total colspan=3 additional=additional %]
</table>
[% END %]
[% BLOCK do_one_owner %]
[% global.owner_count.$owner = 1 %]
<tr><td colspan="5" class="owner_header">
<b>[% owner FILTER html %]</b>
</td></tr>
[% col = 0 subtotal = 0%]
[% FOREACH bugdata=ownerdata.nsort("bug_id") %]
[% bug_id = bugdata.bug_id %]
[% global.bug_count.$bug_id = 1 %]
[% IF detailed %]
[%# XXX oy what a hack %]
[% timerow = '<td width="100" align="right" valign="top">' _ bugdata.total_time _ '</td>' %]
[% INCLUDE bug_header cid=col id=bug_id bug_status=bugdata.bug_status
short_desc=bugdata.short_desc extra=timerow %]
[% col = col + 1 %]
[% END %]
[% subtotal = subtotal + bugdata.total_time %]
[% END %]
<tr>
<td colspan="3"> </td>
<td align="right">
<b>Total</b>:
</td>
<td align="right" class="subtotal" width="100">
<b>[% subtotal FILTER format("%.2f") %]</b></td>
[% global.total = global.total + subtotal %]
</tr>
[% END %]
[%#
#
# Bug Number reporting
#
#%]
[% BLOCK number_report %]
[% global.total = 0 global.owner_count = {} global.bug_count = {} %]
<table cellpadding="4" cellspacing="0" width="90%" class="realitems number">
[% keys = sort_bug_keys(workdata.keys) %]
[% FOREACH bug = keys %]
[% INCLUDE do_one_bug bug=bug bugdata=workdata.$bug
detailed=detailed %]
[% END %]
[% additional = "$global.bug_count.size $terms.bugs &
$global.owner_count.size developers" %]
[% INCLUDE section_total additional=additional colspan=2 %]
</table>
[% END %]
[% BLOCK do_one_bug %]
[% subtotal = 0.00 cid = 0 %]
[%# hack apart the ID and summary. Sad. %]
[% items = bug.split(";") %]
[% id = items.shift %]
[% status = items.shift %]
[% global.bug_count.$id = 1 %]
[% INCLUDE bug_header id=id bug_status=status short_desc=items.join(";") %]
[% FOREACH owner = bugdata.sort("login_name") %]
[% work_time = owner.total_time %]
[% subtotal = subtotal + work_time %]
[% login_name = owner.login_name %]
[% global.owner_count.$login_name = 1 %]
[% IF detailed %]
[% cid = cid + 1 %]
<tr class="owner_header[% 2 FILTER none IF cid % 2 %]">
<td> </td>
<td colspan="2"><b>[% login_name FILTER html %]</b></td>
<td align="right">
[% work_time FILTER format("%.2f") %]</td>
</tr>
[% END %]
[% END %]
<tr>
<td colspan="2"> </td>
<td align="right">
<b>Total</b>:
</td>
<td align="right" class="subtotal" width="100">
<b>[% subtotal FILTER format("%.2f") %]</b>
</td></tr>
[% global.total = global.total + subtotal %]
[% END %]
[% BLOCK bug_header %]
<tr class="bug_header[% '2' IF cid % 2 %]">
<td width="10" valign="top">
[% INCLUDE buglink id=id %]</td>
<td width="10"><b>[% bug_status FILTER html %]</b></td>
<td colspan="2">[% short_desc FILTER html %]</td>
[% extra FILTER none %]
</tr>
[% END %]
[% BLOCK inactive_report %]
<h3>Inactive [% terms.bugs %]</h3>
<table cellpadding="4" cellspacing="0" width="90%" class="zeroitems">
[% cid = 0 %]
[% FOREACH bug_id = null.keys.nsort %]
[% INCLUDE bug_header id=bug_id bug_status=null.$bug_id.1
short_desc=null.$bug_id.0 cid=cid %]
[% cid = cid + 1 %]
[% END %]
</table>
[% END %]
[% BLOCK section_total %]
[% IF global.total > 0 %]
<tr class="section_total">
<td align="left" width="10">
<b>Totals</b></td>
<td colspan="[% colspan FILTER none %]" align="right"><b>[% additional FILTER none %]</b></td>
<td align="right">
<b>[% global.total FILTER format("%.2f") %]</b>
</td></tr>
[% ELSE %]
<tr><td>
No time allocated during the specified period.
</td></tr>
[% END %]
[% global.grand_total = global.grand_total + global.total %]
[% END %]
[%#
#
# The query form
#
#%]
[% BLOCK query_form %]
<hr noshade size=1>
<form method="post" name="summary" style="display: inline" action="">
<input type="hidden" name="do_depends" value="[% do_depends FILTER html %]">
<input type="hidden" name="id" value="[% ids.join(",") FILTER html %]">
<input type="hidden" name="do_report" value="1">
[% IF warn_swap_dates %]
<h4 style="border: 1px solid red; margin: 1em; padding: 0.5em">The
end date specified occurs before the start date, which doesn't
make sense; the dates below have therefore been swapped.</h4>
[% END %]
<table>
<tr>
<td align="right">
<b>Period <label accesskey="s"
for="start_date"><u>s</u>tarting</label></b>:
</td><td colspan="3">
<input type="text" id="start_date" name="start_date" size="11"
align="right" value="[% start_date FILTER html %]" maxlength="10">
<b>and <label accesskey="e" for="end_date"><u>e</u>nding</label></b>:
<input type="text" name="end_date" size="11" id="end_date"
align="right" value ="[% end_date FILTER html %]" maxlength="10">
</td><td align="right">
<input type="submit" value="Summarize">
</td></tr>
<tr>
<td> </td><td colspan="4">
<small>(Dates are optional, and in YYYY-MM-DD format)</small>
</td>
<tr><td align="right">
<b>Group by</b>:
</td><td colspan="2">
<input type="radio" name="group_by" id="number" value="number" [%
'checked="checked"' IF group_by == "number"
%]><label
for="number" accesskey="n">[% terms.Bug %] <u>N</u>umber</label>
<input type="radio" name="group_by" id="owner" value="owner" [%
'checked="checked"' IF group_by == "owner"
%]><label
for="owner" accesskey="d"><u>D</u>eveloper</label>
</td><td colspan="2">
<b>Format</b>: <select name="ctype">
<option value="html">HTML Report</option>
</select>
</td></tr><tr>
<td> </td><td colspan="4">
<label for="monthly" accesskey="m">
<input type="checkbox" name="monthly" [% 'checked="checked"' IF
monthly %] id="monthly">
Split by <u>m</u>onth</label>
[%# XXX: allow splitting by other intervals %]
<label for="detailed" accesskey="t">
<input type="checkbox" name="detailed" [% 'checked="checked"' IF
detailed %] id="detailed">
De<u>t</u>ailed summaries</label>
<label for="inactive" accesskey="i">
<input type="checkbox" name="inactive" [% 'checked="checked"' IF
inactive %] id="inactive">
Also show <u>i</u>nactive [% terms.bugs %]</label>
</td>
</tr></table>
</form>
<script type="application/x-javascript">
<!--
document.forms['summary'].start_date.focus()
//--></script>
<hr noshade size=1>
[% END %]
[%#
#
# Utility
#
#%]
[% BLOCK buglink %]
<a href="show_bug.cgi?id=[% id FILTER html %]"><b>[% terms.Bug %] [% id FILTER html %]</b></a>
[% END %]
template/en/default/filterexceptions.pl
View file @
8b0a6508
...
...
@@ -370,6 +370,14 @@
'field'
,
],
'bug/summarize-time.html.tmpl'
=>
[
'global.grand_total FILTER format("%.2f")'
,
'subtotal FILTER format("%.2f")'
,
'work_time FILTER format("%.2f")'
,
'global.total FILTER format("%.2f")'
,
],
'bug/time.html.tmpl'
=>
[
'time_unit FILTER format(\'%.1f\')'
,
'time_unit FILTER format(\'%.2f\')'
,
...
...
template/en/default/list/list.html.tmpl
View file @
8b0a6508
...
...
@@ -133,8 +133,15 @@
<input type="hidden" name="id" value="[% id FILTER html %]">
[% END %]
<input type="hidden" name="format" value="multiple">
<input type="submit" value="
Long Format
">
<input type="submit" value="
Long Format
">
</form>
[% IF UserInGroup(Param('timetrackinggroup')) %]
<form method="post" action="summarize_time.cgi">
<input type="hidden" name="id" value="[% buglist_joined FILTER html %]">
<input type="submit" value="Time Summary">
</form>
[% END %]
</td>
<td> </td>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment