Hi List, sometimes there are doubled appointments in SOGo. I don't know how and why, but sometimes it still happens.
I've written a little script which checks a users tables for events which have the same uid or same begin/end date+time and the same title. Duplicates gets removed. Regards Michael ############################################################### #!/usr/bin/env python # coding: utf-8 # Python script to remove doubled events in SOGo # in PostgreSQL Databases only # Written by Michael Heide <m...@os-cillation.de> # os-cillation GmbH - http://www.os-cillation.de/ # Licensed via PSF, see http://docs.python.org/license.html # !!! this script might cause DAMAGE to your computer !!! # !!! it comes with absolutely NO WARRANTY !!! # !!! use it at your own risk !!! import sys import psycopg2 import datetime ############################################################ #Code from http://code.activestate.com/recipes/134892/ # #Licensed via PSF, see http://docs.python.org/license.html # ############################################################ class _Getch: """Gets a single character from standard input. Does not echo to the screen.""" def __init__(self): try: self.impl = _GetchWindows() except ImportError: self.impl = _GetchUnix() def __call__(self): return self.impl() class _GetchUnix: def __init__(self): import tty, sys def __call__(self): import sys, tty, termios fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(sys.stdin.fileno()) ch = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch class _GetchWindows: def __init__(self): import msvcrt def __call__(self): import msvcrt return msvcrt.getch() ########################################################## # End activestate.com recipes # ########################################################## def usage(): print "usage... 1 or 2 parameter: c_path2 (Name) [and c_foldername (Folder)]" print "(remember: you have to use quotation marks if there are whitespaces in a single parameter)" print sys.exit() def deletefromtables(dry): notdeleted=0 deleted=0 for duplist in duplicates: firstdate=0 first=0 print for dup in duplist: cursor.execute("SELECT c_creationdate FROM %s WHERE c_name=%%s;" % (datatable), (dup,)) if cursor.rowcount==0: print "Warning: there is no c_name %s in %s where it is in %s." % (dup,datatable,quicktable) break if cursor.rowcount>1: print "Error: c_name (%s) no primary key in %s?" % (dup,datatable) print sys.exit() row = cursor.fetchone() if dry: print "Date %s: %s" % (datetime.datetime.fromtimestamp(row[0]).strftime('%Y-%m-%d %H:%M:%S'),dup) if row[0]<firstdate or firstdate==0: firstdate=row[0] first=dup for dup in duplist: if dup!=first: #print "Deleting c_name %s from %s" % (dup,datatable) #print "Deleting c_name %s from %s" % (dup,quicktable) if dry: print "DELETE FROM %s WHERE c_name=%%s" % (datatable) % (dup) print "DELETE FROM %s WHERE c_name=%%s" % (quicktable) % (dup) notdeleted=notdeleted+2; else: cursor.execute("DELETE FROM %s WHERE c_name=%%s" % (datatable), (dup,)) if cursor.rowcount==1: print "successfully deleted %s from %s" % (dup,datatable) deleted=deleted+1; else: print "Error while deleting %s from %s: %d rows affected" % (dup,datatable,cursor.rowcount) notdeleted=notdeleted+1; cursor.execute("DELETE FROM %s WHERE c_name=%%s" % (quicktable), (dup,)) if cursor.rowcount==1: print "successfully deleted %s from %s" % (dup,quicktable) deleted=deleted+1; else: print "Error while deleting %s from %s: %d rows affected" % (dup,quicktable,cursor.rowcount) notdeleted=notdeleted+1; else: if dry: print "Preserving first duplicate %s (c_name) in %s and %s" % (dup,datatable, quicktable) return (deleted, notdeleted) ############ MAIN ############# def main(): global cursor global duplicates global datatable global quicktable if len(sys.argv) != 2 and len(sys.argv) != 3: usage() getch = _Getch() ###### connect to the database and search tables conn = psycopg2.connect("dbname=sogo user=sogo password=sogo") cursor = conn.cursor() folder = "" row = [] if len(sys.argv) == 2: cursor.execute("SELECT c_location, c_quick_location, c_foldername FROM sogo_folder_info WHERE c_path2=%s and c_folder_type='Appointment';",(sys.argv[1],)) if cursor.rowcount > 1: print "Valid folder Names: " for row in cursor.fetchall(): print row[2] print "For this user you have to add a c_foldername parameter." print "(remember: you have to use quotation marks if there are whitespaces in a single parameter)" print sys.exit() else: row = cursor.fetchone() folder = row[2] else: folder = sys.argv[2] cursor.execute("SELECT c_location, c_quick_location, c_foldername FROM sogo_folder_info WHERE c_path2=%s and c_folder_type='Appointment' and c_foldername=%s;",(sys.argv[1],folder)) row = cursor.fetchone() folder = row[2] if cursor.rowcount == 0: print "rowcount is 0: no such user?" print sys.exit() if cursor.rowcount != 1: print "rowcount is not 1: %d! Stopping..." % (cursor.rowcount) print sys.exit() print "\n\nDeleting duplicate entries for folder %s of user %s " % (folder, sys.argv[1]) #row=cursor.fetchone() if len(row) != 3: print "Result row is not a 3-tuple: %d! Stopping..." % (len(row)) print sys.exit() #print row #print "datatable:", row[0][row[0].rfind("/")+1:len(row[0])] datatable=row[0][row[0].rfind("/")+1:len(row[0])] quicktable=row[1][row[1].rfind("/")+1:len(row[1])] print "Folder: ", row[2] print "Datatable:", datatable, "/ Quicktable:", quicktable ###### we've connected to the db and found our tables print "Seems ok? (y/n) ", answer = getch() print "answer: ", answer #answer = sys.stdin.read(1) if answer != 'y': print "answer not \"y\" -> stopping on user request..." print sys.exit() duplicates = [] nondupcounter = 0 dupcounter = 0 ######## find duplicates via startdate, enddate and title cursor.execute("SELECT c_name, c_startdate, c_enddate, c_title FROM %s" % (quicktable)) rows = [] row = cursor.fetchone() while row != None: rows.append(row) row = cursor.fetchone() rows.sort(key=lambda col: col[1:4]) # if sortet all dups are side by side rowold = ("","","") lastdup = False # last was duplicate currdups = [] # current duplicates for row in rows: if row[1:4]==rowold[1:4]: # and row[0]!=rowlastdup: if len(currdups)==0: currdups.append(rowold[0]) currdups.append(row[0]) lastdup = True dupcounter = dupcounter+1 else: if lastdup: lastdup = False duplicates.append(currdups) currdups = [] nondupcounter = nondupcounter+1 rowold = row ######## find duplicates via c_uid cursor.execute("SELECT c_name, c_uid FROM %s" % (quicktable)) rows = [] row = cursor.fetchone() while row != None: rows.append(row) row = cursor.fetchone() rows.sort(key=lambda col: col[1]) # if sortet all dups are side by side rowold = ("","") lastdup = False # last was duplicate currdups = [] # current duplicates for row in rows: if row[1]==rowold[1]: # and row[0]!=rowlastdup: if len(currdups)==0: currdups.append(rowold[0]) currdups.append(row[0]) lastdup = True dupcounter = dupcounter+1 else: if lastdup: lastdup = False duplicates.append(currdups) currdups = [] nondupcounter = nondupcounter+1 rowold = row print duplicates print "rows: ", len(rows) print "duplicates: ", dupcounter print "added deduplicated: ", dupcounter print # at this pount we have a list (duplicates) of lists, where the inner lists # are c_names of duplicated events # (events where starttime, endtime and title are the same) result = deletefromtables(True) print print "Deleted: %d, not deleted: %d" % result print "Dry run done. Seems ok? Sure to really delete those entries? (y/n) ", answer = getch() print "answer: ", answer if answer != 'y': print "answer not \"y\" -> stopping on user request..." print sys.exit() result = deletefromtables(False) print print "Deleted: %d, not deleted: %d" % result print "Okay... all DELETE events are sent and the transaction state is pending." print " Are you sure you want to commit that? (y/n) ", answer = getch() print "answer: ", answer if answer != 'y': print "answer not \"y\" -> stopping on user request..." print sys.exit() conn.commit() print # Standard boilerplate to call the main() function to begin # the program. if __name__ == '__main__': main() ############################################################### -- ---------------------------------------------------------- os-cillation GmbH ---------------------------------------------------------- Softwareentwicklung, Auftragsprogrammierung, IT-Consulting iPhone/iPad, Linux, Windows, Web, Android, Embedded-System ---------------------------------------------------------- GF: Oliver Schweissgut, Dipl.-Ing. technische Informatik T: +49 271 31368-0 F: +49 271 31368-18 Hohler Weg 75, 57072 Siegen, Germany USt-ID: DE276692229, HRB 9478, AG Siegen ---------------------------------------------------------- -- users@sogo.nu https://inverse.ca/sogo/lists