Source

headstak / StakDB.m

/**
 headstak - a stack for your head
 
 Copyright (C) 2012 dreadtech.com
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 **/
//
//  StakDB.m
//  headstak
//
//  Created by Veghead on 4/1/12.

#import "StakApplication.h"
#import "StakDB.h"
#import "StakItem.h"
#import "FMDatabaseAdditions.h"
#import "STakView.h"

#define HSAppDomain @"com.dreadtech.headstak"
#define HSDBName    @"headstak.db"

@interface NSString(CSVExtensions)
- (NSString *)csvField;
@end

@implementation NSString(CSVExtensions)

- (NSString *)csvField {
    return [NSString stringWithFormat:@"\"%@\"",
            [self stringByReplacingOccurrencesOfString:@"\"" withString:@"\"\""]];
}

@end




@implementation StakDB

+ (id)stackDB {
    // Look for Application Support directory
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask, YES);
    if (![paths count]) {
        // Fallback to /tmp
        paths = [NSArray arrayWithObject:@"/tmp"];
    }
    
    NSString *fullPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:HSAppDomain];
    [[NSFileManager defaultManager] createDirectoryAtPath:fullPath
                              withIntermediateDirectories:YES
                                               attributes:nil
                                                    error:nil];
    
   return [self databaseWithPath:[fullPath stringByAppendingPathComponent:HSDBName]];
}

- (BOOL)open {
    _itemTypes = nil;
    if (! [super open]) return NO;
    if (! [self tableExists:@"stakitems"]) {
        [self buildFreshStak];
    }
    [self itemTypes];
    return YES;
}

- (void)buildFreshStak {
    NSArray *itypes = @[@[@"1",@"Task",@"#00ff99",@"t"],
                       @[@"2",@"Distraction",@"#ffcc00",@"d"],
                       @[@"3",@"Sidetracked",@"#0099ff",@"d"]];
    
    if (![self executeUpdate:@"CREATE TABLE IF NOT EXISTS itemtypes \
     (id INTEGER PRIMARY KEY NOT NULL,\
     description TEXT,\
     colour TEXT,\
     hotkey CHAR, \
     pop CHAR)",nil]) {
        LogError(@"insert failed");
    }
    
    [self executeUpdate:
     @"CREATE TABLE IF NOT EXISTS stakitems \
     (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, \
     description TEXT, \
     pushdate DATETIME NOT NULL, \
     popdate DATETIME, \
     itemtype INTEGER NOT NULL )",nil];
    
    for (NSArray *itype in itypes) {
        [self setTraceExecution:YES];
        [self executeUpdate:@"INSERT INTO itemtypes (id,description, colour, hotkey) VALUES (?,?,?,?)"
       withArgumentsInArray:itype];
    }
}

- (void)dumpStakToFileHandle:(NSFileHandle *)file {
    FMResultSet *fmr = [self executeQuery:@"SELECT * FROM stakitems\
                                            ORDER BY pushdate",nil];
    while ([fmr next]) {
        NSDictionary *dict = [fmr resultDictionary];
        NSDateFormatter *fmt = [[NSDateFormatter alloc] init ];
        [fmt setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
        [fmt setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];

        // Convert the dates to CSVable strings
        NSDate *popDate = [StakDB timestampFieldToDate:[dict objectForKey:@"popdate"]];
        NSDate *pushDate = [StakDB timestampFieldToDate:[dict objectForKey:@"pushdate"]];
        NSString *pushTime = (pushDate ? [[fmt stringFromDate:pushDate] csvField] : @"\"\"");
        NSString *popTime = (popDate ? [[fmt stringFromDate:popDate] csvField] : @"\"\"");
        
        NSString *ftype = [[self typeNameForId:[[dict objectForKey:@"itemtype"] integerValue] ] csvField];
        
        // Write the item out to the file handle
        [file writeData:[[NSString stringWithFormat:@"%@,%@,%@,%@\n",
                pushTime,
                popTime,
                ftype,
                [dict objectForKey:@"description"]
                ] dataUsingEncoding:NSUTF8StringEncoding]];
    }
}

- (NSDictionary *)itemTypes {
    if (! _itemTypes) {
        NSMutableDictionary  *coloursById       = [NSMutableDictionary dictionary];
        NSMutableDictionary  *descriptionsById  = [NSMutableDictionary dictionary];
        NSMutableDictionary  *idByHotkey        = [NSMutableDictionary dictionary];

        FMResultSet *fmr = [self executeQuery:@"SELECT id,description,colour,hotkey FROM itemtypes",nil];
        while ([fmr next]) {
            [coloursById setObject:[fmr stringForColumn:@"colour"] forKey:[NSNumber numberWithInt:[fmr intForColumn:@"id"]]];
            [descriptionsById setObject:[fmr stringForColumn:@"description"] forKey:[fmr stringForColumn:@"id"]];
            [idByHotkey setObject:[fmr stringForColumn:@"id"] forKey:[fmr stringForColumn:@"hotkey"]];
        }
        [self setItemTypes:[NSDictionary dictionaryWithObjectsAndKeys:
                            coloursById, @"colours",
                            descriptionsById, @"descriptions",
                            idByHotkey, @"ids",nil]];
    }
    LogDebug(@"%@",_itemTypes);
    return _itemTypes;
}

- (NSString *)typeNameForId:(NSUInteger)typeId {
    NSDictionary *descriptions = [[self itemTypes] objectForKey:@"descriptions"];
    return [descriptions objectForKey:[NSString stringWithFormat:@"%lu",typeId]];
}

- (void)loadStakIntoHolder:(NSMutableArray *)holder {
    FMResultSet *fmr = [self executeQuery:@"SELECT * FROM stakitems\
                        WHERE popdate IS NULL\
                        ORDER BY pushdate",nil];
    while ([fmr next]) {
        StakItem *item = [[StakItem alloc] initWithDictionary:[fmr resultDictionary] inDb:self ];
        [holder addObject:item];
    }
}

- (void)hardDeleteStak {
    [self executeUpdate:
     @"DELETE FROM STAKITEMS",nil];
}

+ (NSDate *)timestampFieldToDate:(NSNumber *)timestamp {
    if (![timestamp isEqualTo:[NSNull null]]) {
        return [NSDate dateWithTimeIntervalSince1970:[timestamp doubleValue]];
    }
    return nil;
}
@end
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.