Search in sources :

Example 6 with IMPORT

use of com.ichi2.async.CollectionTask.TASK_TYPE.IMPORT in project AnkiChinaAndroid by ankichinateam.

the class NoteImporter method importNotes.

/**
 * Convert each card into a note, apply attributes and add to col.
 */
public void importNotes(List<ForeignNote> notes) {
    Assert.that(mappingOk());
    // note whether tags are mapped
    mTagsMapped = false;
    for (String f : mMapping) {
        if ("_tags".equals(f)) {
            mTagsMapped = true;
            break;
        }
    }
    // gather checks for duplicate comparison
    HashMap<Long, List<Long>> csums = new HashMap<>();
    try (Cursor c = mCol.getDb().query("select csum, id from notes where mid = ?", mModel.getLong("id"))) {
        while (c.moveToNext()) {
            long csum = c.getLong(0);
            long id = c.getLong(1);
            if (csums.containsKey(csum)) {
                csums.get(csum).add(id);
            } else {
                csums.put(csum, new ArrayList<>(Collections.singletonList(id)));
            }
        }
    }
    HashMap<String, Boolean> firsts = new HashMap<>();
    int fld0index = mMapping.indexOf(mModel.getJSONArray("flds").getJSONObject(0).getString("name"));
    mFMap = mCol.getModels().fieldMap(mModel);
    mNextId = mCol.getTime().timestampID(mCol.getDb(), "notes");
    // loop through the notes
    List<Object[]> updates = new ArrayList<>();
    List<String> updateLog = new ArrayList<>();
    // PORT: Translations moved closer to their sources
    List<Object[]> _new = new ArrayList<>();
    _ids = new ArrayList<>();
    _cards = new ArrayList<>();
    mEmptyNotes = false;
    int dupeCount = 0;
    List<String> dupes = new ArrayList<>();
    for (ForeignNote n : notes) {
        for (int c = 0; c < n.mFields.size(); c++) {
            if (!this.mAllowHTML) {
                n.mFields.set(c, HtmlUtils.escape(n.mFields.get(c)));
            }
            n.mFields.set(c, n.mFields.get(c).trim());
            if (!this.mAllowHTML) {
                n.mFields.set(c, n.mFields.get(c).replace("\n", "<br>"));
            }
        }
        String fld0 = n.mFields.get(fld0index);
        long csum = fieldChecksum(fld0);
        // first field must exist
        if (fld0 == null || fld0.length() == 0) {
            getLog().add(getString(R.string.note_importer_error_empty_first_field, TextUtils.join(" ", n.mFields)));
            continue;
        }
        // earlier in import?
        if (firsts.containsKey(fld0) && mImportMode != ADD_MODE) {
            // duplicates in source file; log and ignore
            getLog().add(getString(R.string.note_importer_error_appeared_twice, fld0));
            continue;
        }
        firsts.put(fld0, true);
        // already exists?
        boolean found = false;
        if (csums.containsKey(csum)) {
            // csum is not a guarantee; have to check
            for (Long id : csums.get(csum)) {
                String flds = mCol.getDb().queryString("select flds from notes where id = ?", id);
                String[] sflds = splitFields(flds);
                if (fld0.equals(sflds[0])) {
                    // duplicate
                    found = true;
                    if (mImportMode == UPDATE_MODE) {
                        Object[] data = updateData(n, id, sflds);
                        if (data != null && data.length > 0) {
                            updates.add(data);
                            updateLog.add(getString(R.string.note_importer_error_first_field_matched, fld0));
                            dupeCount += 1;
                            found = true;
                        }
                    } else if (mImportMode == IGNORE_MODE) {
                        dupeCount += 1;
                    } else if (mImportMode == ADD_MODE) {
                        // allow duplicates in this case
                        if (!dupes.contains(fld0)) {
                            // only show message once, no matter how many
                            // duplicates are in the collection already
                            updateLog.add(getString(R.string.note_importer_error_added_duplicate_first_field, fld0));
                            dupes.add(fld0);
                        }
                        found = false;
                    }
                }
            }
        }
        // newly add
        if (!found) {
            Object[] data = newData(n);
            if (data != null && data.length > 0) {
                _new.add(data);
                // note that we've seen this note once already
                firsts.put(fld0, true);
            }
        }
    }
    addNew(_new);
    addUpdates(updates);
    // make sure to update sflds, etc
    mCol.updateFieldCache(collection2Array(_ids));
    // generate cards
    if (!mCol.genCards(_ids).isEmpty()) {
        this.getLog().add(0, getString(R.string.note_importer_empty_cards_found));
    }
    // apply scheduling updates
    updateCards();
    // we randomize or order here, to ensure that siblings
    // have the same due#
    long did = mCol.getDecks().selected();
    DeckConfig conf = mCol.getDecks().confForDid(did);
    // in order due?
    if (conf.getJSONObject("new").getInt("order") == NEW_CARDS_RANDOM) {
        mCol.getSched().randomizeCards(did);
    }
    String part1 = getQuantityString(R.plurals.note_importer_notes_added, _new.size());
    String part2 = getQuantityString(R.plurals.note_importer_notes_updated, mUpdateCount);
    int unchanged;
    if (mImportMode == UPDATE_MODE) {
        unchanged = dupeCount - mUpdateCount;
    } else if (mImportMode == IGNORE_MODE) {
        unchanged = dupeCount;
    } else {
        unchanged = 0;
    }
    String part3 = getQuantityString(R.plurals.note_importer_notes_unchanged, unchanged);
    mLog.add(String.format("%s, %s, %s.", part1, part2, part3));
    mLog.addAll(updateLog);
    if (mEmptyNotes) {
        mLog.add(getString(R.string.note_importer_error_empty_notes));
    }
    mTotal = _ids.size();
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) ArrayList(java.util.ArrayList) List(java.util.List) JSONObject(com.ichi2.utils.JSONObject) DeckConfig(com.ichi2.libanki.DeckConfig)

