Ejemplo n.º 1
0
def procSync(inputData):
    json = {}
    json['error'] = 0
    json['exception'] = 'Unset'
    try:
        data = simplejson.loads(inputData)
        #print >> sys.stderr, "request"
        #pretty.pretty(data)
        try:
            if data['method'] == 'getdeck':
                ui.logMsg('Sync started')
                #json['deck'] = ui.ankiQt.syncName
                json['deck'] = ui.sync_names
            elif data['method'] == 'realsync':
                # ToDo: Error if syncName doesn't match the current deck
                ui.logMsg('Syncing deck ' + data['syncName'])
                #deck = DeckStorage.Deck(ui.ankiQt.deckPath)
                deck = DeckStorage.Deck(ui.sync_paths[ui.sync_names.index(data['syncName'])])
                try:
                    deck.rebuildQueue()
                    # Apply client updates
                    if len(data['reviewHistory']) > 0:
                        ui.logMsg(' Applying %d new reviews' % len(data['reviewHistory']))
                        timeSave = time.time
                        thinkingTimeSave = cards.Card.thinkingTime
                        totalTimeSave = cards.Card.totalTime
                        numApplied = 0
                        numDropped = 0
                        try:
                            def timeOverride():
                                    return review['time']
                            def thinkingTimeOverride(self):
                                return review['thinkingTime']

                            try:
                                deck._globalStats = globalStats(deck)
                                deck._dailyStats = dailyStats(deck)
                            except:
                                deck._globalStats = globalStats(deck.s)
                                deck._dailyStats = dailyStats(deck.s)

                            time.time = timeOverride
                            cards.Card.thinkingTime = thinkingTimeOverride
                            cards.Card.totalTime = thinkingTimeOverride

                            for review in data['reviewHistory']:
                                try:
                                    card = deck.cardFromId(int(review['cardId']))
                                    if review['reps'] == card.reps + 1: # Apply only in order reps
                                        deck.answerCard(card, review['ease'])
                                        numApplied += 1
                                    else:
                                        numDropped += 1
                                except:
                                    numDropped += 1
                        finally:
                            time.time = timeSave
                            cards.Card.thinkingTime = thinkingTimeSave
                            cards.Card.totalTime = totalTimeSave
                            if numApplied > 0:
                                # Update anything that may have changed
                                deck.modified = time.time()
                                deck.save()
                            if numDropped > 0:
                                ui.logMsg(' Dropped %d stale reviews' % numDropped)

                    # Send host updates
                    json['lastSyncHost'] = time.time()
                    global update
                    update = {}

                    cardFields = tables['cards']
                    cardIdIndex = cardFields.index('id')
                    cardFactIdIndex = cardFields.index('factId')
                    cardModifiedIndex = cardFields.index('modified')
                    factFields = tables['facts']
                    factIdIndex = factFields.index('id')
                    factModelIdIndex = factFields.index('modelId')
                    factModifiedIndex = factFields.index('modified')
                    modelFields = tables['models']
                    modelIdIndex = modelFields.index('id')
                    modelModifiedIndex = modelFields.index('modified')

                    # Get cards to review for the next 2 days, up to maxCards
                    maxCards = ui.sync_cards
                    gotCards = 0
                    pickCards = []
                    pickCardsIds = set()
                    checkTime = time.time()
                    prevTime = 0
                    hourSkip = 4
                    for hour in range(0, ui.sync_days * 24, hourSkip):
                        if gotCards == maxCards:
                            break
                        # First pick due failed cards
                        if deck.revCardOrder == 0:
                            dueOrder = 'priority desc, interval desc'
                        elif deck.revCardOrder == 1:
                            dueOrder = 'priority desc, interval'
                        elif deck.revCardOrder == 2:
                            dueOrder = 'priority desc, combinedDue'
                        else:
                            dueOrder = 'priority desc, factId, ordinal'
                        
                        failedCards = deck.s.all('SELECT %s FROM cards WHERE \
                                            type = 0 AND combinedDue >= %f AND combinedDue <= %f AND priority != 0 \
                                            ORDER BY %s LIMIT %d' % (getFieldList(tables['cards']), prevTime, checkTime, dueOrder, maxCards-gotCards))

                        for c in failedCards:
                            cardId = c[cardIdIndex]
                            if cardId not in pickCardsIds:
                                pickCardsIds.add(cardId)
                                pickCards.append(c)
                                gotCards += 1

                        # Next pick due review cards
                        reviewCards = deck.s.all('SELECT %s FROM cards WHERE \
                                            type = 1 AND combinedDue >= %f AND combinedDue <= %f AND priority != 0 \
                                            ORDER BY priority desc, interval desc LIMIT %d' % (getFieldList(tables['cards']), prevTime, checkTime, maxCards-gotCards))

                        for c in reviewCards:
                            cardId = c[cardIdIndex]
                            if cardId not in pickCardsIds:
                                pickCardsIds.add(cardId)
                                pickCards.append(c)
                                gotCards += 1

                        prevTime = checkTime - 60
                        checkTime += 60 * 60 * hourSkip

                    # Finally pick new cards
                    if gotCards < maxCards:
                        if deck.newCardOrder == 0: # random
                            newCards = deck.s.all('SELECT %s FROM cards WHERE \
                                                type = 2 \
                                                ORDER BY priority desc, factId, ordinal LIMIT %d' % (getFieldList(tables['cards']), maxCards - gotCards))
                        else: # ordered
                            newCards = deck.s.all('SELECT %s FROM cards WHERE \
                                                type = 2 \
                                                ORDER BY priority desc, due LIMIT %d' % (getFieldList(tables['cards']), maxCards - gotCards))
                        for c in newCards:
                            cardId = c[cardIdIndex]
                            if cardId not in pickCardsIds:
                                pickCardsIds.add(cardId)
                                pickCards.append(c)
                                gotCards += 1

                    #print >> sys.stderr, 'Sync', len(pickCards)

                    haveCards = set()
                    for i in data['cardIds']:
                        haveCards.add(int(i))
                    haveFacts = set()
                    for i in data['factIds']:
                        haveFacts.add(int(i))
                    haveModels = set()
                    for i in data['modelIds']:
                        haveModels.add(int(i))

                    updateCards = [[],[],[]] # modify, add, remove
                    updateFacts = [[],[],[]] # modify, add, remove
                    updateModels = [[],[],[]] # modify, add, remove

                    pickFacts = {}
                    for card in pickCards:
                        cardId = card[cardIdIndex]
                        if cardId in haveCards:
                            if card[cardModifiedIndex] > data['lastSyncHost']:
                                updateCards[0].append( procRow(cardFields, card, True) ) # Modify
                            haveCards.remove(cardId)
                        else:
                            updateCards[1].append( procRow(cardFields, card, False) ) # Add
                        pickFacts[card[cardFactIdIndex]] = None

                    pickModels = {}
                    for factId in pickFacts.keys():
                        fact = deck.s.first('SELECT %s FROM facts WHERE id=%d' % (getFieldList(factFields), factId))
                        if factId in haveFacts:
                            if fact[factModifiedIndex] > data['lastSyncHost']:
                                updateFacts[0].append( procRow(factFields, fact, True) ) # Modify
                            haveFacts.remove(factId)
                        else:
                            updateFacts[1].append( procRow(factFields, fact, False) ) # Add
                        pickModels[fact[factModelIdIndex]] = None

                    for modelId in pickModels.keys():
                        model = deck.s.first('SELECT %s FROM models WHERE id=%d' % (getFieldList(modelFields), modelId))
                        if modelId in haveModels:
                            if model[modelModifiedIndex] > data['lastSyncHost']:
                                updateModels[0].append( procRow(modelFields, model, True) ) # Modify
                            haveModels.remove(modelId)
                        else:
                            updateModels[1].append( procRow(modelFields, model, False) ) # Add

                    # Mark the remaining items for removal
                    for x in haveCards:
                        updateCards[2].append(str(x))
                    for x in haveFacts:
                        updateFacts[2].append(str(x))
                    for x in haveModels:
                        updateModels[2].append(str(x))

                    update['cards'] = {'modified': updateCards[0], 'added': updateCards[1], 'removed': updateCards[2]}
                    update['facts'] = {'modified': updateFacts[0], 'added': updateFacts[1], 'removed': updateFacts[2]}
                    update['models'] = {'modified': updateModels[0], 'added': updateModels[1], 'removed': updateModels[2]}

                    addedDeck = deck.s.all('SELECT %s FROM %s WHERE created > %f' % (getFieldList(tables['decks']), 'decks', data['lastSyncHost']))
                    modifiedDeck = deck.s.all('SELECT %s FROM %s WHERE modified > %f and created <= %f' % (getFieldList(tables['decks']), 'decks', data['lastSyncHost'], data['lastSyncHost']))
                    update['decks'] = { 'modified': [procRow(tables['decks'], x, True) for x in modifiedDeck],
                                        'added': [procRow(tables['decks'], x, False) for x in addedDeck],
                                        'removed': [] }

                    for t in tables.keys():
                        json[t+'_sql_insert'] = 'INSERT INTO ' + t + ' (' + getFieldList(tables[t]) + ') VALUES (' + getValueList(tables[t]) + ')'
                        json[t+'_sql_update'] = 'UPDATE ' + t + ' SET ' + getSetList(tables[t]) + ' WHERE id = ?'
                    json['numUpdates'] = countUpdates()
                    json['updates'] = getUpdate(200)
                    #printUpdate(json['updates'])
                    #ui.logMsg(' Sending %d items' % (json['updates']['numUpdates']))
                finally:
                    deck.rebuildCounts()
                    deck.modified = time.time()
                    deck.save()
                    deck.close()

                #ui.logMsg('Sync complete')
            elif data['method'] == 'nextsync':
                json['updates'] = getUpdate(200)
                #printUpdate(json['updates'])
                #ui.logMsg(' Sending %d items' % (json['updates']['numUpdates']))
            else:
                json['error'] = 1
                json['exception'] = 'Invalid method:' + data['method']
        finally:
            pass
    #finally:
    #    pass
    except Exception, e:
        json['error'] = 1
        json['exception'] = str(e)
        print >> sys.stderr, "Exception", e
        ui.logMsg('There were errors during sync.')
