mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 07:22:23 -04:00
Merge remote-tracking branch 'upstream/master' into add-tags-on-update
This commit is contained in:
commit
e481cce816
602 changed files with 202264 additions and 148 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -6,8 +6,7 @@
|
||||||
.build
|
.build
|
||||||
.coverage
|
.coverage
|
||||||
.DS_Store
|
.DS_Store
|
||||||
build
|
dist
|
||||||
pyenv
|
pyenv
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
__pycache__
|
__pycache__
|
||||||
i18n
|
|
||||||
|
|
52
CONTRIBUTORS
Normal file
52
CONTRIBUTORS
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
If you have made changes to Anki's AGPL code, you are welcome to distribute
|
||||||
|
the changed code under the AGPL license.
|
||||||
|
|
||||||
|
If you would like to contribute your code back to the official release, we ask
|
||||||
|
that you license your contributions under the BSD 3 clause license. Portions
|
||||||
|
of the code are also used in AnkiWeb and AnkiMobile, and accepting
|
||||||
|
contributions under an AGPL license would mean we could no longer use the code
|
||||||
|
we have written in those projects.
|
||||||
|
|
||||||
|
In your first pull request, please add your name below. By adding your name to
|
||||||
|
this file, you assert that any code you contribute to the Anki project is
|
||||||
|
licensed under the BSD 3 clause license. If any pull request you make contains
|
||||||
|
code that you don't own the copyright to, you agree to make that clear when
|
||||||
|
submitting the request.
|
||||||
|
|
||||||
|
For users who previously confirmed the license of their contributions on the
|
||||||
|
support site, it would be great if you could add your name below as well.
|
||||||
|
|
||||||
|
********************
|
||||||
|
|
||||||
|
- Sample Name
|
||||||
|
|
||||||
|
********************
|
||||||
|
|
||||||
|
The text of the 3 clause BSD license follows:
|
||||||
|
|
||||||
|
Contributions copyright the above contributors, 2010-Present.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
49
LICENSE
49
LICENSE
|
@ -1 +1,48 @@
|
||||||
Primarily GNU AGPL3+ - please see the LICENSE file in each subfolder.
|
Anki is licensed under the GNU Affero General Public License, version 3 or
|
||||||
|
later, with portions contributed by Anki users licensed under the BSD-3
|
||||||
|
license (see CONTRIBUTORS).
|
||||||
|
|
||||||
|
The following included source code items use a license other than AGPL3:
|
||||||
|
|
||||||
|
In the pylib folder:
|
||||||
|
|
||||||
|
* The anki/template/ folder is based off pystache: MIT.
|
||||||
|
* The SuperMemo importer: GPL3.
|
||||||
|
* The Pauker importer: BSD-3.
|
||||||
|
* statsbg.py: CC BY-SA 3.0.
|
||||||
|
|
||||||
|
In the qt folder:
|
||||||
|
|
||||||
|
* Anki's translations are a mix of BSD and public domain.
|
||||||
|
* mpv.py: MIT.
|
||||||
|
* winpaths.py: MIT.
|
||||||
|
* MathJax: Apache 2.
|
||||||
|
* jQuery and jQuery-UI: MIT.
|
||||||
|
* browsersel.js: CC BY 2.5.
|
||||||
|
* plot.js: MIT.
|
||||||
|
|
||||||
|
The above list only covers the source code that is vendored in this
|
||||||
|
repository. Binary distributions also include copies of Qt translation
|
||||||
|
files (LGPL), and all of the Python, Rust and Javascript libraries
|
||||||
|
that this code references.
|
||||||
|
|
||||||
|
Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3 like the
|
||||||
|
rest of Anki's code.
|
||||||
|
|
||||||
|
The logo is also available under a limited alternative license for inclusion
|
||||||
|
in books, blogs, videos and so on. If the following conditions are met, you
|
||||||
|
may use the logo in your work without the need to license your work under an
|
||||||
|
AGPL3-compatible license:
|
||||||
|
|
||||||
|
* The logo must be used to refer to Anki, AnkiWeb, AnkiMobile or AnkiDroid,
|
||||||
|
and a link to https://apps.ankiweb.net must be provided. When your
|
||||||
|
content is focused specifically on AnkiDroid, a link to
|
||||||
|
https://play.google.com/store/apps/details?id=com.ichi2.anki&hl=en
|
||||||
|
may be provided instead of the first link.
|
||||||
|
* The work must make it clear that the text/video/etc you
|
||||||
|
are publishing is your own content and not something originating
|
||||||
|
from the Anki project.
|
||||||
|
* The logo must be used unmodified - no cropping, changing of colours
|
||||||
|
or adding or deleting content is allowed. You may resize the image
|
||||||
|
provided the horizontal and vertical dimensions are resized
|
||||||
|
equally.
|
||||||
|
|
|
@ -100,10 +100,7 @@ requests instead.
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Please add yourself to the contributors file before sending a pull request
|
Please add yourself to the contributors file in your first pull request.
|
||||||
for other components:
|
|
||||||
|
|
||||||
https://github.com/ankitects/anki-contributors
|
|
||||||
|
|
||||||
Add-ons
|
Add-ons
|
||||||
========
|
========
|
||||||
|
|
|
@ -36,14 +36,13 @@ of http://ankisrs.net/docs/manual.html#_contributing
|
||||||
Subcomponents
|
Subcomponents
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
- pylib contains a Python module (anki) with most of the non-GUI code.
|
||||||
|
- qt contains the Qt GUI implementation (aqt).
|
||||||
|
- rspy contains a Python module (ankirspy) for accessing the Rust code.
|
||||||
|
- rslib contains the parts of the code implemented in Rust. This
|
||||||
|
is only a tiny subsection at the moment.
|
||||||
- proto contains the interface used to communicate between the Rust and
|
- proto contains the interface used to communicate between the Rust and
|
||||||
Python code.
|
Python code.
|
||||||
- lib-rust contains the parts of the code implemented in Rust. This
|
|
||||||
is only a tiny subsection at the moment.
|
|
||||||
- lib-rspy contains a Python module (ankirspy) for accessing the Rust code.
|
|
||||||
- lib-python contains a Python module (anki) that covers all of the work
|
|
||||||
not related to the user interface.
|
|
||||||
- anki-qt contains the Qt GUI implementation (aqt).
|
|
||||||
|
|
||||||
Helper scripts
|
Helper scripts
|
||||||
--------------
|
--------------
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
Anki is licensed under the GNU Affero General Public License, version 3 or
|
|
||||||
later, with portions contributed by Anki users licensed under the BSD-3
|
|
||||||
license: https://github.com/ankitects/anki-contributors
|
|
||||||
|
|
||||||
Please see LICENSE.logo for the copyright and license of Anki's logo.
|
|
||||||
|
|
||||||
The following included source code items use a license other than AGPL3:
|
|
||||||
|
|
||||||
* Anki's translations are a mix of BSD and public domain.
|
|
||||||
* The Qt translations that are copied in as part of the build process
|
|
||||||
are licensed under the LGPL.
|
|
||||||
* mpv.py: MIT.
|
|
||||||
* winpaths.py: MIT.
|
|
||||||
* MathJax: Apache 2.
|
|
||||||
* jQuery and jQuery-UI: MIT.
|
|
||||||
* browsersel.js: CC BY 2.5.
|
|
||||||
* plot.js: MIT.
|
|
|
@ -1,20 +0,0 @@
|
||||||
Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3 like the
|
|
||||||
rest of Anki's code.
|
|
||||||
|
|
||||||
The logo is also available under a limited alternative license for inclusion
|
|
||||||
in books, blogs, videos and so on. If the following conditions are met, you
|
|
||||||
may use the logo in your work without the need to license your work under an
|
|
||||||
AGPL3-compatible license:
|
|
||||||
|
|
||||||
* The logo must be used to refer to Anki, AnkiMobile or AnkiDroid,
|
|
||||||
and a link to https://apps.ankiweb.net must be provided. When your
|
|
||||||
content is focused specifically on AnkiDroid, a link to
|
|
||||||
https://play.google.com/store/apps/details?id=com.ichi2.anki&hl=en
|
|
||||||
may be provided instead of the first link.
|
|
||||||
* The work must make it clear that the text/video/etc you
|
|
||||||
are publishing is your own content and not something originating
|
|
||||||
from the Anki project.
|
|
||||||
* The logo must be used unmodified - no cropping, changing of colours
|
|
||||||
or adding or deleting content is allowed. You may resize the image
|
|
||||||
provided the horizontal and vertical dimensions are resized
|
|
||||||
equally.
|
|
|
@ -1,23 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# tools/tests.sh # run all tests
|
|
||||||
# tools/tests.sh decks # test only test_decks.py
|
|
||||||
# coverage=1 tools/tests.sh # run with coverage test
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
BIN="$(cd "`dirname "$0"`"; pwd)"
|
|
||||||
export PYTHONPATH=${BIN}/..:${PYTHONPATH}
|
|
||||||
|
|
||||||
nose="python -m nose2 --plugin=nose2.plugins.mp -N 16"
|
|
||||||
|
|
||||||
dir=.
|
|
||||||
|
|
||||||
if [ x$1 = x ]; then
|
|
||||||
lim="tests"
|
|
||||||
else
|
|
||||||
lim="tests.test_$1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
(cd $dir && $nose $lim $args)
|
|
17
build
Executable file
17
build
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. scripts.inc
|
||||||
|
|
||||||
|
rm -rf dist
|
||||||
|
|
||||||
|
for dir in $DEVEL; do
|
||||||
|
echo $dir
|
||||||
|
(cd $dir && make build BUILDFLAGS="$BUILDFLAGS")
|
||||||
|
done
|
||||||
|
|
||||||
|
# add build hash to outputs
|
||||||
|
ver=$(cat meta/version)
|
||||||
|
hash=$(cat meta/buildhash)
|
||||||
|
(cd dist && rename "s/$ver/${ver}+$hash/" *.whl)
|
10
bundle
10
bundle
|
@ -1,10 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
. scripts.inc
|
|
||||||
|
|
||||||
for dir in $DEVEL; do
|
|
||||||
echo $dir
|
|
||||||
(cd $dir && make build BUILDFLAGS="$BUILDFLAGS")
|
|
||||||
done
|
|
2
clean
2
clean
|
@ -4,6 +4,8 @@ set -e
|
||||||
|
|
||||||
. scripts.inc
|
. scripts.inc
|
||||||
|
|
||||||
|
rm -rf dist
|
||||||
|
|
||||||
for dir in $DEVEL; do
|
for dir in $DEVEL; do
|
||||||
echo $dir
|
echo $dir
|
||||||
(cd $dir && make clean)
|
(cd $dir && make clean)
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Licensed under the GNU AGPL 3 or later.
|
|
|
@ -1 +0,0 @@
|
||||||
Licensed under the GNU AGPL 3 or later.
|
|
1
meta/.gitignore
vendored
Normal file
1
meta/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
buildhash
|
1
meta/version
Normal file
1
meta/version
Normal file
|
@ -0,0 +1 @@
|
||||||
|
2.1.17
|
0
lib-python/.gitignore → pylib/.gitignore
vendored
0
lib-python/.gitignore → pylib/.gitignore
vendored
|
@ -6,10 +6,10 @@ MAKEFLAGS += --warn-undefined-variables
|
||||||
MAKEFLAGS += --no-builtin-rules
|
MAKEFLAGS += --no-builtin-rules
|
||||||
RUNARGS :=
|
RUNARGS :=
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
BLACKARGS := -t py36 anki tests --exclude=backend_pb2
|
BLACKARGS := -t py36 anki tests --exclude='backend_pb2|buildinfo'
|
||||||
ISORTARGS := anki tests
|
ISORTARGS := anki tests
|
||||||
|
|
||||||
$(shell mkdir -p .build ../build)
|
$(shell mkdir -p .build ../dist)
|
||||||
|
|
||||||
PHONY: all
|
PHONY: all
|
||||||
all: check
|
all: check
|
||||||
|
@ -28,7 +28,7 @@ PROTODEPS := $(wildcard ../proto/*.proto)
|
||||||
protoc --proto_path=../proto --python_out=anki --mypy_out=anki $(PROTODEPS)
|
protoc --proto_path=../proto --python_out=anki --mypy_out=anki $(PROTODEPS)
|
||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
BUILD_STEPS := .build/run-deps .build/dev-deps .build/py-proto
|
BUILD_STEPS := .build/run-deps .build/dev-deps .build/py-proto anki/buildinfo.py
|
||||||
|
|
||||||
# Checking
|
# Checking
|
||||||
######################
|
######################
|
||||||
|
@ -48,14 +48,14 @@ clean:
|
||||||
# Checking python
|
# Checking python
|
||||||
######################
|
######################
|
||||||
|
|
||||||
CHECKDEPS := $(shell find anki tests -name '*.py' | grep -v buildhash.py)
|
CHECKDEPS := $(shell find anki tests -name '*.py')
|
||||||
|
|
||||||
.build/mypy: $(CHECKDEPS)
|
.build/mypy: $(CHECKDEPS)
|
||||||
mypy anki
|
mypy anki
|
||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
.build/test: $(CHECKDEPS)
|
.build/test: $(CHECKDEPS)
|
||||||
python -m nose2 --plugin=nose2.plugins.mp -N 16
|
python -m pytest --durations=1
|
||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
.build/lint: $(CHECKDEPS)
|
.build/lint: $(CHECKDEPS)
|
||||||
|
@ -78,10 +78,13 @@ CHECKDEPS := $(shell find anki tests -name '*.py' | grep -v buildhash.py)
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: $(BUILD_STEPS) $(CHECKDEPS)
|
build: $(BUILD_STEPS) $(CHECKDEPS)
|
||||||
rm -rf dist
|
rm -rf dist
|
||||||
echo "build='$$(git rev-parse --short HEAD)'" > anki/buildhash.py
|
|
||||||
python setup.py bdist_wheel
|
python setup.py bdist_wheel
|
||||||
rsync -a dist/*.whl ../build/
|
rsync -a dist/*.whl ../dist/
|
||||||
|
|
||||||
# prepare code for running in place
|
# prepare code for running in place
|
||||||
.PHONY: develop
|
.PHONY: develop
|
||||||
develop: $(BUILD_STEPS)
|
develop: $(BUILD_STEPS)
|
||||||
|
|
||||||
|
anki/buildinfo.py: ../meta/version ../meta/buildhash
|
||||||
|
echo "buildhash='$$(cat ../meta/buildhash)'" > $@
|
||||||
|
echo "version='$$(cat ../meta/version)'" >> $@
|
1
pylib/anki/.gitignore
vendored
Normal file
1
pylib/anki/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
buildinfo.py
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from anki.buildinfo import version
|
||||||
from anki.storage import Collection
|
from anki.storage import Collection
|
||||||
|
|
||||||
if sys.version_info[0] < 3 or sys.version_info[1] < 5:
|
if sys.version_info[0] < 3 or sys.version_info[1] < 5:
|
||||||
|
@ -11,8 +12,5 @@ if sys.version_info[0] < 3 or sys.version_info[1] < 5:
|
||||||
if sys.getfilesystemencoding().lower() in ("ascii", "ansi_x3.4-1968"):
|
if sys.getfilesystemencoding().lower() in ("ascii", "ansi_x3.4-1968"):
|
||||||
raise Exception("Anki requires a UTF-8 locale.")
|
raise Exception("Anki requires a UTF-8 locale.")
|
||||||
|
|
||||||
# fmt: off
|
|
||||||
version="2.1.17" # build scripts grep this line, so preserve formatting
|
|
||||||
# fmt: on
|
|
||||||
|
|
||||||
__all__ = ["Collection"]
|
__all__ = ["Collection"]
|
|
@ -5,9 +5,12 @@ from typing import Dict, List
|
||||||
import ankirspy # pytype: disable=import-error
|
import ankirspy # pytype: disable=import-error
|
||||||
|
|
||||||
import anki.backend_pb2 as pb
|
import anki.backend_pb2 as pb
|
||||||
|
import anki.buildinfo
|
||||||
|
|
||||||
from .types import AllTemplateReqs
|
from .types import AllTemplateReqs
|
||||||
|
|
||||||
|
assert ankirspy.buildhash() == anki.buildinfo.buildhash
|
||||||
|
|
||||||
SchedTimingToday = pb.SchedTimingTodayOut
|
SchedTimingToday = pb.SchedTimingTodayOut
|
||||||
|
|
||||||
|
|
|
@ -101,8 +101,6 @@ def addClozeModel(col) -> NoteType:
|
||||||
txt = _("Text")
|
txt = _("Text")
|
||||||
fm = mm.newField(txt)
|
fm = mm.newField(txt)
|
||||||
mm.addField(m, fm)
|
mm.addField(m, fm)
|
||||||
fm = mm.newField(_("Extra"))
|
|
||||||
mm.addField(m, fm)
|
|
||||||
t = mm.newTemplate(_("Cloze"))
|
t = mm.newTemplate(_("Cloze"))
|
||||||
fmt = "{{cloze:%s}}" % txt
|
fmt = "{{cloze:%s}}" % txt
|
||||||
m[
|
m[
|
||||||
|
@ -116,7 +114,7 @@ def addClozeModel(col) -> NoteType:
|
||||||
color: lightblue;
|
color: lightblue;
|
||||||
}"""
|
}"""
|
||||||
t["qfmt"] = fmt
|
t["qfmt"] = fmt
|
||||||
t["afmt"] = fmt + "<br>\n{{%s}}" % _("Extra")
|
t["afmt"] = fmt
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.add(m)
|
mm.add(m)
|
||||||
return m
|
return m
|
|
@ -494,10 +494,6 @@ class TimedLog:
|
||||||
|
|
||||||
|
|
||||||
def versionWithBuild() -> str:
|
def versionWithBuild() -> str:
|
||||||
from anki import version
|
from anki.buildinfo import version, buildhash
|
||||||
|
|
||||||
try:
|
return "%s (%s)" % (version, buildhash)
|
||||||
from anki.buildhash import build # type: ignore # pylint: disable=import-error,no-name-in-module
|
|
||||||
except:
|
|
||||||
build = "dev"
|
|
||||||
return "%s (%s)" % (version, build)
|
|
|
@ -2,7 +2,7 @@ wheel
|
||||||
mypy
|
mypy
|
||||||
mypy_protobuf
|
mypy_protobuf
|
||||||
black
|
black
|
||||||
nose2
|
pytest
|
||||||
# fixme: when isort 5.0 is released, switch to pypi
|
# fixme: when isort 5.0 is released, switch to pypi
|
||||||
git+https://github.com/ankitects/isort#egg=isort
|
git+https://github.com/ankitects/isort#egg=isort
|
||||||
# fixme: when pylint supports isort 5.0, switch to pypi
|
# fixme: when pylint supports isort 5.0, switch to pypi
|
|
@ -3,6 +3,9 @@ import setuptools, sys
|
||||||
with open("README.md", "r") as fh:
|
with open("README.md", "r") as fh:
|
||||||
long_description = fh.read()
|
long_description = fh.read()
|
||||||
|
|
||||||
|
with open("../meta/version") as fh:
|
||||||
|
version = fh.read().strip()
|
||||||
|
|
||||||
platform_reqs = []
|
platform_reqs = []
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
platform_reqs.append("psutil")
|
platform_reqs.append("psutil")
|
||||||
|
@ -11,7 +14,7 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="anki",
|
name="anki",
|
||||||
version="0.1.0",
|
version=version,
|
||||||
author="Ankitects Pty Ltd",
|
author="Ankitects Pty Ltd",
|
||||||
description="Anki's library code",
|
description="Anki's library code",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
|
@ -3,8 +3,6 @@
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from nose2.tools.decorators import with_setup
|
|
||||||
|
|
||||||
from anki import Collection as aopen
|
from anki import Collection as aopen
|
||||||
from anki.exporting import *
|
from anki.exporting import *
|
||||||
from anki.importing import Anki2Importer
|
from anki.importing import Anki2Importer
|
||||||
|
@ -41,8 +39,8 @@ def setup1():
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
@with_setup(setup1)
|
|
||||||
def test_export_anki():
|
def test_export_anki():
|
||||||
|
setup1()
|
||||||
# create a new deck with its own conf to test conf copying
|
# create a new deck with its own conf to test conf copying
|
||||||
did = deck.decks.id("test")
|
did = deck.decks.id("test")
|
||||||
dobj = deck.decks.get(did)
|
dobj = deck.decks.get(did)
|
||||||
|
@ -83,8 +81,8 @@ def test_export_anki():
|
||||||
assert d2.cardCount() == 1
|
assert d2.cardCount() == 1
|
||||||
|
|
||||||
|
|
||||||
@with_setup(setup1)
|
|
||||||
def test_export_ankipkg():
|
def test_export_ankipkg():
|
||||||
|
setup1()
|
||||||
# add a test file to the media folder
|
# add a test file to the media folder
|
||||||
with open(os.path.join(deck.media.dir(), "今日.mp3"), "w") as f:
|
with open(os.path.join(deck.media.dir(), "今日.mp3"), "w") as f:
|
||||||
f.write("test")
|
f.write("test")
|
||||||
|
@ -99,8 +97,8 @@ def test_export_ankipkg():
|
||||||
e.exportInto(newname)
|
e.exportInto(newname)
|
||||||
|
|
||||||
|
|
||||||
@with_setup(setup1)
|
|
||||||
def test_export_anki_due():
|
def test_export_anki_due():
|
||||||
|
setup1()
|
||||||
deck = getEmptyCol()
|
deck = getEmptyCol()
|
||||||
f = deck.newNote()
|
f = deck.newNote()
|
||||||
f["Front"] = "foo"
|
f["Front"] = "foo"
|
||||||
|
@ -132,8 +130,8 @@ def test_export_anki_due():
|
||||||
assert c.due - deck2.sched.today == 1
|
assert c.due - deck2.sched.today == 1
|
||||||
|
|
||||||
|
|
||||||
# @with_setup(setup1)
|
|
||||||
# def test_export_textcard():
|
# def test_export_textcard():
|
||||||
|
# setup1()
|
||||||
# e = TextCardExporter(deck)
|
# e = TextCardExporter(deck)
|
||||||
# f = unicode(tempfile.mkstemp(prefix="ankitest")[1])
|
# f = unicode(tempfile.mkstemp(prefix="ankitest")[1])
|
||||||
# os.unlink(f)
|
# os.unlink(f)
|
||||||
|
@ -142,8 +140,8 @@ def test_export_anki_due():
|
||||||
# e.exportInto(f)
|
# e.exportInto(f)
|
||||||
|
|
||||||
|
|
||||||
@with_setup(setup1)
|
|
||||||
def test_export_textnote():
|
def test_export_textnote():
|
||||||
|
setup1()
|
||||||
e = TextNoteExporter(deck)
|
e = TextNoteExporter(deck)
|
||||||
fd, f = tempfile.mkstemp(prefix="ankitest")
|
fd, f = tempfile.mkstemp(prefix="ankitest")
|
||||||
f = str(f)
|
f = str(f)
|
|
@ -1,5 +1,5 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from nose2.tools.such import helper
|
import pytest
|
||||||
|
|
||||||
from anki.find import Finder
|
from anki.find import Finder
|
||||||
from tests.shared import getEmptyCol
|
from tests.shared import getEmptyCol
|
||||||
|
@ -111,7 +111,7 @@ def test_findCards():
|
||||||
assert len(deck.findCards("nid:%d" % f.id)) == 2
|
assert len(deck.findCards("nid:%d" % f.id)) == 2
|
||||||
assert len(deck.findCards("nid:%d,%d" % (f1id, f2id))) == 2
|
assert len(deck.findCards("nid:%d,%d" % (f1id, f2id))) == 2
|
||||||
# templates
|
# templates
|
||||||
with helper.assertRaises(Exception):
|
with pytest.raises(Exception):
|
||||||
deck.findCards("card:foo")
|
deck.findCards("card:foo")
|
||||||
assert len(deck.findCards("'card:card 1'")) == 4
|
assert len(deck.findCards("'card:card 1'")) == 4
|
||||||
assert len(deck.findCards("card:reverse")) == 1
|
assert len(deck.findCards("card:reverse")) == 1
|
||||||
|
@ -147,7 +147,7 @@ def test_findCards():
|
||||||
assert len(deck.findCards("-deck:foo")) == 5
|
assert len(deck.findCards("-deck:foo")) == 5
|
||||||
assert len(deck.findCards("deck:def*")) == 5
|
assert len(deck.findCards("deck:def*")) == 5
|
||||||
assert len(deck.findCards("deck:*EFAULT")) == 5
|
assert len(deck.findCards("deck:*EFAULT")) == 5
|
||||||
with helper.assertRaises(Exception):
|
with pytest.raises(Exception):
|
||||||
deck.findCards("deck:*cefault")
|
deck.findCards("deck:*cefault")
|
||||||
# full search
|
# full search
|
||||||
f = deck.newNote()
|
f = deck.newNote()
|
||||||
|
@ -164,7 +164,7 @@ def test_findCards():
|
||||||
# assert len(deck.findCards("helloworld", full=True)) == 2
|
# assert len(deck.findCards("helloworld", full=True)) == 2
|
||||||
# assert len(deck.findCards("back:helloworld", full=True)) == 2
|
# assert len(deck.findCards("back:helloworld", full=True)) == 2
|
||||||
# searching for an invalid special tag should not error
|
# searching for an invalid special tag should not error
|
||||||
with helper.assertRaises(Exception):
|
with pytest.raises(Exception):
|
||||||
len(deck.findCards("is:invalid"))
|
len(deck.findCards("is:invalid"))
|
||||||
# should be able to limit to parent deck, no children
|
# should be able to limit to parent deck, no children
|
||||||
id = deck.db.scalar("select id from cards limit 1")
|
id = deck.db.scalar("select id from cards limit 1")
|
||||||
|
@ -238,9 +238,9 @@ def test_findCards():
|
||||||
assert len(deck.findCards("added:1")) == deck.cardCount() - 1
|
assert len(deck.findCards("added:1")) == deck.cardCount() - 1
|
||||||
assert len(deck.findCards("added:2")) == deck.cardCount()
|
assert len(deck.findCards("added:2")) == deck.cardCount()
|
||||||
# flag
|
# flag
|
||||||
with helper.assertRaises(Exception):
|
with pytest.raises(Exception):
|
||||||
deck.findCards("flag:01")
|
deck.findCards("flag:01")
|
||||||
with helper.assertRaises(Exception):
|
with pytest.raises(Exception):
|
||||||
deck.findCards("flag:12")
|
deck.findCards("flag:12")
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,10 @@ def test_changes():
|
||||||
def removed():
|
def removed():
|
||||||
return d.media.db.execute("select fname from media where csum is null")
|
return d.media.db.execute("select fname from media where csum is null")
|
||||||
|
|
||||||
|
def advanceTime():
|
||||||
|
d.media.db.execute("update media set mtime=mtime-1")
|
||||||
|
d.media.db.execute("update meta set dirMod = dirMod - 1")
|
||||||
|
|
||||||
assert not list(added())
|
assert not list(added())
|
||||||
assert not list(removed())
|
assert not list(removed())
|
||||||
# add a file
|
# add a file
|
||||||
|
@ -95,27 +99,26 @@ def test_changes():
|
||||||
path = os.path.join(dir, "foo.jpg")
|
path = os.path.join(dir, "foo.jpg")
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("hello")
|
f.write("hello")
|
||||||
time.sleep(1)
|
|
||||||
path = d.media.addFile(path)
|
path = d.media.addFile(path)
|
||||||
# should have been logged
|
# should have been logged
|
||||||
d.media.findChanges()
|
d.media.findChanges()
|
||||||
assert list(added())
|
assert list(added())
|
||||||
assert not list(removed())
|
assert not list(removed())
|
||||||
# if we modify it, the cache won't notice
|
# if we modify it, the cache won't notice
|
||||||
time.sleep(1)
|
advanceTime()
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("world")
|
f.write("world")
|
||||||
assert len(list(added())) == 1
|
assert len(list(added())) == 1
|
||||||
assert not list(removed())
|
assert not list(removed())
|
||||||
# but if we add another file, it will
|
# but if we add another file, it will
|
||||||
time.sleep(1)
|
advanceTime()
|
||||||
with open(path + "2", "w") as f:
|
with open(path + "2", "w") as f:
|
||||||
f.write("yo")
|
f.write("yo")
|
||||||
d.media.findChanges()
|
d.media.findChanges()
|
||||||
assert len(list(added())) == 2
|
assert len(list(added())) == 2
|
||||||
assert not list(removed())
|
assert not list(removed())
|
||||||
# deletions should get noticed too
|
# deletions should get noticed too
|
||||||
time.sleep(1)
|
advanceTime()
|
||||||
os.unlink(path + "2")
|
os.unlink(path + "2")
|
||||||
d.media.findChanges()
|
d.media.findChanges()
|
||||||
assert len(list(added())) == 1
|
assert len(list(added())) == 1
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue