ankidroid / Anki-Android

AnkiDroid: Anki flashcards on Android. Your secret trick to achieve superhuman information retention.
GNU General Public License v3.0
8.38k stars 2.18k forks source link

JSONException: No value for replayq #2906

Closed hssm closed 9 years ago

hssm commented 9 years ago

Originally reported on Google Code with ID 2005

1. Install 2.1beta3 or 2.1beta4
2. Review any card (even most basic front+back model, with no sound)
3. Press "Show answer"
4. Crash:

E/AndroidRuntime(13641): java.lang.RuntimeException: org.json.JSONException: No value
for replayq
E/AndroidRuntime(13641):    at com.ichi2.anki.Reviewer.playSounds(Reviewer.java:2692)
E/AndroidRuntime(13641):    at com.ichi2.anki.Reviewer.updateCard(Reviewer.java:2646)
E/AndroidRuntime(13641):    at com.ichi2.anki.Reviewer.displayCardAnswer(Reviewer.java:2552)
E/AndroidRuntime(13641):    at com.ichi2.anki.Reviewer.access$1300(Reviewer.java:135)
E/AndroidRuntime(13641):    at com.ichi2.anki.Reviewer$6.onClick(Reviewer.java:516)
E/AndroidRuntime(13641):    at android.view.View.performClick(View.java:4100)
E/AndroidRuntime(13641):    at android.view.View$PerformClick.run(View.java:17021)
E/AndroidRuntime(13641):    at android.os.Handler.handleCallback(Handler.java:615)
E/AndroidRuntime(13641):    at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime(13641):    at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime(13641):    at android.app.ActivityThread.main(ActivityThread.java:4784)
E/AndroidRuntime(13641):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(13641):    at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime(13641):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:776)
E/AndroidRuntime(13641):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:543)
E/AndroidRuntime(13641):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(13641): Caused by: org.json.JSONException: No value for replayq
E/AndroidRuntime(13641):    at org.json.JSONObject.get(JSONObject.java:354)
E/AndroidRuntime(13641):    at org.json.JSONObject.getBoolean(JSONObject.java:375)
E/AndroidRuntime(13641):    at com.ichi2.anki.Reviewer.playSounds(Reviewer.java:2677)
E/AndroidRuntime(13641):    ... 15 more
W/ActivityManager(  341):   Force finishing activity com.ichi2.anki/.Reviewer

It started happening suddenly. 2.1beta3 was working normally until it started crashing
this way. Now it crashes on any card. I did not review any card with sounds, the whole
time.

Triage: http://ankidroid-triage.appspot.com/view_bug?bug_id=5244280561991680

Reported by nicolas.raoul on 2014-03-02 03:57:21

hssm commented 9 years ago
To investigate this issue I uninstalled the release version and executed from Eclipse
ADT... and the bug does not reproduce anymore.

Could anyone familiar with sound replay investigate this?

If the problem happens to you, please tell us here, and then try uninstalling/reinstalling
AnkiDroid.

Reported by nicolas.raoul on 2014-03-03 05:35:43

hssm commented 9 years ago
This may be related to issue 2006... there seem to sometimes be missing values in the
conf dictionary, which are causing crashes. If you reproduce the crash, please open
the collection.anki2 file with an sqlite browser like this   

(https://play.google.com/store/apps/details?id=com.xuecs.sqlitemanager&hl=en)

and go to "col" table -> "dconf" field -> and check if "replayq" is missing from any
of the models.

Reported by perceptualchaos2 on 2014-03-03 08:24:36

hssm commented 9 years ago
Oh hey! I think I know what this is from. I made an incidental change that was merged
here: https://github.com/ankidroid/Anki-Android/commit/d529c353b82644ed787a2dd5eb83e1b262ba7468

It just makes the UI more consistent, in that it disables a checkbox not applicable
to the user under those conditions, but I'm thinking that the disabling of the checkbox
is keeping the preference's state from being accessed in the code, leading to a null
value of sorts. When you re-installed, that would provide fresh preferences, so this
would not affect fresh installs, only upgrades, assuming I've got it pegged right,
which I classify as likely.

I had only expected the checkbox to be enabled/disabled, but I didn't think the actual
state would be unobtainable. Maybe if the checkbox is disabled programatically, rather
than using android:dependency in deck_options.xml

Reported by chajadan on 2014-03-03 08:28:37

hssm commented 9 years ago
Maybe the issue got fixed because reinstalling forced a "Checking database" (which took
a long time but shrunk by zero).

I had tried with several decks using several models, all caused this crash.

Reported by nicolas.raoul on 2014-03-03 08:28:38

hssm commented 9 years ago
I don't remember having opened the preferences screen since a few weeks, if that matters.

@chajadan: Would you have time to try and fix it? Thanks a lot :-)

Reported by nicolas.raoul on 2014-03-03 08:34:15

hssm commented 9 years ago
Well I have a two definite fix options (I believe) and then a better fix (codewise).

Obvious fix number 1: just don't disable that checkbox. This would work but is not
UI one would shot for.

A fix I trust to work: in commit(), set the value to false at the front of the function,
and if it seems a value of true (which means the preference is enabled) let it update
the value as it normally would later in that function in the loop. I trust this would
work, but it's a bit unclean codewise to have this sole exception isolated from how
everything else works.

What would be nicest is to just not use dependency, and then programatically disable
the checkbox when rendering preferences. However, I have no reason to believe this
would work any better. For all I know, android:dependency is equivalent to enabling/disabling
the view, and my research does not indicate it affects access of the dependent preference's
state. So I don't trust this, nor understand the "PreferenceHack".

But the first two I trust would handle this. What would you shoot for? I'm thinking
option two is the best compromise, when option three is seeking perfection I can't
offer at the moment.

Reported by chajadan on 2014-03-03 08:48:36

hssm commented 9 years ago
How about option 2 and create an enhancement for the ideal behavior your described?