Ejemplo n.º 2
0
def procSync(inputData):
    json = {}
    json['error'] = 0
    json['exception'] = 'Unset'
    try:
        data = simplejson.loads(inputData)
        #print >> sys.stderr, "request"
        #pretty.pretty(data)
        try:
            if data['method'] == 'getdeck':
                ui.logMsg(' ')
                #json['deck'] = ui.ankiQt.syncName
                json['deck'] = ui.sync_names
            elif data['method'] == 'realsync':
                # ToDo: Error if syncName doesn't match the current deck
                ui.logMsg('Syncing deck ' + data['syncName'])
                #deck = DeckStorage.Deck(ui.ankiQt.deckPath)
                deck = DeckStorage.Deck(ui.sync_paths[ui.sync_names.index(data['syncName'])])
        
                if deck == None:
                    json['error'] = 1
                    json['exception'] = "Could not open deck: %s" % data['syncName'];
                    ui.logMsg("Could not open deck: %s" % data['syncName'])
                    res = simplejson.dumps(json, ensure_ascii=False)
                    return res
                
                old_requeueCard = deck.requeueCard
                deck.requeueCard = dummy_requeueCard
                
                try:
                    #deck.rebuildQueue()
                    # Apply client updates
                    if len(data['reviewHistory']) > 0:
                        ui.logMsg(' Applying %d new reviews' % len(data['reviewHistory']))
                        timeSave = time.time
                        thinkingTimeSave = cards.Card.thinkingTime
                        totalTimeSave = cards.Card.totalTime
                        numApplied = 0
                        numDropped = 0
                        try:
                            def timeOverride():
                                    return review['time']
                            def thinkingTimeOverride(self):
                                return review['thinkingTime']

                            try:
                                deck._globalStats = globalStats(deck)
                                deck._dailyStats = dailyStats(deck)
                            except:
                                deck._globalStats = globalStats(deck.s)
                                deck._dailyStats = dailyStats(deck.s)

                            time.time = timeOverride
                            cards.Card.thinkingTime = thinkingTimeOverride
                            cards.Card.totalTime = thinkingTimeOverride

                            for review in data['reviewHistory']:
                                try:
                                    card = deck.cardFromId(int(review['cardId']))
                                    if review['reps'] == card.reps + 1: # Apply only in order reps
                                        deck.answerCard(card, review['ease'])
                                        numApplied += 1
                                    else:
                                        numDropped += 1
                                except:
                                    numDropped += 1
                        finally:
                            time.time = timeSave
                            cards.Card.thinkingTime = thinkingTimeSave
                            cards.Card.totalTime = totalTimeSave
                            if numApplied > 0:
                                # Update anything that may have changed
                                deck.modified = time.time()
                                deck.save()
                            if numDropped > 0:
                                ui.logMsg(' Dropped %d stale reviews' % numDropped)

                    # Send host updates
                    json['lastSyncHost'] = time.time()
                    global update
                    update = {}

                    cardFields = tables['cards']
                    cardIdIndex = cardFields.index('id')
                    cardFactIdIndex = cardFields.index('factId')
                    cardModifiedIndex = cardFields.index('modified')
                    cardModelIdIndex = cardFields.index('cardModelId')
                    cardQuestionIndex = cardFields.index('question')
                    cardAnswerIndex = cardFields.index('answer')
                    
                    factFields = tables['facts']
                    factIdIndex = factFields.index('id')
                    factModelIdIndex = factFields.index('modelId')
                    factModifiedIndex = factFields.index('modified')
                    modelFields = tables['models']
                    modelIdIndex = modelFields.index('id')
                    modelModifiedIndex = modelFields.index('modified')

                    # Get cards to review for the next 2 days, up to maxCards
                    maxCards = ui.sync_cards
                    gotCards = 0
                    pickCards = []
                    pickCardsIds = set()
                    checkTime = time.time()
                    prevTime = 0
                    hourSkip = 4
                    for hour in range(0, ui.sync_days * 24, hourSkip):
                        if gotCards == maxCards:
                            break
                        # First pick due failed cards
                        if deck.revCardOrder == 0:
                            dueOrder = 'priority desc, interval desc'
                        elif deck.revCardOrder == 1:
                            dueOrder = 'priority desc, interval'
                        elif deck.revCardOrder == 2:
                            dueOrder = 'priority desc, combinedDue'
                        else:
                            dueOrder = 'priority desc, factId, ordinal'
                        
                        failedCards = deck.s.all('SELECT %s FROM cards WHERE \
                                            type = 0 AND combinedDue >= %f AND combinedDue <= %f AND priority != 0 \
                                            ORDER BY %s LIMIT %d' % (getFieldList(tables['cards']), prevTime, checkTime, dueOrder, maxCards-gotCards))

                        for c in failedCards:
                            cardId = c[cardIdIndex]
                            if cardId not in pickCardsIds:
                                pickCardsIds.add(cardId)
                                pickCards.append(c)
                                gotCards += 1

                        # Next pick due review cards
                        reviewCards = deck.s.all('SELECT %s FROM cards WHERE \
                                            type = 1 AND combinedDue >= %f AND combinedDue <= %f AND priority != 0 \
                                            ORDER BY priority desc, interval desc LIMIT %d' % (getFieldList(tables['cards']), prevTime, checkTime, maxCards-gotCards))

                        for c in reviewCards:
                            cardId = c[cardIdIndex]
                            if cardId not in pickCardsIds:
                                pickCardsIds.add(cardId)
                                pickCards.append(c)
                                gotCards += 1

                        prevTime = checkTime - 60
                        checkTime += 60 * 60 * hourSkip

                    # Finally pick new cards
                    if gotCards < maxCards:
                        if deck.newCardOrder == 0: # random
                            newCards = deck.s.all('SELECT %s FROM cards WHERE \
                                                type = 2 \
                                                ORDER BY priority desc, factId, ordinal LIMIT %d' % (getFieldList(tables['cards']), maxCards - gotCards))
                        else: # ordered
                            newCards = deck.s.all('SELECT %s FROM cards WHERE \
                                                type = 2 \
                                                ORDER BY priority desc, due LIMIT %d' % (getFieldList(tables['cards']), maxCards - gotCards))
                        for c in newCards:
                            cardId = c[cardIdIndex]
                            if cardId not in pickCardsIds:
                                pickCardsIds.add(cardId)
                                pickCards.append(c)
                                gotCards += 1

                    #print >> sys.stderr, 'Sync', len(pickCards)

                    haveCards = set()
                    for i in data['cardIds']:
                        haveCards.add(int(i))
                    haveFacts = set()
                    for i in data['factIds']:
                        haveFacts.add(int(i))
                    haveModels = set()
                    for i in data['modelIds']:
                        haveModels.add(int(i))

                    updateCards = [[],[],[]] # modify, add, remove
                    updateFacts = [[],[],[]] # modify, add, remove
                    updateModels = [[],[],[]] # modify, add, remove
                    
                    try:
                        deckName = os.path.basename(deck.path)
                        mediaDir = re.sub("(?i)\.(anki)$", ".media/", deckName)
                    except:
                        mediaDir = "media/"
                    mediaDir = ""
                    soundRe = '\[sound:([^\Z]+?)\]'
                    soundSub = '<embed target="myself" type="audio/mpeg" loop="true" height="50" width="50" href="%s\\1"></embed>' % mediaDir
                    imageRe = '\[image:([^\Z]+?)\]'
                    imageSub = 'image:%s/\\1' % mediaDir
                    
                    pickTemp = pickCards
                    pickCards = []
                    for c in pickTemp:
                        # convert rows to lists
                        c = [x for x in c]
                        # get html formatted q&a
                        card = deck.s.query(cards.Card).get(c[cardIdIndex])
                        q = card.htmlQuestion()
                        a = card.htmlAnswer()
                        try:
                            q = runFilter("drawQuestion", q, card)
                            a = runFilter("drawAnswer", a, card)
                        except:
                            pass
                        #[sound:0f69b0f7e968546b4dce403c5386a07d.mp3]
                        q = re.sub(soundRe, soundSub, q)
                        a = re.sub(soundRe, soundSub, a)
                        q = re.sub(imageRe, imageSub, q)
                        a = re.sub(imageRe, imageSub, a)
                        c[cardQuestionIndex] = q
                        c[cardAnswerIndex] = a
                        pickCards.append(c)

                    pickFacts = {}
                    for card in pickCards:
                        cardId = card[cardIdIndex]
                        if cardId in haveCards:
                            if card[cardModifiedIndex] > data['lastSyncHost']:
                                updateCards[0].append( procRow(cardFields, card, True) ) # Modify
                            haveCards.remove(cardId)
                        else:
                            updateCards[1].append( procRow(cardFields, card, False) ) # Add
                        pickFacts[card[cardFactIdIndex]] = None

                    pickModels = {}
                    for factId in pickFacts.keys():
                        fact = deck.s.first('SELECT %s FROM facts WHERE id=%d' % (getFieldList(factFields), factId))
                        if factId in haveFacts:
                            if fact[factModifiedIndex] > data['lastSyncHost']:
                                updateFacts[0].append( procRow(factFields, fact, True) ) # Modify
                            haveFacts.remove(factId)
                        else:
                            updateFacts[1].append( procRow(factFields, fact, False) ) # Add
                        pickModels[fact[factModelIdIndex]] = None

                    for modelId in pickModels.keys():
                        model = deck.s.first('SELECT %s FROM models WHERE id=%d' % (getFieldList(modelFields), modelId))
                        if modelId in haveModels:
                            if model[modelModifiedIndex] > data['lastSyncHost']:
                                updateModels[0].append( procRow(modelFields, model, True) ) # Modify
                            haveModels.remove(modelId)
                        else:
                            updateModels[1].append( procRow(modelFields, model, False) ) # Add

                    # Mark the remaining items for removal
                    for x in haveCards:
                        updateCards[2].append(str(x))
                    for x in haveFacts:
                        updateFacts[2].append(str(x))
                    for x in haveModels:
                        updateModels[2].append(str(x))

                    update['cards'] = {'modified': updateCards[0], 'added': updateCards[1], 'removed': updateCards[2]}
                    update['facts'] = {'modified': updateFacts[0], 'added': updateFacts[1], 'removed': updateFacts[2]}
                    update['models'] = {'modified': updateModels[0], 'added': updateModels[1], 'removed': updateModels[2]}

                    addedDeck = deck.s.all('SELECT %s FROM %s WHERE created > %f' % (getFieldList(tables['decks']), 'decks', data['lastSyncHost']))
                    modifiedDeck = deck.s.all('SELECT %s FROM %s WHERE modified > %f and created <= %f' % (getFieldList(tables['decks']), 'decks', data['lastSyncHost'], data['lastSyncHost']))
                    update['decks'] = { 'modified': [procRow(tables['decks'], x, True) for x in modifiedDeck],
                                        'added': [procRow(tables['decks'], x, False) for x in addedDeck],
                                        'removed': [] }

                    for t in tables.keys():
                        json[t+'_sql_insert'] = 'INSERT INTO ' + t + ' (' + getFieldList(tables[t]) + ') VALUES (' + getValueList(tables[t]) + ')'
                        json[t+'_sql_update'] = 'UPDATE ' + t + ' SET ' + getSetList(tables[t]) + ' WHERE id = ?'
                    json['numUpdates'] = countUpdates()
                    json['updates'] = getUpdate(200)
                    
                    css = deck.css
                    try:
                        css = runFilter("addStyles", css, None)
                    except:
                        pass
                    
                    css = re.sub("font-size:([0-9]+)px", changeFontScale, css)
                    
                    json['deckcss'] = css
                    #ui.logMsg(' css\n %s' % json['deckcss'])
                    
                    #printUpdate(json['updates'])
                    #ui.logMsg(' Sending %d items' % (json['updates']['numUpdates']))
                finally:
                    deck.requeueCard = old_requeueCard
                    deck.rebuildCounts()
                    deck.modified = time.time()
                    deck.save()
                    deck.close()

                #ui.logMsg('Sync complete')
            elif data['method'] == 'nextsync':
                json['updates'] = getUpdate(200)
                #printUpdate(json['updates'])
                #ui.logMsg(' Sending %d items' % (json['updates']['numUpdates']))
            else:
                json['error'] = 1
                json['exception'] = 'Invalid method:' + data['method']
        finally:
            pass
    #finally:
    #    pass
    except:
        json['error'] = 1
        json['exception'] = traceback.format_exc()
        print >> sys.stderr, json['exception']
        ui.logMsg('There were errors during sync.')
    #print >> sys.stderr, "response"
    #pretty.pretty(json)
    res = simplejson.dumps(json, ensure_ascii=False)
    return res