-
Notifications
You must be signed in to change notification settings - Fork 4
/
LHWeightingOperation.m
109 lines (81 loc) · 3.8 KB
/
LHWeightingOperation.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//
// LHHistoryEntryWeightingOperation.m
// LastHistory
//
// Created by Frederik Seiffert on 19.11.09.
// Copyright 2009 Frederik Seiffert. All rights reserved.
//
#import "LHWeightingOperation.h"
#import "LHDocument.h"
#import "LHHistoryEntry.h"
#import "LHTrack.h"
#import "LHArtist.h"
#define PROCESS_CHUNK_SIZE 1000
#define HISTORY_ENTRY_DEFAULT_WEIGHT 0.01
#define HISTORY_ENTRY_WEIGHT_DATE_RANGE 30 // days
#define TIME_POINT_MODIFIER_WEIGHT 0.5
@implementation LHWeightingOperation
- (void)process
{
NSManagedObjectContext *context = self.context;
// setup predicate for similar entries
NSPredicate *similarEntriesPredicate = [NSPredicate predicateWithFormat:@"track == $track AND timestamp >= $startDate AND timestamp <= $endDate"];
// fetch tracks and history entries
NSArray *tracks = [self.document objectsForEntity:@"Track" withPredicate:nil fetchLimit:0 ascending:YES inContext:context];
NSArray *historyEntries = [self.document objectsForEntity:@"HistoryEntry" withPredicate:nil fetchLimit:0 ascending:YES inContext:context];
if (historyEntries.count == 0)
return;
self.progressMessage = [NSString stringWithFormat:@"Calculating weights for %u history entries...", historyEntries.count];
self.progressIndeterminate = NO;
NSUInteger maxTrackCount = [[tracks valueForKeyPath:@"@max.historyEntries.@count"] unsignedIntegerValue];
NSLog(@"max. track count: %u", maxTrackCount);
LHHistoryEntry *firstHistoryEntry = [historyEntries objectAtIndex:0];
LHHistoryEntry *lastHistoryEntry = [historyEntries lastObject];
NSDate *historyStartDate = firstHistoryEntry.timestamp;
NSTimeInterval historyDuration = [lastHistoryEntry.timestamp timeIntervalSinceDate:historyStartDate];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [NSDateComponents new];
NSUInteger processedCount = 0;
for (LHHistoryEntry *historyEntry in historyEntries)
{
if ([self isCancelled]) {
[context rollback];
return;
}
float weight = HISTORY_ENTRY_DEFAULT_WEIGHT;
LHTrack *track = historyEntry.track;
NSUInteger trackCount = track.trackCount;
NSAssert(track, @"track");
if (trackCount >= 2)
{
comps.day = HISTORY_ENTRY_WEIGHT_DATE_RANGE/2;
NSDate *endDate = [calendar dateByAddingComponents:comps toDate:historyEntry.timestamp options:0];
comps.day = -HISTORY_ENTRY_WEIGHT_DATE_RANGE/2;
NSDate *startDate = [calendar dateByAddingComponents:comps toDate:historyEntry.timestamp options:0];
NSPredicate *predicate = [similarEntriesPredicate predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
track, @"track",
startDate, @"startDate",
endDate, @"endDate",
nil]];
NSUInteger similarHistoryEntryCount = [self.document countForEntity:@"HistoryEntry" withPredicate:predicate inContext:context];
NSTimeInterval timeSinceHistoryStart = [historyEntry.timestamp timeIntervalSinceDate:historyStartDate];
float timePointModifier = (1.0 - TIME_POINT_MODIFIER_WEIGHT/2) + ((timeSinceHistoryStart / historyDuration) * TIME_POINT_MODIFIER_WEIGHT);
weight = ((float)similarHistoryEntryCount / trackCount) * ((float)trackCount / maxTrackCount) * timePointModifier;
// NSLog(@"%f: %@ - %@, time: %f", weight, track.artist.name, track.name, timePointModifier);
if (weight > 1.0) {
// NSLog(@"outlier: %f: %@ - %@", weight, track.artist.name, track.name);
weight = 1.0;
}
}
[historyEntry setWeightValue:weight];
if ((++processedCount % PROCESS_CHUNK_SIZE) == 0) {
self.progress = (float)processedCount / historyEntries.count;
if (![self saveContext])
return;
// NSLog(@"Calculated %u history entries", processedCount);
}
}
[self saveContext];
NSLog(@"Finished calculating history entries");
}
@end