Since we are approaching release, let's be on the safe side. Even 1 would be OK actually,
if it the safest.

Reported by nicolas.raoul on 2014-03-03 08:55:14

hssm commented 9 years ago
another option would be to surround access of the JSON object with a try block, but
that leaves holes in other accesses that exist or spring up

Reported by chajadan on 2014-03-03 08:55:39

hssm commented 9 years ago
If the desktop accesses it with .get() it means it was not in the initial release of
Anki 2, and AnkiDroid should fall back to a default value when it doesn't exist as
well.

Reported by dae@ichi2.net on 2014-03-03 12:49:02

hssm commented 9 years ago
If we fall back to default values for any parameters in the collection which were not
in the initial release of Anki2, then would it be worth throwing a warning that the
value was not found, to aid with debugging behavioral problems! 

There could be some underlying problem for why the value was not there, other than
the collection simply having originated from an older version of Anki.

Reported by perceptualchaos2 on 2014-03-05 04:52:11

hssm commented 9 years ago
Sorry, I don't think that would be useful. Such variables may or may not be defined,
and it's not a sign of a problem if they haven't been set yet - it simply indicates
that the user has not customized the value since upgrading.

Reported by dae@ichi2.net on 2014-03-05 05:04:39

hssm commented 9 years ago
Yeah probably not... but at least I think we need to write something in the comments
every-time we include a default value in the get() call...

P.S. I'm sure it was obvious, but I meant a programmatic warning visible in the log-file,
rather than a warning shown to the user.

Reported by perceptualchaos2 on 2014-03-05 06:11:42

hssm commented 9 years ago
In 2.1beta6: http://ankidroid-triage.appspot.com/view_crash?crash_id=4610053478809600

Reported by nicolas.raoul on 2014-03-07 16:21:18

hssm commented 9 years ago
I fixed the replayq crash with this commit:
https://github.com/nicolas-raoul/Anki-Android/commit/3792faa1d362a5eb723aff35c2841ef95eb2f14c
A method getBoolean(JSONObject, Boolean defaultValue) would be cleaner, let's see what
other values need the same thing before deciding what is the best way to improve this.

So, what are the other values that can be absent?
All of defaultConf in decks.py?

Reported by nicolas.raoul on 2014-03-09 15:01:56

hssm commented 9 years ago
Only attributes that are referenced in the code with thedeck.get() instead of thedeck[attr]

Reported by dae@ichi2.net on 2014-03-10 02:45:44

hssm commented 9 years ago
One way to handle the issue of new options that will be lacking from the database would
be to use the upgrade paths. When a new setting is introduced, simply write a default
to the database if relevant to the upgrade at hand. Before releasing v2.1 we could
do a one time catch-up upgrade that ensures all current options have at least a default
value present, and that would avoid the access crashes.

Reported by chajadan on 2014-03-16 09:00:10

hssm commented 9 years ago
That is not a robust way to handle it, as a user could sync an older client and undo
the upgraded changes. IMHO the best way to handle it is to access those variables conditionally,
which should be happening already if the desktop code has been ported without modifications.

Reported by dae@ichi2.net on 2014-03-16 10:28:04

hssm commented 9 years ago
OK so to proceed with this, we need to make a list of all the attributes in desktop
which are referenced by deck.get()? Is this something we want to include in 2.1?

Reported by perceptualchaos2 on 2014-03-18 10:15:08

hssm commented 9 years ago
In Anki Desktop source code:
find . -name "*.py" | xargs grep -R "\.get(" * |grep -v "js.py"

