def __init__(self): gtk.Window.__init__(self) UpdateEngine.__init__(self) utils.set_icon(self) self.set_title(_("Web Update")) self.set_size_request(600, 400) vbox = gtk.VBox(False, 2) self.nb = gtk.Notebook() vbox.pack_start(self.nb) self.status = gtk.Statusbar() vbox.pack_start(self.status, False, False, 0) self.add(vbox) self.connect('delete-event', self._on_delete_event) # --------------------------------------------------------------------------------------- self.store = gtk.TreeStore( gtk.gdk.Pixbuf, # icona str, # nome file str, # new_revision str, # old_revision int, # bytes int, # percentuale scaricamento bool, # da scaricare gtk.gdk.Color # colre di background ) self.tree = gtk.TreeView(self.store) self.tree.append_column(gtk.TreeViewColumn("", gtk.CellRendererPixbuf(), pixbuf=0)) rend = gtk.CellRendererText(); id = 1 for i in (_("File"), _("New"), _("Current"), _("Bytes")): col = gtk.TreeViewColumn(i, rend, text=id) self.tree.append_column(col) id += 1 # Colonna percentuale rend = gtk.CellRendererProgress() col = gtk.TreeViewColumn(_("%"), rend, value=COL_PERC) self.tree.append_column(col) # Background su tutte le celle for i in self.tree.get_columns(): i.add_attribute(i.get_cell_renderers()[0], 'cell_background-gdk', COL_COLO) sw = gtk.ScrolledWindow() sw.add(self.tree) vbox = gtk.VBox(False, 2) vbox.pack_start(sw) bb = gtk.HButtonBox() bb.set_layout(gtk.BUTTONBOX_END) self.update_btn = btn = utils.new_button(_("Aggiorna"), gtk.STOCK_REFRESH) btn.connect('clicked', self._on_start_update) bb.pack_start(btn) btn.set_sensitive(False) self.get_btn = btn = utils.new_button(_("Controlla Aggiornamenti"), gtk.STOCK_APPLY) btn.connect('clicked', self.onGetList) bb.pack_start(btn) vbox.pack_start(bb, False, False, 0) self.nb.append_page(vbox) self.file = None self.it = None self.program_list = None self.icon_add = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "add.png")) self.icon_del = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "del.png")) self.icon_done = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "done.png")) self.icon_error = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "error.png")) self.icon_program = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "program.png")) self.color_add = gtk.gdk.color_parse('#70ef70') self.color_del = gtk.gdk.color_parse('#ff8080') self.color_done = gtk.gdk.color_parse('#bcfffc') self.color_error = gtk.gdk.color_parse('#ff9060') self.color_wait = gtk.gdk.color_parse('#ebebeb') # --------------------------------------------------------------------------------------- # Dobbiamo inserire una checklist per scegliere quali componenti aggiornare. # Quindi facciamo un for sulle entry del database locale per creare la lista # dei vari programmi. vbox = gtk.VBox(False, 2) self.choice_store = gtk.ListStore( gtk.gdk.Pixbuf, # icona str, # nome programma bool, # checked ) self.choice_tree = gtk.TreeView(self.choice_store) col = gtk.TreeViewColumn("", gtk.CellRendererPixbuf(), pixbuf=0) col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) col.set_fixed_width(50) self.choice_tree.append_column(col) col = gtk.TreeViewColumn(_("Program"), gtk.CellRendererText(), text=1) col.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY) self.choice_tree.append_column(col) rend = gtk.CellRendererToggle() rend.connect('toggled', self.onToggled, self.choice_tree.get_model()) col = gtk.TreeViewColumn("", rend, active=2) col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) col.set_fixed_width(50) self.choice_tree.append_column(col) sw = gtk.ScrolledWindow() sw.add(self.choice_tree) vbox.pack_start(sw) self.program_db = DatabaseWrapper(os.path.join(utils.DHOME_DIR, DB_FILE)) self.program_iters = [] self.lockInterface() self.fetchComponents(self.populateChoiceStore) bb = gtk.HButtonBox() bb.set_layout(gtk.BUTTONBOX_END) btn = utils.new_button(_("Procedi con l'aggiornamento"), gtk.STOCK_GO_FORWARD) btn.connect('clicked', self.onGoForward) bb.pack_start(btn) vbox.pack_start(bb, False, False, 0) self.nb.append_page(vbox) self.show_all()
class WebUpdate(gtk.Window, UpdateEngine): def __init__(self): gtk.Window.__init__(self) UpdateEngine.__init__(self) utils.set_icon(self) self.set_title(_("Web Update")) self.set_size_request(600, 400) vbox = gtk.VBox(False, 2) self.nb = gtk.Notebook() vbox.pack_start(self.nb) self.status = gtk.Statusbar() vbox.pack_start(self.status, False, False, 0) self.add(vbox) self.connect('delete-event', self._on_delete_event) # --------------------------------------------------------------------------------------- self.store = gtk.TreeStore( gtk.gdk.Pixbuf, # icona str, # nome file str, # new_revision str, # old_revision int, # bytes int, # percentuale scaricamento bool, # da scaricare gtk.gdk.Color # colre di background ) self.tree = gtk.TreeView(self.store) self.tree.append_column(gtk.TreeViewColumn("", gtk.CellRendererPixbuf(), pixbuf=0)) rend = gtk.CellRendererText(); id = 1 for i in (_("File"), _("New"), _("Current"), _("Bytes")): col = gtk.TreeViewColumn(i, rend, text=id) self.tree.append_column(col) id += 1 # Colonna percentuale rend = gtk.CellRendererProgress() col = gtk.TreeViewColumn(_("%"), rend, value=COL_PERC) self.tree.append_column(col) # Background su tutte le celle for i in self.tree.get_columns(): i.add_attribute(i.get_cell_renderers()[0], 'cell_background-gdk', COL_COLO) sw = gtk.ScrolledWindow() sw.add(self.tree) vbox = gtk.VBox(False, 2) vbox.pack_start(sw) bb = gtk.HButtonBox() bb.set_layout(gtk.BUTTONBOX_END) self.update_btn = btn = utils.new_button(_("Aggiorna"), gtk.STOCK_REFRESH) btn.connect('clicked', self._on_start_update) bb.pack_start(btn) btn.set_sensitive(False) self.get_btn = btn = utils.new_button(_("Controlla Aggiornamenti"), gtk.STOCK_APPLY) btn.connect('clicked', self.onGetList) bb.pack_start(btn) vbox.pack_start(bb, False, False, 0) self.nb.append_page(vbox) self.file = None self.it = None self.program_list = None self.icon_add = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "add.png")) self.icon_del = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "del.png")) self.icon_done = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "done.png")) self.icon_error = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "error.png")) self.icon_program = gtk.gdk.pixbuf_new_from_file(os.path.join(utils.DPIXM_DIR, "program.png")) self.color_add = gtk.gdk.color_parse('#70ef70') self.color_del = gtk.gdk.color_parse('#ff8080') self.color_done = gtk.gdk.color_parse('#bcfffc') self.color_error = gtk.gdk.color_parse('#ff9060') self.color_wait = gtk.gdk.color_parse('#ebebeb') # --------------------------------------------------------------------------------------- # Dobbiamo inserire una checklist per scegliere quali componenti aggiornare. # Quindi facciamo un for sulle entry del database locale per creare la lista # dei vari programmi. vbox = gtk.VBox(False, 2) self.choice_store = gtk.ListStore( gtk.gdk.Pixbuf, # icona str, # nome programma bool, # checked ) self.choice_tree = gtk.TreeView(self.choice_store) col = gtk.TreeViewColumn("", gtk.CellRendererPixbuf(), pixbuf=0) col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) col.set_fixed_width(50) self.choice_tree.append_column(col) col = gtk.TreeViewColumn(_("Program"), gtk.CellRendererText(), text=1) col.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY) self.choice_tree.append_column(col) rend = gtk.CellRendererToggle() rend.connect('toggled', self.onToggled, self.choice_tree.get_model()) col = gtk.TreeViewColumn("", rend, active=2) col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) col.set_fixed_width(50) self.choice_tree.append_column(col) sw = gtk.ScrolledWindow() sw.add(self.choice_tree) vbox.pack_start(sw) self.program_db = DatabaseWrapper(os.path.join(utils.DHOME_DIR, DB_FILE)) self.program_iters = [] self.lockInterface() self.fetchComponents(self.populateChoiceStore) bb = gtk.HButtonBox() bb.set_layout(gtk.BUTTONBOX_END) btn = utils.new_button(_("Procedi con l'aggiornamento"), gtk.STOCK_GO_FORWARD) btn.connect('clicked', self.onGoForward) bb.pack_start(btn) vbox.pack_start(bb, False, False, 0) self.nb.append_page(vbox) self.show_all() def lockInterface(self, lock=True): print "lockInterface", lock def reportError(self, data, response, args, where): UpdateEngine.reportError(data, response, args, where) def populateChoiceStore(self, programs): for i in programs: self.choice_store.append([self.icon_program, i, False]) self.lockInterface(False) def getVersionFromDatabase(self, program): a = self.program_db.select("SELECT mainversion,version,revision FROM program WHERE name='%s'" % self.program_db.sanitize(program))[0] return self.versionize(a) def versionize(self, a): return "-r".join((".".join(map(str,a[:2])), str(a[2]))) def populateUpdateTree(self, p_list): for i in p_list: self.store.append(None, [ self.icon_program, i, "...", # FIXME self.getVersionFromDatabase(i), 0, 0, False, self.color_wait ] ) self.program_list = p_list def onToggled(self, cell, path, model): iter = model.get_iter((int(path),)) fixed = model.get_value(iter, 2) fixed = not fixed model.set(iter, 2, fixed) def onGoForward(self, widget): # Facciamo un for sugli iter e controlliamo quali sottoprogrammi abbiamo abilitato model = self.choice_tree.get_model() it = self.choice_tree.get_model().get_iter_first() p_list = [] while it: if model.get_value(it, 2): p_list.append(model.get_value(it, 1)) it = model.iter_next(it) if len(p_list) == 0: self.status.push(0, _("Seleziona almeno un componente per l'aggiornamento.")) else: self.choice_store.clear() self.nb.set_current_page(0) self.populateUpdateTree(p_list) def onGetList(self, widget): #TODO #self.lockInterface() idx = 0 for i in self.program_list: self.startThread( self.populateProgramIter, BASE_DIR + i + "-update.xml", idx ) idx += 1 def startThread(self, callback, url, args=None): print _(">> Creo un thread per %s") % url f = Fetcher(callback, url, args) f.setDaemon(True) f.start() def getIterFromIndex(self, idx): prog = self.program_list[idx] model = self.tree.get_model() it = self.tree.get_model().get_iter_first() while it: if model.get_value(it, 1) == prog: return it it = model.iter_next(it) raise Exception("ARGHHH No Iter!") def populateProgramIter(self, data, response, index): #TODO: unlock interface it = self.getIterFromIndex(index) model = self.tree.get_model() #print response.status, data if data == None or response.status != 200: self.reportError(data, response, index, STEP_PROGRAM) #self.status.push(0, _("Errore durante lo scaricamento della lista dei file(HTTP %d)") % response.status) model.set_value(it, COL_COLO, self.color_error) return #try: # TODO: in pratica qui dovremmo leggere la revisione e piazzarla nella colonna # infatti suggerisco di modificare il nome delle colonne eliminando la col per l'md5 # e inserirne solo 2 una per la revision nuova e una per la vecchia # NOTA: una sola colonna contenente revision tipo 1.2-r2 new_schema = ReportReader(data) old_schema = ReportReader(None, os.path.join(utils.DHOME_DIR, "pyacqua.xml")) a = (new_schema.get("mainversion"), new_schema.get("secondversion"), new_schema.get("revision")) model.set_value(it, COL_NREV, self.versionize(a)) ret = old_schema.checkDiff(new_schema) if ret == 0: # messaggio nessun aggiornamento disponibile ecc... utils.info(_("Nessun aggiornamento disponibile")) if ret == 1: # versioni compatibili possiamo aggiornare self.__checkFileToUpdate(new_schema) if ret == 2: # TODO: Una choice .. # come una clean install utils.warning(_("Le versioni sono potenzialmente compatibili\nma _NON_ viene garantito il perfetto aggiornamento")) pass if ret == 3: utils.error(_("Versioni incompatibili per procedere con l'aggiornamento")) pass #except: # self.status.push(0, _("Impossibile interpretare il file xml")) # return self.update_btn.set_sensitive(True) def __checkFileToUpdate(self, schema): self.mirrors = schema.getList("mirrors", "url") self.program = schema.getProgramName() if self.program == None or self.mirrors == None: utils.error(_("Impossibile procedere con l'aggiornamento. Nessun mirror trovato o nome programma assente")) self.status.push(0, _("Nessun mirror fornito o nome programma assente")) return self._thread(self.__markFileForUpdate, utils.url_encode("%s/%s-update.db" % (self.mirrors[0], self.program))) def __markFileForUpdate(self, data, response): # Finche non abbiamo scaricato il database proviamo con il mirror successivo if not data or response.status != 200: if len(self.mirrors) == 0: utils.error(_("Impossibile scaricare il database delle revisioni")) self.status.push(0, _("Impossibile scaricare il database delle revisioni")) return else: self._thread(self.__markFileForUpdate, utils.url_encode("%s/%s-update.db" % (self.mirrors[0], self.program))) del self.mirrors[0] # Ok abbiamo scaricato correttamente il database self.__diffDatabase(data) def __diffDatabase(self, data, programs_list): f = open(os.path.join(utils.UPDT_DIR, self.file), 'wb') f.write(data) f.close() new_db = DatabaseReader(os.path.join(utils.UPDT_DIR, self.file)) old_db = DatabaseReader(os.path.join(utils.DHOME_DIR, DB_FILE)) # Bisogna fare un diff sulle row e controllare le entry delle revisioni # Possiamo avere un update tra revision differenti e tra # revision e secondversion differenti if new_db.v_main != old_db.v_main: return False if new_db.v_ver != old_db.v_ver: # Full update di tutti i file.. pass if new_db.v_rev == old_db.v_rev: return True return True def _on_start_update(self, widget): self.it = self.tree.get_model().get_iter_first() self._update_from_iter() def _update_file(self, data, response): # Dovremmo semplicemente salvare in una directory temporanea # del tipo ~/.pyacqua/.update o .update nella directory corrente # insieme ad una lista di file|bytes|checksum per il controllo sull'update # Al riavvio il programma dovrebbe controllare che in .update ci siano file # e aggiornare di conseguenza # TODO: da finire if not data: print _(">> Nessun file da ricevere") if response.status != 200: self._sign_error(response) return print response.status # Creiamo le subdirectory necessarie dirs = self.file.split(os.path.sep); dirs.pop() path = utils.UPDT_DIR try: for i in dirs: path = os.path.join(path, i) if not os.path.exists(path): os.mkdir(path) print _(">> File ricevuto %s") % self.file f = open(os.path.join(utils.UPDT_DIR, self.file), 'wb') f.write(data) f.close() self._update_percentage() self._go_with_next_iter() except: self._sign_error(response) def _sign_error(self, response): # Qualcosa di strano e' successo.. mhuahuahuau *_* # -.- come stiamo sotto self.tree.get_model().set_value(self.it, 0, self.icon_error) self.tree.get_model().set_value(self.it, 10, self.color_error) self.tree.get_model().set_value(self.it, 8, 0) self.status.push(0, _("Errore durante lo scaricamento di %s(response: %d)") %(self.file, response.status)) # Qui dovresti bloccare tutto e cancellare i file gia scaricati # altrimenti nella callback di scaricamento dovresti inserire un check # se esistono gia dei file che dovrebbero essere scaricati controlli md5 e bytes e se giusti # non li scarichi def _update_percentage(self): self.tree.get_model().set_value(self.it, 0, self.icon_done) self.tree.get_model().set_value(self.it, 10, self.color_done) self.tree.get_model().set_value(self.it, 8, 100) def _go_with_next_iter(self): self.it = self.tree.get_model().iter_next(self.it) self._update_from_iter() def _update_from_iter(self): if self.it != None: self.file = self.tree.get_model().get_value(self.it, 1) if self.tree.get_model().get_value(self.it, 9): # FIXME: Controlla se esiste gia il file(se l'abbiamo scaricato precedentemente) tmp = os.path.join(utils.UPDT_DIR, self.file) if os.path.exists(tmp): # Controlliamo se il file e' corretto bytes = os.path.getsize(tmp) md5 = generate.Generator.checksum(tmp) if md5 != self.tree.get_model().get_value(self.it, 4) or int(bytes) != self.tree.get_model().get_value(self.it, 3): os.remove(tmp) self._thread(self._update_file, utils.url_encode(BASE_DIR + self.file)) else: self._update_percentage() self._go_with_next_iter() else: self._thread(self._update_file, utils.url_encode(BASE_DIR + self.file)) else: self._update_percentage() self._go_with_next_iter() else: self.xml_util.dump_tree_to_file(self.diff_object, os.path.join(utils.UPDT_DIR, ".diff.xml")) utils.info(_("Riavvia per procedere all'aggiornamento di PyAcqua")) self.destroy() # La list.xml la si becca dal sito.. ergo no problem def _on_delete_event(self, *w): app.App.p_window["update"] = None