Anki/anki/storage.py
Damien Elmes b5c0b1f2c7 drop required/unique field properties
Instead of having required and unique flags for every field, enforce both
requirements on the first field, and neither on the rest. This mirrors the
subject/body format people are used to in note-taking apps. The subject
defines the object being learnt, and the remaining fields represent properties
of that object.

In the past, duplicate checking served two purposes: it quickly notified the
user that they're entering the same fact twice, and it notified the user if
they'd accidentally mistyped a secondary field. The former behaviour is
important for avoiding wasted effort, and so it should be done in real time.
The latter behaviour is not essential however - a typo is not wasted effort,
and it could be fixed in a periodic 'find duplicates' function. Given that
some users ended up with sluggish decks due to the overhead a large number of
facts * a large number of unique fields caused, this seems like a change for
the better.

This also means Anki will let you add notes as long as as the first field has
been filled out. Again, this is not a big deal: Anki is still checking to make
sure one or more cards will be generated, and the user can easily add any
missing fields later.

As a bonus, this change simplifies field configuration somewhat. As the card
layout and field dialogs are a popular point of confusion, the more they can
be simplified, the better.
2011-11-24 22:16:03 +09:00

178 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import os, simplejson
from anki.lang import _
from anki.utils import intTime
from anki.db import DB
from anki.collection import _Collection
from anki.consts import *
from anki.stdmodels import addBasicModel, addClozeModel
def Collection(path, queue=True, lock=True, server=False):
"Open a new or existing collection. Path must be unicode."
assert path.endswith(".anki2")
path = os.path.abspath(path)
create = not os.path.exists(path)
if create:
base = os.path.basename(path)
for c in ("/", ":", "\\"):
assert c not in base
# connect
db = DB(path)
if create:
ver = _createDB(db)
else:
ver = _upgradeSchema(db)
db.execute("pragma temp_store = memory")
db.execute("pragma cache_size = 10000")
# add db to col and do any remaining upgrades
col = _Collection(db, server)
if ver < SCHEMA_VERSION:
_upgrade(col, ver)
elif create:
# add in reverse order so basic is default
addClozeModel(col)
addBasicModel(col)
col.save()
if lock:
col.lock()
if not queue:
return col
# rebuild queue
col.reset()
return col
# no upgrades necessary at the moment
def _upgradeSchema(db):
return SCHEMA_VERSION
def _upgrade(col, ver):
return
# Creating a new collection
######################################################################
def _createDB(db):
db.execute("pragma page_size = 4096")
db.execute("pragma legacy_file_format = 0")
db.execute("vacuum")
_addSchema(db)
_updateIndices(db)
db.execute("analyze")
return SCHEMA_VERSION
def _addSchema(db, setColConf=True):
db.executescript("""
create table if not exists col (
id integer primary key,
crt integer not null,
mod integer not null,
scm integer not null,
ver integer not null,
dty integer not null,
usn integer not null,
ls integer not null,
conf text not null,
models text not null,
decks text not null,
dconf text not null,
tags text not null
);
create table if not exists notes (
id integer primary key,
guid integer not null,
mid integer not null,
did integer not null,
mod integer not null,
usn integer not null,
tags text not null,
flds text not null,
sfld integer not null,
csum integer not null,
flags integer not null,
data text not null
);
create table if not exists cards (
id integer primary key,
nid integer not null,
did integer not null,
ord integer not null,
mod integer not null,
usn integer not null,
type integer not null,
queue integer not null,
due integer not null,
ivl integer not null,
factor integer not null,
reps integer not null,
lapses integer not null,
left integer not null,
edue integer not null,
flags integer not null,
data text not null
);
create table if not exists revlog (
id integer primary key,
cid integer not null,
usn integer not null,
ease integer not null,
ivl integer not null,
lastIvl integer not null,
factor integer not null,
time integer not null,
type integer not null
);
create table if not exists graves (
usn integer not null,
oid integer not null,
type integer not null
);
insert or ignore into col
values(1,0,0,0,%(v)s,0,0,0,'','{}','','','{}');
""" % ({'v':SCHEMA_VERSION}))
if setColConf:
_addColVars(db, *_getColVars(db))
def _getColVars(db):
import anki.collection
import anki.decks
g = anki.decks.defaultDeck.copy()
for k,v in anki.decks.defaultTopConf.items():
g[k] = v
g['id'] = 1
g['name'] = _("Default")
g['conf'] = 1
g['mod'] = intTime()
gc = anki.decks.defaultConf.copy()
gc['id'] = 1
return g, gc, anki.collection.defaultConf.copy()
def _addColVars(db, g, gc, c):
db.execute("""
update col set conf = ?, decks = ?, dconf = ?""",
simplejson.dumps(c),
simplejson.dumps({'1': g}),
simplejson.dumps({'1': gc}))
def _updateIndices(db):
"Add indices to the DB."
db.executescript("""
-- syncing
create index if not exists ix_notes_usn on notes (usn);
create index if not exists ix_cards_usn on cards (usn);
create index if not exists ix_revlog_usn on revlog (usn);
-- card spacing, etc
create index if not exists ix_cards_nid on cards (nid);
-- scheduling and deck limiting
create index if not exists ix_cards_sched on cards (did, queue, due);
-- revlog by card
create index if not exists ix_revlog_cid on revlog (cid);
-- field uniqueness
create index if not exists ix_notes_csum on notes (csum);
""")