#!/usr/bin/python
# Report pulished updates to -proposed/-updates/-security since a given date
#
# Copyright (C) 2009-2010, Canonical, Ltd.
# Author: Steve Beattie <sbeattie@ubuntu.com>
# License: GPLv3
#
# usage: report_updates YYYY-MM-DD
#
#
import sys
import datetime
import time
import os

from launchpadlib.launchpad import Launchpad
try:
    from launchpadlib.uris import EDGE_SERVICE_ROOT, LPNET_SERVICE_ROOT
    no_lpnet = False
except ImportError:
    from launchpadlib.launchpad import EDGE_SERVICE_ROOT
    no_lpnet = True

from launchpadlib.credentials import Credentials

def lp_connect(use_edge=True, beta=False):
    global no_lpnet
    cachedir = os.path.expanduser('~/.launchpadlib/cache')
    if not os.path.exists(cachedir):
        os.makedirs(cachedir,0700)

    version="1.0"
    if beta:
        version="beta"

    if use_edge or no_lpnet:
        root = EDGE_SERVICE_ROOT
        credfile = os.path.expanduser('~/.launchpadlib/credentials')
    else:
        root = LPNET_SERVICE_ROOT
        credfile = os.path.expanduser('~/.launchpadlib/credentials-lpnet')
    try:
        credentials = Credentials()
        credentials.load(open(credfile))
        launchpad = Launchpad(credentials, root, cachedir)
    except:
        launchpad = Launchpad.get_token_and_login(sys.argv[0], root, cachedir)
        credfd = tempfile.NamedTemporaryFile(dir=os.path.dirname(credfile))
        launchpad.credentials.save(credfd)
        os.link(credfd.name, credfile)
        credfd.close()
    return launchpad

lp = lp_connect()

# You're kidding me, I have to define my own UTC tzinfo class?!
class UTC(datetime.tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return datetime.timedelta(0)

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return datetime.timedelta(0)

date = sys.argv[1]
if len(sys.argv) == 3:
    enddate = datetime.datetime.strptime(sys.argv[2], "%Y-%m-%d")
else:
    enddate = datetime.datetime.fromtimestamp(time.time())
enddate = enddate.replace(tzinfo=UTC())

ubuntu = lp.distributions['ubuntu']
archive = ubuntu.main_archive

# Might need to improve this once karmic is released but before the next
# release is branched
series = filter(lambda x: x.name != ubuntu.current_series.name and x.active, ubuntu.series)

def printhdr():
     print "|| pocket || package || version || component || date published ||"

def convert_date(raw_date):
    # Bah, have to coerce dates for older versions of lplib	
    # see LP: #356632 for the workaround we're mimicing
    if not isinstance(raw_date, datetime.datetime) and \
       not isinstance(raw_date, datetime.date):
        try:
	    date = datetime.datetime.strptime(raw_date[:19], "%Y-%m-%dT%H:%M:%S")
	except ValueError:
	    date = datetime.datetime.strptime(raw_date[:11], "%Y-%m-%dT%H:%M:%S")
    else:
        date = raw_date
    return date

def printline(type, package):
    print "|| %s || %s || %s || %s || %s ||" \
	  %(type, package.source_package_name, package.source_package_version, \
	    package.component_name, convert_date(package.date_published).strftime("%Y-%m-%d %H:%M"))

for release in series:
    pcnt = ucnt = pdcnt = scnt = 0
    proposed = []
    updates = []
    security = []
    for p in archive.getPublishedSources(distro_series=release, created_since_date=date,
                                           status='Published', pocket='Proposed'):
	if (convert_date(p.date_published) > enddate):
	    continue
        proposed.append(p)
    for p in archive.getPublishedSources(distro_series=release, created_since_date=date,
                                                status='Deleted', pocket='Proposed'):
	if (convert_date(p.date_published) > enddate):
	    continue
        proposed.append(p)

    for p in archive.getPublishedSources(distro_series=release, created_since_date=date,
                                          status='Published', pocket='Updates'):
	if (convert_date(p.date_published) > enddate):
	    continue
        updates.append(p)
    for p in archive.getPublishedSources(distro_series=release, created_since_date=date,
                                          status='Superseded', pocket='Updates'):
	if (convert_date(p.date_published) > enddate):
	    continue
        updates.append(p)

    for p in archive.getPublishedSources(distro_series=release, created_since_date=date,
                                           status='Published', pocket='Security'):
	if (convert_date(p.date_published) > enddate):
	    continue
        security.append(p)
    for p in archive.getPublishedSources(distro_series=release, created_since_date=date,
                                           status='Superseded', pocket='Security'):
	if (convert_date(p.date_published) > enddate):
	    continue
        security.append(p)

    proposed_deleted = []
    proposed.sort(lambda x, y: cmp(x.source_package_name, y.source_package_name))
    updates.sort(lambda x, y: cmp(x.source_package_name, y.source_package_name))
    security.sort(lambda x, y: cmp(x.source_package_name, y.source_package_name))


    print '=== %s changes ===' %(release.name)
    printhdr()
    for p in proposed:
	skip = False
        if p.status == "deleted":
	    skip = True
	    for u in updates:
                if p.source_package_name == u.source_package_name and \
                   p.source_package_version == u.source_package_version:
                    skip = False
                    break
	    proposed_deleted.append(p)
        if skip:
            continue
    	pcnt += 1
        printline("Proposed", p)

    for p in proposed_deleted:
    	pcnt += 1
        printline("Proposed-Deleted", p)

    for p in updates:
        skip = False
        for s in security:
            if p.source_package_name == s.source_package_name and \
               p.source_package_version == s.source_package_version:
                skip = True
                break
        if skip:
            continue
    	ucnt += 1
        printline("Updates", p)

    for p in proposed_deleted:
    	pdcnt += 1
        printline("Deleted from Proposed", p)

    for p in security:
    	scnt += 1
        printline("Security", p)
    print
    print "Totals for %s: proposed = %d updates = %d proposed-deleted = %s security = %d" \
          %(release.name, pcnt, ucnt, pdcnt, scnt)
    print
