Logo Search packages:      
Sourcecode: debian-bts-applet version File versions  Download package

model.py

# -*- coding: utf-8 -*-

"""
  debian-bts-applet - GNOME applet for monitoring Debian bugs
  Copyright (C) 2008  Chris Lamb <chris@chris-lamb.co.uk>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import gtk
import gobject
import gconf
import sys
import math

from btsutils.debbugs import debbugs, BugExceptions

import BtsApplet

class Model(object):
    def __init__(self, controller, gconf_base):
        self.controller = controller
        self.gconf_key = "%s/bug_list" % gconf_base

        self._bugs = gtk.ListStore(*BtsApplet.FIELD_TYPES)
        self._queue = BtsApplet.DownloadQueue(self._update_iter)
        self._queue.run(BtsApplet.NUM_WORKERS)

        self._gconf_client = gconf.client_get_default()
        for bug_number in self._gconf_client.get_list(self.gconf_key, gconf.VALUE_INT):
            self.add_bug(bug_number, sync_to_gconf=False)

    def add_bug(self, bug_number, sync_to_gconf=True):
        if self.contains_bug(bug_number):
            return False

        # Add unqueued entry to ListStore
        iter = self._bugs.append((bug_number, BtsApplet.ST_NEW, None, None, None, None, None, BtsApplet.TICK_DEFAULT))

        # Trigger an update of all unqueued entruies
        self._update(BtsApplet.ST_NEW, BtsApplet.ST_DOWNLOADING)

        # Trigger a save to gconf
        if sync_to_gconf:
            self._sync_to_gconf()

        return True

    def update_all(self):
        self._update(BtsApplet.ST_DOWNLOADED, BtsApplet.ST_UPDATING)

    def tick(self):
        print >>sys.stderr, "tick()"

        def update_p(tick):
            if tick >= 64:
                return tick % 30 == 0
            elif tick > 1:
                n = math.log(tick, 2)
                return float(n) - int(n) <= 0
            else:
                return False

        iter = self._bugs.get_iter_first()
        while iter is not None:
            tick = self._bugs.get_value(iter, BtsApplet.FIELDS.index('tick'))
            self._bugs.set(iter, BtsApplet.FIELDS.index('tick'), tick + 1)

            if update_p(tick):
                self.refresh_bug(iter, reset_tick=False)

            iter = self._bugs.iter_next(iter)


    def delete_bug(self, iter):
        path = self._bugs.get_path(iter)
        self._bugs.remove(iter)
        self._sync_to_gconf()

    def refresh_bug(self, iter, reset_tick=True):
        if not self._bugs.iter_is_valid(iter):
            return False

        if reset_tick:
            self._bugs.set(iter, BtsApplet.FIELDS.index('tick'), BtsApplet.TICK_DEFAULT)

        bug_number = self._bugs.get_value(iter, BtsApplet.FIELDS.index('bug_number'))
        current_state = self._bugs.get_value(iter, BtsApplet.FIELDS.index('state'))

        if current_state < BtsApplet.ST_UPDATING:
            self._bugs.set(iter, 1, BtsApplet.ST_DOWNLOADING)
        else:
            self._bugs.set(iter, 1, BtsApplet.ST_UPDATING)

        self._queue.enqueue(iter, bug_number)

    def get_treemodel(self):
        return self._bugs

    def close(self):
        self._queue.stop()

    def contains_bug(self, bug_number):
        for bug in self._bugs:
            if bug_number == bug[0]: return True
        return False

    def is_active(self):
        return self._queue.is_active()

    def get_bug_number(self, iter):
        return self._bugs.get_value(iter, 0)

    # Private

    def _update(self, filter_state, update_state):
        iter = self._bugs.get_iter_first()
        while iter is not None:
            bug_number = self._bugs.get_value(iter, 0)
            state = self._bugs.get_value(iter, 1)

            if state == filter_state:
                self._bugs.set(iter, 1, update_state)
                self._queue.enqueue(iter, bug_number)

            iter = self._bugs.iter_next(iter)


    def _iter_from_bug(self, bug_number):
        iter = self._bugs.get_iter_first()
        while iter is not None:
            if bug_number == self._bugs.get_value(iter, 0):
                return iter
            iter = self._bugs.iter_next(iter)
        return False

    def _update_iter(self, iter, bug_number):
        print >>sys.stderr, "Querying status for bug #%d..." % bug_number

        try:
            # Actually download the entry
            bts_entry = debbugs().get(bug_number)

            print >>sys.stderr, "Got status for bug #%d." % bug_number

            gtk.gdk.threads_enter()
            iter = self._iter_from_bug(bug_number)
            if not iter:
                # Bug has been deleted since the entry was downloaded
                gtk.gdk.threads_leave()
                return

            old = {}
            for idx, field in enumerate(BtsApplet.FIELDS):
                old[field] = self._bugs.get_value(iter, idx)

            new = {
                'bug_number': bug_number,
                'state': BtsApplet.ST_DOWNLOADED,
                'package': str(bts_entry.package),
                'summary': str(bts_entry.summary),
                'severity': str(bts_entry.severity),
                'tags': ', '.join(bts_entry.tags),
                'status': str(bts_entry.status),
                'tick': old['tick'],
            }

            # Update backing ListStore
            if self._bugs.iter_is_valid(iter):
                for idx, field in enumerate(BtsApplet.FIELDS):
                    self._bugs.set(iter, idx, new[field])

            gtk.gdk.threads_leave()

            # Check whether the bug has changed. We only check for changes
            # if we already have data!
            if old['state'] >= BtsApplet.ST_UPDATING:
                delta = []
                for idx, field in enumerate(BtsApplet.FIELDS):

                    # We don't want to know if the bug downloaded state changes
                    if idx == BtsApplet.FIELDS.index('state'):
                        continue

                    # .. or the tick count
                    if idx == BtsApplet.FIELDS.index('tick'):
                        continue

                    if old[field] != new[field]:
                        delta.append(field)

                # If there are changes, reset tick and notify user
                if len(delta) > 0:
                    gtk.gdk.threads_enter()
                    if self._bugs.iter_is_valid(iter):
                        self._bugs.set(iter, BtsApplet.FIELDS.index('tick'), BtsApplet.TICK_DEFAULT)
                    gtk.gdk.threads_leave()

                    self.controller.do_notify(old, new, delta)

        except:
            gtk.gdk.threads_enter()

            print >>sys.stderr, "Error getting status for bug #%d." % bug_number
            import traceback
            traceback.print_exc()

            # Mark error state in bug
            iter = self._iter_from_bug(bug_number)
            if not iter:
                # Bug was deleted
                gtk.gdk.threads_leave()
                return

            # Caclulate new state (see HACKING for state transitions)
            old_state = self._bugs.get_value(iter, BtsApplet.FIELDS.index('state'))
            new_status = {
                BtsApplet.ST_DOWNLOADING: BtsApplet.ST_ERROR_DOWNLOADING,
                BtsApplet.ST_DOWNLOADED: BtsApplet.ST_ERROR_UPDATING,
                BtsApplet.ST_UPDATING: BtsApplet.ST_ERROR_UPDATING,
            }[old_state]
            self._bugs.set(iter, BtsApplet.FIELDS.index('state'), new_status)

            # Reset tick to error state
            self._bugs.set(iter, BtsApplet.FIELDS.index('tick'), BtsApplet.TICK_ERROR_RESET)

            gtk.gdk.threads_leave()

    def _sync_to_gconf(self):
        bugs = [x[0] for x in self._bugs]
        self._gconf_client.set_list(self.gconf_key, gconf.VALUE_INT, bugs)

Generated by  Doxygen 1.6.0   Back to index