Commit graph

333 commits

Author SHA1 Message Date
Damien Elmes
d3a3edb707 move models into the deck table
Like the previous change, models have been moved from a separate DB table to
an entry in the deck. We need them for many operations including reviewing,
and it's easier to keep them in memory than half on disk with a cache that
gets cleared every time we .reset(). This means they are easily serialized as
well - previously they were part Python and part JSON, which made access
confusing.

Because the data is all pulled from JSON now, the instance methods have been
moved to the model registry. Eg:
  model.addField(...) -> deck.models.addField(model, ...).

- IDs are now timestamped as with groups et al.

- The data field for plugins was also removed. Config info can be added to
  deck.conf; larger data should be stored externally.

- Upgrading needs to be updated for the new model structure.

- HexifyID() now accepts strings as well, as our IDs get converted to strings
  in the serialization process.
2011-08-27 22:27:09 +09:00
Damien Elmes
7afe6a9a7d convert groups to json; use timestamp ids for all but default
Rather than use a combination of id lookups on the groups table and a group
configuration cache in the scheduler, I've moved the groups and group config
into json objects on the deck table. This results in a net saving of code and
saves one or more DB lookups on each card answer, in exchange for a small
increase in deck load/save work.

I did a quick survey of AnkiWeb, and the vast majority of decks use less than
100 tags, and it's safe to assume groups will follow a similar pattern.

All groups and group configs except the default one will use integer
timestamps now, to simplify merging when syncing and importing.

defaultGroup() has been removed in favour of keeping the models up to date
(not yet done).
2011-08-27 17:13:04 +09:00
Damien Elmes
47be8b0546 use the timestamps instead of forcing id on fact/card creation
- we ditch nextCid/nextFid as we don't need incrementing ids anymore
- we add nextPos so we can maintain a user-friendly position number
2011-08-27 00:36:39 +09:00
Damien Elmes
f7b89c9fa1 ensure unique id on per-object add, too 2011-08-26 22:51:08 +09:00
Damien Elmes
ebac628187 ensure duplicate model creation times are accounted for 2011-08-26 22:33:24 +09:00
Damien Elmes
5868ff52b9 handle duplicate fact creation times 2011-08-26 22:28:35 +09:00
Damien Elmes
644a885a07 update fact ids, graves
- should never skip recording graves, for the sake of merging
- 1.0 upgrade will fail on decks that have the same fact creation date. need
      to work around this in the future
2011-08-26 21:23:16 +09:00
Damien Elmes
6644c04852 start work on id refactor - models first
The approach of using incrementing id numbers works for syncing if we assume
the server is canonical and all other clients rewrite their ids as necessary,
but upon reflection it is not sufficient for merging decks in general, as we
have no way of knowing whether objects with the same id are actually the same
or not. So we need some way of uniquely identifying the object.

One approach would be to go back to Anki 1.0's random 64bit numbers, but as
outlined in a previous commit such large numbers can't be handled easy in some
languages like Javascript, and they tend to be fragmented on disk which
impacts performance. It's much better if we can keep content added at the same
time in the same place on disk, so that operations like syncing which are mainly
interested in newly added content can run faster.

Another approach is to add a separate column containing the unique id, which
is what Mnemosyne 2.0 will be doing. Unfortunately it means adding an index
for that column, leading to slower inserts and larger deck files. And if the
current sequential ids are kept, a bunch of code needs to be kept to ensure ids
don't conflict when merging.

To address the above, the plan is to use a millisecond timestamp as the id.
This ensures disk order reflects creation order, allows us to merge the id and
crt columns, avoids the need for a separate index, and saves us from worrying
about rewriting ids. There is of course a small chance that the objects to be
merged were created at exactly the same time, but this is extremely unlikely.

This commit changes models. Other objects will follow.
2011-08-26 21:08:30 +09:00
Damien Elmes
91efb8f30b some initial sync work 2011-05-29 08:13:54 +09:00
Damien Elmes
0af03a6a8a only use deletion log when necessary 2011-05-07 21:45:55 +09:00
Damien Elmes
3d370f675b restore the deletion log
the initial plan was to zero the creation time and leave the cards/facts there
until we have a chance to garbage collect them on a schema change, but such an
approach won't work with deck subscriptions
2011-05-04 19:00:38 +09:00
Damien Elmes
a65f241258 gracefully handle invalid queries 2011-04-29 11:46:26 +09:00
Damien Elmes
ddcf83bc7f add [] to cloze 2011-04-29 11:43:06 +09:00
Damien Elmes
0bbdd722c2 very basic eta 2011-04-28 09:24:05 +09:00
Damien Elmes
19fd581839 change default lrn timing; leechAction doesn't need an array 2011-04-28 09:24:05 +09:00
Damien Elmes
754dcef4f7 handle the case where learning cards have a grade higher than their config 2011-04-28 09:24:05 +09:00
Damien Elmes
0d6064b933 rename() 2011-04-28 09:24:05 +09:00
Damien Elmes
47c30f172f fix issues suspending learning cards
- cards in final review are first reset as rev cards so that type==queue and
  they can be restored correctly
- new cards in learning have type set to 1 so they too can be restored
  correctly
