mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
151 lines
4.5 KiB
Python
151 lines
4.5 KiB
Python
# Copyright: Damien Elmes <anki@ichi2.net>
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
# Profile handling
|
|
##########################################################################
|
|
# - Saves in pickles rather than json to easily store Qt window state.
|
|
# - Saves in sqlite rather than a flat file so the config can't be corrupted
|
|
|
|
from aqt.qt import *
|
|
import os, sys, time, random, cPickle, re
|
|
from anki.db import DB
|
|
from anki.utils import isMac, isWin, intTime
|
|
|
|
metaConf = dict(
|
|
ver=0,
|
|
updates=True,
|
|
created=intTime(),
|
|
id=random.randrange(0, 2**63),
|
|
lastMsg=-1,
|
|
suppressUpdate=False,
|
|
)
|
|
|
|
profileConf = dict(
|
|
# profile
|
|
key=None,
|
|
mainWindowGeom=None,
|
|
mainWindowState=None,
|
|
numBackups=30,
|
|
lang="en",
|
|
|
|
# editing
|
|
fullSearch=False,
|
|
searchHistory=[],
|
|
recentColours=["#000000", "#0000ff"],
|
|
stripHTML=True,
|
|
editFontFamily='Arial',
|
|
editFontSize=12,
|
|
editLineSize=20,
|
|
deleteMedia=False,
|
|
preserveKeyboard=True,
|
|
|
|
# reviewing
|
|
autoplay=True,
|
|
showDueTimes=True,
|
|
showProgress=True,
|
|
|
|
# syncing
|
|
syncKey=None,
|
|
proxyHost='',
|
|
proxyPass='',
|
|
proxyPort=8080,
|
|
proxyUser='',
|
|
)
|
|
|
|
class ProfileManager(object):
|
|
|
|
def __init__(self, base=None, profile=None):
|
|
self.name = None
|
|
# instantiate base folder
|
|
if not base:
|
|
base = self._defaultBase()
|
|
if not os.path.exists(base):
|
|
try:
|
|
os.makedirs(base)
|
|
except:
|
|
QMessageBox.critical(
|
|
None, "Error", """\
|
|
Anki can't write to the harddisk. Please see the \
|
|
documentation for information on using a flash drive.""")
|
|
raise
|
|
self.base = base
|
|
# load database and cmdline-provided profile
|
|
self._load()
|
|
if profile:
|
|
try:
|
|
self.load(profile)
|
|
except TypeError:
|
|
raise Exception("Provided profile does not exist.")
|
|
|
|
# Profile load/save
|
|
######################################################################
|
|
|
|
def profiles(self):
|
|
return [x for x in self.db.scalar("select name from profiles")
|
|
if x != "_global"]
|
|
|
|
def load(self, name):
|
|
self.name = name
|
|
self.idself.profile = cPickle.loads(
|
|
self.db.scalar("select oid, data from profiles where name = ?", name))
|
|
|
|
def save(self):
|
|
sql = "update profiles set data = ? where name = ?"
|
|
self.db.execute(sql, cPickle.dumps(self.profile), self.name)
|
|
self.db.execute(sql, cPickle.dumps(self.meta, "_global"))
|
|
|
|
def create(self, name):
|
|
assert re.match("^[A-Za-z0-9 ]+$", name)
|
|
self.db.execute("insert into profiles values (?, ?)",
|
|
name, cPickle.dumps(profileConf))
|
|
|
|
# Folder handling
|
|
######################################################################
|
|
|
|
def profileFolder(self):
|
|
return self._ensureExists(os.path.join(self.base, self.name))
|
|
|
|
def addonFolder(self):
|
|
return self._ensureExists(os.path.join(self.base, "addons"))
|
|
|
|
def backupFolder(self):
|
|
return self._ensureExists(
|
|
os.path.join(self.profileFolder(), "backups"))
|
|
|
|
def collectionPath(self):
|
|
return os.path.join(self.profileFolder(), "collection.anki2")
|
|
|
|
# Helpers
|
|
######################################################################
|
|
|
|
def _ensureExists(self, path):
|
|
if not exists(path):
|
|
os.makedirs(path)
|
|
return path
|
|
|
|
def _defaultBase(self):
|
|
if isWin:
|
|
s = QSettings(QSettings.UserScope, "Microsoft", "Windows")
|
|
s.beginGroup("CurrentVersion/Explorer/Shell Folders")
|
|
d = s.value("Personal")
|
|
return os.path.join(d, "Anki")
|
|
elif isMac:
|
|
return os.path.expanduser("~/Documents/Anki")
|
|
else:
|
|
return os.path.expanduser("~/Anki")
|
|
|
|
def _load(self):
|
|
path = os.path.join(self.base, "prefs.db")
|
|
self.db = DB(path, text=str)
|
|
self.db.execute("""
|
|
create table if not exists profiles
|
|
(name text primary key, data text not null);""")
|
|
try:
|
|
self.meta = self.loadProfile("_global")
|
|
except:
|
|
# create a default global profile
|
|
self.meta = metaConf.copy()
|
|
self.db.execute("insert into profiles values ('_global', ?)",
|
|
cPickle.dumps(metaConf))
|
|
# and save a default user profile for later (commits)
|
|
self.create("User 1")
|