use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class ContentProviderTest method testInsertTemplate.
/**
* Check that inserting and removing a note into default deck works as expected
*/
@Test
public void testInsertTemplate() throws Exception {
// Get required objects for test
final ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext().getContentResolver();
Collection col = getCol();
// Add a new basic model that we use for testing purposes (existing models could potentially be corrupted)
Model model = StdModels.basicModel.add(col, BASIC_MODEL_NAME);
long modelId = model.getLong("id");
// Add the note
Uri modelUri = ContentUris.withAppendedId(FlashCardsContract.Model.CONTENT_URI, modelId);
// choose the last one because not the same as the basic model template
int testIndex = TEST_MODEL_CARDS.length - 1;
int expectedOrd = model.getJSONArray("tmpls").length();
ContentValues cv = new ContentValues();
cv.put(FlashCardsContract.CardTemplate.NAME, TEST_MODEL_CARDS[testIndex]);
cv.put(FlashCardsContract.CardTemplate.QUESTION_FORMAT, TEST_MODEL_QFMT[testIndex]);
cv.put(FlashCardsContract.CardTemplate.ANSWER_FORMAT, TEST_MODEL_AFMT[testIndex]);
cv.put(FlashCardsContract.CardTemplate.BROWSER_QUESTION_FORMAT, TEST_MODEL_QFMT[testIndex]);
cv.put(FlashCardsContract.CardTemplate.BROWSER_ANSWER_FORMAT, TEST_MODEL_AFMT[testIndex]);
Uri templatesUri = Uri.withAppendedPath(modelUri, "templates");
Uri templateUri = cr.insert(templatesUri, cv);
// test that the changes are physically saved to the DB
col = reopenCol();
assertNotNull("Check template uri", templateUri);
assertEquals("Check template uri ord", expectedOrd, ContentUris.parseId(templateUri));
model = col.getModels().get(modelId);
assertNotNull("Check model", model);
JSONObject template = model.getJSONArray("tmpls").getJSONObject(expectedOrd);
assertEquals("Check template JSONObject ord", expectedOrd, template.getInt("ord"));
assertEquals("Check template name", TEST_MODEL_CARDS[testIndex], template.getString("name"));
assertEquals("Check qfmt", TEST_MODEL_QFMT[testIndex], template.getString("qfmt"));
assertEquals("Check afmt", TEST_MODEL_AFMT[testIndex], template.getString("afmt"));
assertEquals("Check bqfmt", TEST_MODEL_QFMT[testIndex], template.getString("bqfmt"));
assertEquals("Check bafmt", TEST_MODEL_AFMT[testIndex], template.getString("bafmt"));
col.getModels().rem(model);
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class AnkiChinaSyncer method handleServerData.
// 35%+25%
private void handleServerData(JSONObject item) {
mCol = CollectionHelper.getInstance().getColSafe(AnkiDroidApp.getInstance());
CollectionHelper.getInstance().lockCollection();
updateDialogProgress(SYNCING_DATA, "更新全局配置中", mCurrentProgress + 1);
DB db = mCol.getDb();
try {
JSONObject remoteCol = item.getJSONObject("col").getJSONObject("replace");
// db.execute("update col set id ="+remoteCol.getInt("id")+","+"set crt =\"+remoteCol.getLong(\"crt\")");
// db.execute("update col set crt ="+remoteCol.getLong("crt"));
// db.execute("update col set mod ="+remoteCol.getLong("mod"));
// db.execute("update col set scm ="+remoteCol.getLong("scm"));
// db.execute("update col set ver ="+remoteCol.getInt("ver"));
// db.execute("update col set dty ="+remoteCol.getInt("dty"));
// db.execute("update col set usn ="+remoteCol.getInt("usn"));
// db.execute("update col set ls ="+remoteCol.getLong("ls"));
// db.execute("update col set conf ="+remoteCol.getString("conf"));
// db.execute("update col set tags ="+remoteCol.getString("tags"));
// db.execute("update col set tags ="+remoteCol.getString("tags"));
// db.execute("update col set id = %d,");
Timber.i("remote col config:%s", remoteCol.toString());
@SuppressLint("DefaultLocale") String sql = String.format("update col set id = %d,crt = %d,mod=%d,scm=%d,ver=%d,dty=%d,usn=%d,ls=%d,conf='%s',tags='%s'", remoteCol.getInt("id"), remoteCol.getLong("crt"), remoteCol.getLong("mod"), remoteCol.getLong("scm"), remoteCol.getInt("ver"), remoteCol.getInt("dty"), remoteCol.getInt("usn"), remoteCol.getLong("ls"), remoteCol.getString("conf"), remoteCol.getString("tags") == null || !remoteCol.getString("tags").startsWith("{") ? "{}" : remoteCol.getString("tags"));
db.execute(sql);
mCol.load();
} catch (Exception e) {
e.printStackTrace();
}
Decks currentDecks = mCol.getDecks();
// 删除多余的内容
try {
JSONArray deletedDecks = item.getJSONObject("decks").getJSONArray("delete");
if (deletedDecks.length() > 0) {
double percent = 2.0 / deletedDecks.length();
for (int i = 0; i < deletedDecks.length(); i++) {
String deckID = deletedDecks.getString(i);
currentDecks.rem(Long.parseLong(deckID));
updateDialogProgress(SYNCING_DATA, "删除多余牌组中", mCurrentProgress + percent);
}
mCol.save();
}
} catch (Exception e) {
// e.printStackTrace();
}
try {
JSONArray deletedDConf = item.getJSONObject("dconf").getJSONArray("delete");
if (deletedDConf.length() > 0) {
double percent = 2.0 / deletedDConf.length();
for (int i = 0; i < deletedDConf.length(); i++) {
String id = deletedDConf.getString(i);
mCol.getDecks().remConf(Long.parseLong(id));
updateDialogProgress(SYNCING_DATA, "删除多余牌组配置中", mCurrentProgress + percent);
}
mCol.save();
}
} catch (Exception e) {
// e.printStackTrace();
}
try {
JSONArray deletedModel = item.getJSONObject("models").getJSONArray("delete");
if (deletedModel.length() > 0) {
double percent = 2.0 / deletedModel.length();
for (int i = 0; i < deletedModel.length(); i++) {
String id = deletedModel.getString(i);
mCol.getModels().rem(mCol.getModels().get(Long.parseLong(id)));
updateDialogProgress(SYNCING_DATA, "删除多余模板中", mCurrentProgress + percent);
}
mCol.save();
}
} catch (Exception e) {
e.printStackTrace();
}
updateDialogProgress(SYNCING_DATA, "删除多余卡牌中", mCurrentProgress + 2);
try {
JSONArray deletedCards = item.getJSONObject("cards").getJSONArray("delete");
List<Long> sids = ids2longList(deletedCards);
Timber.e("need delete cards num:%s", sids.size());
// db.execute("DELETE FROM cards WHERE id IN " + sids);
mCol.remCards(sids);
mCol.save();
} catch (Exception e) {
e.printStackTrace();
}
updateDialogProgress(SYNCING_DATA, "删除多余笔记中", mCurrentProgress + 2);
try {
JSONArray deletedNotes = item.getJSONObject("notes").getJSONArray("delete");
long[] sids = ids2longArray(deletedNotes);
// db.execute("DELETE FROM notes WHERE id IN " + sids);
mCol.remNotes(sids);
mCol.save();
} catch (Exception e) {
e.printStackTrace();
}
try {
JSONObject replaceDecks = item.getJSONObject("decks").getJSONObject("replace");
if (replaceDecks.length() > 0) {
double percent = 2.0 / replaceDecks.length();
Iterator<String> it = replaceDecks.keys();
while (it.hasNext()) {
String next = it.next();
try {
// mCol.getDecks().getDecks().put(Long.parseLong(next), new Deck(replaceDecks.getJSONObject(next)));
mCol.getDecks().update(new Deck(replaceDecks.getJSONObject(next)));
updateDialogProgress(SYNCING_DATA, "同步牌组数据中", mCurrentProgress + percent);
} catch (Exception e) {
// 只遍历model id
}
}
mCol.save();
}
} catch (Exception e) {
// e.printStackTrace();
}
db.getDatabase().beginTransaction();
try {
JSONArray replace = item.getJSONObject("revlog").getJSONArray("replace");
if (replace.length() > 0) {
for (int i = 0; i < replace.length(); i++) {
log(replace.getJSONArray(i).get(0), replace.getJSONArray(i).get(1), replace.getJSONArray(i).get(2), replace.getJSONArray(i).get(3), replace.getJSONArray(i).get(4), replace.getJSONArray(i).get(5), replace.getJSONArray(i).get(6), replace.getJSONArray(i).get(7), replace.getJSONArray(i).get(8));
}
db.getDatabase().setTransactionSuccessful();
mCol.save();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
db.getDatabase().endTransaction();
}
// Timber.e("看看是null还是null:%s,%s", (item.getJSONObject("dconf").get("replace")==JSONObject.NULL), (item.getJSONObject("dconf").getString("replace").equals("null")));
try {
JSONObject replaceDConf = item.getJSONObject("dconf").getJSONObject("replace");
if (replaceDConf.length() > 0) {
double percent = 2.0 / replaceDConf.length();
Iterator<String> it = replaceDConf.keys();
while (it.hasNext()) {
String next = it.next();
try {
Long.parseLong(next);
mCol.getDecks().updateConf(new DeckConfig(replaceDConf.getJSONObject(next)));
updateDialogProgress(SYNCING_DATA, "同步牌组配置数据中", mCurrentProgress + percent);
} catch (Exception e) {
// 只遍历model id
}
}
mCol.save();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
JSONObject replaceModels = item.getJSONObject("models").getJSONObject("replace");
if (replaceModels.length() > 0) {
double percent = 5.0 / replaceModels.length();
Iterator<String> it = replaceModels.keys();
while (it.hasNext()) {
String next = it.next();
try {
Long.parseLong(next);
mCol.getModels().update(new Model(replaceModels.getJSONObject(next)));
updateDialogProgress(SYNCING_DATA, "同步模板数据中", mCurrentProgress + percent);
} catch (Exception e) {
// 只遍历model id
}
}
mCol.save();
}
} catch (Exception e) {
e.printStackTrace();
}
db.getDatabase().beginTransaction();
try {
JSONArray replace = item.getJSONObject("notes").getJSONArray("replace");
if (replace.length() > 0) {
double percent = mPullNotesPerPercent / replace.length();
for (int i = 0; i < replace.length(); i++) {
// String values = replace.getJSONArray(i).toString().replace("[", "").replace("]", "").replaceAll("\"","'").replaceAll("\u001f","\u001f");
// String sql = "replace into notes(id,guid,mid,mod,usn,tags,flds,sfld,csum,flags,data) values ( " + values + ")";
//
db.execute("insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", replace.getJSONArray(i).get(0), replace.getJSONArray(i).get(1), replace.getJSONArray(i).get(2), replace.getJSONArray(i).get(3), replace.getJSONArray(i).get(4), replace.getJSONArray(i).get(5), replace.getJSONArray(i).get(6), replace.getJSONArray(i).get(7), replace.getJSONArray(i).get(8), replace.getJSONArray(i).get(9), replace.getJSONArray(i).get(10));
updateDialogProgress(SYNCING_DATA, "同步笔记数据中", mCurrentProgress + percent);
}
db.getDatabase().setTransactionSuccessful();
mCol.save();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
db.getDatabase().endTransaction();
}
db.getDatabase().beginTransaction();
try {
JSONArray replace = item.getJSONObject("cards").getJSONArray("replace");
if (replace.length() > 0) {
double percent = 5.0 / replace.length();
for (int i = 0; i < replace.length(); i++) {
// String values = replace.getJSONArray(i).toString().replace("[", "").replace("]", "").replaceAll("\"","'").replaceAll("\u001f","\u001f");
// String sql = "replace into cards(id,nid,did,ord,mod,usn,type,queue,due,ivl,factor,reps,lapses,left,odue,odid,flags,data) values ( " + values + ")";
// Timber.i("update dialog progress:%d", percent);
db.execute("insert or replace into cards values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", replace.getJSONArray(i).get(0), replace.getJSONArray(i).get(1), replace.getJSONArray(i).get(2), replace.getJSONArray(i).get(3), replace.getJSONArray(i).get(4), replace.getJSONArray(i).get(5), replace.getJSONArray(i).get(6), replace.getJSONArray(i).get(7), replace.getJSONArray(i).get(8), replace.getJSONArray(i).get(9), replace.getJSONArray(i).get(10), replace.getJSONArray(i).get(11), replace.getJSONArray(i).get(12), replace.getJSONArray(i).get(13), replace.getJSONArray(i).get(14), replace.getJSONArray(i).get(15), replace.getJSONArray(i).get(16), replace.getJSONArray(i).get(17));
updateDialogProgress(SYNCING_DATA, "同步卡牌数据中", mCurrentProgress + percent);
}
db.getDatabase().setTransactionSuccessful();
mCol.save();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
db.getDatabase().endTransaction();
}
CollectionHelper.getInstance().unlockCollection();
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class AnkiChinaSyncer method fetchRestNotesFromServer.
private void fetchRestNotesFromServer(String lastID) {
OKHttpUtil.get(Consts.ANKI_CHINA_BASE + Consts.API_VERSION + "napi/sync/pullData?session_key=" + mCurrentSession + "&last_id=" + lastID, mToken, "", new OKHttpUtil.MyCallBack() {
@Override
public void onFailure(Call call, IOException e) {
updateDialogMessage(SYNCING_ERROR, ERROR_NETWORK);
}
@Override
public void onResponse(Call call, String token, Object arg1, Response response) {
if (response.isSuccessful()) {
try {
updateDialogProgress(SYNCING_DATA, "同步更多笔记中", mCurrentProgress + mPullNotesPerPercent);
final JSONObject object = new JSONObject(response.body().string());
// Timber.i("object:%s", object.toString());
if (object.getInt("status_code") != 0) {
updateDialogMessage(SYNCING_ERROR, object.getString("message"));
return;
}
if (object.get("data") != null) {
final JSONObject item = object.getJSONObject("data");
DB db = mCol.getDb();
try {
JSONArray replace = item.getJSONArray("replace");
if (replace.length() > 0) {
db.getDatabase().beginTransaction();
for (int i = 0; i < replace.length(); i++) {
db.execute("insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", replace.getJSONArray(i).get(0), replace.getJSONArray(i).get(1), replace.getJSONArray(i).get(2), replace.getJSONArray(i).get(3), replace.getJSONArray(i).get(4), replace.getJSONArray(i).get(5), replace.getJSONArray(i).get(6), replace.getJSONArray(i).get(7), replace.getJSONArray(i).get(8), replace.getJSONArray(i).get(9), replace.getJSONArray(i).get(10));
}
db.getDatabase().setTransactionSuccessful();
mCol.save();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
db.getDatabase().endTransaction();
}
try {
String lastID = item.getString("last_id");
if (lastID != null && !lastID.equals("null")) {
// 还有多余数据就继续递归处理
fetchRestNotesFromServer(lastID);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 完成本次同步
completeDataSync(token);
} catch (Exception e) {
e.printStackTrace();
updateDialogMessage(SYNCING_ERROR, ERROR_DATA);
}
} else {
Timber.e("pullData error, code %d", response.code());
}
}
});
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class Anki2Importer method _importNotes.
/**
* Notes
* ***********************************************************
*/
private void _importNotes() {
// build guid -> (id,mod,mid) hash & map of existing note ids
mNotes = new HashMap<>();
Set<Long> existing = new HashSet<>();
Cursor cur = null;
try {
cur = mDst.getDb().getDatabase().query("select id, guid, mod, mid from notes", null);
while (cur.moveToNext()) {
long id = cur.getLong(0);
String guid = cur.getString(1);
long mod = cur.getLong(2);
long mid = cur.getLong(3);
mNotes.put(guid, new Object[] { id, mod, mid });
existing.add(id);
}
} finally {
if (cur != null) {
cur.close();
}
}
// we may need to rewrite the guid if the model schemas don't match,
// so we need to keep track of the changes for the card import stage
mChangedGuids = new HashMap<>();
// we ignore updates to changed schemas. we need to note the ignored
// guids, so we avoid importing invalid cards
mIgnoredGuids = new HashMap<>();
// iterate over source collection
ArrayList<Object[]> add = new ArrayList<>();
int totalAddCount = 0;
final int thresExecAdd = 1000;
ArrayList<Object[]> update = new ArrayList<>();
int totalUpdateCount = 0;
final int thresExecUpdate = 1000;
ArrayList<Long> dirty = new ArrayList<>();
int totalDirtyCount = 0;
final int thresExecDirty = 1000;
int usn = mDst.usn();
int dupes = 0;
ArrayList<String> dupesIgnored = new ArrayList<>();
try {
mDst.getDb().getDatabase().beginTransaction();
cur = mSrc.getDb().getDatabase().query("select * from notes", null);
// Counters for progress updates
int total = cur.getCount();
boolean largeCollection = total > 200;
int onePercent = total / 100;
int i = 0;
while (cur.moveToNext()) {
// turn the db result into a mutable list
Object[] note = new Object[] { cur.getLong(0), cur.getString(1), cur.getLong(2), cur.getLong(3), cur.getInt(4), cur.getString(5), cur.getString(6), cur.getString(7), cur.getLong(8), cur.getInt(9), cur.getString(10) };
boolean shouldAdd = _uniquifyNote(note);
if (shouldAdd) {
// ensure id is unique
while (existing.contains(note[0])) {
note[0] = ((Long) note[0]) + 999;
}
existing.add((Long) note[0]);
// bump usn
note[4] = usn;
// update media references in case of dupes
note[6] = _mungeMedia((Long) note[MID], (String) note[6]);
add.add(note);
dirty.add((Long) note[0]);
// note we have the added guid
mNotes.put((String) note[GUID], new Object[] { note[0], note[3], note[MID] });
} else {
// a duplicate or changed schema - safe to update?
dupes += 1;
if (mAllowUpdate) {
Object[] n = mNotes.get(note[GUID]);
long oldNid = (Long) n[0];
long oldMod = (Long) n[1];
long oldMid = (Long) n[2];
// will update if incoming note more recent
if (oldMod < (Long) note[MOD]) {
// safe if note types identical
if (oldMid == (Long) note[MID]) {
// incoming note should use existing id
note[0] = oldNid;
note[4] = usn;
note[6] = _mungeMedia((Long) note[MID], (String) note[6]);
update.add(note);
dirty.add((Long) note[0]);
} else {
dupesIgnored.add(String.format("%s: %s", mCol.getModels().get(oldMid).getString("name"), ((String) note[6]).replace("\u001f", ",")));
mIgnoredGuids.put((String) note[GUID], true);
}
}
}
}
i++;
// add to col partially, so as to avoid OOM
if (add.size() >= thresExecAdd) {
totalAddCount += add.size();
addNotes(add);
add.clear();
Timber.d("add notes: %d", totalAddCount);
}
// add to col partially, so as to avoid OOM
if (update.size() >= thresExecUpdate) {
totalUpdateCount += update.size();
updateNotes(update);
update.clear();
Timber.d("update notes: %d", totalUpdateCount);
}
// add to col partially, so as to avoid OOM
if (dirty.size() >= thresExecDirty) {
totalDirtyCount += dirty.size();
long[] das = Utils.collection2Array(dirty);
mDst.updateFieldCache(das);
mDst.getTags().registerNotes(das);
dirty.clear();
Timber.d("dirty notes: %d", totalDirtyCount);
}
if (total != 0 && (!largeCollection || i % onePercent == 0)) {
// Calls to publishProgress are reasonably expensive due to res.getString()
publishProgress(i * 100 / total, 0, 0);
}
}
publishProgress(100, 0, 0);
// summarize partial add/update/dirty results for total values
totalAddCount += add.size();
totalUpdateCount += update.size();
totalDirtyCount += dirty.size();
if (dupes > 0) {
mLog.add(getRes().getString(R.string.import_update_details, totalUpdateCount, dupes));
if (dupesIgnored.size() > 0) {
mLog.add(getRes().getString(R.string.import_update_ignored));
}
}
// export info for calling code
mDupes = dupes;
mAdded = totalAddCount;
mUpdated = totalUpdateCount;
Timber.d("add notes total: %d", totalAddCount);
Timber.d("update notes total: %d", totalUpdateCount);
Timber.d("dirty notes total: %d", totalDirtyCount);
// add to col (for last chunk)
addNotes(add);
add.clear();
updateNotes(update);
update.clear();
mDst.getDb().getDatabase().setTransactionSuccessful();
} finally {
if (cur != null) {
cur.close();
}
if (mDst.getDb().getDatabase().inTransaction()) {
try {
mDst.getDb().getDatabase().endTransaction();
} catch (Exception e) {
Timber.w(e);
}
}
}
long[] das = Utils.collection2Array(dirty);
mDst.updateFieldCache(das);
mDst.getTags().registerNotes(das);
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class Sched method _fillLrn.
// sub-day learning
@Override
protected boolean _fillLrn() {
if (mLrnCount == 0) {
return false;
}
if (!mLrnQueue.isEmpty()) {
return true;
}
Cursor cur = null;
mLrnQueue.clear();
SupportSQLiteDatabase db = mCol.getDb().getDatabase();
try {
/* Difference with upstream:
* Current card can't come in the queue.
*
* In standard usage, a card is not requested before the previous card is marked as reviewed. However, if we
* decide to query a second card sooner, we don't want to get the same card a second time. This simulate
* _getLrnCard which did remove the card from the queue. _sortIntoLrn will add the card back to the queue if
* required when the card is reviewed.
*/
cur = db.query("SELECT due, id FROM cards WHERE did IN " + _deckLimit() + " AND queue = " + Consts.QUEUE_TYPE_LRN + " AND due < ? AND id != ? LIMIT ?", new Object[] { mDayCutoff, currentCardId(), mReportLimit });
while (cur.moveToNext()) {
mLrnQueue.add(cur.getLong(0), cur.getLong(1));
}
// as it arrives sorted by did first, we need to sort it
mLrnQueue.sort();
return !mLrnQueue.isEmpty();
} finally {
if (cur != null && !cur.isClosed()) {
cur.close();
}
}
}
Aggregations