2011-04-28 09:24:05 +09:00
Damien Elmes
434b5442fd fix unit tests 2011-04-28 09:24:05 +09:00
Damien Elmes
88c4f010d3 update rescheduling
like forgetCards(), we no longer adjust the stats or revlog for the card
2011-04-28 09:24:05 +09:00
Damien Elmes
8a9174fd4d we don't need rep in the revlog 2011-04-28 09:24:05 +09:00
Damien Elmes
7acdbfa9ae support for shifting 2011-04-28 09:24:04 +09:00
Damien Elmes
b69031cd4f generalize order/random 2011-04-28 09:24:04 +09:00
Damien Elmes
974324e3dd add unit test for order 2011-04-28 09:24:04 +09:00
Damien Elmes
82c3119c90 update randomizing/ordering code and forgetCards()
instead of completely resetting a card like we did in resetCards() in the
past, forgetCards() just puts the card back in the new queue and leaves the
factor and revlog alone. If users want to complete reset a card, they'll need to
export it.
2011-04-28 09:24:04 +09:00
Damien Elmes
0df95eafc0 revlog updates
- use negative numbers to denote second intervals
- record the rev ivl when leaving lrn queue
- improve revlog upgrade
- don't truncate precision when recording time taken
2011-04-28 09:24:04 +09:00
Damien Elmes
7d64036a07 drop streak, make reps log all entries
reps should now be equal to the number of entries in the revlog, and only
exists so that we can order by review count in the browser efficiently

streak is no longer necessary as we have a learn queue now
2011-04-28 09:24:04 +09:00
Damien Elmes
1c2b403348 apply selective groups to learning queue too
originally the plan was to get the user to "forget learning cards" or "remove
final drill" when switching between categories, but that's cumbersome and not
intuitive
2011-04-28 09:24:04 +09:00
Damien Elmes
2243b691cc add full search to ignore formatting 2011-04-28 09:24:03 +09:00
Damien Elmes
0fdb966766 wrap clozes in a classed span instead of bold 2011-04-28 09:24:03 +09:00
Damien Elmes
03d00228aa support stripping context of cloze 2011-04-28 09:24:03 +09:00
Damien Elmes
e8d6714130 make sure the failed card count reflects the cutoff 2011-04-28 09:24:03 +09:00
Damien Elmes
d51cd5a433 find & replace 2011-04-28 09:24:03 +09:00
Damien Elmes
98a63285e1 port changeModel() 2011-04-28 09:24:02 +09:00
Damien Elmes
d7b86da811 canonify tags after bulk update 2011-04-28 09:24:02 +09:00
Damien Elmes
93d80678f9 _fields -> fields 2011-04-28 09:24:02 +09:00
Damien Elmes
d611299715 make sure add/delTags() is limited to provided ids 2011-04-28 09:24:02 +09:00
Damien Elmes
8c1c729544 fix card state negation 2011-04-28 09:24:02 +09:00
Damien Elmes
13a484ea36 allow user to abort schema mod 2011-04-28 09:24:02 +09:00
Damien Elmes
2a225b1fae use the sort property set in the deck 2011-04-28 09:24:02 +09:00
Damien Elmes
d089deae5a add the ability to reverse sort order 2011-04-28 09:24:02 +09:00
Damien Elmes
d6116a5377 model and group searching 2011-04-28 09:24:02 +09:00
Damien Elmes
efe6177c7a refactor ordering 2011-04-28 09:24:02 +09:00
Damien Elmes
291bd399b7 field searching
dropped support for field:foo, as you can type 'foo:' instead to accomplish
the same thing
2011-04-28 09:24:01 +09:00
Damien Elmes
57938927e7 users can pass a number for template ordinal; makes show:one obsolete 2011-04-28 09:24:01 +09:00
Damien Elmes
94d4e319ae fids and template searches 2011-04-28 09:24:01 +09:00
Damien Elmes
de81f0238a template moving 2011-04-28 09:24:01 +09:00
Damien Elmes
84d2f32685 move graph code into stats.py; remove old deck stats 2011-04-28 09:24:00 +09:00
Damien Elmes
4be8b9d38c fix zerodiv and other errors 2011-04-28 09:24:00 +09:00
Damien Elmes
11f3de525f groupConf() takes gcid, not gid 2011-04-28 09:23:59 +09:00
Damien Elmes
692fba2ea3 use the deck's groups instead of holding on to a private copy 2011-04-28 09:23:59 +09:00
Damien Elmes
77ee8f1385 ditch useGroups 2011-04-28 09:23:59 +09:00
Damien Elmes
f75e2af195 use a single group setting 2011-04-28 09:23:59 +09:00
Damien Elmes
73625e5751 include the gid in the tree so we can tell which groups are real 2011-04-28 09:23:59 +09:00
Damien Elmes
fc96e12a0a add some randomness to lrn interval 2011-04-28 09:23:59 +09:00
Damien Elmes
495b058618 include total count in with rev+due 2011-04-28 09:23:59 +09:00
Damien Elmes
2a1355eb16 make the group tree part of the scheduler instead 2011-04-28 09:23:59 +09:00
Damien Elmes
728715ff84 counts by group 2011-04-28 09:23:59 +09:00
Damien Elmes
2d00163323 tree grouping; add column to groups so they can remember tags 2011-04-28 09:23:59 +09:00
Damien Elmes
e547b0586a simplify undo
The undo code was using triggers and a temporary table to write out all changed rows before making a change. This made for powerful undo/redo support, but had some problems:
- creating the tables and triggers wasn't cheap, especially on mobile devices
- likewise, every data modification required writing into two separate databases, almost doubling the amount of writes required
- it was possible to leave the DB in an inconsistent state if an undoable operation is followed by a non-undoable operation that references the undoable operation, and the user then rolls back the undoable operation.

To address these issues, we simplify undo by integrating it with the autosave changes:
- .save() can be passed a name to mark a rollback point. If the user undoes the change, any changes since the last save are lost
- autosaves happen every 5 minutes, and are pushed back on a .save(), so the maximum work a user can lose is 5 minutes.
- reviews are handled separately, so we can let the user undo multiple reviews at once
- if necessary, special cases could be added for other operations like marking