anki/notes.py:        self._model = self.col.models.get(self.mid)
anki/importing/supermemo_xml.py:    x.get('first_name').
anki/importing/anki2.py:                                self.col.models.get(oldMid)['name'],
anki/importing/anki2.py:        srcModel = self.src.models.get(srcMid)
anki/importing/anki2.py:            dstModel = self.dst.models.get(mid)
anki/importing/anki2.py:        g = self.src.decks.get(did)
anki/importing/anki2.py:            g2 = self.dst.decks.get(newid)
anki/importing/anki2.py:        deck = self.dst.decks.get(newid)
anki/importing/anki1.py:        d = self.col.decks.get(did)
anki/importing/mnemo.py:                fld = self._mungeField(orig.get(f, ''))
anki/importing/mnemo.py:            n.cards = orig.get('cards', {})
anki/importing/mnemo.py:            fld = orig.get("text", "")
anki/importing/mnemo.py:            n.cards = orig.get('cards', {})
Binary file anki/importing/supermemo_xml.pyc matches
anki/sync.py:        self.uname = meta.get("uname", "")
anki/sync.py:            l = self.col.models.get(r['id'])
anki/sync.py:            l = self.col.decks.get(r['id'], False)
anki/db.py:        self.echo = os.environ.get("DBECHO")
anki/template/view.py:        val = self.get(attr, None)
anki/template/view.py:        attr = self.context.get(attr, getattr(self, attr, default))
anki/decks.py:            deck = self.get(did)
anki/decks.py:        deck = self.get(did)
anki/decks.py:        deck = self.get(did)
anki/decks.py:        deck = self.get(did)
anki/decks.py:        collapsed = deck.get('browserCollapsed', False)
anki/decks.py:        draggedDeck = self.get(draggedDeckDid)
anki/decks.py:        ontoDeckName = self.get(ontoDeckDid)['name']
anki/decks.py:            draggedDeck = self.get(draggedDeckDid)
anki/decks.py:            ontoDeckName = self.get(ontoDeckDid)['name']
anki/decks.py:        deck = self.get(did, default=False)
anki/decks.py:        deck = self.get(did, default=default)
anki/decks.py:        deck = self.get(did, default=False)
anki/decks.py:        return self.get(self.selected())
anki/decks.py:        name = self.get(did)['name']
anki/decks.py:        for part in self.get(did)['name'].split("::")[:-1]:
anki/decks.py:            parents[c] = self.get(self.id(p))
anki/decks.py:        return self.get(did)['dyn']
anki/collection.py:        if not self.conf.get("newBury", False):
anki/collection.py:        id = self.conf.get(type, 1)
anki/collection.py:            model = self.models.get(mid)
anki/collection.py:            did = dids.get(nid) or model['did']
anki/collection.py:                    did = self.decks.get(did)['id']
anki/collection.py:        deck = self.decks.get(card.did)
anki/collection.py:            model = self.models.get(mid)
anki/collection.py:        model = self.models.get(data[2])
anki/collection.py:            limit=2+kwargs.get("stack", 0))[0]
anki/collection.py:        if os.environ.get("ANKIDEV"):
anki/cards.py:                args = (t.get('bqfmt'), t.get('bafmt'))
anki/cards.py:        return self.col.models.get(self.note().mid)
anki/media.py:        model = self.col.models.get(mid)
anki/storage.py:            r['ivlFct'] = r.get("ivlfct", 1)
anki/sched.py:        for g in ([self.col.decks.get(card.did)] +
anki/sched.py:        children = [self.col.decks.get(did) for (name, did) in
anki/sched.py:            lim = limFn(self.col.decks.get(did))
anki/sched.py:            deck = self.col.decks.get(did)
anki/sched.py:        sel = self.col.decks.get(did)
anki/sched.py:                    if self.col.decks.get(did)['dyn']:
anki/sched.py:        new = ivl * conf.get('ivlFct', 1)
anki/sched.py:        deck = self.col.decks.get(did)
anki/sched.py:        deck = self.col.decks.get(did)
anki/sched.py:            bury=oconf['new'].get("bury", True),
anki/sched.py:        unburied = self.col.conf.get("lastUnburied", 0)
anki/sched.py:        buryNew = nconf.get("bury", True)
anki/sched.py:        buryRev = rconf.get("bury", True)
anki/find.py:            model = col.models.get(mid)
anki/hooks.py:    hook = _hooks.get(hook, None)
anki/hooks.py:    hook = _hooks.get(hook, None)
anki/hooks.py:    if not _hooks.get(hook, None):
anki/hooks.py:    hook = _hooks.get(hook, [])
anki/models.py:        m = self.get(self.col.decks.current().get('mid'))
anki/models.py:            m = self.get(self.col.conf['curModel'])
anki/models.py:                flds.append(newflds.get(c, ""))
anki/consts.py:SYNC_URL = os.environ.get("SYNC_URL") or "https://ankiweb.net/sync/"
aqt/clayout.py:        f.qfmt.setText(t.get('bqfmt', ""))
aqt/clayout.py:        f.afmt.setText(t.get('bafmt', ""))
aqt/clayout.py:        f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
aqt/clayout.py:        f.fontSize.setValue(t.get('bsize', 12))
aqt/clayout.py:            te.setText(self.col.decks.get(t['did'])['name'])
aqt/deckbrowser.py:                self.mw.pm.profile.get("hideDeckLotsMsg")):
aqt/deckbrowser.py:        deck = self.mw.col.decks.get(did)
aqt/deckbrowser.py:        if self.mw.col.decks.get(did)['collapsed']:
aqt/deckbrowser.py:        deck = self.mw.col.decks.get(did)
aqt/deckbrowser.py:        deck = self.mw.col.decks.get(did)
aqt/deckconf.py:            deck = self.mw.col.decks.get(did)
aqt/deckconf.py:        f.bury.setChecked(c.get("bury", True))
aqt/deckconf.py:        f.buryRev.setChecked(c.get("bury", True))
aqt/deckconf.py:        f.showTimer.setChecked(c.get('timer', 0))
aqt/deckconf.py:        f.replayQuestion.setChecked(c.get('replayq', True))
aqt/upgrade.py:        loc = self.oldprefs.get("mediaLocation")
aqt/browser.py:        self.activeCols = self.col.conf.get(
aqt/browser.py:            f.setFamily(t.get("bfont", self.browser.mw.fontFamily))
aqt/browser.py:            f.setPixelSize(t.get("bsize", self.browser.mw.fontHeight))
aqt/browser.py:        if c.template().get('bafmt'):
aqt/browser.py:                bsize = t.get("bsize", 0)
aqt/browser.py:                collapsed = self.mw.col.decks.get(g[1]).get('browserCollapsed',
False)
aqt/browser.py:        current=self.mw.col.decks.get(did)['name']
aqt/browser.py:        deck = self.col.decks.get(did)
aqt/browser.py:        self.oldModel = self.browser.col.models.get(
aqt/editor.py:        self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
aqt/editor.py:        if self.editor.mw.pm.profile.get("pastePNG", False):
aqt/importing.py:        self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML',
True))
aqt/importing.py:        self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode',
1))
aqt/importing.py:        if self.mw.col.conf.get("addToCur", True):
aqt/deckchooser.py:        if self.mw.col.conf.get("addToCur", True):
aqt/deckchooser.py:        if not self.mw.col.conf.get("addToCur", True):
aqt/taglimit.py:        yes = self.deck.get("activeTags", [])
aqt/taglimit.py:        no = self.deck.get("inactiveTags", [])
aqt/main.py:        if self.pm.profile.get('compressBackups', True):
aqt/main.py:        return self.col.decks.get(did)
aqt/main.py:        if self.pm.meta.get('suppressUpdate', None) != ver:
aqt/overview.py:        self.sid = deck.get("sharedFrom")
aqt/overview.py:            self.sidVer = deck.get("ver", None)
aqt/overview.py:            desc = deck.get("desc", "")
aqt/qt.py:if os.environ.get("DEBUG"):
aqt/customstudy.py:            sval = min(new, self.deck.get('extendNew', 10))
aqt/customstudy.py:            sval = min(rev, self.deck.get('extendRev', 10))
aqt/customstudy.py:            dyn = self.mw.col.decks.get(did)
aqt/models.py:        m = AddModel(self.mw, self).get()
aqt/utils.py:        dir = aqt.mw.pm.profile.get(dirkey, "")
aqt/utils.py:    base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base)
aqt/utils.py:    if aqt.mw.pm.profile.get(key):
aqt/utils.py:    if aqt.mw.pm.profile.get(key):
aqt/utils.py:    if aqt.mw.pm.profile.get(key):
aqt/utils.py:    if aqt.mw.pm.profile.get(key):
aqt/preferences.py:        f.useCurrent.setCurrentIndex(int(not qc.get("addToCur",
True)))
aqt/preferences.py:            self.form.syncUser.setText(self.prof.get('syncUser',
""))
aqt/preferences.py:        self.form.compressBackups.setChecked(self.prof.get("compressBackups",
True))
aqt/preferences.py:        self.form.pastePNG.setChecked(self.prof.get("pastePNG",
False))
aqt/reviewer.py:            self.card.odid or self.card.did).get('replayq', True)
aqt/reviewer.py:        self.mw.onDeckConf(self.mw.col.decks.get(
oldanki/sync.py:        mod = self.deck.s.query(Model).get(id)
oldanki/stats.py:                    boxes[n] = boxes.get(n, 0) + 1
oldanki/deck.py:            card = self.s.query(oldanki.cards.Card).get(id)
oldanki/deck.py:        model = self.s.query(Model).get(oldFact.model.id)
oldanki/deck.py:                deck = s.query(Deck).get(1)
oldanki/template/view.py:        val = self.get(attr, None)
oldanki/template/view.py:        attr = self.context.get(attr, getattr(self, attr,
default))
oldanki/stdmodels.py:    fn = models.get(name)
oldanki/hooks.py:    hook = _hooks.get(hook, None)
oldanki/hooks.py:    hook = _hooks.get(hook, None)
oldanki/hooks.py:    if not _hooks.get(hook, None):
oldanki/hooks.py:    hook = _hooks.get(hook, [])
oldanki/hooks.py:    return not _hooks.get(hook)
oldanki/graphs.py:                    days[day] = days.get(day, 0) + 1
oldanki/graphs.py:                    next[indays] = next.get(indays, 0) + 1 # type-agnostic
stats
oldanki/graphs.py:                    dest[indays] = dest.get(indays, 0) + 1 # type-specific
stats
oldanki/graphs.py:            days[d] = days.get(d, 0) + 1
tests/test_exporting.py:    dobj = deck.decks.get(did)
tests/test_exporting.py:    dobj = d2.decks.get(did)
tests/test_decks.py:    d.decks.rename(d.decks.get(id), "foo::bar")
tests/test_decks.py:        Exception, lambda: d.decks.rename(d.decks.get(id), "foo"))
tests/test_decks.py:    d.decks.rename(d.decks.get(id), "yo")
tests/test_sync.py:    assert deck2.models.get(cm['id'])['name'].startswith("Basic")
tests/test_sync.py:    assert deck2.models.get(cm['id'])['name'] == "new"
tests/test_sched.py:    d.decks.setConf(d.decks.get(g2), c2)
tests/test_sched.py:    cram = d.decks.get(did)
tests/test_sched.py:    cram = d.decks.get(did)
thirdparty/send2trash/plat_other.py:XDG_DATA_HOME = op.expanduser(os.environ.get(u'XDG_DATA_HOME',
u'~/.local/share'))
thirdparty/httplib2/__init__.py:    hopbyhop.extend([x.strip() for x in response.get('connection',
'').split(',')])
thirdparty/httplib2/__init__.py:        encoding = response.get('content-encoding',
None)
thirdparty/httplib2/__init__.py:        raise FailedToDecompressContent(_("Content
purported to be compressed with %s but failed to decompress.") % response.get('content-encoding'),
response, content)
thirdparty/httplib2/__init__.py:            vary = response_headers.get('vary', None)
thirdparty/httplib2/__init__.py:        qop = self.challenge.get('qop', 'auth')
thirdparty/httplib2/__init__.py:        self.challenge['algorithm'] = self.challenge.get('algorithm',
'MD5').upper()
thirdparty/httplib2/__init__.py:        if self.challenge.get('opaque'):
thirdparty/httplib2/__init__.py:            challenge = _parse_www_authenticate(response,
'www-authenticate').get('digest', {})
thirdparty/httplib2/__init__.py:            if 'true' == challenge.get('stale'):
thirdparty/httplib2/__init__.py:            updated_challenge = _parse_www_authenticate(response,
'authentication-info').get('digest', {})
thirdparty/httplib2/__init__.py:        self.challenge['reason'] = self.challenge.get('reason',
'unauthorized')
thirdparty/httplib2/__init__.py:        self.challenge['salt'] = self.challenge.get('salt',
'')
thirdparty/httplib2/__init__.py:        if not self.challenge.get('snonce'):
thirdparty/httplib2/__init__.py:        self.challenge['algorithm'] = self.challenge.get('algorithm',
'HMAC-SHA-1')
thirdparty/httplib2/__init__.py:        self.challenge['pw-algorithm'] = self.challenge.get('pw-algorithm',
'SHA-1')
thirdparty/httplib2/__init__.py:        challenge = _parse_www_authenticate(response,
'www-authenticate').get('hmacdigest', {})
thirdparty/httplib2/__init__.py:        if challenge.get('reason') in ['integrity',
'stale']:
thirdparty/httplib2/__init__.py:        service = challenge['googlelogin'].get('service',
'xapi')
thirdparty/httplib2/__init__.py:    url = os.environ.get(env_var, os.environ.get(env_var.upper()))
thirdparty/httplib2/__init__.py:    no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY',
''))
thirdparty/httplib2/__init__.py:        self.response['reason'] = httplib.responses.get(response.status_code,
'Ok')
thirdparty/httplib2/__init__.py:                cached_value = self.cache.get(cachekey)
thirdparty/httplib2/__init__.py:                    if headers.get(header, None) !=
value:
thirdparty/httplib2/__init__.py:            self.status = int(self.get('status', self.status))
thirdparty/httplib2/__init__.py:            self.reason = self.get('reason', self.reason)
thirdparty/BeautifulSoup.py:        return self._getAttrMap().get(key, default)
thirdparty/BeautifulSoup.py:                    attrValue = markupAttrMap.get(attr)
thirdparty/BeautifulSoup.py:        nestingResetTriggers = self.NESTABLE_TAGS.get(name)
thirdparty/BeautifulSoup.py:                data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
thirdparty/BeautifulSoup.py:            not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
thirdparty/BeautifulSoup.py:        sub = self.MS_CHARS.get(orig)
thirdparty/BeautifulSoup.py:        return self._codec(self.CHARSET_ALIASES.get(charset,
charset)) \
./oldanki/sync.py:        mod = self.deck.s.query(Model).get(id)
./oldanki/stats.py:                    boxes[n] = boxes.get(n, 0) + 1
./oldanki/deck.py:            card = self.s.query(oldanki.cards.Card).get(id)
./oldanki/deck.py:        model = self.s.query(Model).get(oldFact.model.id)
./oldanki/deck.py:                deck = s.query(Deck).get(1)
./oldanki/template/view.py:        val = self.get(attr, None)
./oldanki/template/view.py:        attr = self.context.get(attr, getattr(self, attr,
default))
./oldanki/stdmodels.py:    fn = models.get(name)
./oldanki/hooks.py:    hook = _hooks.get(hook, None)
./oldanki/hooks.py:    hook = _hooks.get(hook, None)
./oldanki/hooks.py:    if not _hooks.get(hook, None):
./oldanki/hooks.py:    hook = _hooks.get(hook, [])
./oldanki/hooks.py:    return not _hooks.get(hook)
./oldanki/graphs.py:                    days[day] = days.get(day, 0) + 1
./oldanki/graphs.py:                    next[indays] = next.get(indays, 0) + 1 # type-agnostic
stats
./oldanki/graphs.py:                    dest[indays] = dest.get(indays, 0) + 1 # type-specific
stats
./oldanki/graphs.py:            days[d] = days.get(d, 0) + 1
./thirdparty/send2trash/plat_other.py:XDG_DATA_HOME = op.expanduser(os.environ.get(u'XDG_DATA_HOME',
u'~/.local/share'))
./thirdparty/httplib2/__init__.py:    hopbyhop.extend([x.strip() for x in response.get('connection',
'').split(',')])
./thirdparty/httplib2/__init__.py:        encoding = response.get('content-encoding',
None)
./thirdparty/httplib2/__init__.py:        raise FailedToDecompressContent(_("Content
purported to be compressed with %s but failed to decompress.") % response.get('content-encoding'),
response, content)
./thirdparty/httplib2/__init__.py:            vary = response_headers.get('vary', None)
./thirdparty/httplib2/__init__.py:        qop = self.challenge.get('qop', 'auth')
./thirdparty/httplib2/__init__.py:        self.challenge['algorithm'] = self.challenge.get('algorithm',
'MD5').upper()
./thirdparty/httplib2/__init__.py:        if self.challenge.get('opaque'):
./thirdparty/httplib2/__init__.py:            challenge = _parse_www_authenticate(response,
'www-authenticate').get('digest', {})
./thirdparty/httplib2/__init__.py:            if 'true' == challenge.get('stale'):
./thirdparty/httplib2/__init__.py:            updated_challenge = _parse_www_authenticate(response,
'authentication-info').get('digest', {})
./thirdparty/httplib2/__init__.py:        self.challenge['reason'] = self.challenge.get('reason',
'unauthorized')
./thirdparty/httplib2/__init__.py:        self.challenge['salt'] = self.challenge.get('salt',
'')
./thirdparty/httplib2/__init__.py:        if not self.challenge.get('snonce'):
./thirdparty/httplib2/__init__.py:        self.challenge['algorithm'] = self.challenge.get('algorithm',
'HMAC-SHA-1')
./thirdparty/httplib2/__init__.py:        self.challenge['pw-algorithm'] = self.challenge.get('pw-algorithm',
'SHA-1')
./thirdparty/httplib2/__init__.py:        challenge = _parse_www_authenticate(response,
'www-authenticate').get('hmacdigest', {})
./thirdparty/httplib2/__init__.py:        if challenge.get('reason') in ['integrity',
'stale']:
./thirdparty/httplib2/__init__.py:        service = challenge['googlelogin'].get('service',
'xapi')
./thirdparty/httplib2/__init__.py:    url = os.environ.get(env_var, os.environ.get(env_var.upper()))
./thirdparty/httplib2/__init__.py:    no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY',
''))
./thirdparty/httplib2/__init__.py:        self.response['reason'] = httplib.responses.get(response.status_code,
'Ok')
./thirdparty/httplib2/__init__.py:                cached_value = self.cache.get(cachekey)
./thirdparty/httplib2/__init__.py:                    if headers.get(header, None)
!= value:
./thirdparty/httplib2/__init__.py:            self.status = int(self.get('status',
self.status))
./thirdparty/httplib2/__init__.py:            self.reason = self.get('reason', self.reason)
./thirdparty/BeautifulSoup.py:        return self._getAttrMap().get(key, default)
./thirdparty/BeautifulSoup.py:                    attrValue = markupAttrMap.get(attr)
./thirdparty/BeautifulSoup.py:        nestingResetTriggers = self.NESTABLE_TAGS.get(name)
./thirdparty/BeautifulSoup.py:                data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
./thirdparty/BeautifulSoup.py:            not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
./thirdparty/BeautifulSoup.py:        sub = self.MS_CHARS.get(orig)
./thirdparty/BeautifulSoup.py:        return self._codec(self.CHARSET_ALIASES.get(charset,
charset)) \
./anki/notes.py:        self._model = self.col.models.get(self.mid)
./anki/importing/supermemo_xml.py:    x.get('first_name').
./anki/importing/anki2.py:                                self.col.models.get(oldMid)['name'],
./anki/importing/anki2.py:        srcModel = self.src.models.get(srcMid)
./anki/importing/anki2.py:            dstModel = self.dst.models.get(mid)
./anki/importing/anki2.py:        g = self.src.decks.get(did)
./anki/importing/anki2.py:            g2 = self.dst.decks.get(newid)
./anki/importing/anki2.py:        deck = self.dst.decks.get(newid)
./anki/importing/anki1.py:        d = self.col.decks.get(did)
./anki/importing/mnemo.py:                fld = self._mungeField(orig.get(f, ''))
./anki/importing/mnemo.py:            n.cards = orig.get('cards', {})
./anki/importing/mnemo.py:            fld = orig.get("text", "")
./anki/importing/mnemo.py:            n.cards = orig.get('cards', {})
./anki/sync.py:        self.uname = meta.get("uname", "")
./anki/sync.py:            l = self.col.models.get(r['id'])
./anki/sync.py:            l = self.col.decks.get(r['id'], False)
./anki/db.py:        self.echo = os.environ.get("DBECHO")
./anki/template/view.py:        val = self.get(attr, None)
./anki/template/view.py:        attr = self.context.get(attr, getattr(self, attr, default))
./anki/decks.py:            deck = self.get(did)
./anki/decks.py:        deck = self.get(did)
./anki/decks.py:        deck = self.get(did)
./anki/decks.py:        deck = self.get(did)
./anki/decks.py:        collapsed = deck.get('browserCollapsed', False)
./anki/decks.py:        draggedDeck = self.get(draggedDeckDid)
./anki/decks.py:        ontoDeckName = self.get(ontoDeckDid)['name']
./anki/decks.py:            draggedDeck = self.get(draggedDeckDid)
./anki/decks.py:            ontoDeckName = self.get(ontoDeckDid)['name']
./anki/decks.py:        deck = self.get(did, default=False)
./anki/decks.py:        deck = self.get(did, default=default)
./anki/decks.py:        deck = self.get(did, default=False)
./anki/decks.py:        return self.get(self.selected())
./anki/decks.py:        name = self.get(did)['name']
./anki/decks.py:        for part in self.get(did)['name'].split("::")[:-1]:
./anki/decks.py:            parents[c] = self.get(self.id(p))
./anki/decks.py:        return self.get(did)['dyn']
./anki/collection.py:        if not self.conf.get("newBury", False):
./anki/collection.py:        id = self.conf.get(type, 1)
./anki/collection.py:            model = self.models.get(mid)
./anki/collection.py:            did = dids.get(nid) or model['did']
./anki/collection.py:                    did = self.decks.get(did)['id']
./anki/collection.py:        deck = self.decks.get(card.did)
./anki/collection.py:            model = self.models.get(mid)
./anki/collection.py:        model = self.models.get(data[2])
./anki/collection.py:            limit=2+kwargs.get("stack", 0))[0]
./anki/collection.py:        if os.environ.get("ANKIDEV"):
./anki/cards.py:                args = (t.get('bqfmt'), t.get('bafmt'))
./anki/cards.py:        return self.col.models.get(self.note().mid)
./anki/media.py:        model = self.col.models.get(mid)
./anki/storage.py:            r['ivlFct'] = r.get("ivlfct", 1)
./anki/sched.py:        for g in ([self.col.decks.get(card.did)] +
./anki/sched.py:        children = [self.col.decks.get(did) for (name, did) in
./anki/sched.py:            lim = limFn(self.col.decks.get(did))
./anki/sched.py:            deck = self.col.decks.get(did)
./anki/sched.py:        sel = self.col.decks.get(did)
./anki/sched.py:                    if self.col.decks.get(did)['dyn']:
./anki/sched.py:        new = ivl * conf.get('ivlFct', 1)
./anki/sched.py:        deck = self.col.decks.get(did)
./anki/sched.py:        deck = self.col.decks.get(did)
./anki/sched.py:            bury=oconf['new'].get("bury", True),
./anki/sched.py:        unburied = self.col.conf.get("lastUnburied", 0)
./anki/sched.py:        buryNew = nconf.get("bury", True)
./anki/sched.py:        buryRev = rconf.get("bury", True)
./anki/find.py:            model = col.models.get(mid)
./anki/hooks.py:    hook = _hooks.get(hook, None)
./anki/hooks.py:    hook = _hooks.get(hook, None)
./anki/hooks.py:    if not _hooks.get(hook, None):
./anki/hooks.py:    hook = _hooks.get(hook, [])
./anki/models.py:        m = self.get(self.col.decks.current().get('mid'))
./anki/models.py:            m = self.get(self.col.conf['curModel'])
./anki/models.py:                flds.append(newflds.get(c, ""))
./anki/consts.py:SYNC_URL = os.environ.get("SYNC_URL") or "https://ankiweb.net/sync/"
./tests/test_exporting.py:    dobj = deck.decks.get(did)
./tests/test_exporting.py:    dobj = d2.decks.get(did)
./tests/test_decks.py:    d.decks.rename(d.decks.get(id), "foo::bar")
./tests/test_decks.py:        Exception, lambda: d.decks.rename(d.decks.get(id), "foo"))
./tests/test_decks.py:    d.decks.rename(d.decks.get(id), "yo")
./tests/test_sync.py:    assert deck2.models.get(cm['id'])['name'].startswith("Basic")
./tests/test_sync.py:    assert deck2.models.get(cm['id'])['name'] == "new"
./tests/test_sched.py:    d.decks.setConf(d.decks.get(g2), c2)
./tests/test_sched.py:    cram = d.decks.get(did)
./tests/test_sched.py:    cram = d.decks.get(did)
./aqt/clayout.py:        f.qfmt.setText(t.get('bqfmt', ""))
./aqt/clayout.py:        f.afmt.setText(t.get('bafmt', ""))
./aqt/clayout.py:        f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
./aqt/clayout.py:        f.fontSize.setValue(t.get('bsize', 12))
./aqt/clayout.py:            te.setText(self.col.decks.get(t['did'])['name'])
./aqt/deckbrowser.py:                self.mw.pm.profile.get("hideDeckLotsMsg")):
./aqt/deckbrowser.py:        deck = self.mw.col.decks.get(did)
./aqt/deckbrowser.py:        if self.mw.col.decks.get(did)['collapsed']:
./aqt/deckbrowser.py:        deck = self.mw.col.decks.get(did)
./aqt/deckbrowser.py:        deck = self.mw.col.decks.get(did)
./aqt/deckconf.py:            deck = self.mw.col.decks.get(did)
./aqt/deckconf.py:        f.bury.setChecked(c.get("bury", True))
./aqt/deckconf.py:        f.buryRev.setChecked(c.get("bury", True))
./aqt/deckconf.py:        f.showTimer.setChecked(c.get('timer', 0))
./aqt/deckconf.py:        f.replayQuestion.setChecked(c.get('replayq', True))
./aqt/upgrade.py:        loc = self.oldprefs.get("mediaLocation")
./aqt/browser.py:        self.activeCols = self.col.conf.get(
./aqt/browser.py:            f.setFamily(t.get("bfont", self.browser.mw.fontFamily))
./aqt/browser.py:            f.setPixelSize(t.get("bsize", self.browser.mw.fontHeight))
./aqt/browser.py:        if c.template().get('bafmt'):
./aqt/browser.py:                bsize = t.get("bsize", 0)
./aqt/browser.py:                collapsed = self.mw.col.decks.get(g[1]).get('browserCollapsed',
False)
./aqt/browser.py:        current=self.mw.col.decks.get(did)['name']
./aqt/browser.py:        deck = self.col.decks.get(did)
./aqt/browser.py:        self.oldModel = self.browser.col.models.get(
./aqt/editor.py:        self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
./aqt/editor.py:        if self.editor.mw.pm.profile.get("pastePNG", False):
./aqt/importing.py:        self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML',
True))
./aqt/importing.py:        self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode',
1))
./aqt/importing.py:        if self.mw.col.conf.get("addToCur", True):
./aqt/deckchooser.py:        if self.mw.col.conf.get("addToCur", True):
./aqt/deckchooser.py:        if not self.mw.col.conf.get("addToCur", True):
./aqt/taglimit.py:        yes = self.deck.get("activeTags", [])
./aqt/taglimit.py:        no = self.deck.get("inactiveTags", [])
./aqt/main.py:        if self.pm.profile.get('compressBackups', True):
./aqt/main.py:        return self.col.decks.get(did)
./aqt/main.py:        if self.pm.meta.get('suppressUpdate', None) != ver:
./aqt/overview.py:        self.sid = deck.get("sharedFrom")
./aqt/overview.py:            self.sidVer = deck.get("ver", None)
./aqt/overview.py:            desc = deck.get("desc", "")
./aqt/qt.py:if os.environ.get("DEBUG"):
./aqt/customstudy.py:            sval = min(new, self.deck.get('extendNew', 10))
./aqt/customstudy.py:            sval = min(rev, self.deck.get('extendRev', 10))
./aqt/customstudy.py:            dyn = self.mw.col.decks.get(did)
./aqt/models.py:        m = AddModel(self.mw, self).get()
./aqt/utils.py:        dir = aqt.mw.pm.profile.get(dirkey, "")
./aqt/utils.py:    base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base)
./aqt/utils.py:    if aqt.mw.pm.profile.get(key):
./aqt/utils.py:    if aqt.mw.pm.profile.get(key):
./aqt/utils.py:    if aqt.mw.pm.profile.get(key):
./aqt/utils.py:    if aqt.mw.pm.profile.get(key):
./aqt/preferences.py:        f.useCurrent.setCurrentIndex(int(not qc.get("addToCur",
True)))
./aqt/preferences.py:            self.form.syncUser.setText(self.prof.get('syncUser',
""))
./aqt/preferences.py:        self.form.compressBackups.setChecked(self.prof.get("compressBackups",
True))
./aqt/preferences.py:        self.form.pastePNG.setChecked(self.prof.get("pastePNG",
False))
./aqt/reviewer.py:            self.card.odid or self.card.did).get('replayq', True)
./aqt/reviewer.py:        self.mw.onDeckConf(self.mw.col.decks.get(

Not sure how to filter only those that are called on an object of type deck... many
seem to be other data structures.

Reported by nicolas.raoul on 2014-03-18 10:20:26

hssm commented 9 years ago
I tried with the regex: \.get\([\"\'].*[\"\'],.*

Name    Line    Text
collection.py   75           if not self.conf.get("newBury", False):
collection.py   803              limit=2+kwargs.get("stack", 0))[0]
decks.py    202          collapsed = deck.get('browserCollapsed', False)
mnemo.py    115              n.cards = orig.get('cards', {})
mnemo.py    166              fld = orig.get("text", "")
mnemo.py    179              n.cards = orig.get('cards', {})
sched.py    920          new = ivl * conf.get('ivlFct', 1)
sched.py    1082                 bury=oconf['new'].get("bury", True),
sched.py    1147             unburied = self.col.conf.get("lastUnburied", 0)
sched.py    1300             buryNew = nconf.get("bury", True)
sched.py    1302             buryRev = rconf.get("bury", True)
storage.py  172              r['ivlFct'] = r.get("ivlfct", 1)
sync.py 123          self.uname = meta.get("uname", "")
browser.py  80               f.setFamily(t.get("bfont", self.browser.mw.fontFamily))
browser.py  81               f.setPixelSize(t.get("bsize", self.browser.mw.fontHeight))
browser.py  453                  bsize = t.get("bsize", 0)
browser.py  844                  collapsed = self.mw.col.decks.get(g[1]).get('browserCollapsed',
False)
clayout.py  356          f.qfmt.setText(t.get('bqfmt', ""))
clayout.py  357          f.afmt.setText(t.get('bafmt', ""))
clayout.py  358          f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
clayout.py  359          f.fontSize.setValue(t.get('bsize', 12))
customstudy.py  59               sval = min(new, self.deck.get('extendNew', 10))
customstudy.py  65               sval = min(rev, self.deck.get('extendRev', 10))
deckchooser.py  35           if self.mw.col.conf.get("addToCur", True):
deckchooser.py  69           if not self.mw.col.conf.get("addToCur", True):
deckconf.py 183          f.bury.setChecked(c.get("bury", True))
deckconf.py 192          f.buryRev.setChecked(c.get("bury", True))
deckconf.py 203          f.showTimer.setChecked(c.get('timer', 0))
deckconf.py 205          f.replayQuestion.setChecked(c.get('replayq', True))
editor.py   722          self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")
editor.py   1183             if self.editor.mw.pm.profile.get("pastePNG", False):
importing.py    80           self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML',
True))
importing.py    81           self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode',
1))
importing.py    97           if self.mw.col.conf.get("addToCur", True):
main.py 329          if self.pm.profile.get('compressBackups', True):
main.py 828          if self.pm.meta.get('suppressUpdate', None) != ver:
overview.py 90               self.sidVer = deck.get("ver", None)
overview.py 112              desc = deck.get("desc", "")
preferences.py  56           f.useCurrent.setCurrentIndex(int(not qc.get("addToCur",
True)))
preferences.py  86               self.form.syncUser.setText(self.prof.get('syncUser',
""))
preferences.py  115          self.form.compressBackups.setChecked(self.prof.get("compressBackups",
True))
preferences.py  132          self.form.pastePNG.setChecked(self.prof.get("pastePNG",
False))
reviewer.py 223              self.card.odid or self.card.did).get('replayq', True)
taglimit.py 23           yes = self.deck.get("activeTags", [])
taglimit.py 24           no = self.deck.get("inactiveTags", [])
__init__.py 194      hopbyhop.extend([x.strip() for x in response.get('connection',
'').split(',')])
__init__.py 388          encoding = response.get('content-encoding', None)
__init__.py 417              vary = response_headers.get('vary', None)
__init__.py 507          qop = self.challenge.get('qop', 'auth')
__init__.py 511          self.challenge['algorithm'] = self.challenge.get('algorithm',
'MD5').upper()
__init__.py 545              challenge = _parse_www_authenticate(response, 'www-authenticate').get('digest',
{})
__init__.py 551              updated_challenge = _parse_www_authenticate(response,
'authentication-info').get('digest', {})
__init__.py 568          self.challenge['reason'] = self.challenge.get('reason', 'unauthorized')
__init__.py 571          self.challenge['salt'] = self.challenge.get('salt', '')
__init__.py 574          self.challenge['algorithm'] = self.challenge.get('algorithm',
'HMAC-SHA-1')
__init__.py 577          self.challenge['pw-algorithm'] = self.challenge.get('pw-algorithm',
'SHA-1')
__init__.py 615          challenge = _parse_www_authenticate(response, 'www-authenticate').get('hmacdigest',
{})
__init__.py 616          if challenge.get('reason') in ['integrity', 'stale']:
__init__.py 650          service = challenge['googlelogin'].get('service', 'xapi')
__init__.py 795      no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY',
''))
__init__.py 1676                 self.status = int(self.get('status', self.status))
__init__.py 1677                 self.reason = self.get('reason', self.reason)

