Search in sources :

Example 51 with TAGS

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);
    }
}
Also used : ContentValues(android.content.ContentValues) Note(com.ichi2.libanki.Note) Card(com.ichi2.libanki.Card) SupportSQLiteDatabase(androidx.sqlite.db.SupportSQLiteDatabase) Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) Collection(com.ichi2.libanki.Collection) Models(com.ichi2.libanki.Models)

Example 52 with TAGS

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();
}
Also used : ArrayList(java.util.ArrayList) JSONArray(com.ichi2.utils.JSONArray) SuppressLint(android.annotation.SuppressLint) JSONObject(com.ichi2.utils.JSONObject) JSONObject(com.ichi2.utils.JSONObject) File(java.io.File)

Example 53 with TAGS

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);
}
Also used : ArrayList(java.util.ArrayList) TagsDialog(com.ichi2.anki.dialogs.tags.TagsDialog)

Example 54 with TAGS

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));
}
Also used : ContentValues(android.content.ContentValues) Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) Uri(android.net.Uri) Card(com.ichi2.libanki.Card) ContentResolver(android.content.ContentResolver) Test(org.junit.Test)

Example 55 with TAGS

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());
}
Also used : Note(com.ichi2.libanki.Note) TextImporter(com.ichi2.libanki.importer.TextImporter) Test(org.junit.Test) InstrumentedTest(com.ichi2.anki.tests.InstrumentedTest) SdkSuppress(androidx.test.filters.SdkSuppress)

Aggregations

JSONObject (com.ichi2.utils.JSONObject)27 Note (com.ichi2.libanki.Note)18 JSONArray (com.ichi2.utils.JSONArray)18 ArrayList (java.util.ArrayList)13 Card (com.ichi2.libanki.Card)12 Collection (com.ichi2.libanki.Collection)12 Model (com.ichi2.libanki.Model)11 Map (java.util.Map)10 HashMap (java.util.HashMap)9 SuppressLint (android.annotation.SuppressLint)8 Resources (android.content.res.Resources)8 Deck (com.ichi2.libanki.Deck)8 JSONException (com.ichi2.utils.JSONException)8 JSONException (org.json.JSONException)8 Intent (android.content.Intent)7 View (android.view.View)7 TextView (android.widget.TextView)7 Pair (android.util.Pair)5 ContentValues (android.content.ContentValues)4 Uri (android.net.Uri)4