This means that if a user does two damaging operations in a row they won't be able to restore the first one, but such an event is both unlikely, and is also covered by the backups made each time a deck is opened.
2011-04-28 09:23:59 +09:00
Damien Elmes
63efc4dbaa remove the separate timeGraph() 2011-04-28 09:23:58 +09:00
Damien Elmes
40706f3493 break reps graph into separate graphs; exclude cumulative line from stack 2011-04-28 09:23:58 +09:00
Damien Elmes
9c1e0befc6 bundle js libs; include them in report(); fix some graph ids 2011-04-28 09:23:58 +09:00
Damien Elmes
6ec2500eb8 reps & time graphs 2011-04-28 09:23:58 +09:00
Damien Elmes
60ef1ec49f eases graph 2011-04-28 09:23:58 +09:00
Damien Elmes
7d5d72adf8 add intervals, boxing in weeks for now 2011-04-28 09:23:58 +09:00
Damien Elmes
89fa08c548 due and cumulative due graphs ported 2011-04-28 09:23:58 +09:00
Damien Elmes
2ca9568196 initial graph code reorganization 2011-04-28 09:23:58 +09:00
Damien Elmes
cc9f5b8d86 stripMedia->strip 2011-04-28 09:23:58 +09:00
Damien Elmes
3c40854583 fix collapsing; make sure learning cards are put back on the heap 2011-04-28 09:23:57 +09:00
Damien Elmes
942bf43b52 fix stats
they're running now, but need to be sanity checked to make sure they're doing the right thing
2011-04-28 09:23:57 +09:00
Damien Elmes
63d1448d1e only bump lrn count when cramming if card not immediately graduated 2011-04-28 09:23:57 +09:00
Damien Elmes
464eb2b684 make sure cramming works if there are no selected groups 2011-04-28 09:23:57 +09:00
Damien Elmes
31427f0133 fix lapse card scheduling
- make sure we set a timestamp due time, and put the card back in the queue
- add a unit test for it
2011-04-28 09:23:57 +09:00
Damien Elmes
e407697fb9 fix interval calculation for lapsed cards in learning queue 2011-04-28 09:23:57 +09:00
Damien Elmes
1d91e459ec wrap the q/a in the card template div 2011-04-28 09:23:57 +09:00
Damien Elmes
9d52196949 add a bgclass helper func 2011-04-28 09:23:57 +09:00
Damien Elmes
c63b4085c6 add queueless counts; fix special field references on upgrade 2011-04-28 09:23:57 +09:00
Damien Elmes
31a548ee42 add a dirty flag
when we make changes that need to be cleaned up on exit, we mark the deck
dirty so that if we exit without saving, we can clean up on next open
2011-04-28 09:23:57 +09:00
Damien Elmes
ed75e4bee2 refactor cloze deletions and text:field, add forecast
Previously cloze deletions were handled by copying the contents of one field
into another and applying transforms to it. This had a number of problems:
- after you add a card, you can't undo the cloze deletion
- if you spot a mistake, you have to edit it twice (or more if you have more
  than one cloze for a sentence)
- making multiple clozes requires copying & pasting the sentence multiple
  times
- this also lead to much bigger decks if the sentences being cloze-deleted are
  large
- related clozes can't be spaced apart as siblings

To address these issues, we introduce the idea of cloze tags in the card
template and fields. If the template has the text:

{{cloze:1:field}}

And a field has the following contents:

{{c1::hello}}

Then the template will automatically replace that part of the text with either
occluded text, or a highlighted answer. All other clozes in the field are
displayed normally.

At the same time, we add support for text: into the template library, instead
of manually creating text: fields in the dict for every field.

Finally, add a forecast routine to get the due counts for the next week, which
is used in the GUI.
2011-04-28 09:23:56 +09:00
Damien Elmes
8705085200 update latex support 2011-04-28 09:23:56 +09:00
Damien Elmes
da0fb0c555 space exiting new cards; fix 1 fact freeze 2011-04-28 09:23:56 +09:00
Damien Elmes
ccc325f87b remove utcOffset; make it a property of crt instead 2011-04-28 09:23:56 +09:00
Damien Elmes
138d9881d3 add the ability to limit a cram to a given period 2011-04-28 09:23:56 +09:00
Damien Elmes
9b70af678c add rescheduling and interval reset to cram; don't include already due cards 2011-04-28 09:23:56 +09:00
Damien Elmes
be045d451c remove the media table
The media table was originally introduced when Anki hashed media filenames,
and needed a way to remember the original filename. It also helped with:
1) getting a quick list of all media used in the deck, or the media added
   since the last sync, for mobile clients
2) merging identical files with different names

But had some drawbacks:
- every operation that modifies templates, models or facts meant generating
  the q/a and checking if any new media had appeared
- each entry is about 70 bytes, and some decks have 100k+ media files

So we remove the media table. We address 1) by being more intelligent about
media downloads on the mobile platform. We ask the user after a full sync if
they want to look for missing media, and they can choose not to if they know
they haven't added any. And on a partial sync, we can scan the contents of the
incoming facts for media references, and download any references we find. This
also avoids all the issues people had with media not downloading because it
was in their media folder but not in the media database.

