Commits

Moises Henriquez committed a98477f

First steps in a working GUI with job manager and history display

Comments (0)

Files changed (3)

 
 import gtk
 from vpackager import guitools
+from vpackager import dbutils
+from vpackager import buildutils
 import jobmanage
 import historymanager
+import threading
+import time
+import os
+import sqlite3
+import urllib
+
 
 __author__ = 'M0E-lnx'
 __author_email__ = 'moc.liamg@xnl.E0M'[::-1]
 __version__ = '0.1'
 
+class Dispatcher(threading.Thread):
+    """ Dispatcher thread used for dispatching jobs from the queue"""
+    def __init__(self, parent=None):
+        threading.Thread.__init__(self)
+        self.name = 'job-dispatcher'
+#        threading.Thread.__init__(self)
+#        self.set_name(self.name)
+        self.parent = parent
+#        self.db = sqlite3.connect('/tmp/vpackager.db')
+
+    def _get_next_job(self):
+        db = sqlite3.connect('/tmp/vpackager.db')
+        c = db.cursor()
+        c.execute('select * from queue')
+        ret = c.next()
+        c.close()
+        return ret
+
+    def _log_job_start(self, _id):
+        db = sqlite3.connect('/tmp/vpackager.db')
+        c = db.cursor()
+        # Get the details from the queue
+        t = (_id,)
+        c.execute('delete from history where id=?',t)
+        c.execute('select id,app,ver,srcURI,descURI,release from queue where id=?',t)
+        res = c.next()
+        c.execute('''insert into history
+        (id,app,ver,srcURI,descURI,release, buildstart) 
+        values (?,?,?,?,?,?,CURRENT_TIMESTAMP)''', res)
+        c.execute('select * from history')
+        #print c.fetchall()
+        c.close()
+        db.commit()
+
+    def _log_job_end(self, _id, stdoutpath, result):
+        db = sqlite3.connect('/tmp/vpackager.db')
+        c = db.cursor()
+        t = (_id,)
+        c.execute('''update history set buildend=CURRENT_TIMESTAMP where
+                id=?''',t)
+        t = (stdoutpath,_id)
+        c.execute('''update history set stdout=? where id=?''',t)
+        t = (result, _id)
+        c.execute('''update history set result=? where id=?''',t)
+        c.close()
+        db.commit()
+
+    def run(self):
+        while self.parent._bot_run:
+            time.sleep(0.25)
+            (_id,pkg,ver,src,desc,rel) = self._get_next_job()
+            self._log_job_start(_id)
+            self.parent.job_indicator.set_markup('<b>%s-%s</b>'%(pkg,ver))
+            if src.startswith('/'):
+                srcob = buildutils.Source(src)
+                sbpath = srcob.makeSlackBuild(buildno = rel)
+#                print sbpath
+                os.chdir(os.path.dirname(sbpath))
+#                os.chdir(srcob.__myhome)
+                if not desc.startswith('/'):
+                # Download the description
+                    urllib.urlretrieve(desc, 'slack-desc')
+                else:
+                    shutil.copy(desc, 'slack-desc')
+                job = srcob.buildSlackBuild()
+                job.observer.add_callback(self.parent._play_output)
+                job.run()
+                while job._popen_running:
+                    time.sleep(1)
+                retval = job.popen.returncode
+                if retval > 0:
+                    result='FAIL'
+                else:
+                    result='Success'
+#                print 'job ended with code', job.popen.returncode
+                # remove the job from the queue
+                db = sqlite3.connect('/tmp/vpackager.db')
+                c = db.cursor()
+                t=(_id,)
+                c.execute('delete from queue where id=?',t)
+                c.close()
+                db.commit()
+                # Log the job result
+                self._log_job_end(_id, 'foo.txt',result)
+                # Refresh the history display
+                self._parent.historyman._refresh_history_display()
+
+
+
+    def _run_job(self, pkg, ver, src, desc, rel):
+        _src_dir = buildutils._sources_home
+        _prevdir = os.getcwd()
+        os.chdir(_src_dir)
+        if src.startswith('/'):
+            srcob = buildutils.Source(src)
+            srcob.makeSlackBuild(buildno=rel)
+            # ^^ that method also copies the source to the right place, so we
+            # just need to cd into it.
+            os.chdir(srcob.__myhome)
+            if not desc.startswith('/'):
+                # Download the description file
+                urllib.urlretrieve(desc, 'slack-desc')
+            job = srcob.buildSlackBuild()
+            job.observer.add_callback(self._parent._play_output)
+            job.run()
+            while job._popen_running:
+                time.sleep(1)
+            print 'Job ended with code', job.popen.returncode
+
+
+
+
+
+
 class Tab(gtk.VBox):
     ''' Generic tab for use in the tabstrip '''
     def __init__(self, label='', stock=None, parent=None):
         lb1 = gtk.Label()
         lb0.set_use_markup(True)
         lb1.set_use_markup(True)
