Monotone-Parent: fd483b959e9ace05c008817b99952497b2b47141

Monotone-Revision: b0c0f20c06bd2f8385a77d9c2d072d367d86789a

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2009-06-03T14:33:36
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2009-06-03 14:33:36 +00:00
parent 72a7d598d5
commit 7bda2acefd
3 changed files with 142 additions and 47 deletions

View File

@ -13,6 +13,8 @@ SOGO_REMOVEDOUBLES = sogo-contacts-removedoubles
$(SOGO_REMOVEDOUBLES)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) $(SOGO_REMOVEDOUBLES)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS)
$(SOGO_REMOVEDOUBLES)_OBJC_FILES += \ $(SOGO_REMOVEDOUBLES)_OBJC_FILES += \
sogo-contacts-removedoubles.m sogo-contacts-removedoubles.m
$(SOGO_REMOVEDOUBLES)_LIB_DIRS = \
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards
TOOL_NAME = $(SOGO_CHECKDOUBLES) $(SOGO_REMOVEDOUBLES) TOOL_NAME = $(SOGO_CHECKDOUBLES) $(SOGO_REMOVEDOUBLES)

View File

@ -42,10 +42,10 @@ typedef void (*NSUserDefaultsInitFunction) ();
static unsigned int ContactsCountWarningLimit = 1000; static unsigned int ContactsCountWarningLimit = 1000;
static void // static void
NSLogInhibitor (NSString *message) // NSLogInhibitor (NSString *message)
{ // {
} // }
@interface SOGoDoublesChecker : NSObject @interface SOGoDoublesChecker : NSObject
@ -60,7 +60,7 @@ NSLogInhibitor (NSString *message)
NSUserDefaults *ud; NSUserDefaults *ud;
NSNumber *warningLimit; NSNumber *warningLimit;
_NSLog_printf_handler = NSLogInhibitor; // _NSLog_printf_handler = NSLogInhibitor;
ud = [NSUserDefaults standardUserDefaults]; ud = [NSUserDefaults standardUserDefaults];
[ud addSuiteNamed: @"sogod"]; [ud addSuiteNamed: @"sogod"];
@ -119,10 +119,11 @@ NSLogInhibitor (NSString *message)
channel = [cm acquireOpenChannelForURL: [fom folderInfoLocation]]; channel = [cm acquireOpenChannelForURL: [fom folderInfoLocation]];
if (channel) if (channel)
{ {
sqlString = [NSString stringWithFormat: @"SELECT c_path, c_path2, c_foldername" sqlString
@" FROM %@" = [NSString stringWithFormat: @"SELECT c_path, c_path2, c_foldername"
@" WHERE c_folder_type = 'Contact'", @" FROM %@"
[fom folderInfoTableName]]; @" WHERE c_folder_type = 'Contact'",
[fom folderInfoTableName]];
ex = [channel evaluateExpressionX: sqlString]; ex = [channel evaluateExpressionX: sqlString];
if (ex) if (ex)
{ {

View File

@ -34,6 +34,10 @@
#import <Foundation/NSUserDefaults.h> #import <Foundation/NSUserDefaults.h>
#import <Foundation/NSValue.h> #import <Foundation/NSValue.h>
#import <NGCards/NGVList.h>
#import <EOControl/EOQualifier.h>
#import <GDLAccess/EOAdaptorChannel.h> #import <GDLAccess/EOAdaptorChannel.h>
#import <GDLAccess/EOAdaptorContext.h> #import <GDLAccess/EOAdaptorContext.h>
@ -43,7 +47,31 @@
typedef void (*NSUserDefaultsInitFunction) (); typedef void (*NSUserDefaultsInitFunction) ();
static unsigned int ContactsCountWarningLimit = 1000; @interface NGVList (RemoveDoubles)
- (NSArray *) cardNames;
@end
@implementation NGVList (RemoveDoubles)
- (NSArray *) cardNames
{
NSEnumerator *cardReferences;
NSMutableArray *cardNames;
CardElement *currentReference;
cardNames = [NSMutableArray array];
cardReferences = [[self cardReferences] objectEnumerator];
while ((currentReference = [cardReferences nextObject]))
[cardNames addObject: [currentReference value: 0]];
return cardNames;
}
@end
@interface SOGoDoublesRemover : NSObject @interface SOGoDoublesRemover : NSObject
@end @end
@ -53,16 +81,9 @@ static unsigned int ContactsCountWarningLimit = 1000;
+ (void) initialize + (void) initialize
{ {
NSUserDefaults *ud; NSUserDefaults *ud;
NSNumber *warningLimit;
ud = [NSUserDefaults standardUserDefaults]; ud = [NSUserDefaults standardUserDefaults];
[ud addSuiteNamed: @"sogod"]; [ud addSuiteNamed: @"sogod"];
warningLimit = [ud objectForKey: @"SOGoContactsCountWarningLimit"];
if (warningLimit)
ContactsCountWarningLimit = [warningLimit unsignedIntValue];
NSLog (@"The warning limit for folder records is set at %u",
ContactsCountWarningLimit);
} }
- (void) feedDoubleEmails: (NSMutableDictionary *) doubleEmails - (void) feedDoubleEmails: (NSMutableDictionary *) doubleEmails
@ -92,10 +113,8 @@ static unsigned int ContactsCountWarningLimit = 1000;
keys = [[doubleEmails allKeys] objectEnumerator]; keys = [[doubleEmails allKeys] objectEnumerator];
while ((currentKey = [keys nextObject])) while ((currentKey = [keys nextObject]))
{ if ([[doubleEmails objectForKey: currentKey] count] < 2)
if ([[doubleEmails objectForKey: currentKey] count] < 2) [doubleEmails removeObjectForKey: currentKey];
[doubleEmails removeObjectForKey: currentKey];
}
} }
- (NSDictionary *) detectDoubleEmailsFromRecords: (NSArray *) records - (NSDictionary *) detectDoubleEmailsFromRecords: (NSArray *) records
@ -113,6 +132,32 @@ static unsigned int ContactsCountWarningLimit = 1000;
return doubleEmails; return doubleEmails;
} }
- (NSArray *) fetchCardsInListsFromFolder: (GCSFolder *) folder
{
EOQualifier *qualifier;
NSMutableArray *cardsInLists;
NSDictionary *currentRecord;
NSArray *records;
NSEnumerator *recordsEnum;
NGVList *list;
cardsInLists = [NSMutableArray array];
qualifier = [EOQualifier qualifierWithQualifierFormat: @"c_component = %@",
@"vlist"];
records = [folder fetchFields: [NSArray arrayWithObject: @"c_content"]
matchingQualifier: qualifier];
recordsEnum = [records objectEnumerator];
while ((currentRecord = [recordsEnum nextObject]))
{
list = [NGVList parseSingleFromSource:
[currentRecord objectForKey: @"c_content"]];
[cardsInLists addObjectsFromArray: [list cardNames]];
}
return cardsInLists;
}
- (void) removeRecord: (NSString *) recordName - (void) removeRecord: (NSString *) recordName
fromTable: (NSString *) tableName fromTable: (NSString *) tableName
andQuickTable: (NSString *) quickTableName andQuickTable: (NSString *) quickTableName
@ -141,6 +186,8 @@ static unsigned int ContactsCountWarningLimit = 1000;
NSString *tableName, *quickTableName, *currentRecordName; NSString *tableName, *quickTableName, *currentRecordName;
NSEnumerator *recordsEnum; NSEnumerator *recordsEnum;
fprintf (stderr, "Removing %d records...\n", [recordNames count]);
channel = [folder acquireStoreChannel]; channel = [folder acquireStoreChannel];
context = [channel adaptorContext]; context = [channel adaptorContext];
[context beginTransaction]; [context beginTransaction];
@ -153,7 +200,6 @@ static unsigned int ContactsCountWarningLimit = 1000;
[self removeRecord: currentRecordName [self removeRecord: currentRecordName
fromTable: tableName andQuickTable: quickTableName fromTable: tableName andQuickTable: quickTableName
usingChannel: channel]; usingChannel: channel];
fprintf (stderr, "Removing %d records...\n", [recordNames count]);
[context commitTransaction]; [context commitTransaction];
[folder releaseChannel: channel]; [folder releaseChannel: channel];
@ -207,8 +253,8 @@ static unsigned int ContactsCountWarningLimit = 1000;
count: max]; count: max];
} }
- (unsigned int) mostModifiedRecord: (NSArray *) records - (int) mostModifiedRecord: (NSArray *) records
count: (unsigned int) max count: (unsigned int) max
{ {
unsigned int mostModified, count, highestVersion, version; unsigned int mostModified, count, highestVersion, version;
NSNumber *currentVersion; NSNumber *currentVersion;
@ -231,11 +277,11 @@ static unsigned int ContactsCountWarningLimit = 1000;
return mostModified; return mostModified;
} }
- (unsigned int) amountOfFilledQuickFields: (NSDictionary *) record - (int) amountOfFilledQuickFields: (NSDictionary *) record
{ {
static NSArray *quickFields = nil; static NSArray *quickFields = nil;
id value; id value;
unsigned int amount, count, max; int amount, count, max;
amount = 0; amount = 0;
@ -263,10 +309,10 @@ static unsigned int ContactsCountWarningLimit = 1000;
return amount; return amount;
} }
- (unsigned int) recordWithTheMostQuickFields: (NSArray *) records - (int) recordWithTheMostQuickFields: (NSArray *) records
count: (unsigned int) max count: (unsigned int) max
{ {
unsigned int mostQuickFields, count, highestQFields, currentQFields; int mostQuickFields, count, highestQFields, currentQFields;
mostQuickFields = 0; mostQuickFields = 0;
@ -285,9 +331,9 @@ static unsigned int ContactsCountWarningLimit = 1000;
return mostQuickFields; return mostQuickFields;
} }
- (unsigned int) linesInContent: (NSString *) content - (int) linesInContent: (NSString *) content
{ {
unsigned int nbrLines; int nbrLines;
NSArray *lines; NSArray *lines;
lines = [content componentsSeparatedByString: @"\n"]; lines = [content componentsSeparatedByString: @"\n"];
@ -300,10 +346,10 @@ static unsigned int ContactsCountWarningLimit = 1000;
return nbrLines; return nbrLines;
} }
- (unsigned int) mostCompleteRecord: (NSArray *) records - (int) mostCompleteRecord: (NSArray *) records
count: (unsigned int) max count: (unsigned int) max
{ {
unsigned int mostComplete, count, highestLines, lines; int mostComplete, count, highestLines, lines;
NSString *content; NSString *content;
mostComplete = 0; mostComplete = 0;
@ -323,11 +369,36 @@ static unsigned int ContactsCountWarningLimit = 1000;
return mostComplete; return mostComplete;
} }
- (int) record: (NSArray *) records
referencedInLists: (NSArray *) cardsInLists
{
int recordIndex, count, max;
NSDictionary *currentRecord;
recordIndex = -1;
max = [records count];
count = 0;
while (recordIndex == -1 && count < max)
{
currentRecord = [records objectAtIndex: count];
if ([cardsInLists
containsObject: [currentRecord objectForKey: @"c_name"]])
recordIndex = count;
else
count++;
}
return recordIndex;
}
- (void) assignScores: (unsigned int *) scores - (void) assignScores: (unsigned int *) scores
toRecords: (NSArray *) records toRecords: (NSArray *) records
count: (unsigned int) max count: (unsigned int) max
withCardsInLists: (NSArray *) cardsInLists
{ {
unsigned int recordIndex; int recordIndex;
recordIndex = [self mostModifiedRecord: records count: max]; recordIndex = [self mostModifiedRecord: records count: max];
(*(scores + recordIndex))++; (*(scores + recordIndex))++;
@ -335,9 +406,17 @@ static unsigned int ContactsCountWarningLimit = 1000;
(*(scores + recordIndex)) += 2; (*(scores + recordIndex)) += 2;
recordIndex = [self recordWithTheMostQuickFields: records count: max]; recordIndex = [self recordWithTheMostQuickFields: records count: max];
(*(scores + recordIndex)) += 3; (*(scores + recordIndex)) += 3;
/* TODO: this method is ugly. Instead of replacing the card references in the
list with the most useful one, we remove the cards that are not
mentionned in the list. */
recordIndex = [self record: records referencedInLists: cardsInLists];
if (recordIndex > -1)
(*(scores + recordIndex)) += 6;
} }
- (NSArray *) detectRecordsToRemove: (NSDictionary *) records - (NSArray *) detectRecordsToRemove: (NSDictionary *) records
withCardsInLists: (NSArray *) cardsInLists
{ {
NSMutableArray *recordsToRemove; NSMutableArray *recordsToRemove;
NSEnumerator *recordsEnum; NSEnumerator *recordsEnum;
@ -346,11 +425,14 @@ static unsigned int ContactsCountWarningLimit = 1000;
recordsToRemove = [NSMutableArray arrayWithCapacity: [records count] * 4]; recordsToRemove = [NSMutableArray arrayWithCapacity: [records count] * 4];
recordsEnum = [[records allValues] objectEnumerator]; recordsEnum = [[records allValues] objectEnumerator];
while ((currentRecords = [recordsEnum nextObject])) while ((currentRecords = [recordsEnum nextObject]))
{ {
max = [currentRecords count]; max = [currentRecords count];
scores = NSZoneCalloc (NULL, max, sizeof (unsigned int)); scores = NSZoneCalloc (NULL, max, sizeof (unsigned int));
[self assignScores: scores toRecords: currentRecords count: max]; [self assignScores: scores
toRecords: currentRecords count: max
withCardsInLists: cardsInLists];
[recordsToRemove addObjectsFromArray: [self records: currentRecords [recordsToRemove addObjectsFromArray: [self records: currentRecords
withLowestScores: scores withLowestScores: scores
count: max]]; count: max]];
@ -363,22 +445,33 @@ static unsigned int ContactsCountWarningLimit = 1000;
- (BOOL) removeDoublesFromFolder: (GCSFolder *) folder - (BOOL) removeDoublesFromFolder: (GCSFolder *) folder
{ {
NSArray *fields, *records, *recordsToRemove; NSArray *fields, *records, *recordsToRemove;
EOQualifier *qualifier;
BOOL rc; BOOL rc;
fields = [NSArray arrayWithObjects: @"c_name", @"c_givenname", @"c_cn", fields = [NSArray arrayWithObjects: @"c_name", @"c_givenname", @"c_cn",
@"c_sn", @"c_screenname", @"c_l", @"c_mail", @"c_o", @"c_sn", @"c_screenname", @"c_l", @"c_mail", @"c_o",
@"c_ou", @"c_telephoneNumber", @"c_content", @"c_version", @"c_ou", @"c_telephoneNumber", @"c_content", @"c_version",
@"c_creationdate", @"c_lastmodified", nil]; @"c_creationdate", @"c_lastmodified", nil];
records = [folder fetchFields: fields fetchSpecification: nil]; qualifier = [EOQualifier qualifierWithQualifierFormat: @"c_component = %@",
@"vcard"];
records = [folder fetchFields: fields matchingQualifier: qualifier];
if (records) if (records)
{ {
rc = YES; rc = YES;
recordsToRemove recordsToRemove = [self detectRecordsToRemove:
= [self detectRecordsToRemove: [self detectDoubleEmailsFromRecords: records]
[self detectDoubleEmailsFromRecords: records]]; withCardsInLists:
[self removeRecords: recordsToRemove fromFolder: folder]; [self fetchCardsInListsFromFolder: folder]];
fprintf (stderr, "Removed %d records from %d.\n", if ([recordsToRemove count])
[recordsToRemove count], [records count]); {
[self removeRecords: recordsToRemove fromFolder: folder];
fprintf (stderr, "Removed %d records from %d.\n",
[recordsToRemove count], [records count]);
}
else
fprintf (stderr, "No record to remove. %d records kept.\n",
[records count]);
} }
else else
{ {
@ -401,13 +494,12 @@ static unsigned int ContactsCountWarningLimit = 1000;
username, folderId]; username, folderId];
folder = [fom folderAtPath: folderPath]; folder = [fom folderAtPath: folderPath];
if (folder) if (folder)
{ rc = [self removeDoublesFromFolder: folder];
rc = [self removeDoublesFromFolder: folder];
}
else else
{ {
fprintf (stderr, "Folder '%s' not found.\n", fprintf (stderr, "Folder '%s' of user '%s' not found.\n",
[folderId cStringUsingEncoding: NSUTF8StringEncoding]); [folderId cStringUsingEncoding: NSUTF8StringEncoding],
[username cStringUsingEncoding: NSUTF8StringEncoding]);
rc = NO; rc = NO;
} }