Hi, <br><br>I attach patch and the script process (copy to /usr/bin)<br><br><br>---<br> src/jarabe/journal/backup.py | 231 +++++++++++++++++++++++++++++++++<br> src/jarabe/model/processmanagement.py | 84 ++++++++++++<br>
2 files changed, 315 insertions(+), 0 deletions(-)<br> create mode 100755 src/jarabe/journal/backup.py<br> create mode 100644 src/jarabe/model/processmanagement.py<br><br>diff --git a/src/jarabe/journal/backup.py b/src/jarabe/journal/backup.py<br>
new file mode 100755<br>index 0000000..f509f2c<br>--- /dev/null<br>+++ b/src/jarabe/journal/backup.py<br>@@ -0,0 +1,231 @@<br>+#!/usr/bin/env python<br>+# Journal - Backup<br>+# Copyright (C) 2010 Plan Ceibal<br>+#<br>+# Author: Esteban Arias <<a href="mailto:earias@plan.ceibal.edu.uy">earias@plan.ceibal.edu.uy</a>><br>
+# Contact information: <a href="mailto:comunidad@plan.ceibal.edu.uy">comunidad@plan.ceibal.edu.uy</a><br>+# Plan Ceibal <a href="http://www.ceibal.edu.uy">http://www.ceibal.edu.uy</a><br>+#<br>+# This program is free software: you can redistribute it and/or modify<br>
+# it under the terms of the GNU General Public License as published by<br>+# the Free Software Foundation, either version 3 of the License, or<br>+# (at your option) any later version.<br>+#<br>+# This program is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>+# GNU General Public License for more details.<br>+#<br>+# You should have received a copy of the GNU General Public License<br>
+# along with this program. If not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.<br>+<br>+import os<br>+import gtk<br>+import gobject<br>+import gconf<br>+import logging<br>+<br>+from gettext import gettext as _<br>
+from sugar.graphics import style<br>+from sugar.graphics.icon import Icon<br>+from sugar.graphics.xocolor import XoColor<br>+<br>+from jarabe.model.processmanagement import ProcessManagement<br>+<br>+PATH_BACKUP = '/usr/bin/sugar-journal-backup'<br>
+PATH_RESTORE = '/usr/bin/sugar-journal-restore'<br>+<br>+class Backup(gtk.Window):<br>+<br>+ __gtype_name__ = 'SugarJournalBackup'<br>+<br>+ def __init__(self, type_ps, mount_path):<br>+<br>+ self._type_ps = type_ps<br>
+ self._mount_path = mount_path<br>+<br>+ if self._isValidType(self._type_ps):<br>+ if ((not self._mount_path == "") and (not self._mount_path == None)):<br>+ self._progressBarHandler = None<br>
+<br>+ gtk.Window.__init__(self)<br>+<br>+ self.set_border_width(style.LINE_WIDTH)<br>+ offset = style.GRID_CELL_SIZE<br>+ width = gtk.gdk.screen_width() - offset * 2<br>
+ height = gtk.gdk.screen_height() - offset * 2<br>+ self.set_size_request(width, height)<br>+ self.set_position(gtk.WIN_POS_CENTER_ALWAYS) <br>+ self.set_decorated(False)<br>
+ self.set_resizable(False)<br>+ self.set_modal(True)<br>+<br>+ self._main_view = gtk.EventBox()<br>+ self._vbox = gtk.VBox()<br>+ self._vbox.set_spacing(style.DEFAULT_SPACING)<br>
+ self._vbox.set_border_width(style.GRID_CELL_SIZE)<br>+ self._main_view.modify_bg(gtk.STATE_NORMAL, <br>+ style.COLOR_BLACK.get_gdk_color())<br>+ self._main_view.add(self._vbox)<br>
+ self._vbox.show()<br>+<br>+ client = gconf.client_get_default()<br>+ color = XoColor(client.get_string('/desktop/sugar/user/color'))<br>+<br>+ icon = Icon(icon_name='activity-journal',<br>
+ pixel_size=style.XLARGE_ICON_SIZE,<br>+ xo_color=color)<br>+ self._vbox.pack_start(icon, False)<br>+ icon.show()<br>+<br>+ self._title = gtk.Label()<br>
+ self._title.modify_fg(gtk.STATE_NORMAL, <br>+ style.COLOR_WHITE.get_gdk_color())<br>+ self._title.set_markup('<b>%s</b>' % _(self._type_ps))<br>
+ self._vbox.pack_start(self._title, False)<br>+ self._title.show()<br>+<br>+ if self._type_ps == 'backup':<br>+ lbl_txt = "Close all activities and do not remove the external device during the process. \n\n"+\<br>
+ "To make the backup of the Journal, a restart is required. \n\n"+\<br>+ "The backup can only be restore from this computer " + self._get_serial_number() <br>
+ elif self._type_ps == 'restore': <br>+ lbl_txt = "The content you restore will overwrite the current contents of the Journal. \n\n"+\<br>+ "Do not remove the external device during the process. \n\n"+\<br>
+ "To implement the restoration of the Journal, a restart is required. \n\n"+\<br>+ "It will restore the backup done for this computer " + self._get_serial_number() <br>
+<br>+ self._message = gtk.Label(_(lbl_txt))<br>+ self._message.modify_fg(gtk.STATE_NORMAL, <br>+ style.COLOR_WHITE.get_gdk_color())<br>+ self._vbox.pack_start(self._message, True) <br>
+ self._message.show()<br>+<br>+<br>+ vbox = gtk.VBox(False, 5)<br>+ vbox.show()<br>+ hbox = gtk.HBox(True, 3)<br>+ hbox.show()<br>+<br>+ valign = gtk.Alignment(0, 1, 0, 0)<br>
+ valign.show()<br>+ vbox.pack_start(valign)<br>+<br>+ self._accept = gtk.Button()<br>+ self._accept.set_label(_('Accept'))<br>+ self._accept.show()<br>
+ self._accept.connect('clicked', self.__accept_cb)<br>+<br>+ self._show_journal = gtk.Button()<br>+ self._show_journal.set_label(_('Close'))<br>+ self._show_journal.show()<br>
+ self._show_journal.connect('clicked', self.__show_journal_cb)<br>+<br>+ self._restart = gtk.Button()<br>+ self._restart.set_label(_('Restart'))<br>+ self._restart.hide()<br>
+ self._restart.connect('clicked', self.__restart_cb)<br>+<br>+ hbox.add(self._accept)<br>+ hbox.add(self._show_journal)<br>+ hbox.add(self._restart)<br>
+<br>+ halign = gtk.Alignment(1, 0, 0, 0)<br>+ halign.show()<br>+ halign.add(hbox)<br>+ <br>+ vbox.pack_start(halign, False, False, 3)<br>+<br>+ self._vbox.add(vbox)<br>
+<br>+<br>+ self.add(self._main_view)<br>+ self._main_view.show()<br>+<br>+ self.connect("realize", self.__realize_cb)<br>+<br>+ self._process_management = ProcessManagement()<br>
+ self._process_management.connect('process-management-running', self._set_update_log)<br>+ self._process_management.connect('process-management-started', self._set_status_started)<br>
+ self._process_management.connect('process-management-finished', self._set_status_finished)<br>+ self._process_management.connect('process-management-failed', self._set_status_failed)<br>
+<br>+ def __realize_cb(self, widget):<br>+ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)<br>+ self.window.set_accept_focus(True)<br>+<br>+ def __show_journal_cb(self, button):<br>+ self.destroy()<br>
+<br>+ def __accept_cb(self, *args):<br>+ if ((not self._mount_path == "") and (not self._mount_path == None)):<br>+ if self._isValidType(self._type_ps):<br>+ if self._type_ps == 'backup':<br>
+ self._process_management.do_process([PATH_BACKUP, self._mount_path, self._get_serial_number()])<br>+ elif self._type_ps == 'restore':<br>+ self._process_management.do_process([PATH_RESTORE, self._mount_path, self._get_serial_number()])<br>
+<br>+ def _set_status_started(self, model, data=None):<br>+ self._message.set_text(_("Is running, Please wait"))<br>+ self._accept.hide()<br>+ self._show_journal.hide()<br>+ self._showProgress()<br>
+<br>+ def _set_update_log(self, model, data):<br>+ pass<br>+<br>+ def _set_status_finished(self, model, data=None):<br>+ pass<br>+<br>+ def _set_status_failed(self, model, data=None):<br>+ logging.error(data)<br>
+ self._showMsgError(data)<br>+<br>+ def __restart_cb(self, *args):<br>+ os.system('pkill -f -x "python /usr/bin/sugar-session"')<br>+<br>+ def _showProgress(self):<br>+ self._progress_bar = gtk.ProgressBar(adjustment=None)<br>
+ self._progress_bar.set_fraction(0.0)<br>+ self._progress_bar.show()<br>+ self._vbox.add(self._progress_bar)<br>+ if (self._progressBarHandler == None):<br>+ self._progressBarHandler = gobject.timeout_add(1000, self._timerProgressBar)<br>
+<br>+ def _timerProgressBar(self):<br>+ self._progress_bar.pulse()<br>+ return True <br>+<br>+ def _showMsgError(self, msg):<br>+ self._title.set_markup('<b>%s</b>' % _("Error"))<br>
+ self._message.set_text(msg)<br>+ self._restart.show()<br>+ self._accept.hide()<br>+ self._progress_bar.hide()<br>+<br>+ def _isValidType(self, type_ps):<br>+ return (type_ps == 'backup') | (type_ps == 'restore')<br>
+<br>+ def _get_serial_number(self):<br>+ serial_no = self._read_file('/ofw/serial-number')<br>+ if serial_no is None:<br>+ serial_no = self._get_nick()<br>+ return serial_no<br>
+<br>+ def _read_file(self, path):<br>+ if os.access(path, os.R_OK) == 0:<br>+ return None<br>+<br>+ fd = open(path, 'r')<br>+ value = fd.read()<br>+ fd.close()<br>+ if value:<br>
+ value = value.strip('\n')<br>+ return value<br>+ else:<br>+ logging.error('No information in file or directory: %s' % path)<br>+ return None<br>+<br>+ def _get_nick(self):<br>
+ client = gconf.client_get_default()<br>+ return client.get_string("/desktop/sugar/user/nick")<br>diff --git a/src/jarabe/model/processmanagement.py b/src/jarabe/model/processmanagement.py<br>new file mode 100644<br>
index 0000000..102bdca<br>--- /dev/null<br>+++ b/src/jarabe/model/processmanagement.py<br>@@ -0,0 +1,84 @@<br>+# Copyright (C) 2010, Plan Ceibal <<a href="mailto:comunidad@plan.ceibal.edu.uy">comunidad@plan.ceibal.edu.uy</a>><br>
+# Copyright (C) 2010 Paraguay Educa, Martin Abente, Bernie Innocenti<br>+#<br>+# This program is free software; you can redistribute it and/or modify<br>+# it under the terms of the GNU General Public License as published by<br>
+# the Free Software Foundation; either version 2 of the License, or<br>+# (at your option) any later version.<br>+#<br>+# This program is distributed in the hope that it will be useful,<br>+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>+# GNU General Public License for more details.<br>+#<br>+# You should have received a copy of the GNU General Public License<br>+# along with this program; if not, write to the Free Software<br>
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>+<br>+import os<br>+import gobject<br>+import glib<br>+import gio<br>+<br>+from gettext import gettext as _<br>+<br>+BYTES_TO_READ = 100<br>
+<br>+class ProcessManagement(gobject.GObject):<br>+<br>+ __gtype_name__ = 'ProcessManagement'<br>+<br>+ __gsignals__ = {<br>+ 'process-management-running' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str])),<br>
+ 'process-management-started' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),<br>+ 'process-management-finished' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),<br>+ 'process-management-failed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str]))<br>
+ }<br>+<br>+ def __init__(self):<br>+ gobject.GObject.__init__(self)<br>+ self._running = False<br>+<br>+ def do_process(self, cmd):<br>+ self._run_cmd_async(cmd)<br>+<br>+ def _report_process_status(self, stream, result):<br>
+ data = stream.read_finish(result)<br>+<br>+ if len(data):<br>+ self.emit('process-management-running', data)<br>+ stream.read_async(BYTES_TO_READ, self._report_process_status)<br>
+<br>+ def _report_process_error(self, stream, result):<br>+ data = stream.read_finish(result)<br>+ if len(data):<br>+ self.emit('process-management-failed', data)<br>+<br>+ def _notify_error(self, stderr):<br>
+ stdin_stream = gio.unix.InputStream(stderr, True)<br>+ stdin_stream.read_async(BYTES_TO_READ, self._report_process_error)<br>+<br>+ def _notify_process_status(self, stdout):<br>+ stdin_stream = gio.unix.InputStream(stdout, True)<br>
+ stdin_stream.read_async(BYTES_TO_READ, self._report_process_status)<br>+<br>+ def _run_cmd_async(self, cmd):<br>+ if self._running == False:<br>+ try: <br>+ pid, stdin, stdout, stderr = glib.spawn_async(cmd, flags=glib.SPAWN_DO_NOT_REAP_CHILD, standard_output=True, standard_error=True)<br>
+ gobject.child_watch_add(pid, _handle_process_end, (self, stderr))<br>+ except Exception:<br>+ self.emit('process-management-failed', _("Error - Call process: ") + str(cmd))<br>
+ else:<br>+ self._notify_process_status(stdout)<br>+ self._running = True<br>+ self.emit('process-management-started')<br>+<br>+def _handle_process_end(pid, condition, (myself, stderr)):<br>
+ myself._running = False<br>+<br>+ if os.WIFEXITED(condition) and\<br>+ os.WEXITSTATUS(condition) == 0:<br>+ myself.emit('process-management-finished')<br>+ else:<br>+ myself._notify_error(stderr)<br>
-- <br>1.6.2.5<br><br clear="all"><br>-- <br> Esteban Arias<br> Plan Ceibal - Área Técnica<br> Avda. Italia 6201<br> Montevideo - Uruguay.<br> Tel.: 601.57.73 Interno 2228<br> E-mail : <a href="mailto:earias@plan.ceibal.edu.uy">earias@plan.ceibal.edu.uy</a><br>
<br>