Reported by perceptualchaos2 on 2014-03-18 16:48:31

hssm commented 9 years ago
If you look how this is implemented in libanki, JSON objects have methods which seems
to do exactly what we need:

http://www.json.org/javadoc/org/json/JSONObject.html#optBoolean(java.lang.String, boolean)

optBoolean(java.lang.String key, boolean defaultValue)
optInt(java.lang.String key, int defaultValue)
optString(java.lang.String key, java.lang.String defaultValue)

I've gone through all of the values in the above GREP, and I think these are the only
places that may require a default value to be included:

\libanki\Sched.javaSched.java(1532)     newIvl = ivl * conf.getDouble("ivlFct"); 

libanki\Card.java   279                      args.add(t.getString("bqfmt")); 

libanki\Card.java   280                      args.add(t.getString("bafmt")); 

libanki\Card.java   687     return mCol.getDecks().confForDid(mODid == 0 ? mDid : mODid).getInt("timer")
!= 0; 

libanki\Decks.java  817              return current().getString("desc"); 

Preferences.java    178                  useCurrent.setValueIndex(conf.getBoolean("addToCur")
? 0 : 1); 

Reported by perceptualchaos2 on 2014-03-18 18:33:40

hssm commented 9 years ago
OK this was actually a lot less work than I expected:

I've split what I've done into 2 pull requests:

213 - which just solves the replayq issue in a cleaner way:
https://github.com/ankidroid/Anki-Android/pull/213/files

and 214 - which adds default values for the other parameters in comment 21 (except
bqfmt and bafmt which seem unnecessary):
https://github.com/ankidroid/Anki-Android/pull/214/files

It would be very helpful if someone could check if there were any values which were
missed.

Reported by perceptualchaos2 on 2014-03-18 19:41:29

hssm commented 9 years ago
Fixed in version 2.1.1, available on Google Play

Reported by nicolas.raoul on 2014-03-28 08:19:22

hssm commented 9 years ago
Issue 1649 has been merged into this issue.

Reported by Houssam.Salem.Au on 2014-03-31 05:44:53