Back to index

gcompris  8.2.2
user_list.py
Go to the documentation of this file.
00001 #  gcompris - user_list.py
00002 #
00003 # Copyright (C) 2005 Bruno Coudoin and Yves Combe
00004 #
00005 #   This program is free software; you can redistribute it and/or modify
00006 #   it under the terms of the GNU General Public License as published by
00007 #   the Free Software Foundation; either version 2 of the License, or
00008 #   (at your option) any later version.
00009 #
00010 #   This program is distributed in the hope that it will be useful,
00011 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 #   GNU General Public License for more details.
00014 #
00015 #   You should have received a copy of the GNU General Public License
00016 #   along with this program; if not, write to the Free Software
00017 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 #
00019 
00020 import gcompris
00021 import gcompris.utils
00022 import gcompris.skin
00023 import gtk
00024 import gtk.gdk
00025 import gobject
00026 from gettext import gettext as _
00027 
00028 # Database
00029 from pysqlite2 import dbapi2 as sqlite
00030 
00031 import constants
00032 import user_edit
00033 
00034 # User Management
00035 (
00036   COLUMN_USERID,
00037   COLUMN_LOGIN,
00038   COLUMN_FIRSTNAME,
00039   COLUMN_LASTNAME,
00040   COLUMN_BIRTHDATE,
00041 ) = range(5)
00042 
00043 class User_list:
00044   """GCompris Users List Table"""
00045 
00046 
00047   # The created list will be packed in the given container
00048   #
00049   # Display the users for a given class_id in a table
00050   # The class_id to display must be passed to the reload function
00051   def __init__(self, container, db_connect, db_cursor):
00052 
00053       self.cur = db_cursor
00054       self.con = db_connect
00055 
00056       # ---------------
00057       # User Management
00058       # ---------------
00059 
00060       # The class to work on
00061       self.class_id = 1
00062 
00063       self.user_data = []
00064 
00065       # create tree model
00066       self.model = self.__create_model()
00067 
00068       # First line
00069       group_hbox = gtk.HBox(False, 8)
00070       group_hbox.show()
00071       container.add(group_hbox)
00072 
00073       left_box = gtk.VBox(False, 8)
00074       left_box.show()
00075       group_hbox.add(left_box)
00076 
00077       right_box = gtk.VBox(False, 8)
00078       right_box.show()
00079       group_hbox.add(right_box)
00080 
00081       # Create the table
00082       sw = gtk.ScrolledWindow()
00083       sw.show()
00084       sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
00085       sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
00086 
00087 
00088       # create tree view
00089       treeview = gtk.TreeView(self.model)
00090       treeview.show()
00091       treeview.set_rules_hint(True)
00092       treeview.set_search_column(COLUMN_FIRSTNAME)
00093       treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
00094 
00095       sw.add(treeview)
00096 
00097       left_box.pack_start(sw, True, True, 0)
00098 
00099       # add columns to the tree view
00100       self.__add_columns(treeview)
00101 
00102       # Add buttons
00103       button = gtk.Button(stock='gtk-add')
00104       button.connect("clicked", self.on_add_item_clicked, self.model)
00105       right_box.pack_start(button, False, False, 0)
00106       button.show()
00107 
00108       self.button_edit = gtk.Button(stock='gtk-edit')
00109       self.button_edit.connect("clicked", self.on_edit_clicked, treeview)
00110       right_box.pack_start(self.button_edit, False, False, 0)
00111       self.button_edit.show()
00112       # Not editable until one class is selected
00113       self.button_edit.set_sensitive(False)
00114 
00115       button = gtk.Button(stock='gtk-open')
00116       button.connect("clicked", self.on_import_cvs_clicked, treeview)
00117       right_box.pack_start(button, False, False, 0)
00118       button.show()
00119 
00120       self.button_remove = gtk.Button(stock='gtk-remove')
00121       self.button_remove.connect("clicked", self.on_remove_item_clicked, treeview)
00122       right_box.pack_start(self.button_remove, False, False, 0)
00123       self.button_remove.show()
00124 
00125       # Missing callbacks
00126       selection = treeview.get_selection()
00127       selection.connect('changed', self.user_changed_cb, treeview)
00128 
00129   # -------------------
00130   # User Management
00131   # -------------------
00132 
00133   # Retrieve data from the database for the given class_id
00134   def reload(self, class_id):
00135     print "user_list reload %d" %class_id
00136     self.class_id = class_id
00137 
00138     # Remove all entries in the list
00139     self.model.clear()
00140 
00141     self.button_edit.set_sensitive(False)
00142     self.button_remove.set_sensitive(False)
00143 
00144     # Grab the user data
00145     self.cur.execute('select user_id,login,firstname,lastname,birthdate from users where class_id=? order by login',
00146                      (class_id,))
00147     self.user_data = self.cur.fetchall()
00148 
00149     for user in self.user_data:
00150       self.add_user_in_model(self.model, user)
00151 
00152   # Add user in the model
00153   def add_user_in_model(self, model, user):
00154     iter = model.append()
00155     model.set (iter,
00156                COLUMN_USERID,    user[COLUMN_USERID],
00157                COLUMN_LOGIN,     user[COLUMN_LOGIN],
00158                COLUMN_FIRSTNAME, user[COLUMN_FIRSTNAME],
00159                COLUMN_LASTNAME,  user[COLUMN_LASTNAME],
00160                COLUMN_BIRTHDATE, user[COLUMN_BIRTHDATE]
00161                )
00162 
00163 
00164 
00165   def __create_model(self):
00166     model = gtk.ListStore(
00167       gobject.TYPE_INT,
00168       gobject.TYPE_STRING,
00169       gobject.TYPE_STRING,
00170       gobject.TYPE_STRING,
00171       gobject.TYPE_STRING,
00172       gobject.TYPE_BOOLEAN)
00173 
00174     return model
00175 
00176 
00177   def __add_columns(self, treeview):
00178 
00179     # Total column length must be 400
00180 
00181     model = treeview.get_model()
00182 
00183     # columns for login
00184     renderer = gtk.CellRendererText()
00185     renderer.set_data("column", COLUMN_LOGIN)
00186     column = gtk.TreeViewColumn(_('Login'), renderer,
00187                                 text=COLUMN_LOGIN)
00188     column.set_sort_column_id(COLUMN_LOGIN)
00189     column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
00190     column.set_fixed_width(constants.COLUMN_WIDTH_LOGIN)
00191     treeview.append_column(column)
00192 
00193     # columns for first name
00194     renderer = gtk.CellRendererText()
00195     renderer.set_data("column", COLUMN_FIRSTNAME)
00196     column = gtk.TreeViewColumn(_('First Name'), renderer,
00197                                 text=COLUMN_FIRSTNAME)
00198     column.set_sort_column_id(COLUMN_FIRSTNAME)
00199     column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
00200     column.set_fixed_width(constants.COLUMN_WIDTH_FIRSTNAME)
00201     treeview.append_column(column)
00202 
00203     # column for last name
00204     renderer = gtk.CellRendererText()
00205     renderer.set_data("column", COLUMN_LASTNAME)
00206     column = gtk.TreeViewColumn(_('Last Name'), renderer,
00207                                 text=COLUMN_LASTNAME)
00208     column.set_sort_column_id(COLUMN_LASTNAME)
00209     column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
00210     column.set_fixed_width(constants.COLUMN_WIDTH_LASTNAME)
00211     treeview.append_column(column)
00212 
00213     # column for birth date
00214     renderer = gtk.CellRendererText()
00215     renderer.set_data("column", COLUMN_BIRTHDATE)
00216     column = gtk.TreeViewColumn(_('Birth Date'), renderer,
00217                                 text=COLUMN_BIRTHDATE)
00218     column.set_sort_column_id(COLUMN_BIRTHDATE)
00219     column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
00220     column.set_fixed_width(constants.COLUMN_WIDTH_BIRTHDATE)
00221     treeview.append_column(column)
00222 
00223 
00224   # Return the next user id
00225   def get_next_user_id(self):
00226     self.cur.execute('select max(user_id) from users')
00227     user_id = self.cur.fetchone()[0]
00228     if(user_id == None):
00229       user_id=0
00230     else:
00231       user_id += 1
00232 
00233     return user_id
00234 
00235 
00236   # Edition of a user
00237   # It create a dialog box
00238   # If multiselection is on, it will create a dialog per selection
00239   def on_edit_clicked(self, button, treeview):
00240     model = treeview.get_model()
00241     treestore, paths = treeview.get_selection().get_selected_rows()
00242     paths.reverse()
00243 
00244     for path in paths:
00245       iter = treestore.get_iter(path)
00246       path = model.get_path(iter)[0]
00247 
00248       user_id       = model.get_value(iter, COLUMN_USERID)
00249       login         = model.get_value(iter, COLUMN_LOGIN)
00250       firstname     = model.get_value(iter, COLUMN_FIRSTNAME)
00251       lastname      = model.get_value(iter, COLUMN_LASTNAME)
00252       birthdate     = model.get_value(iter, COLUMN_BIRTHDATE)
00253       user_edit.UserEdit(self.con, self.cur,
00254                          user_id, login, firstname, lastname, birthdate, self.class_id,
00255                          self)
00256 
00257 
00258   # Create a new user
00259   def on_add_item_clicked(self, button, model):
00260     user_id = self.get_next_user_id()
00261 
00262     user_edit.UserEdit(self.con, self.cur,
00263                        user_id, "", "", "", "", self.class_id,
00264                        self)
00265 
00266 
00267 
00268   def on_remove_item_clicked(self, button, treeview):
00269     model = treeview.get_model()
00270     treestore, paths = treeview.get_selection().get_selected_rows()
00271     paths.reverse()
00272 
00273     for path in paths:
00274       iter = treestore.get_iter(path)
00275       path = model.get_path(iter)[0]
00276 
00277       user_id = model.get_value(iter, COLUMN_USERID)
00278       model.remove(iter)
00279       # Remove it from the base
00280       print "Deleting user_id=" + str(user_id)
00281       self.cur.execute('delete from users where user_id=?', (user_id,))
00282       self.con.commit()
00283 
00284 
00285   def on_import_cvs_clicked(self, button, treeview):
00286 
00287     # Tell the user to select a class first
00288     dialog = gtk.MessageDialog(None,
00289                                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
00290                                gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
00291                                _("To import a user list from a file, first select a class.\nFILE FORMAT: Your file must be formatted like this:\nlogin;First name;Last name;Date of birth\nThe separator is autodetected and can be one of ',', ';' or ':'"))
00292     dialog.run()
00293     dialog.destroy()
00294 
00295     model = treeview.get_model()
00296 
00297     dialog = gtk.FileChooserDialog("Open..",
00298                                    None,
00299                                    gtk.FILE_CHOOSER_ACTION_OPEN,
00300                                    (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
00301                                     gtk.STOCK_OPEN, gtk.RESPONSE_OK))
00302     dialog.set_default_response(gtk.RESPONSE_OK)
00303 
00304     filter = gtk.FileFilter()
00305     filter.set_name("All files")
00306     filter.add_pattern("*")
00307     dialog.add_filter(filter)
00308 
00309     response = dialog.run()
00310     if response == gtk.RESPONSE_OK:
00311       filename = dialog.get_filename()
00312     elif response == gtk.RESPONSE_CANCEL:
00313       dialog.destroy()
00314       return
00315 
00316     dialog.destroy()
00317 
00318     # Parse the file and include each line in the user table
00319     file = open(filename, 'r')
00320 
00321     # Determine the separator (the most used in the set ,;:)
00322     # Warning, the input file must use the same separator on each line and
00323     # between each fields
00324     line = file.readline()
00325     file.seek(0)
00326     sep=''
00327     count=0
00328     for asep in [',', ';', ':']:
00329       c = line.count(asep)
00330       if(c>count):
00331         count=c
00332         sep=asep
00333 
00334     self.cur.execute('SELECT login FROM users')
00335 
00336     passed_upper_login = [x[0].upper() for x in self.cur.fetchall()]
00337 
00338     rejected = []
00339 
00340     for line in file.readlines():
00341       print line
00342       line = line.rstrip("\n\r")
00343       user_id = self.get_next_user_id()
00344       login, firstname, lastname, birthdate = line.split(sep)
00345 
00346       #check uppercase are unique.
00347       up_login = login.decode('utf-8').upper()
00348       if up_login in passed_upper_login:
00349         rejected.append(login.encode('utf-8'))
00350         import time
00351         login = login + str(time.time())
00352       else:
00353         passed_upper_login.append(up_login)
00354 
00355       # Save the changes in the base
00356       new_user = [user_id, login, firstname, lastname, birthdate, self.class_id]
00357       self.add_user_in_model(model, new_user)
00358       self.cur.execute('INSERT OR REPLACE INTO users (user_id, login, firstname, lastname, birthdate, class_id) VALUES (?, ?, ?, ?, ?, ?)', new_user)
00359       self.con.commit()
00360 
00361     if len(rejected) != 0:
00362       p = ''
00363       for rej in rejected:
00364         p = p + ' ' + rej
00365 
00366       p.strip()
00367       dialog = gtk.MessageDialog(None,
00368                                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
00369                                gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
00370                                _("One or more logins are not unique !\nYou need to change them: %s !") % p)
00371       dialog.run()
00372       dialog.destroy()
00373 
00374     file.close()
00375 
00376 
00377   # The user is changed ...
00378   def user_changed_cb(self, selection, treeview):
00379     self.button_edit.set_sensitive(True)
00380     self.button_remove.set_sensitive(True)
00381