def slot_deletedb(self, arg): """This is a dangerous one! It will completely delete a database. """ if not confirmationDialog(_("Delete Database?"), argSub(_("Do you really want to delete database '%1'?"), (self.dbname,)), False): return if not self.master: return self.deletedb(self.dbname) # adjust display, select new db self.initDBlist()
def slot_deletedb(self, arg): """This is a dangerous one! It will completely delete a database. """ if not confirmationDialog( _("Delete Database?"), argSub(_("Do you really want to delete database '%1'?"), (self.dbname, )), False): return if not self.master: return self.deletedb(self.dbname) # adjust display, select new db self.initDBlist()
def newRole(self, name, properties): """Create a new role (user) with the given properties. If the user exists already, try to delete it. If that doesn't work, ask if its properties should be altered. If even that fails (or is rejected) raise an exception. """ try: self.send(u"DROP ROLE IF EXISTS %s" % name) except: pass if self.userExists(name) and confirmationDialog( _("Problem removing user"), _("Couldn't drop user '%s'.\n" "Alter its properties?") % name, True ): com = u"ALTER" else: com = u"CREATE" self.send(com + u" ROLE " + name + u" WITH " + properties)
def recreate(dbm, filepath, user, gui): """Recreate the user database, taking master update lock into consideration. NOTE that this 'lock' is not 100% effective, but it is a simple method which I hope will be adequate for the envisaged application. """ dir = os.path.dirname(filepath) while True: udt = getudtime(dbm) if not mUnstable(dbm): break gui.report(_("Master database is in unstable state.\n Waiting ...")) sleep(10) # Dump the master database to the selected file while True: dump = Dump(dbm, dir, user) if dump.filepath: break warning(_("Couldn't create database file in folder %1," " select new folder"), (dir,)) dir = getDirectory(dir) if not dir: if confirmationDialog(_("Abandon?"), _("User database not regenerated," " do you really want to abandon this operation?"), False): break if dump.filepath: dump.run(gui) fp = dump.filepath dump = None if fp: # Check master database hasn't been updated if (udt == getudtime(dbm)): gui.report(_("\nSUCCESS! - User database regenerated")) else: gui.report(_("Master has been updated," " need to repeat...\n")) recreate(dbm, filepath, user, gui) else: gui.report(_("\nFAILED! Couldn't regenerate user database.\n" " --- Please contact administrator!"))
def checkOverwrite(path, force): """If the file exists, ask whether to delete it and return the answer. If the answer was affirmative, delete the file. If the end result is that there is now no file of that name, return True, else False. """ if os.path.exists(path): if force or confirmationDialog(_("Overwrite?"), _("File '%s' exists already. Delete it?") % path): try: os.remove(path) except: warning(_("Couldn't delete file '%s'") % path) return False else: return False return True
def addUser(self, login): """Add a new 'normal' user, with limited rights. """ if self.db.userExists(login): if confirmationDialog(_("User name problem"), argSub(_("User '%1' already exists. Try to recreate?"), (login,)), True): if not self.removeUser(login): raise try: self.db.createRole(login, USERROLE) except: #print_exc() message(_("Database Problem: Couldn't create user '%1'"), (login,)) raise
def checkOverwrite(path, force): """If the file exists, ask whether to delete it and return the answer. If the answer was affirmative, delete the file. If the end result is that there is now no file of that name, return True, else False. """ if os.path.exists(path): if force or confirmationDialog( _("Overwrite?"), _("File '%s' exists already. Delete it?") % path): try: os.remove(path) except: warning(_("Couldn't delete file '%s'") % path) return False else: return False return True
def addUser(self, login): """Add a new 'normal' user, with limited rights. """ if self.db.userExists(login): if confirmationDialog( _("User name problem"), argSub(_("User '%1' already exists. Try to recreate?"), (login, )), True): if not self.removeUser(login): raise try: self.db.createRole(login, USERROLE) except: #print_exc() message(_("Database Problem: Couldn't create user '%1'"), (login, )) raise
def slot_finalize(self, on): """'Finalize' the database. Set the 'finalized' item in the 'config' table and revoke update privelege from teachers. """ if (not self.master) or (on == self.finalized): return if on: if not confirmationDialog(_("Finalize Database?"), argSub(_("Finalizing stops teachers' access to the database.\n" "It may also clear their passwords.\n Continue?"), (self.dbname,)), False): return val = u"1" else: val = u"" self.master.send(u"""UPDATE config SET value= ? WHERE id = 'finalized'""", (val,)) self.showFinalized(on) self.usersPrivileges(self.master)
def slot_finalize(self, on): """'Finalize' the database. Set the 'finalized' item in the 'config' table and revoke update privelege from teachers. """ if (not self.master) or (on == self.finalized): return if on: if not confirmationDialog( _("Finalize Database?"), argSub( _("Finalizing stops teachers' access to the database.\n" "It may also clear their passwords.\n Continue?"), (self.dbname, )), False): return val = u"1" else: val = u"" self.master.send( u"""UPDATE config SET value= ? WHERE id = 'finalized'""", (val, )) self.showFinalized(on) self.usersPrivileges(self.master)
def run(self, ui): minCols = 0 for v in self.columns.values(): if (v > minCols): minCols = v self.ui = ui for f in os.listdir(self.idir): if not f.endswith('.csv'): self.report(_("Ignoring '%s'") % f) continue classTag = f[:-4] # cut off the ending classPath = "classes/" + classTag if not self.configEd.pathExists(classPath + '/'): self.report(_("ERROR: There is no class with tag '%s'") % classTag) continue lastId = self.configEd.getField(classPath + "/info", "lastId") if (lastId > 0): if not confirmationDialog(_("Warning"), _("Reloading pupil information can lead to loss" " or mixing up of reports. Continue?"), False): continue self.report(_("Removing all pupils from class '%s'") % classTag) pupilPath = classPath + "/pupils" self.configEd.emptyDir(pupilPath) # Open the input file fi = os.path.join(self.idir, f) fih = open(fi, "rb") self.report(_("Fetching pupils from file '%s'") % fi) ln = 0 id = 0 # counter for automatic id for line in fih: ln += 1 line = line.strip() if (not line) or (line[0] == '#'): continue # 'Strip' the individual columns cols = [] for cv in [c.strip() for c in line.split(self.colSep)]: r = quoteStrip.match(cv) if r: cv = r.group(1) cols.append(cv) if (len(cols) < minCols): self.report(_("ERROR: Insufficient columns at line %d") % ln) continue # Write out the file for this pupil if (self.idcol == 0 ): id += 1 pfile = "%03d" % id else: id -= 1 pfile = cols[self.idcol] opstring = '' for field, ix in self.columns.items(): if ix: value = cols[ix - 1] else: value = '' if (field != "id"): opstring += "%s = %s\n" % (field, value) self.configEd.addPupil(pupilPath, pfile, opstring) self.report(_("Imported %d pupils\n") % abs(id)) self.configEd.endPupils(classPath, id)
def slot_restore(self, arg): """Restore a dumped database. It can be either an existing one, or one which has been deleted. """ # Get source file: dbpath = self.getBDbPath() if not dbpath: return None restore = Restore(dbpath) dbname = restore.getDbName() if not dbname: message(_("Couldn't open database file '%1'"), (dbpath, )) return state = 0 try: if dbname in self.dbList: if not confirmationDialog( _("Replace Database?"), argSub( _("Are you sure you want to replace database '%1'?" ), (dbname, )), False): restore.close() return self.deletedb(dbname) self.db.send(u"""CREATE DATABASE %s OWNER %s ENCODING 'UTF8'""" % (dbname, ADMIN)) state = 1 # Add to 'databases' table self.db.send(u"INSERT INTO databases VALUES (?, ?, ?, ?)", (self.db.getTime(), dbname, u'', u'')) state = 2 newmaster = self.connect(dbname) state = 3 guimessage = argSub( _("New database '%1' created, now read in the data"), (dbname, )) restore.setMaster(newmaster) guiReport(_("Restore Database"), restore, guimessage) #message(_("New database now set up")) self.usersPrivileges(newmaster) # Ensure connection is closed restore = None newmaster.close() newmaster = None except: print_exc() message(_("Couldn't create new database (%1)"), (dbname, )) if (state >= 3): newmaster.close() if (state >= 2): self.db.send(u"DELETE FROM databases WHERE name = ?", (dbname, )) if (state >= 1): self.db.send(u"DROP DATABASE %s" % dbname) # adjust display, select new db self.initDBlist()
def go(): gui.report(" ------------------------------------\n") # Connect to control database connectData = getConnectInfo(app.settings, "postgres") if not connectData: return False gui.report(_("Connecting to postgres@%s") % connectData[u"host"]) dbzc = connectData[u"db"] connectData[u"db"] = u"postgres" dbp = DB(connectData) if not dbp.isOpen(): error(_("Couldn't open 'postgres' database")) return False gui.report(_("Checking existing databases")) if dbp.read1(u"SELECT datname FROM pg_database WHERE datname = ?", (dbzc,)): if not confirmationDialog(_("Remove Control Database?"), _("The control database (%s) already exists." " Should it be removed, with everything it contains?") % dbzc, False): dbp.close() return False connectData[u"db"] = dbzc userset = set() try: # Remove all databases and users db = DB(connectData) try: info = db.read(u"SELECT * FROM databases") except: info = None db.close() if info: for id, name, finalized, users in info: gui.report(_("Removing database '%s'") % name) dbp.send(u"DROP DATABASE IF EXISTS %s" % name) userset |= set(users.split()) gui.report(_("Removing database '%s'") % dbzc) dbp.send(u"DROP DATABASE %s" % dbzc) except: #print_exc() error(_("Couldn't remove old data -" " summon a PostgreSQL expert:\n%s") % format_exc()) gui.report(_("Trying to remove users ...")) try: dbp.send(u"DROP ROLE IF EXISTS %s" % USERROLE) except: gui.report(_(" ... couldn't remove user '%s'") % USERROLE) try: dbp.send(u"DROP ROLE IF EXISTS %s" % ADMIN) except: gui.report(_(" ... couldn't remove user '%s'") % ADMIN) for u in userset: try: dbp.send(u"DROP ROLE IF EXISTS %s" % u) except: gui.report(_(" ... couldn't remove user '%s'") % u) gui.report(_("\nCreating control database")) dbp.send(u"CREATE DATABASE %s ENCODING 'UTF8'" % dbzc) dbp.close() dbp = None connectData[u"db"] = dbzc db = DB(connectData) if not db.isOpen(): error(_("Couldn't open control database")) return False try: setupCDB(db) except: db.close() error(_("Couldn't set up the '%s' database." " Please try again:\n%s") % (dbzc, format_exc())) return False gui.report(_("\nSuccess! The control database is ready.")) gui.report(_(" Now use the control panel (zgcp) to set up" " report databases.")) db.close() return True
def slot_restore(self, arg): """Restore a dumped database. It can be either an existing one, or one which has been deleted. """ # Get source file: dbpath = self.getBDbPath() if not dbpath: return None restore = Restore(dbpath) dbname = restore.getDbName() if not dbname: message(_("Couldn't open database file '%1'"), (dbpath,)) return state = 0 try: if dbname in self.dbList: if not confirmationDialog(_("Replace Database?"), argSub(_("Are you sure you want to replace database '%1'?"), (dbname,)), False): restore.close() return self.deletedb(dbname) self.db.send(u"""CREATE DATABASE %s OWNER %s ENCODING 'UTF8'""" % (dbname, ADMIN)) state = 1 # Add to 'databases' table self.db.send(u"INSERT INTO databases VALUES (?, ?, ?, ?)", (self.db.getTime(), dbname, u'', u'')) state = 2 newmaster = self.connect(dbname) state = 3 guimessage = argSub(_("New database '%1' created, now read in the data"), (dbname,)) restore.setMaster(newmaster) guiReport(_("Restore Database"), restore, guimessage) #message(_("New database now set up")) self.usersPrivileges(newmaster) # Ensure connection is closed restore = None newmaster.close() newmaster = None except: print_exc() message(_("Couldn't create new database (%1)"), (dbname,)) if (state >= 3): newmaster.close() if (state >= 2): self.db.send(u"DELETE FROM databases WHERE name = ?", (dbname,)) if (state >= 1): self.db.send(u"DROP DATABASE %s" % dbname) # adjust display, select new db self.initDBlist()
def save(self, onlytemp=False, force=False, imageX=None): """Save the data to a temporary file, close it and then ask if the original file should be replaced by it. If onlytemp is True, stop after saving the data to the temporary file, leaving the source file open, and fail if that didn't work first time. Otherwise the possibility of entering an alternative save path will be given. If force is True, don't ask, just do everything. If imageX is a directory path, get the images from there instead of from the source file """ if not self.baseFolder: return cfgFile = os.path.join(self.baseFolder, self.source.cfgName + '.zip') tmpsave = cfgFile + '_' backup = cfgFile + '~' while True: try: if os.path.exists(tmpsave): os.remove(tmpsave) self.saveData(tmpsave, imageX) if onlytemp: messageDialog(_("Information"), _("Saved as '%s'") % tmpsave) return self.source.close() break except: traceback.print_exc() warning(_("Could not save data to '%s'") % tmpsave) if onlytemp: return tmpsave = getFile(_("Save file to"), startDir=os.path.dirname(tmpsave), defaultSuffix="zip", filter=(_("zip Files"), ("*.zip",)), create=True) if isinstance(tmpsave, unicode): tmpsave = tmpsave.encode('utf8') if not tmpsave: return if (not force) and (not confirmationDialog(_("Replace existing file?"), _("The changes have been saved to '%s'.\n" "Should this now replace the previous data?") % tmpsave)): return try: if os.path.exists(backup): os.remove(backup) if os.path.exists(cfgFile): os.rename(cfgFile, backup) os.rename(tmpsave, cfgFile) if imageX: self.baseFolder = None self.slot_openConfig(False) else: # For the control panel, to indicate that the file has # been saved: self.sourcePath = cfgFile self.validationObject.validateAll() self.errorCount = self.validationObject.errorCount except: traceback.print_exc() warning(_("Couldn't update configuration file '%s'") % cfgFile)
def synchronize(dbm, filepath, gui): """Synchronize the given file with the given (open) master database. """ # Current master time mtime = dbm.getTime() # Move the user database file to backup location dir = os.path.dirname(filepath) bfile = re.sub(".zga$", "_%s.zga" % mtime, filepath) if (os.name == 'posix'): try: if Popen(["lsof", filepath], stdout=PIPE).communicate()[0]: warning(_("The database file (%1) is being used" " by another application"), (filepath,)) return except: warning(_("You should install 'lsof' so that usage" " of the file can be tested")) try: os.rename(filepath, bfile) except: # This trap only works on Windows. Linux will happily # allow you to delete a file while another program is # working on it! 'lsof filename' (see above) should be # a way to avoid that. warning(_("Couldn't rename the database file (%1).\n" "Is it being used by another application?"), (filepath,)) return gui.report(_("Database file renamed to %s") % bfile) dbs = DBs(bfile) if not dbs.isOpen(): os.rename(bfile, filepath) return # Teacher's report table teacher = dbs.getConfig(u"me") mtb = teacher2user(teacher) if mtb not in dbm.getTeacherTables(): warning(_("%1: Owning teacher (%2) not known to master database"), (filepath, teacher)) return gui.report(_("Copying reports from user database to master")) # Counter for transferred reports rcount = 0 # Creation time of slave db, i.e. last sync time ctime = dbs.getConfig(u"createtime") # Get all updated reports for id, data in dbs.read(u"SELECT * FROM reports"): # Split off the version data dver, drep = data.split(u"\n", 1) # Get the master version data try: mver = dbm.readValue(mtb, id).split(u"\n", 1)[0] except: gui.report(_("Invalid report, not updated : %s") % id) if (mver > ctime): if confirmationDialog(_("Report update problem"), _("Master version of report has been updated" " since this client was last synchronized.\n" " Replace that version of '%s'?") % id, False): gui.report(_("Revised master version of report '%s'" " will be overwritten") % id) else: gui.report(_("Revised master version of report '%s'" " not overwritten") % id) continue elif (dver <= mver): # Only do anything if the local version is newer than the # the master version continue if (dver > mtime): # The new version has a time stamp later than the # current time on the master, adjust it if dver.endswith(u"$"): dver = mtime + u"$" else: dver = mtime try: sqlupd = u"UPDATE %s SET value = ? WHERE id = ?" % mtb dbm.send(sqlupd, (dver + u"\n" + drep, id)) rcount += 1 except: gui.report(_("Couldn't update report '%s'") % id) gui.report(_("Transferred %d reports") % rcount) # Close the user database dbs.close() # Remember the latest sync time if rcount: dbm.send(u"""UPDATE interface SET value = ? WHERE id = 'lastsynctime'""", (mtime,)) # Recreate the user database gui.report(_("Recreating the user database")) recreate(dbm, filepath, teacher, gui)
def run(self, ui): minCols = 0 for v in self.columns.values(): if (v > minCols): minCols = v self.ui = ui for f in os.listdir(self.idir): if not f.endswith('.csv'): self.report(_("Ignoring '%s'") % f) continue classTag = f[:-4] # cut off the ending classPath = "classes/" + classTag if not self.configEd.pathExists(classPath + '/'): self.report( _("ERROR: There is no class with tag '%s'") % classTag) continue lastId = self.configEd.getField(classPath + "/info", "lastId") if (lastId > 0): if not confirmationDialog( _("Warning"), _("Reloading pupil information can lead to loss" " or mixing up of reports. Continue?"), False): continue self.report(_("Removing all pupils from class '%s'") % classTag) pupilPath = classPath + "/pupils" self.configEd.emptyDir(pupilPath) # Open the input file fi = os.path.join(self.idir, f) fih = open(fi, "rb") self.report(_("Fetching pupils from file '%s'") % fi) ln = 0 id = 0 # counter for automatic id for line in fih: ln += 1 line = line.strip() if (not line) or (line[0] == '#'): continue # 'Strip' the individual columns cols = [] for cv in [c.strip() for c in line.split(self.colSep)]: r = quoteStrip.match(cv) if r: cv = r.group(1) cols.append(cv) if (len(cols) < minCols): self.report( _("ERROR: Insufficient columns at line %d") % ln) continue # Write out the file for this pupil if (self.idcol == 0): id += 1 pfile = "%03d" % id else: id -= 1 pfile = cols[self.idcol] opstring = '' for field, ix in self.columns.items(): if ix: value = cols[ix - 1] else: value = '' if (field != "id"): opstring += "%s = %s\n" % (field, value) self.configEd.addPupil(pupilPath, pfile, opstring) self.report(_("Imported %d pupils\n") % abs(id)) self.configEd.endPupils(classPath, id)
def go(): gui.report(" ------------------------------------\n") # Connect to control database connectData = getConnectInfo(app.settings, "postgres") if not connectData: return False gui.report(_("Connecting to postgres@%s") % connectData[u"host"]) dbzc = connectData[u"db"] connectData[u"db"] = u"postgres" dbp = DB(connectData) if not dbp.isOpen(): error(_("Couldn't open 'postgres' database")) return False gui.report(_("Checking existing databases")) if dbp.read1(u"SELECT datname FROM pg_database WHERE datname = ?", (dbzc, )): if not confirmationDialog( _("Remove Control Database?"), _("The control database (%s) already exists." " Should it be removed, with everything it contains?") % dbzc, False): dbp.close() return False connectData[u"db"] = dbzc userset = set() try: # Remove all databases and users db = DB(connectData) try: info = db.read(u"SELECT * FROM databases") except: info = None db.close() if info: for id, name, finalized, users in info: gui.report(_("Removing database '%s'") % name) dbp.send(u"DROP DATABASE IF EXISTS %s" % name) userset |= set(users.split()) gui.report(_("Removing database '%s'") % dbzc) dbp.send(u"DROP DATABASE %s" % dbzc) except: #print_exc() error( _("Couldn't remove old data -" " summon a PostgreSQL expert:\n%s") % format_exc()) gui.report(_("Trying to remove users ...")) try: dbp.send(u"DROP ROLE IF EXISTS %s" % USERROLE) except: gui.report(_(" ... couldn't remove user '%s'") % USERROLE) try: dbp.send(u"DROP ROLE IF EXISTS %s" % ADMIN) except: gui.report(_(" ... couldn't remove user '%s'") % ADMIN) for u in userset: try: dbp.send(u"DROP ROLE IF EXISTS %s" % u) except: gui.report(_(" ... couldn't remove user '%s'") % u) gui.report(_("\nCreating control database")) dbp.send(u"CREATE DATABASE %s ENCODING 'UTF8'" % dbzc) dbp.close() dbp = None connectData[u"db"] = dbzc db = DB(connectData) if not db.isOpen(): error(_("Couldn't open control database")) return False try: setupCDB(db) except: db.close() error( _("Couldn't set up the '%s' database." " Please try again:\n%s") % (dbzc, format_exc())) return False gui.report(_("\nSuccess! The control database is ready.")) gui.report( _(" Now use the control panel (zgcp) to set up" " report databases.")) db.close() return True