mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
strip out unused syncing code
This commit is contained in:
parent
8325f5b396
commit
d6874de8c8
2 changed files with 38 additions and 469 deletions
137
anki/sync.py
137
anki/sync.py
|
@ -54,9 +54,6 @@ class Syncer:
|
||||||
rts = meta['ts']
|
rts = meta['ts']
|
||||||
self.rmod = meta['mod']
|
self.rmod = meta['mod']
|
||||||
self.maxUsn = meta['usn']
|
self.maxUsn = meta['usn']
|
||||||
# this is a temporary measure to address the problem of users
|
|
||||||
# forgetting which email address they've used - it will be removed
|
|
||||||
# when enough time has passed
|
|
||||||
self.uname = meta.get("uname", "")
|
self.uname = meta.get("uname", "")
|
||||||
meta = self.meta()
|
meta = self.meta()
|
||||||
self.col.log("lmeta", meta)
|
self.col.log("lmeta", meta)
|
||||||
|
@ -143,13 +140,6 @@ class Syncer:
|
||||||
d['crt'] = self.col.crt
|
d['crt'] = self.col.crt
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def applyChanges(self, changes):
|
|
||||||
self.rchg = changes
|
|
||||||
lchg = self.changes()
|
|
||||||
# merge our side before returning
|
|
||||||
self.mergeChanges(lchg, self.rchg)
|
|
||||||
return lchg
|
|
||||||
|
|
||||||
def mergeChanges(self, lchg, rchg):
|
def mergeChanges(self, lchg, rchg):
|
||||||
# then the other objects
|
# then the other objects
|
||||||
self.mergeModels(rchg['models'])
|
self.mergeModels(rchg['models'])
|
||||||
|
@ -177,14 +167,8 @@ class Syncer:
|
||||||
return "tag had usn = -1"
|
return "tag had usn = -1"
|
||||||
found = False
|
found = False
|
||||||
for m in self.col.models.all():
|
for m in self.col.models.all():
|
||||||
if self.col.server:
|
if m['usn'] == -1:
|
||||||
# the web upgrade was mistakenly setting usn
|
return "model had usn = -1"
|
||||||
if m['usn'] < 0:
|
|
||||||
m['usn'] = 0
|
|
||||||
found = True
|
|
||||||
else:
|
|
||||||
if m['usn'] == -1:
|
|
||||||
return "model had usn = -1"
|
|
||||||
if found:
|
if found:
|
||||||
self.col.models.save()
|
self.col.models.save()
|
||||||
self.col.sched.reset()
|
self.col.sched.reset()
|
||||||
|
@ -202,22 +186,10 @@ class Syncer:
|
||||||
len(self.col.decks.allConf()),
|
len(self.col.decks.allConf()),
|
||||||
]
|
]
|
||||||
|
|
||||||
def sanityCheck2(self, client):
|
|
||||||
server = self.sanityCheck()
|
|
||||||
if client != server:
|
|
||||||
return dict(status="bad", c=client, s=server)
|
|
||||||
return dict(status="ok")
|
|
||||||
|
|
||||||
def usnLim(self):
|
def usnLim(self):
|
||||||
if self.col.server:
|
return "usn = -1"
|
||||||
return "usn >= %d" % self.minUsn
|
|
||||||
else:
|
|
||||||
return "usn = -1"
|
|
||||||
|
|
||||||
def finish(self, mod=None):
|
def finish(self, mod=None):
|
||||||
if not mod:
|
|
||||||
# server side; we decide new mod time
|
|
||||||
mod = intTime(1000)
|
|
||||||
self.col.ls = mod
|
self.col.ls = mod
|
||||||
self.col._usn = self.maxUsn + 1
|
self.col._usn = self.maxUsn + 1
|
||||||
# ensure we save the mod time even if no changes made
|
# ensure we save the mod time even if no changes made
|
||||||
|
@ -262,11 +234,10 @@ from notes where %s""" % d)
|
||||||
# table is empty
|
# table is empty
|
||||||
self.tablesLeft.pop(0)
|
self.tablesLeft.pop(0)
|
||||||
self.cursor = None
|
self.cursor = None
|
||||||
# if we're the client, mark the objects as having been sent
|
# mark the objects as having been sent
|
||||||
if not self.col.server:
|
self.col.db.execute(
|
||||||
self.col.db.execute(
|
"update %s set usn=? where usn=-1"%curTable,
|
||||||
"update %s set usn=? where usn=-1"%curTable,
|
self.maxUsn)
|
||||||
self.maxUsn)
|
|
||||||
buf[curTable] = rows
|
buf[curTable] = rows
|
||||||
lim -= fetched
|
lim -= fetched
|
||||||
if not self.tablesLeft:
|
if not self.tablesLeft:
|
||||||
|
@ -288,12 +259,10 @@ from notes where %s""" % d)
|
||||||
cards = []
|
cards = []
|
||||||
notes = []
|
notes = []
|
||||||
decks = []
|
decks = []
|
||||||
if self.col.server:
|
|
||||||
curs = self.col.db.execute(
|
curs = self.col.db.execute(
|
||||||
"select oid, type from graves where usn >= ?", self.minUsn)
|
"select oid, type from graves where usn = -1")
|
||||||
else:
|
|
||||||
curs = self.col.db.execute(
|
|
||||||
"select oid, type from graves where usn = -1")
|
|
||||||
for oid, type in curs:
|
for oid, type in curs:
|
||||||
if type == REM_CARD:
|
if type == REM_CARD:
|
||||||
cards.append(oid)
|
cards.append(oid)
|
||||||
|
@ -301,23 +270,16 @@ from notes where %s""" % d)
|
||||||
notes.append(oid)
|
notes.append(oid)
|
||||||
else:
|
else:
|
||||||
decks.append(oid)
|
decks.append(oid)
|
||||||
if not self.col.server:
|
|
||||||
self.col.db.execute("update graves set usn=? where usn=-1",
|
|
||||||
self.maxUsn)
|
|
||||||
return dict(cards=cards, notes=notes, decks=decks)
|
|
||||||
|
|
||||||
def start(self, minUsn, lnewer, graves):
|
self.col.db.execute("update graves set usn=? where usn=-1",
|
||||||
self.maxUsn = self.col._usn
|
self.maxUsn)
|
||||||
self.minUsn = minUsn
|
|
||||||
self.lnewer = not lnewer
|
return dict(cards=cards, notes=notes, decks=decks)
|
||||||
lgraves = self.removed()
|
|
||||||
self.remove(graves)
|
|
||||||
return lgraves
|
|
||||||
|
|
||||||
def remove(self, graves):
|
def remove(self, graves):
|
||||||
# pretend to be the server so we don't set usn = -1
|
# pretend to be the server so we don't set usn = -1
|
||||||
wasServer = self.col.server
|
|
||||||
self.col.server = True
|
self.col.server = True
|
||||||
|
|
||||||
# notes first, so we don't end up with duplicate graves
|
# notes first, so we don't end up with duplicate graves
|
||||||
self.col._remNotes(graves['notes'])
|
self.col._remNotes(graves['notes'])
|
||||||
# then cards
|
# then cards
|
||||||
|
@ -325,20 +287,18 @@ from notes where %s""" % d)
|
||||||
# and decks
|
# and decks
|
||||||
for oid in graves['decks']:
|
for oid in graves['decks']:
|
||||||
self.col.decks.rem(oid, childrenToo=False)
|
self.col.decks.rem(oid, childrenToo=False)
|
||||||
self.col.server = wasServer
|
|
||||||
|
self.col.server = False
|
||||||
|
|
||||||
# Models
|
# Models
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def getModels(self):
|
def getModels(self):
|
||||||
if self.col.server:
|
mods = [m for m in self.col.models.all() if m['usn'] == -1]
|
||||||
return [m for m in self.col.models.all() if m['usn'] >= self.minUsn]
|
for m in mods:
|
||||||
else:
|
m['usn'] = self.maxUsn
|
||||||
mods = [m for m in self.col.models.all() if m['usn'] == -1]
|
self.col.models.save()
|
||||||
for m in mods:
|
return mods
|
||||||
m['usn'] = self.maxUsn
|
|
||||||
self.col.models.save()
|
|
||||||
return mods
|
|
||||||
|
|
||||||
def mergeModels(self, rchg):
|
def mergeModels(self, rchg):
|
||||||
for r in rchg:
|
for r in rchg:
|
||||||
|
@ -351,20 +311,14 @@ from notes where %s""" % d)
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def getDecks(self):
|
def getDecks(self):
|
||||||
if self.col.server:
|
decks = [g for g in self.col.decks.all() if g['usn'] == -1]
|
||||||
return [
|
for g in decks:
|
||||||
[g for g in self.col.decks.all() if g['usn'] >= self.minUsn],
|
g['usn'] = self.maxUsn
|
||||||
[g for g in self.col.decks.allConf() if g['usn'] >= self.minUsn]
|
dconf = [g for g in self.col.decks.allConf() if g['usn'] == -1]
|
||||||
]
|
for g in dconf:
|
||||||
else:
|
g['usn'] = self.maxUsn
|
||||||
decks = [g for g in self.col.decks.all() if g['usn'] == -1]
|
self.col.decks.save()
|
||||||
for g in decks:
|
return [decks, dconf]
|
||||||
g['usn'] = self.maxUsn
|
|
||||||
dconf = [g for g in self.col.decks.allConf() if g['usn'] == -1]
|
|
||||||
for g in dconf:
|
|
||||||
g['usn'] = self.maxUsn
|
|
||||||
self.col.decks.save()
|
|
||||||
return [decks, dconf]
|
|
||||||
|
|
||||||
def mergeDecks(self, rchg):
|
def mergeDecks(self, rchg):
|
||||||
for r in rchg[0]:
|
for r in rchg[0]:
|
||||||
|
@ -389,17 +343,13 @@ from notes where %s""" % d)
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def getTags(self):
|
def getTags(self):
|
||||||
if self.col.server:
|
tags = []
|
||||||
return [t for t, usn in self.col.tags.allItems()
|
for t, usn in self.col.tags.allItems():
|
||||||
if usn >= self.minUsn]
|
if usn == -1:
|
||||||
else:
|
self.col.tags.tags[t] = self.maxUsn
|
||||||
tags = []
|
tags.append(t)
|
||||||
for t, usn in self.col.tags.allItems():
|
self.col.tags.save()
|
||||||
if usn == -1:
|
return tags
|
||||||
self.col.tags.tags[t] = self.maxUsn
|
|
||||||
tags.append(t)
|
|
||||||
self.col.tags.save()
|
|
||||||
return tags
|
|
||||||
|
|
||||||
def mergeTags(self, tags):
|
def mergeTags(self, tags):
|
||||||
self.col.tags.register(tags, usn=self.maxUsn)
|
self.col.tags.register(tags, usn=self.maxUsn)
|
||||||
|
@ -448,17 +398,6 @@ from notes where %s""" % d)
|
||||||
def mergeConf(self, conf):
|
def mergeConf(self, conf):
|
||||||
self.col.conf = conf
|
self.col.conf = conf
|
||||||
|
|
||||||
# Local syncing for unit tests
|
|
||||||
##########################################################################
|
|
||||||
|
|
||||||
class LocalServer(Syncer):
|
|
||||||
|
|
||||||
# serialize/deserialize payload, so we don't end up sharing objects
|
|
||||||
# between cols
|
|
||||||
def applyChanges(self, changes):
|
|
||||||
l = json.loads; d = json.dumps
|
|
||||||
return l(d(Syncer.applyChanges(self, l(d(changes)))))
|
|
||||||
|
|
||||||
# Wrapper for requests that tracks upload/download progress
|
# Wrapper for requests that tracks upload/download progress
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,370 +0,0 @@
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
import nose, os, shutil, time
|
|
||||||
|
|
||||||
from anki import Collection as aopen, Collection
|
|
||||||
from anki.utils import intTime
|
|
||||||
from anki.sync import Syncer, LocalServer
|
|
||||||
from anki.consts import STARTING_FACTOR
|
|
||||||
from tests.shared import getEmptyCol, getEmptyDeckWith
|
|
||||||
import anki.stdmodels
|
|
||||||
|
|
||||||
# Local tests
|
|
||||||
##########################################################################
|
|
||||||
|
|
||||||
deck1=None
|
|
||||||
deck2=None
|
|
||||||
client=None
|
|
||||||
server=None
|
|
||||||
server2=None
|
|
||||||
|
|
||||||
def setup_basic():
|
|
||||||
global deck1, deck2, client, server
|
|
||||||
deck1 = getEmptyCol()
|
|
||||||
# add a note to deck 1
|
|
||||||
f = deck1.newNote()
|
|
||||||
f['Front'] = "foo"; f['Back'] = "bar"; f.tags = ["foo"]
|
|
||||||
deck1.addNote(f)
|
|
||||||
# answer it
|
|
||||||
deck1.reset(); deck1.sched.answerCard(deck1.sched.getCard(), 4)
|
|
||||||
# repeat for deck2
|
|
||||||
deck2 = getEmptyDeckWith(server=True)
|
|
||||||
f = deck2.newNote()
|
|
||||||
f['Front'] = "bar"; f['Back'] = "bar"; f.tags = ["bar"]
|
|
||||||
deck2.addNote(f)
|
|
||||||
deck2.reset(); deck2.sched.answerCard(deck2.sched.getCard(), 4)
|
|
||||||
# start with same schema and sync time
|
|
||||||
deck1.scm = deck2.scm = 0
|
|
||||||
# and same mod time, so sync does nothing
|
|
||||||
t = intTime(1000)
|
|
||||||
deck1.save(mod=t); deck2.save(mod=t)
|
|
||||||
server = LocalServer(deck2)
|
|
||||||
client = Syncer(deck1, server)
|
|
||||||
|
|
||||||
def setup_modified():
|
|
||||||
setup_basic()
|
|
||||||
# mark deck1 as changed
|
|
||||||
time.sleep(0.1)
|
|
||||||
deck1.setMod()
|
|
||||||
deck1.save()
|
|
||||||
|
|
||||||
@nose.with_setup(setup_basic)
|
|
||||||
def test_nochange():
|
|
||||||
assert client.sync() == "noChanges"
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_changedSchema():
|
|
||||||
deck1.scm += 1
|
|
||||||
deck1.setMod()
|
|
||||||
assert client.sync() == "fullSync"
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_sync():
|
|
||||||
def check(num):
|
|
||||||
for d in deck1, deck2:
|
|
||||||
for t in ("revlog", "notes", "cards"):
|
|
||||||
assert d.db.scalar("select count() from %s" % t) == num
|
|
||||||
assert len(d.models.all()) == num*len(anki.stdmodels.models)
|
|
||||||
# the default deck and config have an id of 1, so always 1
|
|
||||||
assert len(d.decks.all()) == 1
|
|
||||||
assert len(d.decks.dconf) == 1
|
|
||||||
assert len(d.tags.all()) == num
|
|
||||||
check(1)
|
|
||||||
origUsn = deck1.usn()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
# last sync times and mod times should agree
|
|
||||||
assert deck1.mod == deck2.mod
|
|
||||||
assert deck1._usn == deck2._usn
|
|
||||||
assert deck1.mod == deck1.ls
|
|
||||||
assert deck1._usn != origUsn
|
|
||||||
# because everything was created separately it will be merged in. in
|
|
||||||
# actual use, we use a full sync to ensure a common starting point.
|
|
||||||
check(2)
|
|
||||||
# repeating it does nothing
|
|
||||||
assert client.sync() == "noChanges"
|
|
||||||
# if we bump mod time, the decks will sync but should remain the same.
|
|
||||||
deck1.setMod()
|
|
||||||
deck1.save(mod=deck1.mod+1)
|
|
||||||
assert client.sync() == "success"
|
|
||||||
check(2)
|
|
||||||
# crt should be synced
|
|
||||||
deck1.crt = 123
|
|
||||||
deck1.setMod()
|
|
||||||
deck1.save(mod=deck1.mod+1)
|
|
||||||
ret = client.sync(); assert ret == "success"
|
|
||||||
assert deck1.crt == deck2.crt
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_models():
|
|
||||||
test_sync()
|
|
||||||
# update model one
|
|
||||||
cm = deck1.models.current()
|
|
||||||
cm['name'] = "new"
|
|
||||||
time.sleep(1)
|
|
||||||
deck1.models.save(cm)
|
|
||||||
deck1.save()
|
|
||||||
assert deck2.models.get(cm['id'])['name'].startswith("Basic")
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert deck2.models.get(cm['id'])['name'] == "new"
|
|
||||||
# deleting triggers a full sync
|
|
||||||
deck1.scm = deck2.scm = 0
|
|
||||||
deck1.models.rem(cm)
|
|
||||||
deck1.save()
|
|
||||||
assert client.sync() == "fullSync"
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_notes():
|
|
||||||
test_sync()
|
|
||||||
# modifications should be synced
|
|
||||||
nid = deck1.db.scalar("select id from notes")
|
|
||||||
note = deck1.getNote(nid)
|
|
||||||
assert note['Front'] != "abc"
|
|
||||||
note['Front'] = "abc"
|
|
||||||
note.flush()
|
|
||||||
deck1.save()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert deck2.getNote(nid)['Front'] == "abc"
|
|
||||||
# deletions too
|
|
||||||
assert deck1.db.scalar("select 1 from notes where id = ?", nid)
|
|
||||||
deck1.remNotes([nid])
|
|
||||||
deck1.save()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert not deck1.db.scalar("select 1 from notes where id = ?", nid)
|
|
||||||
assert not deck2.db.scalar("select 1 from notes where id = ?", nid)
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_cards():
|
|
||||||
test_sync()
|
|
||||||
nid = deck1.db.scalar("select id from notes")
|
|
||||||
note = deck1.getNote(nid)
|
|
||||||
card = note.cards()[0]
|
|
||||||
# answer the card locally
|
|
||||||
card.startTimer()
|
|
||||||
deck1.sched.answerCard(card, 4)
|
|
||||||
assert card.reps == 2
|
|
||||||
deck1.save()
|
|
||||||
assert deck2.getCard(card.id).reps == 1
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert deck2.getCard(card.id).reps == 2
|
|
||||||
# if it's modified on both sides , later mod time should win
|
|
||||||
for test in ((deck1, deck2), (deck2, deck1)):
|
|
||||||
time.sleep(1)
|
|
||||||
c = test[0].getCard(card.id)
|
|
||||||
c.reps = 5; c.flush()
|
|
||||||
test[0].save()
|
|
||||||
time.sleep(1)
|
|
||||||
c = test[1].getCard(card.id)
|
|
||||||
c.reps = 3; c.flush()
|
|
||||||
test[1].save()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert test[1].getCard(card.id).reps == 3
|
|
||||||
assert test[0].getCard(card.id).reps == 3
|
|
||||||
# removals should work too
|
|
||||||
deck1.remCards([card.id])
|
|
||||||
deck1.save()
|
|
||||||
assert deck2.db.scalar("select 1 from cards where id = ?", card.id)
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert not deck2.db.scalar("select 1 from cards where id = ?", card.id)
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_tags():
|
|
||||||
test_sync()
|
|
||||||
def sortedTags(deck):
|
|
||||||
return sorted(deck.tags.all())
|
|
||||||
assert sortedTags(deck1) == sortedTags(deck2)
|
|
||||||
deck1.tags.register(["abc"])
|
|
||||||
deck2.tags.register(["xyz"])
|
|
||||||
assert sortedTags(deck1) != sortedTags(deck2)
|
|
||||||
deck1.save()
|
|
||||||
time.sleep(0.1)
|
|
||||||
deck2.save()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert sortedTags(deck1) == sortedTags(deck2)
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_decks():
|
|
||||||
test_sync()
|
|
||||||
assert len(deck1.decks.all()) == 1
|
|
||||||
assert len(deck1.decks.all()) == len(deck2.decks.all())
|
|
||||||
deck1.decks.id("new")
|
|
||||||
assert len(deck1.decks.all()) != len(deck2.decks.all())
|
|
||||||
time.sleep(0.1)
|
|
||||||
deck2.decks.id("new2")
|
|
||||||
deck1.save()
|
|
||||||
time.sleep(0.1)
|
|
||||||
deck2.save()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert sorted(deck1.tags.all()) == sorted(deck2.tags.all())
|
|
||||||
assert len(deck1.decks.all()) == len(deck2.decks.all())
|
|
||||||
assert len(deck1.decks.all()) == 3
|
|
||||||
assert deck1.decks.confForDid(1)['maxTaken'] == 60
|
|
||||||
deck2.decks.confForDid(1)['maxTaken'] = 30
|
|
||||||
deck2.decks.save(deck2.decks.confForDid(1))
|
|
||||||
deck2.save()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert deck1.decks.confForDid(1)['maxTaken'] == 30
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_conf():
|
|
||||||
test_sync()
|
|
||||||
assert deck2.conf['curDeck'] == 1
|
|
||||||
deck1.conf['curDeck'] = 2
|
|
||||||
time.sleep(0.1)
|
|
||||||
deck1.setMod()
|
|
||||||
deck1.save()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert deck2.conf['curDeck'] == 2
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_threeway():
|
|
||||||
test_sync()
|
|
||||||
deck1.close(save=False)
|
|
||||||
d3path = deck1.path.replace(".anki", "2.anki")
|
|
||||||
shutil.copy2(deck1.path, d3path)
|
|
||||||
deck1.reopen()
|
|
||||||
deck3 = aopen(d3path)
|
|
||||||
client2 = Syncer(deck3, server)
|
|
||||||
assert client2.sync() == "noChanges"
|
|
||||||
# client 1 adds a card at time 1
|
|
||||||
time.sleep(1)
|
|
||||||
f = deck1.newNote()
|
|
||||||
f['Front'] = "1";
|
|
||||||
deck1.addNote(f)
|
|
||||||
deck1.save()
|
|
||||||
# at time 2, client 2 syncs to server
|
|
||||||
time.sleep(1)
|
|
||||||
deck3.setMod()
|
|
||||||
deck3.save()
|
|
||||||
assert client2.sync() == "success"
|
|
||||||
# at time 3, client 1 syncs, adding the older note
|
|
||||||
time.sleep(1)
|
|
||||||
assert client.sync() == "success"
|
|
||||||
assert deck1.noteCount() == deck2.noteCount()
|
|
||||||
# syncing client2 should pick it up
|
|
||||||
assert client2.sync() == "success"
|
|
||||||
assert deck1.noteCount() == deck2.noteCount() == deck3.noteCount()
|
|
||||||
|
|
||||||
def test_threeway2():
|
|
||||||
# for this test we want ms precision of notes so we don't have to
|
|
||||||
# sleep a lot
|
|
||||||
import anki.notes
|
|
||||||
intTime = anki.notes.intTime
|
|
||||||
anki.notes.intTime = lambda x=1: intTime(1000)
|
|
||||||
def setup():
|
|
||||||
# create collection 1 with a single note
|
|
||||||
c1 = getEmptyCol()
|
|
||||||
f = c1.newNote()
|
|
||||||
f['Front'] = "startingpoint"
|
|
||||||
nid = f.id
|
|
||||||
c1.addNote(f)
|
|
||||||
cid = f.cards()[0].id
|
|
||||||
c1.beforeUpload()
|
|
||||||
# start both clients and server off in this state
|
|
||||||
s1path = c1.path.replace(".anki2", "-s1.anki2")
|
|
||||||
c2path = c1.path.replace(".anki2", "-c2.anki2")
|
|
||||||
shutil.copy2(c1.path, s1path)
|
|
||||||
shutil.copy2(c1.path, c2path)
|
|
||||||
# open them
|
|
||||||
c1 = Collection(c1.path)
|
|
||||||
c2 = Collection(c2path)
|
|
||||||
s1 = Collection(s1path, server=True)
|
|
||||||
return c1, c2, s1, nid, cid
|
|
||||||
c1, c2, s1, nid, cid = setup()
|
|
||||||
# modify c1 then sync c1->s1
|
|
||||||
n = c1.getNote(nid)
|
|
||||||
t = "firstmod"
|
|
||||||
n['Front'] = t
|
|
||||||
n.flush()
|
|
||||||
c1.db.execute("update cards set mod=1, usn=-1")
|
|
||||||
srv = LocalServer(s1)
|
|
||||||
clnt1 = Syncer(c1, srv)
|
|
||||||
clnt1.sync()
|
|
||||||
n.load()
|
|
||||||
assert n['Front'] == t
|
|
||||||
assert s1.getNote(nid)['Front'] == t
|
|
||||||
assert s1.db.scalar("select mod from cards") == 1
|
|
||||||
# sync s1->c2
|
|
||||||
clnt2 = Syncer(c2, srv)
|
|
||||||
clnt2.sync()
|
|
||||||
assert c2.getNote(nid)['Front'] == t
|
|
||||||
assert c2.db.scalar("select mod from cards") == 1
|
|
||||||
# modify c1 and sync
|
|
||||||
time.sleep(0.001)
|
|
||||||
t = "secondmod"
|
|
||||||
n = c1.getNote(nid)
|
|
||||||
n['Front'] = t
|
|
||||||
n.flush()
|
|
||||||
c1.db.execute("update cards set mod=2, usn=-1")
|
|
||||||
clnt1.sync()
|
|
||||||
# modify c2 and sync - both c2 and server should be the same
|
|
||||||
time.sleep(0.001)
|
|
||||||
t2 = "thirdmod"
|
|
||||||
n = c2.getNote(nid)
|
|
||||||
n['Front'] = t2
|
|
||||||
n.flush()
|
|
||||||
c2.db.execute("update cards set mod=3, usn=-1")
|
|
||||||
clnt2.sync()
|
|
||||||
n.load()
|
|
||||||
assert n['Front'] == t2
|
|
||||||
assert c2.db.scalar("select mod from cards") == 3
|
|
||||||
n = s1.getNote(nid)
|
|
||||||
assert n['Front'] == t2
|
|
||||||
assert s1.db.scalar("select mod from cards") == 3
|
|
||||||
# and syncing c1 again should yield the updated note as well
|
|
||||||
clnt1.sync()
|
|
||||||
n = s1.getNote(nid)
|
|
||||||
assert n['Front'] == t2
|
|
||||||
assert s1.db.scalar("select mod from cards") == 3
|
|
||||||
n = c1.getNote(nid)
|
|
||||||
assert n['Front'] == t2
|
|
||||||
assert c1.db.scalar("select mod from cards") == 3
|
|
||||||
|
|
||||||
def _test_speed():
|
|
||||||
t = time.time()
|
|
||||||
deck1 = aopen(os.path.expanduser("~/rapid.anki"))
|
|
||||||
for tbl in "revlog", "cards", "notes", "graves":
|
|
||||||
deck1.db.execute("update %s set usn = -1 where usn != -1"%tbl)
|
|
||||||
for m in deck1.models.all():
|
|
||||||
m['usn'] = -1
|
|
||||||
for tx in deck1.tags.all():
|
|
||||||
deck1.tags.tags[tx] = -1
|
|
||||||
deck1._usn = -1
|
|
||||||
deck1.save()
|
|
||||||
deck2 = getEmptyDeckWith(server=True)
|
|
||||||
deck1.scm = deck2.scm = 0
|
|
||||||
server = LocalServer(deck2)
|
|
||||||
client = Syncer(deck1, server)
|
|
||||||
print("load %d" % ((time.time() - t)*1000)); t = time.time()
|
|
||||||
assert client.sync() == "success"
|
|
||||||
print("sync %d" % ((time.time() - t)*1000)); t = time.time()
|
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
|
||||||
def test_filtered_delete():
|
|
||||||
test_sync()
|
|
||||||
nid = deck1.db.scalar("select id from notes")
|
|
||||||
note = deck1.getNote(nid)
|
|
||||||
card = note.cards()[0]
|
|
||||||
card.queue = 2
|
|
||||||
card.type = 2
|
|
||||||
card.ivl = 10
|
|
||||||
card.factor = STARTING_FACTOR
|
|
||||||
card.due = deck1.sched.today
|
|
||||||
card.flush()
|
|
||||||
# put cards into a filtered deck
|
|
||||||
did = deck1.decks.newDyn("dyn")
|
|
||||||
deck1.sched.rebuildDyn(did)
|
|
||||||
# sync the filtered deck
|
|
||||||
assert client.sync() == "success"
|
|
||||||
# answer the card locally
|
|
||||||
time.sleep(1)
|
|
||||||
card.load()
|
|
||||||
card.startTimer()
|
|
||||||
deck1.sched.answerCard(card, 4)
|
|
||||||
assert card.ivl > 10
|
|
||||||
# delete the filtered deck
|
|
||||||
deck1.decks.rem(did)
|
|
||||||
# sync again
|
|
||||||
assert client.sync() == "success"
|
|
||||||
card.load()
|
|
||||||
assert card.ivl > 10
|
|
||||||
return
|
|
Loading…
Reference in a new issue