Example 7 with IMPORT

use of com.ichi2.async.CollectionTask.TASK_TYPE.IMPORT in project Anki-Android by ankidroid.

the class DeckPicker method onOptionsItemSelected.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Resources res = getResources();
    if (getDrawerToggle().onOptionsItemSelected(item)) {
        return true;
    }
    int itemId = item.getItemId();
    if (itemId == R.id.action_undo) {
        Timber.i("DeckPicker:: Undo button pressed");
        undo();
        return true;
    } else if (itemId == R.id.action_sync) {
        Timber.i("DeckPicker:: Sync button pressed");
        sync();
        return true;
    } else if (itemId == R.id.action_import) {
        Timber.i("DeckPicker:: Import button pressed");
        showDialogFragment(ImportFileSelectionFragment.createInstance(this));
        return true;
    } else if (itemId == R.id.action_new_filtered_deck) {
        CreateDeckDialog createFilteredDeckDialog = new CreateDeckDialog(DeckPicker.this, R.string.new_deck, CreateDeckDialog.DeckDialogType.FILTERED_DECK, null);
        createFilteredDeckDialog.setOnNewDeckCreated((id) -> {
            // a filtered deck was created
            openStudyOptions(true);
        });
        createFilteredDeckDialog.showFilteredDeckDialog();
        return true;
    } else if (itemId == R.id.action_check_database) {
        Timber.i("DeckPicker:: Check database button pressed");
        showDatabaseErrorDialog(DatabaseErrorDialog.DIALOG_CONFIRM_DATABASE_CHECK);
        return true;
    } else if (itemId == R.id.action_check_media) {
        Timber.i("DeckPicker:: Check media button pressed");
        showMediaCheckDialog(MediaCheckDialog.DIALOG_CONFIRM_MEDIA_CHECK);
        return true;
    } else if (itemId == R.id.action_empty_cards) {
        Timber.i("DeckPicker:: Empty cards button pressed");
        handleEmptyCards();
        return true;
    } else if (itemId == R.id.action_model_browser_open) {
        Timber.i("DeckPicker:: Model browser button pressed");
        Intent noteTypeBrowser = new Intent(this, ModelBrowser.class);
        startActivityForResultWithAnimation(noteTypeBrowser, 0, START);
        return true;
    } else if (itemId == R.id.action_restore_backup) {
        Timber.i("DeckPicker:: Restore from backup button pressed");
        showDatabaseErrorDialog(DatabaseErrorDialog.DIALOG_CONFIRM_RESTORE_BACKUP);
        return true;
    } else if (itemId == R.id.action_export) {
        Timber.i("DeckPicker:: Export collection button pressed");
        String msg = getResources().getString(R.string.confirm_apkg_export);
        mExportingDelegate.showExportDialog(msg);
        return true;
    }
    return super.onOptionsItemSelected(item);
}
Also used : DividerItemDecoration(androidx.recyclerview.widget.DividerItemDecoration) StartupFailure(com.ichi2.anki.InitialActivity.StartupFailure) Bundle(android.os.Bundle) NonNull(androidx.annotation.NonNull) Uri(android.net.Uri) DialogHandler(com.ichi2.anki.dialogs.DialogHandler) Drawable(android.graphics.drawable.Drawable) ShortcutManagerCompat(androidx.core.content.pm.ShortcutManagerCompat) Manifest(android.Manifest) Decks(com.ichi2.libanki.Decks) Fragment(androidx.fragment.app.Fragment) JSONException(com.ichi2.utils.JSONException) ContextCompat(androidx.core.content.ContextCompat) DeckPickerBackupNoSpaceLeftDialog(com.ichi2.anki.dialogs.DeckPickerBackupNoSpaceLeftDialog) IntentFilter(android.content.IntentFilter) Triple(com.ichi2.utils.Triple) SearchView(androidx.appcompat.widget.SearchView) DeckPickerContextMenu(com.ichi2.anki.dialogs.DeckPickerContextMenu) Cancellable(com.ichi2.async.Cancellable) DeckRenameException(com.ichi2.libanki.backend.exception.DeckRenameException) StringRes(androidx.annotation.StringRes) Unit(kotlin.Unit) Nullable(androidx.annotation.Nullable) Message(android.os.Message) HostNumFactory(com.ichi2.anki.web.HostNumFactory) CompatHelper(com.ichi2.compat.CompatHelper) DeckAdapter(com.ichi2.anki.widgets.DeckAdapter) LinearLayoutManager(androidx.recyclerview.widget.LinearLayoutManager) DeckPickerNoSpaceToDowngradeDialog(com.ichi2.anki.dialogs.DeckPickerNoSpaceToDowngradeDialog) DeckPickerConfirmDeleteDeckDialog(com.ichi2.anki.dialogs.DeckPickerConfirmDeleteDeckDialog) Direction(com.ichi2.anim.ActivityTransitionAnimation.Direction) FULL_DOWNLOAD(com.ichi2.async.Connection.ConflictResolution.FULL_DOWNLOAD) SdCardReceiver(com.ichi2.anki.receiver.SdCardReceiver) Editor(android.content.SharedPreferences.Editor) CustomSyncServerUrlException(com.ichi2.libanki.sync.CustomSyncServerUrlException) FileSizeFormatter(com.ichi2.anki.dialogs.DeckPickerNoSpaceToDowngradeDialog.FileSizeFormatter) DeckPickerNoSpaceLeftDialog(com.ichi2.anki.dialogs.DeckPickerNoSpaceLeftDialog) StudyOptionsListener(com.ichi2.anki.StudyOptionsFragment.StudyOptionsListener) BadgeDrawableBuilder(com.ichi2.ui.BadgeDrawableBuilder) Menu(android.view.Menu) DeckService(com.ichi2.anki.servicelayer.DeckService) Connection(com.ichi2.async.Connection) Settings(android.provider.Settings) AnkiPackageImporter(com.ichi2.libanki.importer.AnkiPackageImporter) SwipeRefreshLayout(androidx.swiperefreshlayout.widget.SwipeRefreshLayout) CollectionIntegrityStorageCheck(com.ichi2.anki.CollectionHelper.CollectionIntegrityStorageCheck) ActivityExportingDelegate(com.ichi2.anki.export.ActivityExportingDelegate) TextUtils(android.text.TextUtils) File(java.io.File) SharedPreferences(android.content.SharedPreferences) TypedValue(android.util.TypedValue) IconCompat(androidx.core.graphics.drawable.IconCompat) ImportUtils(com.ichi2.utils.ImportUtils) EditText(android.widget.EditText) SchedulerService(com.ichi2.anki.servicelayer.SchedulerService) LinearLayout(android.widget.LinearLayout) AsyncDialogFragment(com.ichi2.anki.dialogs.AsyncDialogFragment) PackageManager(android.content.pm.PackageManager) TaskManager(com.ichi2.async.TaskManager) WindowManager(android.view.WindowManager) UsageAnalytics(com.ichi2.anki.analytics.UsageAnalytics) ModelManager(com.ichi2.libanki.ModelManager) ConfirmationDialog(com.ichi2.anki.dialogs.ConfirmationDialog) AnkiStatsTaskHandler(com.ichi2.anki.stats.AnkiStatsTaskHandler) Permissions(com.ichi2.utils.Permissions) View(android.view.View) RecyclerView(androidx.recyclerview.widget.RecyclerView) SyncStatus(com.ichi2.utils.SyncStatus) FragmentTransaction(androidx.fragment.app.FragmentTransaction) BroadcastReceiver(android.content.BroadcastReceiver) DatabaseErrorDialog(com.ichi2.anki.dialogs.DatabaseErrorDialog) CustomStudyDialogFactory(com.ichi2.anki.dialogs.customstudy.CustomStudyDialogFactory) Timber(timber.log.Timber) UndoService(com.ichi2.anki.servicelayer.UndoService) List(java.util.List) TextView(android.widget.TextView) ImportFileSelectionFragment(com.ichi2.anki.dialogs.ImportFileSelectionFragment) RelativeLayout(android.widget.RelativeLayout) Filterable(android.widget.Filterable) TaskListenerWithContext(com.ichi2.async.TaskListenerWithContext) ViewPropertyAnimator(android.view.ViewPropertyAnimator) MaterialDialog(com.afollestad.materialdialogs.MaterialDialog) Snackbar(com.google.android.material.snackbar.Snackbar) Window(android.view.Window) VersionUtils(com.ichi2.utils.VersionUtils) Context(android.content.Context) TaskListener(com.ichi2.async.TaskListener) KeyEvent(android.view.KeyEvent) GravityEnum(com.afollestad.materialdialogs.GravityEnum) Pair(android.util.Pair) DeckPickerAnalyticsOptInDialog(com.ichi2.anki.dialogs.DeckPickerAnalyticsOptInDialog) ImportDialog(com.ichi2.anki.dialogs.ImportDialog) Intent(android.content.Intent) Collection(com.ichi2.libanki.Collection) StyledProgressDialog(com.ichi2.themes.StyledProgressDialog) PixelFormat(android.graphics.PixelFormat) TypedArray(android.content.res.TypedArray) MenuItem(android.view.MenuItem) WidgetStatus(com.ichi2.widget.WidgetStatus) SyncErrorDialog(com.ichi2.anki.dialogs.SyncErrorDialog) Payload(com.ichi2.async.Connection.Payload) Model(com.ichi2.libanki.Model) Build(android.os.Build) ShortcutInfoCompat(androidx.core.content.pm.ShortcutInfoCompat) Utils(com.ichi2.libanki.Utils) DialogInterface(android.content.DialogInterface) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) Computation(com.ichi2.utils.Computation) AbstractDeckTreeNode(com.ichi2.libanki.sched.AbstractDeckTreeNode) ActivityCompat(androidx.core.app.ActivityCompat) CreateDeckDialog(com.ichi2.anki.dialogs.CreateDeckDialog) CollectionTask(com.ichi2.async.CollectionTask) SQLException(android.database.SQLException) CustomStudyDialog(com.ichi2.anki.dialogs.customstudy.CustomStudyDialog) Syncer(com.ichi2.libanki.sync.Syncer) MediaCheckDialog(com.ichi2.anki.dialogs.MediaCheckDialog) AdaptionUtil(com.ichi2.utils.AdaptionUtil) VisibleForTesting(androidx.annotation.VisibleForTesting) Resources(android.content.res.Resources) OnClickListener(android.view.View.OnClickListener) Intent(android.content.Intent) Resources(android.content.res.Resources) CreateDeckDialog(com.ichi2.anki.dialogs.CreateDeckDialog)

Example 8 with IMPORT

use of com.ichi2.async.CollectionTask.TASK_TYPE.IMPORT in project Anki-Android by ankidroid.

the class ImportTest method testAnki2Mediadupes.

@Test
public void testAnki2Mediadupes() throws IOException, JSONException, ImportExportException {
    // add a note that references a sound
    Note n = mTestCol.newNote();
    n.setField(0, "[sound:foo.mp3]");
    long mid = n.model().getLong("id");
    mTestCol.addNote(n);
    // add that sound to the media directory
    FileOutputStream os = new FileOutputStream(new File(mTestCol.getMedia().dir(), "foo.mp3"), false);
    os.write("foo".getBytes());
    os.close();
    mTestCol.close();
    // it should be imported correctly into an empty deck
    Collection empty = getEmptyCol();
    Importer imp = new Anki2Importer(empty, mTestCol.getPath());
    imp.run();
    List<String> expected = Collections.singletonList("foo.mp3");
    List<String> actual = Arrays.asList(new File(empty.getMedia().dir()).list());
    actual.retainAll(expected);
    assertEquals(expected.size(), actual.size());
    // and importing again will not duplicate, as the file content matches
    empty.remCards(empty.getDb().queryLongList("select id from cards"));
    imp = new Anki2Importer(empty, mTestCol.getPath());
    imp.run();
    expected = Collections.singletonList("foo.mp3");
    actual = Arrays.asList(new File(empty.getMedia().dir()).list());
    actual.retainAll(expected);
    assertEquals(expected.size(), actual.size());
    n = empty.getNote(empty.getDb().queryLongScalar("select id from notes"));
    assertTrue(n.getFields()[0].contains("foo.mp3"));
    // if the local file content is different, and import should trigger a rename
    empty.remCards(empty.getDb().queryLongList("select id from cards"));
    os = new FileOutputStream(new File(empty.getMedia().dir(), "foo.mp3"), false);
    os.write("bar".getBytes());
    os.close();
    imp = new Anki2Importer(empty, mTestCol.getPath());
    imp.run();
    expected = Arrays.asList("foo.mp3", String.format("foo_%s.mp3", mid));
    actual = Arrays.asList(new File(empty.getMedia().dir()).list());
    actual.retainAll(expected);
    assertEquals(expected.size(), actual.size());
    n = empty.getNote(empty.getDb().queryLongScalar("select id from notes"));
    assertTrue(n.getFields()[0].contains("_"));
    // if the localized media file already exists, we rewrite the note and media
    empty.remCards(empty.getDb().queryLongList("select id from cards"));
    os = new FileOutputStream(new File(empty.getMedia().dir(), "foo.mp3"));
    os.write("bar".getBytes());
    os.close();
    imp = new Anki2Importer(empty, mTestCol.getPath());
    imp.run();
    expected = Arrays.asList("foo.mp3", String.format("foo_%s.mp3", mid));
    actual = Arrays.asList(new File(empty.getMedia().dir()).list());
    actual.retainAll(expected);
    assertEquals(expected.size(), actual.size());
    n = empty.getNote(empty.getDb().queryLongScalar("select id from notes"));
    assertTrue(n.getFields()[0].contains("_"));
    empty.close();
}
Also used : Anki2Importer(com.ichi2.libanki.importer.Anki2Importer) Note(com.ichi2.libanki.Note) FileOutputStream(java.io.FileOutputStream) Collection(com.ichi2.libanki.Collection) File(java.io.File) Anki2Importer(com.ichi2.libanki.importer.Anki2Importer) Importer(com.ichi2.libanki.importer.Importer) AnkiPackageImporter(com.ichi2.libanki.importer.AnkiPackageImporter) NoteImporter(com.ichi2.libanki.importer.NoteImporter) TextImporter(com.ichi2.libanki.importer.TextImporter) Test(org.junit.Test) InstrumentedTest(com.ichi2.anki.tests.InstrumentedTest)

