Anki/aqt/upgrade.py

236 lines
8.2 KiB
Python

# Copyright: Damien Elmes <anki@ichi2.net>
# -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import os, cPickle
from aqt.qt import *
from anki.utils import isMac, isWin
from anki import Collection
from anki.importing import Anki1Importer
class Upgrader(object):
def __init__(self, mw):
self.mw = mw
def maybeUpgrade(self):
p = self._oldConfigPath()
# does an old config file exist?
if not os.path.exists(p):
return
# load the new deck user profile
self.mw.pm.load(self.mw.pm.profiles()[0])
# load old settings and copy over
self._loadConf(p)
self._copySettings()
# and show the wizard
self._showWizard()
# Settings
######################################################################
def _oldConfigPath(self):
if isWin:
p = "~/.anki/config.db"
elif isMac:
p = "~/Library/Application Support/Anki/config.db"
else:
p = "~/.anki/config.db"
return os.path.expanduser(p)
def _loadConf(self, path):
self.conf = cPickle.load(open(path))
def _copySettings(self):
p = self.mw.pm.profile
for k in (
"recentColours", "stripHTML", "editFontFamily", "editFontSize",
"editLineSize", "deleteMedia", "preserveKeyboard", "numBackups",
"proxyHost", "proxyPass", "proxyPort", "proxyUser",
"showProgress"):
p[k] = self.conf[k]
p['autoplay'] = self.conf['autoplaySounds']
p['showDueTimes'] = not self.conf['suppressEstimates']
self.mw.pm.save()
# Wizard
######################################################################
def _showWizard(self):
class Wizard(QWizard):
def reject(self):
pass
self.wizard = w = Wizard()
w.addPage(self._welcomePage())
w.addPage(self._decksPage())
w.addPage(self._mediaPage())
w.addPage(self._readyPage())
w.addPage(self._upgradePage())
w.addPage(self._finishedPage())
w.setWindowTitle("Upgrade Wizard")
w.setWizardStyle(QWizard.ModernStyle)
w.exec_()
def _labelPage(self, title, txt):
p = QWizardPage()
p.setTitle(title)
l = QLabel(txt)
l.setTextFormat(Qt.RichText)
l.setTextInteractionFlags(Qt.TextSelectableByMouse)
l.setWordWrap(True)
v = QVBoxLayout()
v.addWidget(l)
p.setLayout(v)
return p
def _welcomePage(self):
return self._labelPage(_("Welcome"), _("""\
This wizard will guide you through the Anki 2.0 upgrade process.
For a smooth upgrade, please read the following pages carefully.
"""))
def _decksPage(self):
return self._labelPage(_("Your Decks"), _("""\
Anki 2 stores your decks in a new format. This wizard will automatically
convert your decks to that format. Your decks will be backed up before
the upgrade, so if you need to revert to the previous version of Anki, your
decks will still be usable."""))
def _mediaPage(self):
return self._labelPage(_("Sounds & Images"), _("""\
When your decks are upgraded, Anki will attempt to copy any sounds and images
from the old decks. If you were using a custom DropBox folder or custom media
folder, the upgrade process may not be able to locate your media. Later on, a
report of the upgrade will be presented to you. If you notice media was not
copied when it should have been, please see the upgrade guide for more
instructions.
<p>
AnkiWeb now supports media syncing directly. No special setup is required, and
media will be synchronized along with your cards when you sync to AnkiWeb."""))
def _readyPage(self):
class ReadyPage(QWizardPage):
def initializePage(self):
self.setTitle(_("Ready to Upgrade"))
self.setCommitPage(True)
l = QLabel(_("""\
When you're ready to upgrade, click the commit button to continue. The upgrade
guide will open in your browser while the upgrade proceeds. Please read it
carefully, as a lot has changed since the previous Anki version."""))
l.setTextFormat(Qt.RichText)
l.setTextInteractionFlags(Qt.TextSelectableByMouse)
l.setWordWrap(True)
v = QVBoxLayout()
v.addWidget(l)
self.setLayout(v)
return ReadyPage()
def _upgradePage(self):
decks = self.conf['recentDeckPaths']
colpath = self.mw.pm.collectionPath()
upgrader = self
class UpgradePage(QWizardPage):
def isComplete(self):
return False
def initializePage(self):
self.setTitle(_("Upgrading"))
self.label = l = QLabel()
l.setTextInteractionFlags(Qt.TextSelectableByMouse)
l.setWordWrap(True)
v = QVBoxLayout()
v.addWidget(l)
prog = QProgressBar()
prog.setMaximum(0)
v.addWidget(prog)
l2 = QLabel(_("Please be patient; this can take a while."))
l2.setTextInteractionFlags(Qt.TextSelectableByMouse)
l2.setWordWrap(True)
v.addWidget(l2)
self.setLayout(v)
# run the upgrade in a different thread
self.thread = UpgradeThread(decks, colpath)
self.thread.start()
# and periodically update the GUI
self.timer = QTimer(self)
self.timer.connect(self.timer, SIGNAL("timeout()"), self.onTimer)
self.timer.start(1000)
self.onTimer()
def onTimer(self):
prog = self.thread.progress()
if not prog:
self.timer.stop()
upgrader.log = self.thread.log
upgrader.wizard.next()
self.label.setText(prog)
return UpgradePage()
def _finishedPage(self):
upgrader = self
class FinishedPage(QWizardPage):
def initializePage(self):
buf = ""
for file in upgrader.log:
buf += "<b>%s</b>" % file[0]
buf += "<ul><li>" + "<li>".join(file[1]) + "</ul><p>"
self.setTitle(_("Upgrade Complete"))
l = QLabel(_("""\
The upgrade has finished, and you're ready to start using Anki 2.0.
<p>
Below is a log of the update:
<p>
%s<br><br>""") % buf)
l.setTextFormat(Qt.RichText)
l.setTextInteractionFlags(Qt.TextSelectableByMouse)
l.setWordWrap(True)
l.setMaximumWidth(400)
a = QScrollArea()
a.setWidget(l)
v = QVBoxLayout()
v.addWidget(a)
self.setLayout(v)
return FinishedPage()
class UpgradeThread(QThread):
def __init__(self, paths, colpath):
QThread.__init__(self)
self.paths = paths
self.max = len(paths)
self.current = 1
self.finished = False
self.colpath = colpath
self.name = ""
self.log = []
def run(self):
# open profile deck
self.col = Collection(self.colpath)
# loop through paths
while True:
path = self.paths.pop()
self.name = os.path.basename(path)
self.upgrade(path)
# abort if finished
if not self.paths:
break
self.current += 1
self.col.close()
self.finished = True
def progress(self):
if self.finished:
return
return _("Upgrading deck %(a)s of %(b)s...\n%(c)s") % \
dict(a=self.current, b=self.max, c=self.name)
def upgrade(self, path):
log = self._upgrade(path)
self.log.append((self.name, log))
def _upgrade(self, path):
if not os.path.exists(path):
return [_("File was missing.")]
imp = Anki1Importer(self.col, path)
try:
imp.run()
except Exception, e:
if unicode(e) == "invalidFile":
# already logged
pass
self.col.save()
return imp.log