diff --git a/anki/collection.py b/anki/collection.py index a468eaca5..dcb74c7b7 100644 --- a/anki/collection.py +++ b/anki/collection.py @@ -2,7 +2,13 @@ # Copyright: Damien Elmes # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import time, os, random, stat, datetime, copy +import time +import os +import random +import stat +import datetime +import copy + from anki.lang import _, ngettext from anki.utils import ids2str, fieldChecksum, stripHTML, \ intTime, splitFields, joinFields, maxID, json @@ -15,9 +21,12 @@ from anki.tags import TagManager from anki.consts import * from anki.errors import AnkiError from anki.sound import stripSounds - import anki.latex # sets up hook -import anki.cards, anki.notes, anki.template, anki.find +import anki.cards +import anki.notes +import anki.template +import anki.find + defaultConf = { # review options @@ -176,15 +185,20 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""", def beforeUpload(self): "Called before a full upload." - tbls = "notes", "cards", "revlog", "graves" + tbls = "notes", "cards", "revlog" for t in tbls: self.db.execute("update %s set usn=0 where usn=-1" % t) + # we can save space by removing the log of deletions + self.db.execute("delete from graves") self._usn += 1 self.models.beforeUpload() self.tags.beforeUpload() self.decks.beforeUpload() self.modSchema() self.ls = self.scm + # ensure db is compacted before upload + self.db.execute("vacuum") + self.db.execute("analyze") self.close() # Object creation helpers diff --git a/anki/consts.py b/anki/consts.py index 682314a23..1b219941b 100644 --- a/anki/consts.py +++ b/anki/consts.py @@ -47,7 +47,7 @@ SCHEMA_VERSION = 11 SYNC_ZIP_SIZE = int(2.5*1024*1024) SYNC_ZIP_COUNT = 100 SYNC_URL = os.environ.get("SYNC_URL") or "https://ankiweb.net/sync/" -SYNC_VER = 7 +SYNC_VER = 8 HELP_SITE="http://ankisrs.net/docs/manual.html" diff --git a/anki/exporting.py b/anki/exporting.py index 0d711d146..cc12e3d5a 100644 --- a/anki/exporting.py +++ b/anki/exporting.py @@ -152,7 +152,8 @@ class AnkiExporter(Exporter): else: # need to reset card state self.dst.sched.resetCards(cids) - # models + # models - start with zero + self.dst.models.models = {} for m in self.src.models.all(): if int(m['id']) in mids: self.dst.models.update(m) diff --git a/anki/models.py b/anki/models.py index fade814b9..8ad5b158d 100644 --- a/anki/models.py +++ b/anki/models.py @@ -124,8 +124,8 @@ class ModelManager(object): "Get all models." return self.models.values() - def allNames(self, curm=None): - return [m['name'] for m in self.all() if m!=curm] + def allNames(self): + return [m['name'] for m in self.all()] def byName(self, name): "Get model with NAME." @@ -171,6 +171,7 @@ select id from cards where nid in (select id from notes where mid = ?)""", if (mcur['name'] == m['name'] and mcur['id'] != m['id']): m['name'] += "-" + checksum(str(time.time()))[:5] + break def update(self, m): "Add or update an existing model. Used for syncing and merging." diff --git a/anki/sync.py b/anki/sync.py index c854ea249..e8a53a002 100644 --- a/anki/sync.py +++ b/anki/sync.py @@ -2,10 +2,16 @@ # Copyright: Damien Elmes # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import urllib, os, sys, httplib2, gzip +import urllib +import os +import sys +import gzip +import random from cStringIO import StringIO + +import httplib2 from anki.db import DB -from anki.utils import ids2str, intTime, json, isWin, isMac, platDesc +from anki.utils import ids2str, intTime, json, isWin, isMac, platDesc, checksum from anki.consts import * from hooks import runHook import anki @@ -519,6 +525,7 @@ class HttpSyncer(object): def __init__(self, hkey=None, con=None): self.hkey = hkey + self.skey = checksum(str(random.random()))[:8] self.con = con or httpCon() def assertOk(self, resp): @@ -541,6 +548,7 @@ class HttpSyncer(object): vars['c'] = 1 if comp else 0 if hkey: vars['k'] = self.hkey + vars['s'] = self.skey for (key, value) in vars.items(): buf.write(bdry + "\r\n") buf.write( diff --git a/aqt/browser.py b/aqt/browser.py index b9f18b971..91c4b5644 100644 --- a/aqt/browser.py +++ b/aqt/browser.py @@ -236,7 +236,7 @@ class DataModel(QAbstractTableModel): elif type == "cardLapses": return str(c.lapses) elif type == "noteTags": - return str(" ".join(c.note().tags)) + return " ".join(c.note().tags) elif type == "note": return c.model()['name'] elif type == "cardIvl": diff --git a/aqt/sync.py b/aqt/sync.py index b8a29a39a..9660ea436 100644 --- a/aqt/sync.py +++ b/aqt/sync.py @@ -2,8 +2,12 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from __future__ import division +import socket +import time +import traceback +import gc + from aqt.qt import * -import socket, time, traceback, gc import aqt from anki import Collection from anki.sync import Syncer, RemoteServer, FullSyncer, MediaSyncer, \ @@ -11,6 +15,7 @@ from anki.sync import Syncer, RemoteServer, FullSyncer, MediaSyncer, \ from anki.hooks import addHook, remHook from aqt.utils import tooltip, askUserDialog, showWarning, showText, showInfo + # Sync manager ###################################################################### @@ -161,7 +166,7 @@ AnkiWeb is too busy at the moment. Please try again in a few minutes.""") elif "504" in err: return _("504 gateway timeout error received. Please try temporarily disabling your antivirus.") elif "409" in err: - return _("A previous sync failed; please try again in a few minutes.") + return _("Only one client can access AnkiWeb at a time. If a previous sync failed, please try again in a few minutes.") elif "10061" in err or "10013" in err: return _( "Antivirus or firewall software is preventing Anki from connecting to the internet.")