Example 9 with IMPORT

use of com.ichi2.async.CollectionTask.TASK_TYPE.IMPORT in project Anki-Android by ankidroid.

the class ImportTest method testApkg.

@Test
public void testApkg() throws IOException, ImportExportException {
    String apkg = Shared.getTestFilePath(getTestContext(), "media.apkg");
    Importer imp = new AnkiPackageImporter(mTestCol, apkg);
    List<String> expected = Collections.emptyList();
    List<String> actual = Arrays.asList(new File(mTestCol.getMedia().dir()).list());
    actual.retainAll(expected);
    assertEquals(actual.size(), expected.size());
    imp.run();
    expected = Collections.singletonList("foo.wav");
    actual = Arrays.asList(new File(mTestCol.getMedia().dir()).list());
    actual.retainAll(expected);
    assertEquals(expected.size(), actual.size());
    // import again should be idempotent in terms of media
    mTestCol.remCards(mTestCol.getDb().queryLongList("select id from cards"));
    imp = new AnkiPackageImporter(mTestCol, apkg);
    imp.run();
    expected = Collections.singletonList("foo.wav");
    actual = Arrays.asList(new File(mTestCol.getMedia().dir()).list());
    actual.retainAll(expected);
    assertEquals(expected.size(), actual.size());
    // but if the local file has different data, it will rename
    mTestCol.remCards(mTestCol.getDb().queryLongList("select id from cards"));
    FileOutputStream os = new FileOutputStream(new File(mTestCol.getMedia().dir(), "foo.wav"), false);
    os.write("xyz".getBytes());
    os.close();
    imp = new AnkiPackageImporter(mTestCol, apkg);
    imp.run();
    assertEquals(2, new File(mTestCol.getMedia().dir()).list().length);
}
Also used : AnkiPackageImporter(com.ichi2.libanki.importer.AnkiPackageImporter) FileOutputStream(java.io.FileOutputStream) File(java.io.File) Anki2Importer(com.ichi2.libanki.importer.Anki2Importer) Importer(com.ichi2.libanki.importer.Importer) AnkiPackageImporter(com.ichi2.libanki.importer.AnkiPackageImporter) NoteImporter(com.ichi2.libanki.importer.NoteImporter) TextImporter(com.ichi2.libanki.importer.TextImporter) Test(org.junit.Test) InstrumentedTest(com.ichi2.anki.tests.InstrumentedTest)