For 2), when copying media to the media folder, if we have a duplicate
filename, we check if that file has the same md5, and avoid copying if so.
This won't merge identical content that has separate names, but instances
where users need that are rare.
2011-04-28 09:23:56 +09:00
Damien Elmes
bd477de1a9 implement cram
still to do:
- altering intervals at cram exit
- tidying up
2011-04-28 09:23:56 +09:00
Damien Elmes
368bdf8d05 set the new types on upgrade 2011-04-28 09:23:56 +09:00
Damien Elmes
22a72d82c6 give card types a more logical order
they are now 0=new, 1=learning, 2=due, to reflect the natural progression
2011-04-28 09:23:56 +09:00
Damien Elmes
be56912caf strip all the old cram code; add way to start cramming from deck 2011-04-28 09:23:56 +09:00
Damien Elmes
17f3cabc25 more unit tests, and unbury on close 2011-04-28 09:23:56 +09:00
Damien Elmes
5e0cc2ff5d next time reports and associated unit tests 2011-04-28 09:23:56 +09:00
Damien Elmes
9e46db5b3e update finished msg 2011-04-28 09:23:56 +09:00
Damien Elmes
a7ec356e0d add tests for leech handling 2011-04-28 09:23:56 +09:00
Damien Elmes
908dccc2c0 implement new review code, add unit tests
Instead of the old approach to sibling spacing, we instead try to pick a due
date that doesn't have any siblings.
2011-04-28 09:23:56 +09:00
Damien Elmes
14c49a50cd more learning unit tests 2011-04-28 09:23:55 +09:00
Damien Elmes
96f36e66f7 parse tags when creating fact, add selective study, fix emptyAns default 2011-04-28 09:23:55 +09:00
Damien Elmes
17ee7757de add some group utilities 2011-04-28 09:23:55 +09:00
Damien Elmes
f9c3b27e5d give facts a group, so that cards can have their group reset if necessary 2011-04-28 09:23:55 +09:00
Damien Elmes
e728d49232 delete -> del for consistency 2011-04-28 09:23:55 +09:00
Damien Elmes
1dc3a0ad4a make sure revlog is removed on immediate delete 2011-04-28 09:23:55 +09:00
Damien Elmes
f96a495b53 fact.model -> fact.model() 2011-04-28 09:23:54 +09:00
Damien Elmes
870c80e076 add genCards(), previewCards(), and more unit tests 2011-04-28 09:23:54 +09:00
Damien Elmes
98290c485d fix integrity check, move dynamic indices into scheduler
A lot of the old checks in fixIntegrity() are no longer relevant, and some of
the others may no longer be required. They can be added back in as the need
arises.
2011-04-28 09:23:54 +09:00
Damien Elmes
0db95ded74 add template deletion & unit test 2011-04-28 09:23:54 +09:00
Damien Elmes
4638d3de46 implement field add/delete/rename/move
- now in model.py instead of deck.py
- added unit tests
2011-04-28 09:23:54 +09:00
Damien Elmes
93dcfceffe convert templates to a json object, and replace tid with ord
it's faster for us to parse another json string than pull a record from a
separate db table, and this makes templates and fields consistent
2011-04-28 09:23:54 +09:00
Damien Elmes
e4cc3e3013 revlog work
- remove revlog.py and move code into scheduler
- add a routine to log a learn repetition
- rename flags to type and set type=0 for learn mode
- add to unit test
2011-04-28 09:23:54 +09:00
Damien Elmes
4f2ecda980 update new card unit test 2011-04-28 09:23:54 +09:00
Damien Elmes
ad68500494 updateCache -> renderQA, move some functions around 2011-04-28 09:23:54 +09:00
Damien Elmes
f5b326c753 more checksum work
- convert checksums to int
- add bulk update & update on upgrade
- add indices pending performance testing. The fsum table & indices add about
  2MB to a deck with 50k unique fields
2011-04-28 09:23:54 +09:00
Damien Elmes
4becd8399c implement field cache, fix unit tests, remove some importers
the field cache (fsums table) also needs to store the model id to preserve the
old behaviour of limiting duplicate checks to a given model, and to ensure
we're actually comparing against the same fields

removed the dingsbums and wcu importers; will accept them back if the authors
port them to the new codebase.
2011-04-28 09:23:54 +09:00
Damien Elmes
1078285f0f change field storage format, improve upgrade speed
Since Anki first moved to an SQL backend, it has stored fields in a fields
table, with one field per line. This is a natural layout in a relational
database, and it had some nice properties. It meant we could retrieve an
individual field of a fact, which we used for limiting searches to a
particular field, for sorting, and for determining if a field was unique, by
adding an index on the field value.

The index was very expensive, so as part of the early work towards 2.0 I added
a checksum field instead, and added an index to that. This was a lot cheaper
than storing the entire value twice for the purpose of fast searches, but it
only partly solved the problem. We still needed an index on factId so that we
could retrieve a given fact's fields quickly. For simple models this was
fairly cheap, but as the number of fields grows the table grows very big. 25k
facts with 30 fields each and the fields table has grown to 750k entries. This
makes the factId index and checksum index really expensive - with the q/a
cache removed, about 30% of the deck in such a situation.

Equally problematic was sorting on those fields. Short of adding another
expensive index, a sort involves a table scan of the entire table.

We solve these problems by moving all fields into the facts table. For this to
work, we need to address some issues:

Sorting: we'll add an option to the model to specify the sort field. When
facts are modified, that field is written to a separate sort column. It can be
HTML stripped, and possibly truncated to a maximum number of letters. This
means that switching sort to a different field involves an expensive rewrite
of the sort column, but people tend to leave their sort field set to the same
value, and we don't need to clear the field if the user switches temporarily
to a non-field sort like due order. And it has the nice properties of allowing
different models to be sorted on different columns at the same time, and
makes it impossible for models to be hidden because the user has sorted on a
field which doesn't appear in some models.

Searching for words with embedded HTML: 1.2 introduced a HTML-stripped cache
of the fields content, which both sped up searches (since we didn't have to
search the possibly large fields table), and meant we could find "bob" in
"b<b>ob</b>" quickly. The ability to quickly search for words peppered with
HTML was nice, but it meant doubling the cost of storing text in many cases,
and meant after any edit more data has to be written to the DB. Instead, we'll
do it on the fly. On this i7 computer, stripping HTML from all fields takes
1-2.6 seconds on 25-50k decks. We could possibly skip the stripping for people
who don't require it - the number of people who bold parts of words is
actually pretty small.

Duplicate detection: one option would be to fetch all fields when the add
cards dialog or editor are opened. But this will be expensive on mobile
devices. Instead, we'll create a separate table of (fid, csum), with an index
on both columns. When we edit a fact, we delete all the existing checksums for
that fact, and add checksums for any fields that must be checked as unique. We
could optionally skip the index on csum - some benchmarking is required.

As for the new table layout, creating separate columns for each field won't
scale. Instead, we store the fields in a single column, separated by an ascii
record separator. We split on that character when extracting from
the database, and join on it when writing to the DB.

Searching on a particular field in the browser will be accomplished by finding
all facts that match, and then unpacking to see if the relevant field matched.

Tags have been moved back to a separate column. Now that fields are on the
facts table, there is no need to pack them in as a field simply to avoid
another table hit.
2011-04-28 09:23:53 +09:00
Damien Elmes
9c247f45bd remove q/a cache, tags in fields, rewrite remaining ids, more
Anki used random 64bit IDs for cards, facts and fields. This had some nice
properties:
- merging data in syncs and imports was simply a matter of copying each way,
  as conflicts were astronomically unlikely
- it made it easy to identify identical cards and prevent them from being
  reimported
But there were some negatives too:
- they're more expensive to store
- javascript can't handle numbers > 2**53, which means AnkiMobile, iAnki and
  so on have to treat the ids as strings, which is slow
- simply copying data in a sync or import can lead to corruption, as while a
  duplicate id indicates the data was originally the same, it may have
  diverged. A more intelligent approach is necessary.
- sqlite was sorting the fields table based on the id, which meant the fields
  were spread across the table, and costly to fetch

So instead, we'll move to incremental ids. In the case of model changes we'll
declare that a schema change and force a full sync to avoid having to deal
with conflicts, and in the case of cards and facts, we'll need to update the
ids on one end to merge. Identical cards can be detected by checking to see if
their id is the same and their creation time is the same.

Creation time has been added back to cards and facts because it's necessary
for sync conflict merging. That means facts.pos is not required.

The graves table has been removed. It's not necessary for schema related
changes, and dead cards/facts can be represented as a card with queue=-4 and
created=0. Because we will record schema modification time and can ensure a
full sync propagates to all endpoints, it means we can remove the dead
cards/facts on schema change.

Tags have been removed from the facts table and are represented as a field
with ord=-1 and fmid=0. Combined with the locality improvement for fields, it
means that fetching fields is not much more expensive than using the q/a
cache.

Because of the above, removing the q/a cache is a possibility now. The q and a
columns on cards has been dropped. It will still be necessary to render the
q/a on fact add/edit, since we need to record media references. It would be
nice to avoid this in the future. Perhaps one way would be the ability to
assign a type to fields, like "image", "audio", or "latex". LaTeX needs
special consider anyway, as it was being rendered into the q/a cache.
2011-04-28 09:23:53 +09:00
Damien Elmes
3cb4ade4a1 simplify bold/italic/underline tags from qt in upgrade 2011-04-28 09:23:53 +09:00
Damien Elmes
668a58b65a update unit tests for sqlalchemy drop 2011-04-28 09:23:53 +09:00
Damien Elmes
2f27133705 drop sqlalchemy; massive refactor
SQLAlchemy is a great tool, but it wasn't a great fit for Anki:
- We often had to drop down to raw SQL for performance reasons.
- The DB cursors and results were wrapped, which incurred a
  sizable performance hit due to introspection. Operations like fetching 50k
  records from a hot cache were taking more than twice as long to complete.
- We take advantage of sqlite-specific features, so SQL language abstraction
  is useless to us.
- The anki schema is quite small, so manually saving and loading objects is
  not a big burden.

In the process of porting to DBAPI, I've refactored the database schema:
- App configuration data that we don't need in joins or bulk updates has been
  moved into JSON objects. This simplifies serializing, and means we won't
  need DB schema changes to store extra options in the future. This change
  obsoletes the deckVars table.
- Renamed tables:
-- fieldModels -> fields
-- cardModels -> templates
-- fields -> fdata
- a number of attribute names have been shortened

Classes like Card, Fact & Model remain. They maintain a reference to the deck.
To write their state to the DB, call .flush().

Objects no longer have their modification time manually updated. Instead, the
modification time is updated when they are flushed. This also applies to the
deck.

Decks will now save on close, because various operations that were done at
deck load will be moved into deck close instead. Operations like undoing
buried card are cheap on a hot cache, but expensive on startup.
Programmatically you can call .close(save=False) to avoid a save and a
modification bump. This will be useful for generating due counts.

Because of the new saving behaviour, the save and save as options will be
removed from the GUI in the future.

The q/a cache and field cache generating has been centralized. Facts will
automatically rebuild the cache on flush; models can do so with
model.updateCache().

Media handling has also been reworked. It has moved into a MediaRegistry
object, which the deck holds. Refcounting has been dropped - it meant we had
to compare old and new value every time facts or models were changed, and
existed for the sole purpose of not showing errors on a missing media
download. Instead we just media.registerText(q+a) when it's updated. The
download function will be expanded to ask the user if they want to continue
after a certain number of files have failed to download, which should be an
adequate alternative. And we now add the file into the media DB when it's
copied to th emedia directory, not when the card is commited. This fixes
duplicates a user would get if they added the same media to a card twice
without adding the card.

