use of com.ichi2.anki.exception.ImportExportException 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.exception.ImportExportException in project Anki-Android by ankidroid.
the class ZipFile method exportFiltered.
private JSONObject exportFiltered(ZipFile z, String path, Context context) throws IOException, JSONException, ImportExportException {
// export into the anki2 file
String colfile = path.replace(".apkg", ".anki2");
super.exportInto(colfile, context);
z.write(colfile, CollectionHelper.COLLECTION_FILENAME);
// and media
prepareMedia();
JSONObject media = _exportMedia(z, mMediaFiles, mCol.getMedia().dir());
// tidy up intermediate files
SQLiteDatabase.deleteDatabase(new File(colfile));
SQLiteDatabase.deleteDatabase(new File(path.replace(".apkg", ".media.ad.db2")));
String tempPath = path.replace(".apkg", ".media");
File file = new File(tempPath);
if (file.exists()) {
String deleteCmd = "rm -r " + tempPath;
Runtime runtime = Runtime.getRuntime();
try {
runtime.exec(deleteCmd);
} catch (IOException ignored) {
Timber.w(ignored);
}
}
return media;
}
use of com.ichi2.anki.exception.ImportExportException in project Anki-Android by ankidroid.
the class ImportTest method testAnki2Updates.
@Test
public void testAnki2Updates() throws IOException, ImportExportException {
// create a new empty deck
String tmp = Shared.getTestFilePath(getTestContext(), "update1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
assertEquals(0, imp.getDupes());
assertEquals(1, imp.getAdded());
assertEquals(0, imp.getUpdated());
// importing again should be idempotent
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(0, imp.getUpdated());
// importing a newer note should update
assertEquals(1, mTestCol.noteCount());
assertTrue(mTestCol.getDb().queryString("select flds from notes").startsWith("hello"));
tmp = Shared.getTestFilePath(getTestContext(), "update2.apkg");
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(1, imp.getUpdated());
assertTrue(mTestCol.getDb().queryString("select flds from notes").startsWith("goodbye"));
}
use of com.ichi2.anki.exception.ImportExportException in project Anki-Android by ankidroid.
the class ImportTest method testAnki2DiffmodelTemplates.
@Test
public void testAnki2DiffmodelTemplates() throws IOException, JSONException, ImportExportException {
// different from the above as this one tests only the template text being
// changed, not the number of cards/fields
// import the first version of the model
String tmp = Shared.getTestFilePath(getTestContext(), "diffmodeltemplates-1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(mTestCol, tmp);
imp.setDupeOnSchemaChange(true);
imp.run();
// then the version with updated template
tmp = Shared.getTestFilePath(getTestContext(), "diffmodeltemplates-2.apkg");
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.setDupeOnSchemaChange(true);
imp.run();
// collection should contain the note we imported
assertEquals(1, mTestCol.noteCount());
// the front template should contain the text added in the 2nd package
Long tcid = mTestCol.findCards("").get(0);
Note tnote = mTestCol.getCard(tcid).note();
assertTrue(mTestCol.findTemplates(tnote).get(0).getString("qfmt").contains("Changed Front Template"));
}
use of com.ichi2.anki.exception.ImportExportException in project Anki-Android by ankidroid.
the class ImportTest method testDupeIgnore.
/**
* Custom tests for AnkiDroid.
*/
@Test
public void testDupeIgnore() throws IOException, ImportExportException {
// create a new empty deck
String tmp = Shared.getTestFilePath(getTestContext(), "update1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
tmp = Shared.getTestFilePath(getTestContext(), "update3.apkg");
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
// there is a dupe, but it was ignored
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(0, imp.getUpdated());
}
Aggregations