Search in sources :

Example 61 with Deck

use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method extendLimits.

public void extendLimits(int newc, int rev) {
    Deck cur = mCol.getDecks().current();
    ArrayList<Deck> decks = new ArrayList<>();
    decks.add(cur);
    decks.addAll(mCol.getDecks().parents(cur.getLong("id")));
    for (long did : mCol.getDecks().children(cur.getLong("id")).values()) {
        decks.add(mCol.getDecks().get(did));
    }
    for (Deck g : decks) {
        // add
        JSONArray today = g.getJSONArray("newToday");
        today.put(1, today.getInt(1) - newc);
        today = g.getJSONArray("revToday");
        today.put(1, today.getInt(1) - rev);
        mCol.getDecks().save(g);
    }
}
Also used : ArrayList(java.util.ArrayList) JSONArray(com.ichi2.utils.JSONArray) Deck(com.ichi2.libanki.Deck)

Example 62 with Deck

use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method _walkingCount.

protected int _walkingCount(@NonNull LimitMethod limFn, @NonNull CountMethod cntFn) {
    int tot = 0;
    HashMap<Long, Integer> pcounts = new HashMap<>();
    // for each of the active decks
    for (long did : mCol.getDecks().active()) {
        // get the individual deck's limit
        int lim = limFn.operation(mCol.getDecks().get(did));
        if (lim == 0) {
            continue;
        }
        // check the parents
        List<Deck> parents = mCol.getDecks().parents(did);
        for (Deck p : parents) {
            // add if missing
            long id = p.getLong("id");
            if (!pcounts.containsKey(id)) {
                pcounts.put(id, limFn.operation(p));
            }
            // take minimum of child and parent
            lim = Math.min(pcounts.get(id), lim);
        }
        // see how many cards we actually have
        int cnt = cntFn.operation(did, lim);
        // if non-zero, decrement from parents counts
        for (Deck p : parents) {
            long id = p.getLong("id");
            pcounts.put(id, pcounts.get(id) - cnt);
        }
        // we may also be a parent
        pcounts.put(did, lim - cnt);
        // and add to running total
        tot += cnt;
    }
    Timber.e("walking deck count:" + pcounts.size() + ",today new  card count:" + tot);
    return tot;
}
Also used : HashMap(java.util.HashMap) Deck(com.ichi2.libanki.Deck)

Example 63 with Deck

use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method _newConf.

// Overridden: different delays for filtered cards.
@NonNull
protected JSONObject _newConf(@NonNull Card card) {
    DeckConfig conf = _cardConf(card);
    // normal deck
    if (card.getODid() == 0) {
        return conf.getJSONObject("new");
    }
    // dynamic deck; override some attributes, use original deck for others
    DeckConfig oconf = mCol.getDecks().confForDid(card.getODid());
    JSONObject dict = new JSONObject();
    // original deck
    dict.put("ints", oconf.getJSONObject("new").getJSONArray("ints"));
    dict.put("initialFactor", oconf.getJSONObject("new").getInt("initialFactor"));
    dict.put("bury", oconf.getJSONObject("new").optBoolean("bury", true));
    dict.put("delays", oconf.getJSONObject("new").getJSONArray("delays"));
    // overrides
    dict.put("separate", conf.getBoolean("separate"));
    dict.put("order", Consts.NEW_CARDS_DUE);
    dict.put("perDay", mReportLimit);
    return dict;
}
Also used : JSONObject(com.ichi2.utils.JSONObject) DeckConfig(com.ichi2.libanki.DeckConfig) NonNull(androidx.annotation.NonNull)

Example 64 with Deck

use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.

the class Syncer method sync.

/**
 * Returns 'noChanges', 'fullSync', 'success', etc
 */