-        lb0.set_markup('<b>Current Status:</b>')
-        lb1.set_markup('<b>Current Job:</b>')
+        lb0.set_markup('Current Status:')
+        lb1.set_markup('Current Job:')
+        self.label_bot_status = gtk.Label()
+        self.label_bot_status.set_use_markup(True)
+        self.label_current_job = gtk.Label()
+        self.label_current_job.set_use_markup(True)
         ltbox.pack_start(lb0, False, False, 2)
+        ltbox.pack_start(self.label_bot_status, False, True, 2)
         lbbox.pack_start(lb1, False, False, 2)
+        lbbox.pack_start(self.label_current_job, False, True, 2)
+        self._parent.bot_indicator = self.label_bot_status
+        self._parent.job_indicator = self.label_current_job
+
+        self._parent.bot_indicator.set_markup('<b>Stopped</b>')
+        self._parent.job_indicator.set_markup('<b>None</b>')
 
         frt = gtk.Frame('Bot Control')
         rvbox = gtk.VBox()
 
         btStopBot = guitools.vButton(label='Stop', stock=gtk.STOCK_NO)
         btStartBot = guitools.vButton(label='Start', stock=gtk.STOCK_YES)
+        self.btStopBot = btStopBot
+        self.btStartBot = btStartBot
+        btStartBot.connect('clicked', self._start_bot)
+        btStopBot.connect('clicked', self._stop_bot)
+
         rtbox.pack_start(btStartBot, False, False, 2)
         rtbox.pack_start(btStopBot, False, False, 2)
 
 
         frmoutput = gtk.Frame('Build Output')
         frmoutput.add(self.scroll)
+        self._parent.outputplayer = self.stdoutmon
+        self._parent.scrollarea = self.scroll
 
         self.pack_start(frmoutput, True, True, 4)
+        self.mon_thread = threading.Thread(target=self.__bot_monitor,
+                name='vpackager-bot-monitor', args=())
+        self.mon_thread.start()
+        self.jobDispatcher = Dispatcher(parent = self._parent)
+
+#        self.jobDispatcher = Dispatcher(parent=self._parent)
+
+    def __bot_monitor(self):
+        while self._parent._bot_run:
+            gtk.gdk.threads_enter()
+            self.btStartBot.set_property('sensitive', False)
+            self.btStopBot.set_property('sensitive', True)
+            self._parent.bot_indicator.set_markup('<b>Running</b>')
+            gtk.gdk.threads_leave()
+            time.sleep(0.25)
+        gtk.gdk.threads_enter()
+        self.btStartBot.set_property('sensitive', True)
+        self.btStopBot.set_property('sensitive', False)
+        self._parent.bot_indicator.set_markup('<b>Stopped</b>')
+        self._parent.job_indicator.set_markup('<b>None</b>')
+        gtk.gdk.threads_leave()
+
+    def __log_line(self, line):
+        buf = self.stdoutmon.get_buffer()
+        buf.insert(buf.get_end_iter(), line + '\n')
+
+    def _stop_bot(self, widget=None):
+        self._parent._bot_run = False
+        return
+
+    def _start_bot(self, widget=None):
+        self._parent._bot_run = True
+        if not self.mon_thread.isAlive():
+            del self.mon_thread
+            self.mon_thread = threading.Thread(target=self.__bot_monitor,
+                    name='vpackager-bot-monitor', args=())
+            self.mon_thread.start()
+        if not self.jobDispatcher.isAlive():
+            del self.jobDispatcher
+            self.jobDispatcher = Dispatcher(parent=self._parent)
+            self.jobDispatcher.start()
+        return
+
 
 class TabQueue(Tab):
     """ Job queue tab. Used to manage build jobs."""
         self.connect('destroy', self._exit)
         self.set_size_request(600, 400)
         self.body = gtk.VBox()