The old DeckStorage object had its upgrade code split in a previous commit;
the opening and upgrading code has been merged back together, and put in a
separate storage.py file. The correct way to open a deck now is import anki; d
= anki.Deck(path).

deck.getCard() -> deck.sched.getCard()
same with answerCard
deck.getCard(id) returns a Card object now.

And the DB wrapper has had a few changes:
- sql statements are a more standard DBAPI:
 - statement() -> execute()
 - statements() -> executemany()
- called like execute(sql, 1, 2, 3) or execute(sql, a=1, b=2, c=3)
- column0 -> list
2011-04-28 09:23:53 +09:00
Damien Elmes
1d6dbf9900 rework tag handling and remove cardTags
The tags tables were initially added to speed up the loading of the browser by
speeding up two operations: gathering a list of all tags to show in the
dropdown box, and finding cards with a given tag. The former functionality is
provided by the tags table, and the latter functionality by the cardTags
table.

Selective study is handled by groups now, which perform better since they
don't require a join or subselect, and can be embedded in the index. So the
only remaining benefit of cardTags is for the browser.

Performance testing indicates that cardTags is not saving us a large amount.
It only takes us 30ms to search a 50k card table for matches with a hot cache.
On a cold cache it means the facts table has to be loaded into memory, which
roughly doubles the load time with the default settings (we need to load the
cards table too, as we're sorting the cards), but that startup time was
necessary with certain settings in the past too (sorting by fact created for
example). With groups implemented, the cost of maintaining a cache just for
initial browser load time is hard to justify.

Other changes:

- the tags table has any missing tags added to it when facts are added/edited.
  This means old tags will stick around even when no cards reference them, but
  is much cheaper than reference counting or a separate table, and simplifies
  updates and syncing.
- the tags table has a modified field now so we can can sync it instead of
  having to scan all facts coming across in a sync
- priority field removed
- we no longer put model names or card templates into the tags table. There
  were two reasons we did this in the past: so we could cram/selective study
  them, and for plugins. Selective study uses groups now, and plugins can
  check the model's name instead (and most already do). This also does away
  with the somewhat confusing behaviour of names also being tags.
- facts have their tags as _tags now. You can get a list with tags(), but
  editing operations should use add/deleteTags() instead of manually editing
  the string.
2011-04-28 09:23:29 +09:00
Damien Elmes
2613143fe9 improve dynamic indices, implement new queue 2011-04-28 09:23:28 +09:00
Damien Elmes
d34a76d5a0 move deck config into json ala models
- limits are stored separately so we can access them quickly when checking
  deck counts
- data is used to store cssCache and hexCache; these may be refactored or go
  away in the future
2011-04-28 09:23:28 +09:00
Damien Elmes
b0b4074cbd start work on learn mode, change models, more
- model config is now stored as a json-serialized dict, which allows us to
  quickly gather the info and allows for adding extra options more easily in
  the future
- denormalize modelId into the cards table, so we can get the model scheduling
  information without having to hit the facts table
- remove position - since we will handle spacing differently we don't need a
  separate variable to due to define sort order
- remove lastInterval from cards; the new cram mode and review early shouldn't
  need it
- successive->streak
- add new columns for learn mode
- move cram mode into new file; learn more and review early need more thought
- initial work on learn mode
- initial unit tests
2011-04-28 09:23:28 +09:00
Damien Elmes
4e7e8b03bc moving scheduling code into separate file, some preliminary refactoring 2011-04-28 09:23:28 +09:00
Damien Elmes
8cbd3c9f3b change deck loading call 2011-04-28 09:23:28 +09:00
Damien Elmes
8a60ee0794 initial changes to models, deck, etc
- move most scheduling parameters from deck to models
- remove obsolete fields in deck and models
- decks->deck
- remove deck id reference in models
- move some deckVars into the deck table
- simplify deckstorage
- lock sessionhelper by default
- add models/currentModel as properties instead of ORM mappings
- remove models.tags
- remove remaining support for memory-backed databases
- use a blank string for syncName instead of null
- remove backup code; will handle in gui
- bump version to 100
- update unit tests
2011-04-28 09:23:28 +09:00
Damien Elmes
2d1d946504 refactor media
- created->modified
- size->refcnt
- originalPath->chksum
- remove description
2011-04-28 09:23:28 +09:00
Damien Elmes
1b9866b4e7 temporarily disable importing/exporting/sync tests 2011-04-28 09:23:28 +09:00
Damien Elmes
f828393de3 rename deck.s to a more understable deck.db; keep s for compat 2011-04-28 09:21:07 +09:00
Damien Elmes
b6bb03025f new history table
- rename to revlog
- change the pk to time, as we want an index on time, and the old multi-column
  index was expensive and not useful
- remove yes/no count; they can be inferred from the ease
- remove lastFactor, as it's in the previous entry
- remove delay, it can be inferred from last entry
- remove 'next' from nextInterval and nextFactor
- rename 'thinkingTime' to 'userTime'
- rename reps to rep
- migrate old data to new table, and fix some problems in the process: ease0
  -> ease1, and limit thinking time to 60 seconds as it should have been
  previously
2011-04-28 09:21:07 +09:00
Damien Elmes
855de47ffe remove the stats table
The stats table was how the early non-SQL versions of Anki kept track of
statistics, before there was a revision log. It is being removed because:

- it's not possible to show the statistics for a subset of the deck
- it can't meaningfully be copied on import/export
- it makes it harder to implement sync merging

Implications:

- graphs and deck stats roughly 1.5-3x longer than before, but we'll have the
  ability to generate stats for subsections of the deck, and it's not time
  critical code
- people who've been using anki since the very early days may notice a drop in
  statistics, as early repetitions were recorded in the stats table but the
  revlog didn't exist at that point.
- due bugs in old syncs and imports/exports, the stats and revlog may not
  match numbers exactly

To remove it, the following changes have been made:

- the graphs and deck stats now generate their data entirely from the revlog
- there are no stats to keep track of how many cards we've answered, so we
  pull that information from the revlog in reset()
- we remove _globalStats and _dailyStats from the deck
- we check if a day rollover has occurred using failedCutoff instead
- we remove the getStats() routine
- the ETA code is currently disabled
- timeboxing routines use repsToday instead of stats
- remove stats delete from export
- remove stats table and index in upgrade
- remove stats syncing and globalStats refresh pre-sync
- remove stats count check in fullSync check, which was redundant anyway
- update unit tests

Also:

- newCountToday -> newCount, to bring it in line with revCount&failedCount
  which also reflect the currently due count
- newCount -> newAvail
- timeboxing routines renamed since the old names were confusingly similar to
  refreshSession() which does something different

Todo:

- update newSeenToday & repsToday when answering a card
- reimplement eta
2011-04-28 09:21:07 +09:00
Damien Elmes
4302306fe9 use a checksum for field values; fixed import->update number
Previously we had an index on the value field, which was very expensive for
long fields. Instead we use a separate column and take the first 8 characters
of the field value's md5sum, and index that. In decks with lots of text in
fields, it can cut the deck size by 30% or more, and many decks improve by
10-20%. Decks with only a few characters in fields may increase in size
slightly, but this is offset by the fact that we only generate a checksum for
fields that have uniqueness checking on.

Also, fixed import->update reporting the total # of available facts instead of
the number of facts that were imported.
2011-04-28 09:21:06 +09:00
Damien Elmes
1a3c8c0c1e remove views and pre 0.9.9.8.6 upgrade code; upgrade test decks to 0.9.9.8.6 2011-04-28 09:21:06 +09:00
Damien Elmes
28604b9d29 remove priorities 2011-04-28 09:21:06 +09:00
Damien Elmes
9355e9b98d fix sync unit tests 2011-04-28 09:21:06 +09:00
Damien Elmes
1b7ac91a2a force a full sync if there have been schema changes on either side 2011-04-28 09:21:06 +09:00
Damien Elmes
ba23502944 fix a unit test 2010-12-25 12:44:06 +09:00
Damien Elmes
6563f0cefb update test for new media handling 2010-12-12 20:07:20 +09:00
Damien Elmes
0c9672e7b8 rewrite media support
- media is no longer hashed, and instead stored in the db using its original
  name
- when adding media, its checksum is calculated and used to look for
  duplicates
- duplicate filenames will result in a number tacked on the file
- the size column is used to count card references to media. If media is
  referenced in a fact but not the question or answer, the count will be zero.
- there is no guarantee media will be listed in the media db if it is unused
  on the question & answer
- if rebuildMediaDir(delete=True), then entries with zero references are
  deleted, along with any unused files in the media dir.
- rebuildMediaDir() will update the internal checksums, and set the checksum
  to "" if a file can't be found
- rebuildMediaDir() is a lot less destructive now, and will leave alone
  directories it finds in the media folder (but not look in them either)
- rebuildMediaDir() returns more information about the state of media now
- the online and mobile clients will need to to make sure that when
  downloading media, entries with no checksum are non-fatal and should not
  abort the download process.
- the ref count is updated every time the q/a is updated - so the db should be
  up to date after every add/edit/import
- since we look for media on the q/a now, card templates like '<img
  src="{{{field}}}">' will work now
- export original files as gone as it is not needed anymore
- move from per-model media URL to deckVar. downloadMissingMedia() uses this
  now. Deck subscriptions will have to be updated to share media another way.
- pass deck in formatQA, as latex support is going to change
2010-12-11 01:19:31 +09:00
Damien Elmes
c4c9847028 fixed sync unit test 2010-12-10 18:02:18 +09:00
Damien Elmes
044f28af5a fix unit tests now that post sync .reset() is explicit 2010-12-08 13:57:58 +09:00
Damien Elmes
e3dd736460 add ability to update fields when importing 2010-11-26 01:36:24 +09:00
Damien Elmes
6ec898ca4b Require explicit reset for most queue-modifying functions
When you call operations like deleteCards(), suspendCards() and so on, it is
now necessary to call deck.reset() afterwards. This allows the calling code to
delay a reset if necessary. If the calling code calls a function that says the
caller must reset, the caller should be sure to call .reset() and fetch the
current card again. Failure to do the latter will result in answerCard()
attempting to remove the card from the queue, when the queue has been cleared.
2010-11-23 17:41:36 +09:00
Damien Elmes
ad743d850d start work on scheduling refactor
Previously we used getCard() to fetch a card at the time. This required a
number of indices to perform efficiently, and the indices were expensive in
terms of disk space and time required to keep them up to date. Instead we now
gather a bunch of cards at once.

- Drop checkDue()/isDue so writes are not necessary to the DB when checking
for due cards
- Due counts checked on deck load, and only updated once a day or at the end
of a session. This prevents cards from expiring during reviews, leading to
confusing undo behaviour and due counts that go up instead of down as you
review. The default will be to only expire cards once a day, which represents
a change from the way things were done previously.
- Set deck var defaults on deck load/create instead of upgrade, which should
fix upgrade issues
- The scheduling code can now have bits and pieces switched out, which should
make review early / cram etc easier to integrate
- Cards with priority <= 0 now have their type incremented by three, so we can
get access to schedulable cards with a single column.
- rebuildQueue() -> reset()
- refresh() -> refreshSession()
- Views and many of the indices on the cards table are now obsolete and will
  be removed in the future. I won't remove them straight away, so as to not
  break backward compatibility.
- Use bigger intervals between successive card templates, as the previous
intervals were too small to represent in doubles in some circumstances

Still to do:

- review early
- learn more
- failing mature cards where delay1 > delay0
2010-10-18 14:35:11 +09:00
Damien Elmes
593a1e75b0 update unit test for new sync behaviour 2010-08-19 11:32:00 +09:00
Damien Elmes
60c76c93ff don't backup in unit tests 2010-02-20 10:03:47 +09:00
Damien Elmes
7bc81c81bb truncate supermemo tests to avoid any possible license concerns 2010-02-18 16:53:29 +09:00
Damien Elmes
095a7dbb6f implement save as without attaching a db
Attaching new to old causes old to be implicitly commited.
We also can't access old from new if a write lock has been
taken out, so the only option left is to move all the data
through Python and take the speed hit.
2010-02-09 01:27:49 +09:00
Rick Gruber-Riemer
4971069856 Added importing for DingsBums?! decks 2009-11-08 14:39:09 +09:00
Damien Elmes
1006943d8e SuperMemo import fix from Petr
Fix supermemo import of Q&A part in escaped html. Supermemo 2004/2006 export
can contain unescaped chars > or < that confuse beautifulsoap library. Switch
autocreation of tags from all titles to True.
2009-07-26 12:38:36 +09:00
Damien Elmes
fe99ff7518 add supermemo importer from Petr Michalec 2009-07-09 23:03:23 +09:00
Damien Elmes
34879181d2 use .anki ext in unit tests 2009-06-25 14:57:14 +09:00
Damien Elmes
3d81181323 bulk media support -> local media copy, always send media table 2009-06-19 11:50:31 +09:00
Damien Elmes
e62967ecb1 switch to python csv 2009-06-18 05:21:47 +09:00
Damien Elmes
2d32e1aaa1 strip all chinese/japanese support out in favour of plugins 2009-06-10 22:58:28 +09:00
Damien Elmes
05ad863e18 remove print statement from tests 2009-06-06 01:12:26 +09:00
Damien Elmes
8a19bd4c57 implement full sync uploading 2009-05-31 01:23:31 +09:00
Damien Elmes
df0e4d2446 ensure tags are synced properly in one way sync 2009-05-21 02:11:33 +09:00
Damien Elmes
6a836e5680 fix attachOld test 2009-04-11 01:08:04 +09:00
Damien Elmes
c7f9cec657 add deck vars 2009-04-07 11:55:08 +09:00
Damien Elmes
36825006d0 new search interface, support negating tags, use tag: not t: 2009-04-06 11:41:50 +09:00
Damien Elmes
95b8d655e6 remove shared cache mode, it's not needed 2009-03-18 22:45:43 +09:00
Damien Elmes
af176921df fix reviewHistory unit test 2009-03-15 02:12:14 +09:00
Damien Elmes
77a6488f6d when importing, tag cards as new, add unit test 2009-02-14 03:03:42 +09:00
Damien Elmes
231b5fe9e7 only use failedcardmax with 'soon' failed card setting, fix tag unit test 2009-02-10 13:22:45 +09:00
Damien Elmes
d44c3792e9 model changing support 2009-02-04 20:43:54 +09:00
Damien Elmes
ff4cc7b0af add importing tag support, fix audio 2009-01-17 22:36:14 +09:00
Damien Elmes
75d6123dc3 identify temp files as anki, quiet lame 2009-01-17 16:29:45 +09:00
Damien Elmes
dd5e7c45b4 make sure field & card models are not duplicated 2009-01-14 15:35:16 +09:00
Damien Elmes
93332079b5 add model copy support 2009-01-14 15:28:27 +09:00
Damien Elmes
7c764763d2 sqlalchemy 0.5 compat 2009-01-07 11:37:01 +09:00
Damien Elmes
9765fdae73 support media for memory backed files 2009-01-04 07:40:32 +09:00
Damien Elmes
c973c1eb4f new decks now default to in-memory storage until saveas
- undo needs fixing
- media support needs to be changed to work with in-memory db
2009-01-04 07:13:30 +09:00
Damien Elmes
4a22f5b6e7 fix a unit test now that back is not required/unique 2009-01-04 05:41:28 +09:00
Damien Elmes
8c9f883e68 dupe fact on add to avoid session issues 2008-12-07 11:38:35 +09:00
Damien Elmes
6726888537 fix unit tests 2008-12-03 19:44:55 +09:00
Damien Elmes
21b59408cd refactor features to use hooks, update stdmodels, update findTags()
- remove description from fields, cards and models
- remove features and use field names instead
2008-12-03 19:22:15 +09:00
Damien Elmes
e1d3f791f5 do need to rebuild counts on export 2008-11-21 15:46:17 +09:00
Damien Elmes
cdd2be3548 fix unit test, badstatusline error 2008-11-14 14:03:47 +09:00
Damien Elmes
e9dc034b9a add refs to relativeDelay again 2008-11-08 20:05:39 +09:00
Damien Elmes
b2d0e5d3df wip 2008-11-07 18:44:49 +09:00
Damien Elmes
e25feed0af support 4am rollover 2008-10-12 04:02:33 +09:00
Damien Elmes
e69522f120 ensure order of received cards 2008-10-06 18:41:50 +09:00
Damien Elmes
472eb4581a public decks wip 2008-10-04 14:48:23 +09:00
Damien Elmes
5da3a0f5d3 initial commit from hg 2008-09-27 23:50:03 +09:00