// public Object[] sync() throws UnknownHttpResponseException {
// return sync(null);
// }
public Object[] sync(Connection con, long restSpace) throws UnknownHttpResponseException, NoEnoughServerSpaceException {
    mSyncMsg = "";
    setRestSpace(restSpace);
    // if the deck has any pending changes, flush them first and bump mod time
    mCol.getSched()._updateCutoff();
    mCol.save();
    // step 1: login & metadata
    Response ret = mServer.meta();
    if (ret == null) {
        return null;
    }
    int returntype = ret.code();
    if (returntype == 403) {
        return new Object[] { "badAuth" };
    }
    try {
        mCol.getDb().getDatabase().beginTransaction();
        try {
            Timber.i("Sync: getting meta data from server");
            JSONObject rMeta = new JSONObject(ret.body().string());
            mCol.log("rmeta", rMeta);
            mSyncMsg = rMeta.getString("msg");
            if (!rMeta.getBoolean("cont")) {
                // Don't add syncMsg; it can be fetched by UI code using the accessor
                return new Object[] { "serverAbort" };
            } else {
            // don't abort, but ui should show messages after sync finishes
            // and require confirmation if it's non-empty
            }
            throwExceptionIfCancelled(con);
            long rscm = rMeta.getLong("scm");
            int rts = rMeta.getInt("ts");
            mRMod = rMeta.getLong("mod");
            mMaxUsn = rMeta.getInt("usn");
            // skip uname, AnkiDroid already stores and shows it
            trySetHostNum(rMeta);
            Timber.i("Sync: building local meta data");
            JSONObject lMeta = meta();
            mCol.log("lmeta", lMeta);
            mLMod = lMeta.getLong("mod");
            mMinUsn = lMeta.getInt("usn");
            long lscm = lMeta.getLong("scm");
            int lts = lMeta.getInt("ts");
            long diff = Math.abs(rts - lts);
            if (diff > 300) {
                mCol.log("clock off");
                return new Object[] { "clockOff", diff };
            }
            if (mLMod == mRMod) {
                Timber.i("Sync: no changes - returning");
                mCol.log("no changes");
                return new Object[] { "noChanges" };
            } else if (lscm != rscm) {
                Timber.i("Sync: full sync necessary - returning");
                mCol.log("schema diff");
                return new Object[] { "fullSync" };
            }
            mLNewer = mLMod > mRMod;
            // step 1.5: check collection is valid
            if (!mCol.basicCheck()) {
                mCol.log("basic check");
                return new Object[] { "basicCheckFailed" };
            }
            throwExceptionIfCancelled(con);
            // step 2: deletions
            publishProgress(con, R.string.sync_deletions_message);
            Timber.i("Sync: collection removed data");
            JSONObject lrem = removed();
            JSONObject o = new JSONObject();
            o.put("minUsn", mMinUsn);
            o.put("lnewer", mLNewer);
            o.put("graves", lrem);
            Timber.i("Sync: sending and receiving removed data");
            JSONObject rrem = mServer.start(o);
            Timber.i("Sync: applying removed data");
            throwExceptionIfCancelled(con);
            remove(rrem);
            // ... and small objects
            publishProgress(con, R.string.sync_small_objects_message);
            Timber.i("Sync: collection small changes");
            JSONObject lchg = changes();
            JSONObject sch = new JSONObject();
            sch.put("changes", lchg);
            Timber.i("Sync: sending and receiving small changes");
            long needSize = sch.toString().length();
            Timber.i("Sync: sending and receiving small changes size:%d", needSize);
            if (needSize > restSpace && Consts.loginAnkiChina()) {
                throwExceptionIfNoSpace(needSize, restSpace);
            } else {
                restSpace -= needSize;
                setRestSpace(restSpace);
                Timber.i("Sync: remain size %d after for small changes ", restSpace);
            }
            JSONObject rchg = mServer.applyChanges(sch);
            throwExceptionIfCancelled(con);
            Timber.i("Sync: merging small changes");
            try {
                mergeChanges(lchg, rchg);
            } catch (UnexpectedSchemaChange e) {
                mServer.abort();
                _forceFullSync();
            }
            // step 3: stream large tables from server
            publishProgress(con, R.string.sync_download_chunk);
            while (true) {
                throwExceptionIfCancelled(con);
                Timber.i("Sync: downloading chunked data");
                JSONObject chunk = mServer.chunk();
                mCol.log("server chunk", chunk);
                Timber.i("Sync: applying chunked data");
                applyChunk(chunk);
                if (chunk.getBoolean("done")) {
                    break;
                }
            }
            // step 4: stream to server
            publishProgress(con, R.string.sync_upload_chunk);
            List<JSONObject> sechs = new ArrayList<>();
            long chunkSize = 0;
            while (true) {
                throwExceptionIfCancelled(con);
                Timber.i("Sync: collecting chunked data");
                JSONObject chunk = chunk();
                mCol.log("client chunk", chunk);
                JSONObject sech = new JSONObject();
                sech.put("chunk", chunk);
                chunkSize += sech.toString().length();
                sechs.add(sech);
                if (chunk.getBoolean("done")) {
                    break;
                }
            }
            Timber.i("Sync: sending chunked data:%d", chunkSize);
            if (chunkSize > restSpace && Consts.loginAnkiChina()) {
                throwExceptionIfNoSpace(chunkSize, restSpace);
            } else {
                restSpace -= chunkSize;
                setRestSpace(restSpace);
            }
            for (JSONObject object : sechs) {
                mServer.applyChunk(object);
            }
            // step 5: sanity check
            JSONObject c = sanityCheck();
            JSONObject sanity = mServer.sanityCheck2(c);
            if (sanity == null || !"ok".equals(sanity.optString("status", "bad"))) {
                mCol.log("sanity check failed", c, sanity);
                return _forceFullSync();
            }
            // finalize
            publishProgress(con, R.string.sync_finish_message);
            Timber.i("Sync: sending finish command");
            long mod = mServer.finish();
            if (mod == 0) {
                return new Object[] { "finishError" };
            }
            Timber.i("Sync: finishing");
            finish(mod);
            publishProgress(con, R.string.sync_writing_db);
            mCol.getDb().getDatabase().setTransactionSuccessful();
        } catch (NoEnoughServerSpaceException e) {
            // mCol.getDb().getDatabase().endTransaction();
            throw new NoEnoughServerSpaceException(e.rest, e.need);
        // e.printStackTrace();
        // mCol.getDb().getDatabase().endTransaction();
        // return new Object[] {"noServerSpace", e.rest, e.need};
        } finally {
            mCol.getDb().getDatabase().endTransaction();
        }
    } catch (NoEnoughServerSpaceException e) {
        Timber.e("NoEnoughServerSpaceException ");
        throw new NoEnoughServerSpaceException(e.rest, e.need);
    // e.printStackTrace();
    // mCol.getDb().getDatabase().endTransaction();
    // return new Object[] {"noServerSpace", e.rest, e.need};
    } catch (IllegalStateException e) {
        throw new RuntimeException(e);
    } catch (OutOfMemoryError e) {
        AnkiDroidApp.sendExceptionReport(e, "Syncer-sync");
        return new Object[] { "OutOfMemoryError" };
    } catch (IOException e) {
        AnkiDroidApp.sendExceptionReport(e, "Syncer-sync");
        return new Object[] { "IOException" };
    }
    return new Object[] { "success", restSpace };
}
Also used : ArrayList(java.util.ArrayList) IOException(java.io.IOException) Response(okhttp3.Response) JSONObject(com.ichi2.utils.JSONObject) JSONObject(com.ichi2.utils.JSONObject) NoEnoughServerSpaceException(com.ichi2.anki.exception.NoEnoughServerSpaceException)