+        self.outputplayer = None
+        self._bot_run = False
         self.body.pack_start(self._get_menubar(), False, True, 4)
         self.body.pack_start(self._get_notebook(), True, True, 4)
         self.body.pack_start(self._get_bottombar(), False, False, 4)
 
         self.add(self.body)
 
+    def _play_output(self, line):
+        buf = self.outputplayer.get_buffer()
+        buf.insert(buf.get_end_iter(), line)
+        gtk.gdk.threads_enter()
+        vad = self.scrollarea.get_vadjustment()
+        newpos = vad.get_upper() - vad.get_page_size()
+        vad.set_value(newpos)
+        self.scrollarea.set_vadjustment(vad)
+        gtk.gdk.threads_leave()
+        # scroll the scrollwindow
+#        vad = self.scrollarea.get_vadjustment()
+#        newpos = vad.get_upper() - vad.get_page_size()
+#        vad.set_value(newpos)
+#
+#        try:
+#            self.scrollarea.set_vadjustment(vad)
+#        except:
+#            pass
+
 
     def _get_notebook(self):
         """ Main content notebook"""
 
 
     def _exit(self, widget=None):
+        self._stop_bot = True
+        time.sleep(0.25)
         return gtk.main_quit()
 
 if __name__ == '__main__':
     w.show_all()
     gtk.gdk.threads_enter()
     gtk.main()
-#    gdk.gdk.threads_leave()
+
             i += 1
         return self.tree
 
+    def _refresh_history_display(self):
+        self.tree.set_model(
+                gtk.ListStore(int,str, str, str, str, str, str, str, str))
+        lst = dbutils.GetHistory()
+        for entry in lst:
+            self.tree.get_moel().append(entry)
 
 db = sqlite3.connect('/tmp/vpackager.db')
 
 def __create_queue_table():
+    """ Create the job queue table. NEVER to be called from the program
+    itself!"""
     c = db.cursor()
     c.execute("""create table queue
     (id integer primary key autoincrement,
     return
 
 def __create_history_table():
+    """ Create the job history table. NEVER to be called from the program
+    itself!"""
     c = db.cursor()
     # The id integer comes from the job queue table, so we can have some
     # consistancy here.
     ver text,
     srcURI text,
     descURI text,
+    release text,
     buildstart text,
     buildend text,
     stdout text,
     return
 
 def NewJob(app, ver, srcURI, descURI, release):
+    """ Add a job to the job queue"""
     t = (app, ver, srcURI, descURI, release)
     c = db.cursor()
     c.execute("insert into queue (app, ver, srcURI, descURI, release) values (?,?,?,?,?)", t)
     c.close()
 
 def RemoveJob(_id):
+    """ Delete a job from the job queue"""
     c = db.cursor()
     t = (_id,)
     c.execute('delete from queue where id=?', t)
     db.commit()
     c.close()
 
-def LogJobStart(jobid, app, ver, src, desc, starttime):
-    t = (jobid, app, ver, src, desc, starttime)
+def LogJobStart(jobid, app, ver, src, desc, release, starttime):
+    """ Move job from queue to history and add the timestamp to the build
+    start"""
+    t = (jobid, app, ver, src, desc, release, starttime)
     c = db.cursor()
-    c.execute('''insert into history (id, app, ver, srcURI, descURI,
-    buildstart) values (?,?,?,?,?,?)''', t)
+    c.execute('''insert into history (id, app, ver, srcURI, descURI, release,
+    buildstart) values (?,?,?,?,?,?,?)''', t)
+    c.execute('remove from queue where id=?',jobid)
 
     db.commit()
     c.close()
 
+def GetNextJob():
+    """ Find the next job in line to be built"""
+    c = db.cursor()
+    c.execute('select * from queue')
+    try:
+        ret = c.next()
+    except StopIteration:
+        ret = []
+    c.close()
+    return ret
 
 def GetQueue():
+    """ Return a list of all pending jobs"""
     c = db.cursor()
     ret = c.execute('select * from queue').fetchall()
     c.close()
     return ret
 
 def GetHistory():
+    """ Return a list of all processed jobs"""
     c = db.cursor()
     ret = c.execute('select * from history').fetchall()
     c.close()