| 1 |
#!/usr/bin/ruby -w
|
| 2 |
require 'dbi'
|
| 3 |
require 'cgi'
|
| 4 |
|
| 5 |
tstart = Time::now
|
| 6 |
|
| 7 |
class Actions
|
| 8 |
attr_reader :actions, :act_todo, :act_status, :act_comment
|
| 9 |
def initialize
|
| 10 |
@actions = []
|
| 11 |
@act_status = ""
|
| 12 |
@act_todo = false
|
| 13 |
@act_comment = ""
|
| 14 |
end
|
| 15 |
|
| 16 |
def add(desc)
|
| 17 |
desc.chomp!
|
| 18 |
date, who, act, comment = desc.split(' ', 4)
|
| 19 |
date = Date::parse(date)
|
| 20 |
if act =~ /^(.+)\((.+)\)$/
|
| 21 |
act_name, act_arg = $1, $2
|
| 22 |
if [ 'PROP_RM', 'PROP_RM_O', 'PROP_O', 'O', 'REQ_RM', 'RM', 'SEC_RM', 'O_PROP_RM' ].include?(act_name)
|
| 23 |
# FIXME check bug
|
| 24 |
elsif act_name == 'WAIT'
|
| 25 |
act_arg = act_arg.to_i
|
| 26 |
else
|
| 27 |
puts "Unknown action: #{act} (#{desc})"
|
| 28 |
end
|
| 29 |
@actions << [date, who, [act_name, act_arg], comment]
|
| 30 |
elsif act == 'OK'
|
| 31 |
act_name = 'OK'
|
| 32 |
act_arg = nil
|
| 33 |
@actions << [date, who, [act_name, act_arg], comment]
|
| 34 |
else
|
| 35 |
puts "Unparseable action: #{act} (#{desc})"
|
| 36 |
exit(1)
|
| 37 |
end
|
| 38 |
end
|
| 39 |
|
| 40 |
def analyze_actions
|
| 41 |
@actions.sort! { |a,b| b[0] <=> a[0] }
|
| 42 |
idx = 0
|
| 43 |
rm_o = false
|
| 44 |
while idx < @actions.length
|
| 45 |
if @actions[idx][2][0] == 'OK'
|
| 46 |
@act_status = ""
|
| 47 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 48 |
break
|
| 49 |
elsif @actions[idx][2][0] == 'WAIT'
|
| 50 |
if @actions[idx][0] + @actions[idx][2][1] <= CURDATE
|
| 51 |
idx += 1
|
| 52 |
next # OK not valid anymore, consider next action
|
| 53 |
else
|
| 54 |
# nothing to do except waiting
|
| 55 |
@act_status = "Waiting until #{@actions[idx][0] + @actions[idx][2][1]}"
|
| 56 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 57 |
break
|
| 58 |
end
|
| 59 |
elsif @actions[idx][2][0] == 'REQ_RM' or @actions[idx][2][0] == 'RM'
|
| 60 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Removal was requested</a>"
|
| 61 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 62 |
break
|
| 63 |
elsif @actions[idx][2][0] == 'O'
|
| 64 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Was orphaned</a>"
|
| 65 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 66 |
break
|
| 67 |
elsif @actions[idx][2][0] == 'PROP_RM_O'
|
| 68 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Was orphaned, will need removal</a>"
|
| 69 |
rm_o = true
|
| 70 |
idx += 1
|
| 71 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 72 |
next
|
| 73 |
elsif @actions[idx][2][0] == 'PROP_RM'
|
| 74 |
ok = false
|
| 75 |
if @actions[idx][0] + WAIT_RM_O <= CURDATE and !rm_o
|
| 76 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Should be orphaned before removal (since #{@actions[idx][0] + 50})</a>"
|
| 77 |
@act_todo = true
|
| 78 |
ok = true
|
| 79 |
end
|
| 80 |
if @actions[idx][0] + WAIT_RM_RM <= CURDATE
|
| 81 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Should be removed (since #{@actions[idx][0] + 100})</a>"
|
| 82 |
@act_todo = true
|
| 83 |
ok = true
|
| 84 |
end
|
| 85 |
if !ok
|
| 86 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Removal suggested (since #{@actions[idx][0]})</a>"
|
| 87 |
end
|
| 88 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 89 |
break
|
| 90 |
elsif @actions[idx][2][0] == 'PROP_O'
|
| 91 |
if @actions[idx][0] + WAIT_O_O <= CURDATE
|
| 92 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Should be orphaned (since #{@actions[idx][0] + 50})</a>"
|
| 93 |
@act_todo = true
|
| 94 |
else
|
| 95 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Orphaning suggested (since #{@actions[idx][0]})</a>"
|
| 96 |
end
|
| 97 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 98 |
break
|
| 99 |
elsif @actions[idx][2][0] == 'O_PROP_RM'
|
| 100 |
if @actions[idx][0] + WAIT_ORM_RM <= CURDATE
|
| 101 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Should be removed (O pkg) (since #{@actions[idx][0] + 50})</a>"
|
| 102 |
@act_todo = true
|
| 103 |
else
|
| 104 |
@act_status = "<a href=\"http://bugs.debian.org/#{@actions[idx][2][1]}\">Removal suggested (O pkg) (since #{@actions[idx][0]})</a>"
|
| 105 |
end
|
| 106 |
@act_comment = @actions[idx][3] if not @actions[idx][3].nil?
|
| 107 |
break
|
| 108 |
else
|
| 109 |
puts "Unknown act: #{@actions[idx][2][0]}"
|
| 110 |
end
|
| 111 |
end
|
| 112 |
end
|
| 113 |
|
| 114 |
def Actions::fetch
|
| 115 |
d = IO::popen("svn cat svn://svn.debian.org/collab-qa/bapase/package-actions.txt")
|
| 116 |
f = d.read
|
| 117 |
d.close
|
| 118 |
return Actions::read(f)
|
| 119 |
end
|
| 120 |
|
| 121 |
def Actions::read(data)
|
| 122 |
pkgs = {}
|
| 123 |
data.each_line do |l|
|
| 124 |
next if l =~/^\s*#/ or l =~/^\s*$/
|
| 125 |
pkg, rest = l.split(' ',2)
|
| 126 |
if pkgs[pkg].nil?
|
| 127 |
pkgs[pkg] = Actions::new
|
| 128 |
end
|
| 129 |
pkgs[pkg].add(rest)
|
| 130 |
end
|
| 131 |
pkgs.each_pair { |k, v| v.analyze_actions }
|
| 132 |
end
|
| 133 |
end
|
| 134 |
|
| 135 |
puts "Content-type: text/html\n\n"
|
| 136 |
|
| 137 |
WAIT_RM_O = 50
|
| 138 |
WAIT_RM_RM = 100
|
| 139 |
WAIT_O_O = 50
|
| 140 |
WAIT_ORM_RM = 50
|
| 141 |
|
| 142 |
DATEZERO = Date::parse('0000-01-01')
|
| 143 |
CURDATE = Date::today
|
| 144 |
|
| 145 |
dbh = DBI::connect('DBI:Pg:dbname=udd;port=5441;host=localhost', 'guest')
|
| 146 |
cgi = CGI.new
|
| 147 |
|
| 148 |
type = cgi.params['t']
|
| 149 |
if not type.nil?
|
| 150 |
type = type[0]
|
| 151 |
end
|
| 152 |
|
| 153 |
if type == 'o'
|
| 154 |
orphaned = true
|
| 155 |
query = "select * from bapase where type is not null and type in ('O', 'ITA') order by orphaned_age desc"
|
| 156 |
elsif type == 'o2'
|
| 157 |
orphaned = true
|
| 158 |
query = "select * from bapase where type is not null and type in ('O', 'ITA') and orphaned_age >= 600 and orphaned_age < 730 and insts < 300 and last_modified_age > 60 order by last_modified_age desc"
|
| 159 |
elsif type == 'rfa'
|
| 160 |
orphaned = true
|
| 161 |
query = "select * from bapase where type is not null and type in ('RFA') order by orphaned_age desc"
|
| 162 |
elsif type == 'nmu'
|
| 163 |
orphaned = true
|
| 164 |
query = "select * from bapase where nmu and nmus > 1 order by nmus desc"
|
| 165 |
elsif type == 'testing'
|
| 166 |
orphaned = true
|
| 167 |
query = "select * from bapase where source not in (select source from sources where distribution='debian' and release='wheezy') order by testing_age desc, first_seen asc"
|
| 168 |
elsif type == 'nodd'
|
| 169 |
orphaned = true
|
| 170 |
query = <<EOF
|
| 171 |
WITH active_emails AS (SELECT email FROM carnivore_emails, active_dds WHERE active_dds.id = carnivore_emails.id)
|
| 172 |
select * from bapase where source in (
|
| 173 |
SELECT source
|
| 174 |
FROM sources
|
| 175 |
WHERE distribution = 'debian' AND release = 'sid'
|
| 176 |
AND sources.source NOT IN (
|
| 177 |
SELECT sources.source
|
| 178 |
FROM sources
|
| 179 |
LEFT OUTER JOIN uploaders ON (sources.source = uploaders.source AND sources.version = uploaders.version AND sources.distribution = uploaders.distribution AND sources.release = uploaders.release AND sources.component = uploaders.component)
|
| 180 |
WHERE sources.distribution = 'debian' AND sources.release = 'sid'
|
| 181 |
AND (maintainer_email in (select email from active_emails)
|
| 182 |
OR email in (SELECT email FROM active_emails)
|
| 183 |
OR maintainer_email ~ '.*@lists.(alioth.)?debian.org'
|
| 184 |
OR email ~ '.*@lists.(alioth.)?debian.org'))
|
| 185 |
) order by upload_age desc
|
| 186 |
EOF
|
| 187 |
elsif type == 'maintnmu'
|
| 188 |
orphaned = true
|
| 189 |
query = <<EOF
|
| 190 |
select * from bapase where source in (
|
| 191 |
select source from sources where distribution='debian' and release='wheezy' and maintainer_email in (
|
| 192 |
select nmus.email from
|
| 193 |
(select email, count(*) as tot from
|
| 194 |
(select maintainer_email as email, source from sources_uniq
|
| 195 |
where release = 'sid'
|
| 196 |
and distribution = 'debian'
|
| 197 |
and component = 'main'
|
| 198 |
union
|
| 199 |
select email, source from uploaders
|
| 200 |
where release = 'sid'
|
| 201 |
and distribution = 'debian'
|
| 202 |
and component = 'main') as foo
|
| 203 |
group by email) as tot,
|
| 204 |
(select email, count(*) as nmus from
|
| 205 |
(select sources.maintainer_email as email, sources.source from sources_uniq sources, upload_history uh
|
| 206 |
where release = 'sid'
|
| 207 |
and distribution = 'debian'
|
| 208 |
and component = 'main'
|
| 209 |
and sources.source = uh.source and sources.version = uh.version
|
| 210 |
and uh.nmu
|
| 211 |
union
|
| 212 |
select email, uploaders.source from uploaders, upload_history uh
|
| 213 |
where release = 'sid'
|
| 214 |
and distribution = 'debian'
|
| 215 |
and component = 'main'
|
| 216 |
and uploaders.source = uh.source and uploaders.version = uh.version
|
| 217 |
and uh.nmu
|
| 218 |
) as foo
|
| 219 |
group by email) as nmus
|
| 220 |
where nmus * 100 / tot >= 100
|
| 221 |
and nmus.email = tot.email)
|
| 222 |
union (select source from uploaders where distribution='debian' and release='wheezy' and email in (
|
| 223 |
select nmus.email from
|
| 224 |
(select email, count(*) as tot from
|
| 225 |
(select maintainer_email as email, source from sources_uniq sources
|
| 226 |
where release = 'sid'
|
| 227 |
and distribution = 'debian'
|
| 228 |
and component = 'main'
|
| 229 |
union
|
| 230 |
select email, source from uploaders
|
| 231 |
where release = 'sid'
|
| 232 |
and distribution = 'debian'
|
| 233 |
and component = 'main') as foo
|
| 234 |
group by email) as tot,
|
| 235 |
(select email, count(*) as nmus from
|
| 236 |
(select sources.maintainer_email as email, sources.source from sources_uniq sources, upload_history uh
|
| 237 |
where release = 'sid'
|
| 238 |
and distribution = 'debian'
|
| 239 |
and component = 'main'
|
| 240 |
and sources.source = uh.source and sources.version = uh.version
|
| 241 |
and uh.nmu
|
| 242 |
union
|
| 243 |
select email, uploaders.source from uploaders, upload_history uh
|
| 244 |
where release = 'sid'
|
| 245 |
and distribution = 'debian'
|
| 246 |
and component = 'main'
|
| 247 |
and uploaders.source = uh.source and uploaders.version = uh.version
|
| 248 |
and uh.nmu
|
| 249 |
) as foo
|
| 250 |
group by email) as nmus
|
| 251 |
where nmus * 100 / tot >= 100
|
| 252 |
and nmus.email = tot.email
|
| 253 |
))) order by nmus
|
| 254 |
EOF
|
| 255 |
else
|
| 256 |
puts <<-EOF
|
| 257 |
<h1>Bapase</h1>
|
| 258 |
<ul>
|
| 259 |
<li><a href="bapase.cgi?t=o">Orphaned packages</a></li>
|
| 260 |
<li><a href="bapase.cgi?t=rfa">RFAed packages</a></li>
|
| 261 |
<li><a href="bapase.cgi?t=nmu">Packages maintained with NMUs</a></li>
|
| 262 |
<li><a href="bapase.cgi?t=testing">Packages not in testing</a></li>
|
| 263 |
<li><a href="bapase.cgi?t=nodd">Packages not maintained by DDs</a></li>
|
| 264 |
<li><a href="bapase.cgi?t=maintnmu">Packages maintained or co-maintained by people with lots of NMUs</a></li>
|
| 265 |
</ul>
|
| 266 |
</body></html>
|
| 267 |
EOF
|
| 268 |
exit(0)
|
| 269 |
end
|
| 270 |
|
| 271 |
# FIXME add case where type is uknown
|
| 272 |
|
| 273 |
$actions = Actions::fetch
|
| 274 |
|
| 275 |
puts <<-EOF
|
| 276 |
<html><head>
|
| 277 |
<style type="text/css">
|
| 278 |
td, th {
|
| 279 |
border: 1px solid gray;
|
| 280 |
padding-left: 2px;
|
| 281 |
padding-right: 2px;
|
| 282 |
}
|
| 283 |
th {
|
| 284 |
font-size: 8pt;
|
| 285 |
}
|
| 286 |
tr:hover {
|
| 287 |
background-color: #ccc;
|
| 288 |
}
|
| 289 |
table {
|
| 290 |
border-collapse: collapse;
|
| 291 |
}
|
| 292 |
</style>
|
| 293 |
<title>Bapase</title>
|
| 294 |
</head><body>
|
| 295 |
<table border="1"><tr>
|
| 296 |
<th></th><th>Package</th><th>Action</th>
|
| 297 |
EOF
|
| 298 |
puts "<th>Orphaned</th>" if orphaned
|
| 299 |
puts <<-EOF
|
| 300 |
<th>Testing</th>
|
| 301 |
<th>Migrate</th>
|
| 302 |
<th>Popcon</th>
|
| 303 |
<th>Bugs</th>
|
| 304 |
<th>Last upload</th>
|
| 305 |
<th>NMUs</th>
|
| 306 |
<th>Comments</th>
|
| 307 |
</tr>
|
| 308 |
EOF
|
| 309 |
tqs = Time::now
|
| 310 |
sth = dbh.prepare(query)
|
| 311 |
sth.execute
|
| 312 |
tqe = Time::now
|
| 313 |
res = sth.fetch_all
|
| 314 |
n = 0
|
| 315 |
res.each do |r|
|
| 316 |
n += 1
|
| 317 |
pkg = r['source']
|
| 318 |
puts "<tr><td>#{n}</td>"
|
| 319 |
puts "<td><a href=\"http://packages.qa.debian.org/#{pkg}\">#{pkg}</a>"
|
| 320 |
# FIXME removals
|
| 321 |
if $actions[pkg]
|
| 322 |
if $actions[pkg].act_todo
|
| 323 |
puts "<td><b>#{$actions[pkg].act_status}</b></td>"
|
| 324 |
else
|
| 325 |
puts "<td>#{$actions[pkg].act_status}</td>"
|
| 326 |
end
|
| 327 |
else
|
| 328 |
puts "<td></td>"
|
| 329 |
end
|
| 330 |
if orphaned
|
| 331 |
if r['type']
|
| 332 |
puts "<td><a href=\"http://bugs.debian.org/#{r['bug']}\">#{r['type']}</a> (#{r['orphaned_age']}, #{r['last_modified_age']})</td>"
|
| 333 |
else
|
| 334 |
puts "<td></td>"
|
| 335 |
end
|
| 336 |
end
|
| 337 |
if r['testing_age'] and r['testing_age'] > 1
|
| 338 |
puts "<td>#{r['testing_age']}</td>"
|
| 339 |
else
|
| 340 |
puts "<td></td>"
|
| 341 |
end
|
| 342 |
if r['sync_age'] and r['sync_age'] > 1
|
| 343 |
puts "<td>#{r['sync_age']}</td>"
|
| 344 |
else
|
| 345 |
puts "<td></td>"
|
| 346 |
end
|
| 347 |
puts "<td>#{r['insts']} / #{r['vote']}</td>"
|
| 348 |
puts "<td><a href=\"http://bugs.debian.org/src:#{pkg}\">#{r['rc_bugs']} / #{r['all_bugs']}</a></td>"
|
| 349 |
puts "<td>#{r['upload_age']}</td>"
|
| 350 |
puts "<td>#{r['nmus']}</td>"
|
| 351 |
if $actions[pkg]
|
| 352 |
comment = $actions[pkg].act_comment.gsub(/#\d+/) do |bug|
|
| 353 |
bugn = bug.gsub(/^#/, '')
|
| 354 |
"<a href=\"http://bugs.debian.org/#{bugn}\">#{bug}</a>"
|
| 355 |
end
|
| 356 |
puts "<td>#{comment}</td>"
|
| 357 |
else
|
| 358 |
puts "<td></td>"
|
| 359 |
end
|
| 360 |
puts "</tr>"
|
| 361 |
end
|
| 362 |
puts "</table>"
|
| 363 |
|
| 364 |
tstop = Time::now
|
| 365 |
puts " -- #{res.length} packages listed. Page generated in #{tstop - tstart} seconds. Query took #{tqe - tqs} seconds."
|
| 366 |
puts "</body></html>"
|
| 367 |
|
| 368 |
|
| 369 |
|
| 370 |
|
| 371 |
#sth.finish
|