use of com.ichi2.anki.CardBrowser.Column.EASE in project Anki-Android by Ramblurr.
the class DeckTask method doInBackgroundAnswerCard.
private TaskData doInBackgroundAnswerCard(TaskData... params) {
Sched sched = params[0].getSched();
Card oldCard = params[0].getCard();
int ease = params[0].getInt();
Card newCard = null;
// TODO: proper leech handling
int oldCardLeech = 0;
// 0: normal; 1: leech; 2: leech & suspended
try {
AnkiDb ankiDB = sched.getCol().getDb();
ankiDB.getDatabase().beginTransaction();
try {
if (oldCard != null) {
sched.answerCard(oldCard, ease);
}
if (newCard == null) {
newCard = getCard(sched);
}
if (newCard != null) {
// render cards before locking database
newCard._getQA(true);
}
publishProgress(new TaskData(newCard, oldCardLeech));
ankiDB.getDatabase().setTransactionSuccessful();
} finally {
ankiDB.getDatabase().endTransaction();
}
} catch (RuntimeException e) {
Log.e(AnkiDroidApp.TAG, "doInBackgroundAnswerCard - RuntimeException on answering card: " + e);
AnkiDroidApp.saveExceptionReportFile(e, "doInBackgroundAnswerCard");
return new TaskData(false);
}
return new TaskData(true);
}
use of com.ichi2.anki.CardBrowser.Column.EASE in project AnkiChinaAndroid by ankichinateam.
the class CardContentProvider method update.
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
if (!hasReadWritePermission() && shouldEnforceUpdateSecurity(uri)) {
throwSecurityException("update", uri);
}
Collection col = CollectionHelper.getInstance().getCol(mContext);
if (col == null) {
throw new IllegalStateException(COL_NULL_ERROR_MSG);
}
col.log(getLogMessage("update", uri));
// Find out what data the user is requesting
int match = sUriMatcher.match(uri);
// Number of updated entries (return value)
int updated = 0;
switch(match) {
case NOTES_V2:
case NOTES:
throw new IllegalArgumentException("Not possible to update notes directly (only through data URI)");
case NOTES_ID:
{
/* Direct access note details
*/
Note currentNote = getNoteFromUri(uri, col);
// the key of the ContentValues contains the column name
// the value of the ContentValues contains the row value.
Set<Map.Entry<String, Object>> valueSet = values.valueSet();
for (Map.Entry<String, Object> entry : valueSet) {
String key = entry.getKey();
// when the client does not specify FLDS, then don't update the FLDS
if (key.equals(FlashCardsContract.Note.FLDS)) {
// Update FLDS
Timber.d("CardContentProvider: flds update...");
String newFldsEncoded = (String) entry.getValue();
String[] flds = Utils.splitFields(newFldsEncoded);
// Check that correct number of flds specified
if (flds.length != currentNote.getFields().length) {
throw new IllegalArgumentException("Incorrect flds argument : " + newFldsEncoded);
}
// Update the note
for (int idx = 0; idx < flds.length; idx++) {
currentNote.setField(idx, flds[idx]);
}
updated++;
} else if (key.equals(FlashCardsContract.Note.TAGS)) {
// Update tags
Timber.d("CardContentProvider: tags update...");
Object tags = entry.getValue();
if (tags != null) {
currentNote.setTagsFromStr(String.valueOf(tags));
}
updated++;
} else {
// Unsupported column
throw new IllegalArgumentException("Unsupported column: " + key);
}
}
Timber.d("CardContentProvider: Saving note...");
currentNote.flush();
break;
}
case NOTES_ID_CARDS:
// TODO: To be implemented
throw new UnsupportedOperationException("Not yet implemented");
// break;
case NOTES_ID_CARDS_ORD:
{
Card currentCard = getCardFromUri(uri, col);
boolean isDeckUpdate = false;
long did = -1;
// the key of the ContentValues contains the column name
// the value of the ContentValues contains the row value.
Set<Map.Entry<String, Object>> valueSet = values.valueSet();
for (Map.Entry<String, Object> entry : valueSet) {
// Only updates on deck id is supported
String key = entry.getKey();
isDeckUpdate = key.equals(FlashCardsContract.Card.DECK_ID);
did = values.getAsLong(key);
}
if (col.getDecks().isDyn(did)) {
throw new IllegalArgumentException("Cards cannot be moved to a filtered deck");
}
/* now update the card
*/
if ((isDeckUpdate) && (did >= 0)) {
Timber.d("CardContentProvider: Moving card to other deck...");
col.getDecks().flush();
currentCard.setDid(did);
currentCard.flush();
col.save();
updated++;
} else {
// User tries an operation that is not (yet?) supported.
throw new IllegalArgumentException("Currently only updates of decks are supported");
}
break;
}
case MODELS:
throw new IllegalArgumentException("Cannot update models in bulk");
case MODELS_ID:
// Get the input parameters
String newModelName = values.getAsString(FlashCardsContract.Model.NAME);
String newCss = values.getAsString(FlashCardsContract.Model.CSS);
String newDid = values.getAsString(FlashCardsContract.Model.DECK_ID);
String newFieldList = values.getAsString(FlashCardsContract.Model.FIELD_NAMES);
if (newFieldList != null) {
// Changing the field names would require a full-sync
throw new IllegalArgumentException("Field names cannot be changed via provider");
}
Integer newSortf = values.getAsInteger(FlashCardsContract.Model.SORT_FIELD_INDEX);
Integer newType = values.getAsInteger(FlashCardsContract.Model.TYPE);
String newLatexPost = values.getAsString(FlashCardsContract.Model.LATEX_POST);
String newLatexPre = values.getAsString(FlashCardsContract.Model.LATEX_PRE);
// Get the original note JSON
Model model = col.getModels().get(getModelIdFromUri(uri, col));
try {
// Update model name and/or css
if (newModelName != null) {
model.put("name", newModelName);
updated++;
}
if (newCss != null) {
model.put("css", newCss);
updated++;
}
if (newDid != null) {
if (col.getDecks().isDyn(Long.parseLong(newDid))) {
throw new IllegalArgumentException("Cannot set a filtered deck as default deck for a model");
}
model.put("did", newDid);
updated++;
}
if (newSortf != null) {
model.put("sortf", newSortf);
updated++;
}
if (newType != null) {
model.put("type", newType);
updated++;
}
if (newLatexPost != null) {
model.put("latexPost", newLatexPost);
updated++;
}
if (newLatexPre != null) {
model.put("latexPre", newLatexPre);
updated++;
}
col.getModels().save(model);
col.save();
} catch (JSONException e) {
Timber.e(e, "JSONException updating model");
}
break;
case MODELS_ID_TEMPLATES:
throw new IllegalArgumentException("Cannot update templates in bulk");
case MODELS_ID_TEMPLATES_ID:
Long mid = values.getAsLong(CardTemplate.MODEL_ID);
Integer ord = values.getAsInteger(CardTemplate.ORD);
String name = values.getAsString(CardTemplate.NAME);
String qfmt = values.getAsString(CardTemplate.QUESTION_FORMAT);
String afmt = values.getAsString(CardTemplate.ANSWER_FORMAT);
String bqfmt = values.getAsString(CardTemplate.BROWSER_QUESTION_FORMAT);
String bafmt = values.getAsString(CardTemplate.BROWSER_ANSWER_FORMAT);
// Throw exception if read-only fields are included
if (mid != null || ord != null) {
throw new IllegalArgumentException("Updates to mid or ord are not allowed");
}
// Update the model
try {
Integer templateOrd = Integer.parseInt(uri.getLastPathSegment());
Model existingModel = col.getModels().get(getModelIdFromUri(uri, col));
JSONArray templates = existingModel.getJSONArray("tmpls");
JSONObject template = templates.getJSONObject(templateOrd);
if (name != null) {
template.put("name", name);
updated++;
}
if (qfmt != null) {
template.put("qfmt", qfmt);
updated++;
}
if (afmt != null) {
template.put("afmt", afmt);
updated++;
}
if (bqfmt != null) {
template.put("bqfmt", bqfmt);
updated++;
}
if (bafmt != null) {
template.put("bafmt", bafmt);
updated++;
}
// Save the model
templates.put(templateOrd, template);
existingModel.put("tmpls", templates);
col.getModels().save(existingModel, true);
col.save();
} catch (JSONException e) {
throw new IllegalArgumentException("Model is malformed", e);
}
break;
case SCHEDULE:
{
Set<Map.Entry<String, Object>> valueSet = values.valueSet();
int cardOrd = -1;
long noteID = -1;
int ease = -1;
long timeTaken = -1;
int bury = -1;
int suspend = -1;
for (Map.Entry<String, Object> entry : valueSet) {
String key = entry.getKey();
if (key.equals(FlashCardsContract.ReviewInfo.NOTE_ID)) {
noteID = values.getAsLong(key);
} else if (key.equals(FlashCardsContract.ReviewInfo.CARD_ORD)) {
cardOrd = values.getAsInteger(key);
} else if (key.equals(FlashCardsContract.ReviewInfo.EASE)) {
ease = values.getAsInteger(key);
} else if (key.equals(FlashCardsContract.ReviewInfo.TIME_TAKEN)) {
timeTaken = values.getAsLong(key);
} else if (key.equals(FlashCardsContract.ReviewInfo.BURY)) {
bury = values.getAsInteger(key);
} else if (key.equals(FlashCardsContract.ReviewInfo.SUSPEND)) {
suspend = values.getAsInteger(key);
}
}
if (cardOrd != -1 && noteID != -1) {
Card cardToAnswer = getCard(noteID, cardOrd, col);
if (cardToAnswer != null) {
if (bury == 1) {
// bury card
buryOrSuspendCard(col, col.getSched(), cardToAnswer, true);
} else if (suspend == 1) {
// suspend card
buryOrSuspendCard(col, col.getSched(), cardToAnswer, false);
} else {
answerCard(col, col.getSched(), cardToAnswer, ease, timeTaken);
}
updated++;
} else {
Timber.e("Requested card with noteId %d and cardOrd %d was not found. Either the provided " + "noteId/cardOrd were wrong or the card has been deleted in the meantime.", noteID, cardOrd);
}
}
break;
}
case DECKS:
throw new IllegalArgumentException("Can't update decks in bulk");
case DECKS_ID:
// TODO: be sure to throw exception if change to the dyn value of a deck is requested
throw new UnsupportedOperationException("Not yet implemented");
case DECK_SELECTED:
{
Set<Map.Entry<String, Object>> valueSet = values.valueSet();
for (Map.Entry<String, Object> entry : valueSet) {
String key = entry.getKey();
if (key.equals(FlashCardsContract.Deck.DECK_ID)) {
long deckId = values.getAsLong(key);
if (selectDeckWithCheck(col, deckId)) {
updated++;
}
}
}
col.save();
break;
}
default:
// Unknown URI type
throw new IllegalArgumentException("uri " + uri + " is not supported");
}
return updated;
}
use of com.ichi2.anki.CardBrowser.Column.EASE in project AnkiChinaAndroid by ankichinateam.
the class Sched method _nextRevIvl.
/**
* Interval management ******************************************************
* *****************************************
*/
/**
* Ideal next interval for CARD, given EASE.
*/
private int _nextRevIvl(@NonNull Card card, @Consts.BUTTON_TYPE int ease) {
long delay = _daysLate(card);
int interval = 0;
JSONObject conf = _revConf(card);
double fct = card.getFactor() / 1000.0;
int ivl2 = _constrainedIvl((int) ((card.getIvl() + delay / 4) * 1.2), conf, card.getIvl());
int ivl3 = _constrainedIvl((int) ((card.getIvl() + delay / 2) * fct), conf, ivl2);
int ivl4 = _constrainedIvl((int) ((card.getIvl() + delay) * fct * conf.getDouble("ease4")), conf, ivl3);
if (ease == Consts.BUTTON_TWO) {
interval = ivl2;
} else if (ease == Consts.BUTTON_THREE) {
interval = ivl3;
} else if (ease == Consts.BUTTON_FOUR) {
interval = ivl4;
}
// interval capped?
return Math.min(interval, conf.getInt("maxIvl"));
}
use of com.ichi2.anki.CardBrowser.Column.EASE in project AnkiChinaAndroid by ankichinateam.
the class Sched method _answerLrnCard.
/**
* @param ease 1=no, 2=yes, 3=remove
*/
@Override
protected void _answerLrnCard(@NonNull Card card, @Consts.BUTTON_TYPE int ease) {
JSONObject conf = _lrnConf(card);
@Consts.CARD_TYPE int type;
if (card.getODid() != 0 && !card.getWasNew()) {
type = Consts.CARD_TYPE_RELEARNING;
} else if (card.getType() == Consts.CARD_TYPE_REV) {
type = Consts.CARD_TYPE_REV;
} else {
type = Consts.CARD_TYPE_NEW;
}
boolean leaving = false;
// lrnCount was decremented once when card was fetched
int lastLeft = card.getLeft();
// immediate graduate?
if (ease == Consts.BUTTON_THREE) {
_rescheduleAsRev(card, conf, true);
leaving = true;
// graduation time?
} else if (ease == Consts.BUTTON_TWO && (card.getLeft() % 1000) - 1 <= 0) {
_rescheduleAsRev(card, conf, false);
leaving = true;
} else {
// one step towards graduation
if (ease == Consts.BUTTON_TWO) {
// decrement real left count and recalculate left today
int left = (card.getLeft() % 1000) - 1;
card.setLeft(_leftToday(conf.getJSONArray("delays"), left) * 1000 + left);
// failed
} else {
card.setLeft(_startingLeft(card));
boolean resched = _resched(card);
if (conf.has("mult") && resched) {
// review that's lapsed
card.setIvl(Math.max(Math.max(1, (int) (card.getIvl() * conf.getDouble("mult"))), conf.getInt("minInt")));
} else {
// new card; no ivl adjustment
// pass
}
if (resched && card.getODid() != 0) {
card.setODue(mToday + 1);
}
}
int delay = _delayForGrade(conf, card.getLeft());
if (card.getDue() < getTime().intTime()) {
// not collapsed; add some randomness
delay *= Utils.randomFloatInRange(1f, 1.25f);
}
card.setDue(getTime().intTime() + delay);
// due today?
if (card.getDue() < mDayCutoff) {
mLrnCount += card.getLeft() / 1000;
// if the queue is not empty and there's nothing else to do, make
// sure we don't put it at the head of the queue and end up showing
// it twice in a row
card.setQueue(Consts.QUEUE_TYPE_LRN);
if (!mLrnQueue.isEmpty() && revCount() == 0 && newCount() == 0) {
long smallestDue = mLrnQueue.getFirstDue();
card.setDue(Math.max(card.getDue(), smallestDue + 1));
}
_sortIntoLrn(card.getDue(), card.getId());
} else {
// the card is due in one or more days, so we need to use the day learn queue
long ahead = ((card.getDue() - mDayCutoff) / SECONDS_PER_DAY) + 1;
card.setDue(mToday + ahead);
card.setQueue(Consts.QUEUE_TYPE_DAY_LEARN_RELEARN);
}
}
_logLrn(card, ease, conf, leaving, type, lastLeft);
}
use of com.ichi2.anki.CardBrowser.Column.EASE in project AnkiChinaAndroid by ankichinateam.
the class FinderTest method test_findCards.
@Test
public void test_findCards() {
Collection col = getCol();
Note note = col.newNote();
note.setItem("Front", "dog");
note.setItem("Back", "cat");
note.addTag("monkey animal_1 * %");
col.addNote(note);
long n1id = note.getId();
long firstCardId = note.cards().get(0).getId();
note = col.newNote();
note.setItem("Front", "goats are fun");
note.setItem("Back", "sheep");
note.addTag("sheep goat horse animal11");
col.addNote(note);
long n2id = note.getId();
note = col.newNote();
note.setItem("Front", "cat");
note.setItem("Back", "sheep");
col.addNote(note);
Card catCard = note.cards().get(0);
Model m = col.getModels().current();
m = col.getModels().copy(m);
Models mm = col.getModels();
JSONObject t = Models.newTemplate("Reverse");
t.put("qfmt", "{{Back}}");
t.put("afmt", "{{Front}}");
mm.addTemplateModChanged(m, t);
mm.save(m);
note = col.newNote();
note.setItem("Front", "test");
note.setItem("Back", "foo bar");
col.addNote(note);
col.save();
List<Long> latestCardIds = note.cids();
// tag searches
assertEquals(5, col.findCards("tag:*").size());
assertEquals(1, col.findCards("tag:\\*").size());
assertEquals(5, col.findCards("tag:%").size());
assertEquals(1, col.findCards("tag:\\%").size());
assertEquals(2, col.findCards("tag:animal_1").size());
assertEquals(1, col.findCards("tag:animal\\_1").size());
assertEquals(0, col.findCards("tag:donkey").size());
assertEquals(1, col.findCards("tag:sheep").size());
assertEquals(1, col.findCards("tag:sheep tag:goat").size());
assertEquals(0, col.findCards("tag:sheep tag:monkey").size());
assertEquals(1, col.findCards("tag:monkey").size());
assertEquals(1, col.findCards("tag:sheep -tag:monkey").size());
assertEquals(4, col.findCards("-tag:sheep").size());
col.getTags().bulkAdd(col.getDb().queryLongList("select id from notes"), "foo bar");
assertEquals(5, col.findCards("tag:foo").size());
assertEquals(5, col.findCards("tag:bar").size());
col.getTags().bulkRem(col.getDb().queryLongList("select id from notes"), "foo");
assertEquals(0, col.findCards("tag:foo").size());
assertEquals(5, col.findCards("tag:bar").size());
// text searches
assertEquals(2, col.findCards("cat").size());
assertEquals(1, col.findCards("cat -dog").size());
assertEquals(1, col.findCards("cat -dog").size());
assertEquals(1, col.findCards("are goats").size());
assertEquals(0, col.findCards("\"are goats\"").size());
assertEquals(1, col.findCards("\"goats are\"").size());
// card states
Card c = note.cards().get(0);
c.setQueue(QUEUE_TYPE_REV);
c.setType(CARD_TYPE_REV);
assertEquals(0, col.findCards("is:review").size());
c.flush();
assertEqualsArrayList((new Long[] { c.getId() }), col.findCards("is:review"));
assertEquals(0, col.findCards("is:due").size());
c.setDue(0);
c.setQueue(QUEUE_TYPE_REV);
c.flush();
assertEqualsArrayList((new Long[] { c.getId() }), col.findCards("is:due"));
assertEquals(4, col.findCards("-is:due").size());
c.setQueue(QUEUE_TYPE_SUSPENDED);
// ensure this card gets a later mod time
c.flush();
col.getDb().execute("update cards set mod = mod + 1 where id = ?", c.getId());
assertEqualsArrayList((new Long[] { c.getId() }), col.findCards("is:suspended"));
// nids
assertEquals(0, col.findCards("nid:54321").size());
assertEquals(2, col.findCards("nid:" + note.getId()).size());
assertEquals(2, col.findCards("nid:" + n1id + "," + n2id).size());
// templates
assertEquals(0, col.findCards("card:foo").size());
assertEquals(4, col.findCards("\"card:card 1\"").size());
assertEquals(1, col.findCards("card:reverse").size());
assertEquals(4, col.findCards("card:1").size());
assertEquals(1, col.findCards("card:2").size());
// fields
assertEquals(1, col.findCards("front:dog").size());
assertEquals(4, col.findCards("-front:dog").size());
assertEquals(0, col.findCards("front:sheep").size());
assertEquals(2, col.findCards("back:sheep").size());
assertEquals(3, col.findCards("-back:sheep").size());
assertEquals(0, col.findCards("front:do").size());
assertEquals(5, col.findCards("front:*").size());
// ordering
col.getConf().put("sortType", "noteCrt");
col.flush();
assertTrue(latestCardIds.contains(getLastListElement(col.findCards("front:*", true))));
assertTrue(latestCardIds.contains(getLastListElement(col.findCards("", true))));
col.getConf().put("sortType", "noteFld");
col.flush();
assertEquals(catCard.getId(), (long) col.findCards("", true).get(0));
assertTrue(latestCardIds.contains(getLastListElement(col.findCards("", true))));
col.getConf().put("sortType", "cardMod");
col.flush();
assertTrue(latestCardIds.contains(getLastListElement(col.findCards("", true))));
assertEquals(firstCardId, (long) col.findCards("", true).get(0));
col.getConf().put("sortBackwards", true);
col.flush();
assertTrue(latestCardIds.contains(col.findCards("", true).get(0)));
/* TODO: Port BuiltinSortKind
assertEquals(firstCardId,
col.findCards("", BuiltinSortKind.CARD_DUE, reverse=false).get(0)
);
assertNotEquals(firstCardId,
col.findCards("", BuiltinSortKind.CARD_DUE, reverse=true).get(0));
*/
// model
assertEquals(3, col.findCards("note:basic").size());
assertEquals(2, col.findCards("-note:basic").size());
assertEquals(5, col.findCards("-note:foo").size());
// col
assertEquals(5, col.findCards("deck:default").size());
assertEquals(0, col.findCards("-deck:default").size());
assertEquals(5, col.findCards("-deck:foo").size());
assertEquals(5, col.findCards("deck:def*").size());
assertEquals(5, col.findCards("deck:*EFAULT").size());
assertEquals(0, col.findCards("deck:*cefault").size());
// full search
note = col.newNote();
note.setItem("Front", "hello<b>world</b>");
note.setItem("Back", "abc");
col.addNote(note);
// as it's the sort field, it matches
assertEquals(2, col.findCards("helloworld").size());
// assertEquals(, col.findCards("helloworld", full=true).size())2 This is commented upstream
// if we put it on the back, it won't
String note_front = note.getItem("Front");
String note_back = note.getItem("Back");
note.setItem("Front", note_back);
note.setItem("Back", note_front);
note.flush();
assertEquals(0, col.findCards("helloworld").size());
// Those lines are commented above
// assertEquals(, col.findCards("helloworld", full=true).size())2
// assertEquals(, col.findCards("back:helloworld", full=true).size())2
// searching for an invalid special tag should not error
// TODO: ensure the search fail
// assertThrows(Exception.class, () -> col.findCards("is:invalid").size());
// should be able to limit to parent col, no children
long id = col.getDb().queryLongScalar("select id from cards limit 1");
col.getDb().execute("update cards set did = ? where id = ?", col.getDecks().id("Default::Child"), id);
col.save();
assertEquals(7, col.findCards("deck:default").size());
assertEquals(1, col.findCards("deck:default::child").size());
assertEquals(6, col.findCards("deck:default -deck:default::*").size());
// properties
id = col.getDb().queryLongScalar("select id from cards limit 1");
col.getDb().execute("update cards set queue=2, ivl=10, reps=20, due=30, factor=2200 where id = ?", id);
assertEquals(1, col.findCards("prop:ivl>5").size());
assertThat(col.findCards("prop:ivl<5").size(), greaterThan(1));
assertEquals(1, col.findCards("prop:ivl>=5").size());
assertEquals(0, col.findCards("prop:ivl=9").size());
assertEquals(1, col.findCards("prop:ivl=10").size());
assertThat(col.findCards("prop:ivl!=10").size(), greaterThan(1));
assertEquals(1, col.findCards("prop:due>0").size());
// due dates should work
assertEquals(0, col.findCards("prop:due=29").size());
assertEquals(1, col.findCards("prop:due=30").size());
// ease factors
assertEquals(0, col.findCards("prop:ease=2.3").size());
assertEquals(1, col.findCards("prop:ease=2.2").size());
assertEquals(1, col.findCards("prop:ease>2").size());
assertThat(col.findCards("-prop:ease>2").size(), greaterThan(1));
// recently failed
if (!isNearCutoff(col)) {
assertEquals(0, col.findCards("rated:1:1").size());
assertEquals(0, col.findCards("rated:1:2").size());
c = col.getSched().getCard();
col.getSched().answerCard(c, 2);
assertEquals(0, col.findCards("rated:1:1").size());
assertEquals(1, col.findCards("rated:1:2").size());
c = col.getSched().getCard();
col.getSched().answerCard(c, 1);
assertEquals(1, col.findCards("rated:1:1").size());
assertEquals(1, col.findCards("rated:1:2").size());
assertEquals(2, col.findCards("rated:1").size());
assertEquals(0, col.findCards("rated:0:2").size());
assertEquals(1, col.findCards("rated:2:2").size());
// added
assertEquals(0, col.findCards("added:0").size());
col.getDb().execute("update cards set id = id - " + SECONDS_PER_DAY * 1000 + " where id = ?", id);
assertEquals(col.cardCount() - 1, col.findCards("added:1").size());
assertEquals(col.cardCount(), col.findCards("added:2").size());
} else {
Timber.w("some find tests disabled near cutoff");
}
// empty field
assertEquals(0, col.findCards("front:").size());
note = col.newNote();
note.setItem("Front", "");
note.setItem("Back", "abc2");
assertEquals(1, col.addNote(note));
assertEquals(1, col.findCards("front:").size());
// OR searches and nesting
assertEquals(2, col.findCards("tag:monkey or tag:sheep").size());
assertEquals(2, col.findCards("(tag:monkey OR tag:sheep)").size());
assertEquals(6, col.findCards("-(tag:monkey OR tag:sheep)").size());
assertEquals(2, col.findCards("tag:monkey or (tag:sheep sheep)").size());
assertEquals(1, col.findCards("tag:monkey or (tag:sheep octopus)").size());
// flag
// Todo: ensure it fails
// assertThrows(Exception.class, () -> col.findCards("flag:12"));
}
Aggregations