Example 10 with IMPORT

use of com.ichi2.async.CollectionTask.TASK_TYPE.IMPORT in project Anki-Android by ankidroid.

the class Collection method addNote.

/**
 * Add a note and cards to the collection. If allowEmpty, at least one card is generated.
 * @param note  The note to add to the collection
 * @param allowEmpty Whether we accept to add it even if it should generate no card. Useful to import note even if buggy
 * @return Number of card added
 */
public int addNote(@NonNull Note note, Models.AllowEmpty allowEmpty) {
    // check we have card models available, then save
    ArrayList<JSONObject> cms = findTemplates(note, allowEmpty);
    // Todo: upstream, we accept to add a not even if it generates no card. Should be ported to ankidroid
    if (cms.isEmpty()) {
        return 0;
    }
    note.flush();
    // deck conf governs which of these are used
    int due = nextID("pos");
    // add cards
    int ncards = 0;
    for (JSONObject template : cms) {
        _newCard(note, template, due);
        ncards += 1;
    }
    return ncards;
}
Also used : JSONObject(com.ichi2.utils.JSONObject) SuppressLint(android.annotation.SuppressLint)

Aggregations

File (java.io.File)11 Test (org.junit.Test)9 Collection (com.ichi2.libanki.Collection)8 AnkiPackageImporter (com.ichi2.libanki.importer.AnkiPackageImporter)7 IOException (java.io.IOException)7 Note (com.ichi2.libanki.Note)6 TextImporter (com.ichi2.libanki.importer.TextImporter)6 FileNotFoundException (java.io.FileNotFoundException)6 HashMap (java.util.HashMap)6 Resources (android.content.res.Resources)5 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)5 Anki2Importer (com.ichi2.libanki.importer.Anki2Importer)5 ArrayList (java.util.ArrayList)5 Cursor (android.database.Cursor)4 InstrumentedTest (com.ichi2.anki.tests.InstrumentedTest)4 Importer (com.ichi2.libanki.importer.Importer)4 NoteImporter (com.ichi2.libanki.importer.NoteImporter)4 FileOutputStream (java.io.FileOutputStream)4 ImportExportException (com.ichi2.anki.exception.ImportExportException)3 List (java.util.List)3