use of com.ichi2.anki.CardBrowser.Column.TAGS in project Anki-Android by ankidroid.
the class CardContentProvider method bulkInsertNotes.
/**
* This implementation optimizes for when the notes are grouped according to model.
*/
private int bulkInsertNotes(ContentValues[] valuesArr, long deckId) {
if (valuesArr == null || valuesArr.length == 0) {
return 0;
}
Collection col = CollectionHelper.getInstance().getCol(mContext);
if (col == null) {
throw new IllegalStateException(COL_NULL_ERROR_MSG);
}
if (col.getDecks().isDyn(deckId)) {
throw new IllegalArgumentException("A filtered deck cannot be specified as the deck in bulkInsertNotes");
}
col.log(String.format(Locale.US, "bulkInsertNotes: %d items.\n%s", valuesArr.length, getLogMessage("bulkInsert", null)));
// for caching model information (so we don't have to query for each note)
long modelId = NOT_FOUND_NOTE_TYPE;
Model model = null;
// is it okay to move this outside the for-loop? Is it needed at all?
col.getDecks().flush();
SupportSQLiteDatabase sqldb = col.getDb().getDatabase();
try {
int result = 0;
sqldb.beginTransaction();
for (int i = 0; i < valuesArr.length; i++) {
ContentValues values = valuesArr[i];
if (values == null) {
continue;
}
String flds = values.getAsString(FlashCardsContract.Note.FLDS);
if (flds == null) {
continue;
}
Models.AllowEmpty allowEmpty = Models.AllowEmpty.fromBoolean(values.getAsBoolean(FlashCardsContract.Note.ALLOW_EMPTY));
Long thisModelId = values.getAsLong(FlashCardsContract.Note.MID);
if (thisModelId == null || thisModelId < 0) {
Timber.d("Unable to get model at index: %d", i);
continue;
}
String[] fldsArray = Utils.splitFields(flds);
if (model == null || thisModelId != modelId) {
// new modelId so need to recalculate model, modelId and invalidate duplicateChecker (which is based on previous model)
model = col.getModels().get(thisModelId);
modelId = thisModelId;
}
// Create empty note
// for some reason we cannot pass modelId in here
com.ichi2.libanki.Note newNote = new com.ichi2.libanki.Note(col, model);
// Check that correct number of flds specified
if (fldsArray.length != newNote.getFields().length) {
throw new IllegalArgumentException("Incorrect flds argument : " + flds);
}
for (int idx = 0; idx < fldsArray.length; idx++) {
newNote.setField(idx, fldsArray[idx]);
}
// Set tags
String tags = values.getAsString(FlashCardsContract.Note.TAGS);
if (tags != null) {
newNote.setTagsFromStr(tags);
}
// Add to collection
col.addNote(newNote, allowEmpty);
for (Card card : newNote.cards()) {
card.setDid(deckId);
card.flush();
}
result++;
}
col.save();
sqldb.setTransactionSuccessful();
return result;
} finally {
DB.safeEndInTransaction(sqldb);
}
}
use of com.ichi2.anki.CardBrowser.Column.TAGS in project Anki-Android by ankidroid.
the class ZipFile method exportInto.
/**
* Export source database into new destination database Note: The following python syntax isn't supported in
* Android: for row in mSrc.db.execute("select * from cards where id in "+ids2str(cids)): therefore we use a
* different method for copying tables
*
* @param path String path to destination database
* @throws JSONException
* @throws IOException
*/
public void exportInto(@NonNull String path, Context context) throws JSONException, IOException, ImportExportException {
// create a new collection at the target
new File(path).delete();
Collection dst = Storage.Collection(context, path);
mSrc = mCol;
// find cards
Long[] cids = cardIds();
// attach dst to src so we can copy data between them. This isn't done in original libanki as Python more
// flexible
dst.close();
Timber.d("Attach DB");
mSrc.getDb().getDatabase().execSQL("ATTACH '" + path + "' AS DST_DB");
// copy cards, noting used nids (as unique set)
Timber.d("Copy cards");
mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.cards select * from cards where id in " + Utils.ids2str(cids));
List<Long> uniqueNids = mSrc.getDb().queryLongList("select distinct nid from cards where id in " + Utils.ids2str(cids));
// notes
Timber.d("Copy notes");
String strnids = Utils.ids2str(uniqueNids);
mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.notes select * from notes where id in " + strnids);
// remove system tags if not exporting scheduling info
if (!mIncludeSched) {
Timber.d("Stripping system tags from list");
ArrayList<String> srcTags = mSrc.getDb().queryStringList("select tags from notes where id in " + strnids);
ArrayList<Object[]> args = new ArrayList<>(srcTags.size());
Object[] arg = new Object[2];
for (int row = 0; row < srcTags.size(); row++) {
arg[0] = removeSystemTags(srcTags.get(row));
arg[1] = uniqueNids.get(row);
args.add(row, arg);
}
mSrc.getDb().executeMany("UPDATE DST_DB.notes set tags=? where id=?", args);
}
// models used by the notes
Timber.d("Finding models used by notes");
ArrayList<Long> mids = mSrc.getDb().queryLongList("select distinct mid from DST_DB.notes where id in " + strnids);
// card history and revlog
if (mIncludeSched) {
Timber.d("Copy history and revlog");
mSrc.getDb().getDatabase().execSQL("insert into DST_DB.revlog select * from revlog where cid in " + Utils.ids2str(cids));
// reopen collection to destination database (different from original python code)
mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
dst.reopen();
} else {
Timber.d("Detaching destination db and reopening");
// first reopen collection to destination database (different from original python code)
mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
dst.reopen();
// then need to reset card state
Timber.d("Resetting cards");
dst.getSched().resetCards(cids);
}
// models - start with zero
Timber.d("Copy models");
for (Model m : mSrc.getModels().all()) {
if (mids.contains(m.getLong("id"))) {
dst.getModels().update(m);
}
}
// decks
Timber.d("Copy decks");
java.util.Collection<Long> dids = null;
if (mDid != null) {
dids = new HashSet<>(mSrc.getDecks().children(mDid).values());
dids.add(mDid);
}
JSONObject dconfs = new JSONObject();
for (Deck d : mSrc.getDecks().all()) {
if ("1".equals(d.getString("id"))) {
continue;
}
if (dids != null && !dids.contains(d.getLong("id"))) {
continue;
}
if (d.isStd() && d.getLong("conf") != 1L) {
if (mIncludeSched) {
dconfs.put(Long.toString(d.getLong("conf")), true);
}
}
Deck destinationDeck = d.deepClone();
if (!mIncludeSched) {
// scheduling not included, so reset deck settings to default
destinationDeck.put("conf", 1);
}
dst.getDecks().update(destinationDeck);
}
// copy used deck confs
Timber.d("Copy deck options");
for (DeckConfig dc : mSrc.getDecks().allConf()) {
if (dconfs.has(dc.getString("id"))) {
dst.getDecks().updateConf(dc);
}
}
// find used media
Timber.d("Find used media");
JSONObject media = new JSONObject();
mMediaDir = mSrc.getMedia().dir();
if (mIncludeMedia) {
ArrayList<Long> mid = mSrc.getDb().queryLongList("select mid from notes where id in " + strnids);
ArrayList<String> flds = mSrc.getDb().queryStringList("select flds from notes where id in " + strnids);
for (int idx = 0; idx < mid.size(); idx++) {
for (String file : mSrc.getMedia().filesInStr(mid.get(idx), flds.get(idx))) {
// skip files in subdirs
if (file.contains(File.separator)) {
continue;
}
media.put(file, true);
}
}
if (mMediaDir != null) {
for (File f : new File(mMediaDir).listFiles()) {
if (f.isDirectory()) {
continue;
}
String fname = f.getName();
if (fname.startsWith("_")) {
// Loop through every model that will be exported, and check if it contains a reference to f
for (JSONObject model : mSrc.getModels().all()) {
if (_modelHasMedia(model, fname)) {
media.put(fname, true);
break;
}
}
}
}
}
}
JSONArray keys = media.names();
if (keys != null) {
mMediaFiles.ensureCapacity(keys.length());
addAll(mMediaFiles, keys.stringIterable());
}
Timber.d("Cleanup");
dst.setCrt(mSrc.getCrt());
// todo: tags?
mCount = dst.cardCount();
dst.setMod();
postExport();
dst.close();
}
use of com.ichi2.anki.CardBrowser.Column.TAGS in project Anki-Android by ankidroid.
the class AbstractFlashcardViewer method showTagsDialog.
protected void showTagsDialog() {
ArrayList<String> tags = new ArrayList<>(getCol().getTags().all());
ArrayList<String> selTags = new ArrayList<>(mCurrentCard.note().getTags());
TagsDialog dialog = mTagsDialogFactory.newTagsDialog().withArguments(TagsDialog.DialogType.EDIT_TAGS, selTags, tags);
showDialogFragment(dialog);
}
use of com.ichi2.anki.CardBrowser.Column.TAGS in project Anki-Android by ankidroid.
the class ContentProviderTest method testUpdateTags.
/**
* Update tags on a note
*/
@Test
public void testUpdateTags() {
// get the first card due
// ----------------------
Collection col = getCol();
Card card = getFirstCardFromScheduler(col);
Note note = card.note();
long noteId = note.getId();
// make sure the tag is what we expect initially
// ---------------------------------------------
List<String> tagList = note.getTags();
assertEquals("only one tag", 1, tagList.size());
assertEquals("check tag value", TEST_TAG, tagList.get(0));
// update tags
// -----------
String tag2 = "mynewtag";
ContentResolver cr = getContentResolver();
Uri updateNoteUri = Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, Long.toString(noteId));
ContentValues values = new ContentValues();
values.put(FlashCardsContract.Note.TAGS, TEST_TAG + " " + tag2);
int updateCount = cr.update(updateNoteUri, values, null, null);
assertEquals("updateCount is 1", 1, updateCount);
// lookup the note now and verify tags
// -----------------------------------
Note noteAfterUpdate = col.getNote(noteId);
List<String> newTagList = noteAfterUpdate.getTags();
assertEquals("two tags", 2, newTagList.size());
assertEquals("check first tag", TEST_TAG, newTagList.get(0));
assertEquals("check second tag", tag2, newTagList.get(1));
}
use of com.ichi2.anki.CardBrowser.Column.TAGS in project Anki-Android by ankidroid.
the class ImportTest method testCsv.
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
public void testCsv() throws IOException {
String file = Shared.getTestFilePath(getTestContext(), "text-2fields.txt");
TextImporter i = new TextImporter(mTestCol, file);
i.initMapping();
i.run();
if (TestEnvironment.isDisplayingDefaultEnglishStrings()) {
assertThat(i.getLog(), contains("‘多すぎる too many fields’ had 3 fields, expected 2", "‘not, enough, fields’ had 1 fields, expected 2", "Appeared twice in file: 飲む", "Empty first field: to play", "5 notes added, 0 notes updated, 0 notes unchanged."));
} else {
assertThat(i.getLog(), hasSize(5));
}
assertEquals(5, i.getTotal());
// if we run the import again, it should update instead
i.run();
if (TestEnvironment.isDisplayingDefaultEnglishStrings()) {
assertThat(i.getLog(), contains("‘多すぎる too many fields’ had 3 fields, expected 2", "‘not, enough, fields’ had 1 fields, expected 2", "Appeared twice in file: 飲む", "Empty first field: to play", "0 notes added, 0 notes updated, 5 notes unchanged.", "First field matched: 食べる", "First field matched: 飲む", "First field matched: テスト", "First field matched: to eat", "First field matched: 遊ぶ"));
} else {
assertThat(i.getLog(), hasSize(10));
}
assertEquals(5, i.getTotal());
// but importing should not clobber tags if they're unmapped
Note n = mTestCol.getNote(mTestCol.getDb().queryLongScalar("select id from notes"));
n.addTag("test");
n.flush();
i.run();
n.load();
assertThat(n.getTags(), contains("test"));
assertThat(n.getTags(), hasSize(1));
// if add-only mode, count will be 0
i.setImportMode(NoteImporter.ImportMode.IGNORE_MODE);
i.run();
assertEquals(0, i.getTotal());
// and if dupes mode, will reimport everything
assertEquals(5, mTestCol.cardCount());
i.setImportMode(NoteImporter.ImportMode.ADD_MODE);
i.run();
// includes repeated field
assertEquals(6, i.getTotal());
assertEquals(11, mTestCol.cardCount());
}
Aggregations