use of com.ichi2.utils.JSONException in project AnkiChinaAndroid by ankichinateam.
the class Utils method ids2str.
/**
* Given a list of integers, return a string '(int1,int2,...)'.
*/
public static String ids2str(JSONArray ids) {
StringBuilder str = new StringBuilder(512);
str.append("(");
if (ids != null) {
int len = ids.length();
for (int i = 0; i < len; i++) {
try {
if (i == (len - 1)) {
str.append(ids.getLong(i));
} else {
str.append(ids.getLong(i)).append(",");
}
} catch (JSONException e) {
Timber.e(e, "ids2str :: JSONException");
}
}
}
str.append(")");
return str.toString();
}
use of com.ichi2.utils.JSONException in project AnkiChinaAndroid by ankichinateam.
the class Collection method deleteCardsWithInvalidModelOrdinals.
private ArrayList<String> deleteCardsWithInvalidModelOrdinals(Runnable notifyProgress, JSONObject m) throws JSONException {
Timber.d("deleteCardsWithInvalidModelOrdinals()");
ArrayList<String> problems = new ArrayList<>();
notifyProgress.run();
if (m.getInt("type") == Consts.MODEL_STD) {
ArrayList<Integer> ords = new ArrayList<>();
JSONArray tmpls = m.getJSONArray("tmpls");
for (int t = 0; t < tmpls.length(); t++) {
ords.add(tmpls.getJSONObject(t).getInt("ord"));
}
// cards with invalid ordinal
ArrayList<Long> ids = mDb.queryLongList("SELECT id FROM cards WHERE ord NOT IN " + Utils.ids2str(ords) + " AND nid IN ( " + "SELECT id FROM notes WHERE mid = ?)", m.getLong("id"));
if (ids.size() > 0) {
problems.add("Deleted " + ids.size() + " card(s) with missing template.");
remCards(ids);
}
}
return problems;
}
use of com.ichi2.utils.JSONException in project AnkiChinaAndroid by ankichinateam.
the class Collection method fixIntegrity.
/**
* Fix possible problems and rebuild caches.
*/
public CheckDatabaseResult fixIntegrity(CollectionTask.ProgressCallback progressCallback) {
File file = new File(mPath);
CheckDatabaseResult result = new CheckDatabaseResult(file.length());
final int[] currentTask = { 1 };
// a few fixes are in all-models loops, the rest are one-offs
int totalTasks = (getModels().all().size() * 4) + 27;
Runnable notifyProgress = progressCallback == null ? null : () -> fixIntegrityProgress(progressCallback, currentTask[0]++, totalTasks);
FunctionalInterfaces.Consumer<FunctionalInterfaces.FunctionThrowable<Runnable, List<String>, JSONException>> executeIntegrityTask = (FunctionalInterfaces.FunctionThrowable<Runnable, List<String>, JSONException> function) -> {
// DEFECT: notifyProgress will lag if an exception is thrown.
try {
mDb.getDatabase().beginTransaction();
if (notifyProgress != null)
result.addAll(function.apply(notifyProgress));
mDb.getDatabase().setTransactionSuccessful();
} catch (Exception e) {
Timber.e(e, "Failed to execute integrity check");
AnkiDroidApp.sendExceptionReport(e, "fixIntegrity");
} finally {
try {
mDb.getDatabase().endTransaction();
} catch (Exception e) {
Timber.e(e, "Failed to end integrity check transaction");
AnkiDroidApp.sendExceptionReport(e, "fixIntegrity - endTransaction");
}
}
};
try {
mDb.getDatabase().beginTransaction();
save();
notifyProgress.run();
if (!mDb.getDatabase().isDatabaseIntegrityOk()) {
return result.markAsFailed();
}
mDb.getDatabase().setTransactionSuccessful();
} catch (SQLiteDatabaseLockedException ex) {
Timber.e("doInBackgroundCheckDatabase - Database locked");
return result.markAsLocked();
} catch (RuntimeException e) {
Timber.e(e, "doInBackgroundCheckDatabase - RuntimeException on marking card");
AnkiDroidApp.sendExceptionReport(e, "doInBackgroundCheckDatabase");
return result.markAsFailed();
} finally {
// if the database was locked, we never got the transaction.
if (mDb.getDatabase().inTransaction()) {
mDb.getDatabase().endTransaction();
}
}
executeIntegrityTask.consume(this::deleteNotesWithMissingModel);
// for each model
for (JSONObject m : getModels().all()) {
executeIntegrityTask.consume((callback) -> deleteCardsWithInvalidModelOrdinals(callback, m));
executeIntegrityTask.consume((callback) -> deleteNotesWithWrongFieldCounts(callback, m));
}
executeIntegrityTask.consume(this::deleteNotesWithMissingCards);
executeIntegrityTask.consume(this::deleteCardsWithMissingNotes);
executeIntegrityTask.consume(this::removeOriginalDuePropertyWhereInvalid);
executeIntegrityTask.consume(this::removeDynamicPropertyFromNonDynamicDecks);
executeIntegrityTask.consume(this::removeDeckOptionsFromDynamicDecks);
executeIntegrityTask.consume(this::resetInvalidDeckOptions);
executeIntegrityTask.consume(this::rebuildTags);
executeIntegrityTask.consume(this::updateFieldCache);
executeIntegrityTask.consume(this::fixNewCardDuePositionOverflow);
executeIntegrityTask.consume(this::resetNewCardInsertionPosition);
executeIntegrityTask.consume(this::fixExcessiveReviewDueDates);
// v2 sched had a bug that could create decimal intervals
executeIntegrityTask.consume(this::fixDecimalCardsData);
executeIntegrityTask.consume(this::fixDecimalRevLogData);
executeIntegrityTask.consume(this::restoreMissingDatabaseIndices);
executeIntegrityTask.consume(this::ensureModelsAreNotEmpty);
executeIntegrityTask.consume((progressNotifier) -> this.ensureCardsHaveHomeDeck(progressNotifier, result));
// and finally, optimize (unable to be done inside transaction).
try {
optimize(notifyProgress);
} catch (Exception e) {
Timber.e(e, "optimize");
AnkiDroidApp.sendExceptionReport(e, "fixIntegrity - optimize");
}
file = new File(mPath);
long newSize = file.length();
result.setNewSize(newSize);
// if any problems were found, force a full sync
if (result.hasProblems()) {
modSchemaNoCheck();
}
logProblems(result.getProblems());
return result;
}
use of com.ichi2.utils.JSONException in project AnkiChinaAndroid by ankichinateam.
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(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));
Set<Long> nids = new HashSet<>(mSrc.getDb().queryLongList("select nid from cards where id in " + Utils.ids2str(cids)));
// notes
Timber.d("Copy notes");
ArrayList<Long> uniqueNids = new ArrayList<>(nids);
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"))) {
Timber.d("Copy models:%s", m.getLong("id"));
dst.getModels().update(m);
}
}
for (Model m : dst.getModels().all()) {
Timber.d("check dst model:%s", m.getLong("id"));
}
// decks
Timber.d("Copy decks");
ArrayList<Long> dids = new ArrayList<>();
if (mDid != null) {
dids.add(mDid);
for (Long x : mSrc.getDecks().children(mDid).values()) {
dids.add(x);
}
}
JSONObject dconfs = new JSONObject();
for (Deck d : mSrc.getDecks().all()) {
if ("1".equals(d.getString("id"))) {
continue;
}
if (mDid != null && !dids.contains(d.getLong("id"))) {
continue;
}
if (d.getInt("dyn") != 1 && 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) {
for (int i = 0; i < keys.length(); i++) {
mMediaFiles.add(keys.getString(i));
}
}
Timber.d("Cleanup");
dst.setCrt(mSrc.getCrt());
// todo: tags?
mCount = dst.cardCount();
dst.setMod();
postExport();
dst.close();
}
use of com.ichi2.utils.JSONException in project AnkiChinaAndroid by ankichinateam.
the class Storage method _upgrade.
private static void _upgrade(Collection col, int ver) {
try {
if (ver < 3) {
// new deck properties
for (Deck d : col.getDecks().all()) {
d.put("dyn", 0);
d.put("collapsed", false);
col.getDecks().save(d);
}
}
if (ver < 4) {
col.modSchemaNoCheck();
ArrayList<Model> clozes = new ArrayList<>();
for (Model m : col.getModels().all()) {
if (!m.getJSONArray("tmpls").getJSONObject(0).getString("qfmt").contains("{{cloze:")) {
m.put("type", Consts.MODEL_STD);
} else {
clozes.add(m);
}
}
for (Model m : clozes) {
try {
_upgradeClozeModel(col, m);
} catch (ConfirmModSchemaException e) {
// Will never be reached as we already set modSchemaNoCheck()
throw new RuntimeException(e);
}
}
col.getDb().execute("UPDATE col SET ver = 4");
}
if (ver < 5) {
col.getDb().execute("UPDATE cards SET odue = 0 WHERE queue = 2");
col.getDb().execute("UPDATE col SET ver = 5");
}
if (ver < 6) {
col.modSchemaNoCheck();
for (Model m : col.getModels().all()) {
m.put("css", new JSONObject(Models.defaultModel).getString("css"));
JSONArray ar = m.getJSONArray("tmpls");
for (int i = 0; i < ar.length(); i++) {
JSONObject t = ar.getJSONObject(i);
if (!t.has("css")) {
continue;
}
m.put("css", m.getString("css") + "\n" + t.getString("css").replace(".card ", ".card" + t.getInt("ord") + 1));
t.remove("css");
}
col.getModels().save(m);
}
col.getDb().execute("UPDATE col SET ver = 6");
}
if (ver < 7) {
col.modSchemaNoCheck();
col.getDb().execute("UPDATE cards SET odue = 0 WHERE (type = " + Consts.CARD_TYPE_LRN + " OR queue = 2) AND NOT odid");
col.getDb().execute("UPDATE col SET ver = 7");
}
if (ver < 8) {
col.modSchemaNoCheck();
col.getDb().execute("UPDATE cards SET due = due / 1000 WHERE due > 4294967296");
col.getDb().execute("UPDATE col SET ver = 8");
}
if (ver < 9) {
col.getDb().execute("UPDATE col SET ver = 9");
}
if (ver < 10) {
col.getDb().execute("UPDATE cards SET left = left + left * 1000 WHERE queue = " + Consts.QUEUE_TYPE_LRN);
col.getDb().execute("UPDATE col SET ver = 10");
}
if (ver < 11) {
col.modSchemaNoCheck();
for (Deck d : col.getDecks().all()) {
if (d.getInt("dyn") != 0) {
int order = d.getInt("order");
// failed order was removed
if (order >= 5) {
order -= 1;
}
JSONArray terms = new JSONArray(Arrays.asList(d.getString("search"), d.getInt("limit"), order));
d.put("terms", new JSONArray());
d.getJSONArray("terms").put(0, terms);
d.remove("search");
d.remove("limit");
d.remove("order");
d.put("resched", true);
d.put("return", true);
} else {
if (!d.has("extendNew")) {
d.put("extendNew", 10);
d.put("extendRev", 50);
}
}
col.getDecks().save(d);
}
for (DeckConfig c : col.getDecks().allConf()) {
JSONObject r = c.getJSONObject("rev");
r.put("ivlFct", r.optDouble("ivlFct", 1));
if (r.has("ivlfct")) {
r.remove("ivlfct");
}
r.put("maxIvl", 36500);
col.getDecks().save(c);
}
for (Model m : col.getModels().all()) {
JSONArray tmpls = m.getJSONArray("tmpls");
for (int ti = 0; ti < tmpls.length(); ++ti) {
JSONObject t = tmpls.getJSONObject(ti);
t.put("bqfmt", "");
t.put("bafmt", "");
}
col.getModels().save(m);
}
col.getDb().execute("update col set ver = 11");
}
// if (ver < 12) {
// col.getDb().execute("create table if not exists synclog (" + " id integer not null,"
// + " type integer not null,"+ " mod integer not null" + ")");
// col.getDb().execute("update col set ver = 12");
// _updateIndices(col.getDb());
// }
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
Aggregations