Example 65 with Deck

use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.

the class Syncer method mergeDecks.

private void mergeDecks(JSONArray rchg) {
    JSONArray decks = rchg.getJSONArray(0);
    for (int i = 0; i < decks.length(); i++) {
        Deck r = new Deck(decks.getJSONObject(i));
        Deck l = mCol.getDecks().get(r.getLong("id"), false);
        // if missing locally or server is newer, update
        if (l == null || r.getLong("mod") > l.getLong("mod")) {
            mCol.getDecks().update(r);
        }
    }
    JSONArray confs = rchg.getJSONArray(1);
    for (int i = 0; i < confs.length(); i++) {
        DeckConfig r = new DeckConfig(confs.getJSONObject(i));
        DeckConfig l = mCol.getDecks().getConf(r.getLong("id"));
        // if missing locally or server is newer, update
        if (l == null || r.getLong("mod") > l.getLong("mod")) {
            mCol.getDecks().updateConf(r);
        }
    }
}
Also used : JSONArray(com.ichi2.utils.JSONArray) Deck(com.ichi2.libanki.Deck) DeckConfig(com.ichi2.libanki.DeckConfig)

Aggregations

Deck (com.ichi2.libanki.Deck)100 Collection (com.ichi2.libanki.Collection)97 JSONObject (com.ichi2.utils.JSONObject)88 Test (org.junit.Test)80 JSONArray (com.ichi2.utils.JSONArray)55 Card (com.ichi2.libanki.Card)53 Note (com.ichi2.libanki.Note)50 ArrayList (java.util.ArrayList)47 RobolectricTest (com.ichi2.anki.RobolectricTest)44 DeckConfig (com.ichi2.libanki.DeckConfig)37 JSONException (com.ichi2.utils.JSONException)34 NonNull (androidx.annotation.NonNull)30 HashMap (java.util.HashMap)29 Model (com.ichi2.libanki.Model)23 Map (java.util.Map)22 Intent (android.content.Intent)21 Resources (android.content.res.Resources)18 TextView (android.widget.TextView)18 SharedPreferences (android.content.SharedPreferences)17 Cursor (android.database.Cursor)17