Name | leitner-box JSON |
Version |
0.3.0
JSON |
| download |
home_page | None |
Summary | Leitner system spaced repetition |
upload_time | 2024-12-07 22:05:42 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.10 |
license | MIT License Copyright (c) 2024 Open Spaced Repetition Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
keywords |
leitner
spaced-repetition
flashcard
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
<div align="center">
<img src="https://avatars.githubusercontent.com/u/96821265?s=200&v=4" height="100" alt="Open Spaced Repetition logo"/>
</div>
<div align="center">
# Leitner Box
</div>
<div align="center">
<em>📦🔄 Build your own Leitner System in Python 📦🔄</em>
</div>
<br />
<div align="center" style="text-decoration: none;">
<a href="https://pypi.org/project/leitner-box/"><img src="https://img.shields.io/pypi/v/leitner-box"></a>
<a href="https://github.com/open-spaced-repetition/leitner-box/blob/main/LICENSE" style="text-decoration: none;"><img src="https://img.shields.io/badge/License-MIT-brightgreen.svg"></a>
</div>
<br />
<div align="center">
<strong>
Python package implementing the <a href="https://en.wikipedia.org/wiki/Leitner_system">Leitner system</a> for spaced repetition scheduling.
</strong>
</div>
<div align="center">
<img src="https://raw.githubusercontent.com/open-spaced-repetition/leitner-box/refs/heads/main/leitner-system.svg" height="300"/>
</div>
## Installation
You can install the leitner-box python package from [PyPI](https://pypi.org/project/leitner-box/) using pip:
```
pip install leitner-box
```
## Quickstart
Import and initialize the Leitner scheduler
```python
from leitner_box import Scheduler, Card, Rating, ReviewLog
scheduler = Scheduler()
```
Create a new Card object
```python
card = Card()
print(f"Card is in box {card.box}")
# => Card is in box 1
```
Choose a rating and review the card
```python
"""
Rating.Fail # (==0) forgot the card
Rating.Pass # (==1) remembered the card
"""
rating = Rating.Pass
card, review_log = scheduler.review_card(card, rating)
print(f"Card in box {review_log.card.box} rated {review_log.rating} \
on {review_log.review_datetime}")
# => Card in box 1 rated 1 on 2024-10-21 20:58:29.758259
```
See when the card is due next
```python
print(f"Card in box {card.box} due on {card.due}")
# => Card in box 2 due on 2024-10-22 00:00:00
```
## Usage
### The scheduler
The `Scheduler` has three parameters: 1) `box_intervals`, 2) `start_datetime`, and 3) `on_fail`.
`box_intervals` is a list of integers corresponding to the interval lengths of each box.
```python
box_intervals = [1,2,7] # this also the default
scheduler = Scheduler(box_intervals=box_intervals)
```
In this example, cards in box 1 are reviewed every day, cards in box 2 are reviewed every 2 days and cards in box 3 are reviewed every 7 days. There are only three boxes in this example.
Note: in the current version of this package, the interval for box 1 must always be set to 1 day. There may be more flexible options in future versions.
`start_datetime` is the datetime that you first created the Leitner System. It is an important parameter in determining when the cards in each box are reviewed. It should be noted that the important information lies in which day the Leitner System was created, not the exact hour, minute, etc. This is because the scheduler schedules cards to be due at the beginning of each day.
```python
from datetime import datetime
start_datetime = datetime.now() # also default datetime if not specified
scheduler = Scheduler(start_datetime=start_datetime)
print(f"Scheduler created on {scheduler.start_datetime}")
# => Scheduler created on 2024-10-21 21:15:23.491825
card = Card()
rating = Rating.Pass
card, review_log = scheduler.review_card(card, rating)
print(f"Card is due on {card.due}")
# => Card is due on 2024-10-22 00:00:00
```
In the above example, even though the scheduler was created in the evening of 2024-10-21 (and the card was also reviewed late in the evening of 2024-10-21), the card becomes due first thing the next day - *not* a full 24 hours later.
`on_fail` has two possible values 1) `first_box` or 2) `prev_box`.
If `on_fail='first_box'`, cards that are failed will be put back in box 1 and if `on_fail='prev_box'`, failed cards will be put in the previous box. `on_fail='first_box'` is the default value.
### Serialization
`Scheduler`, `Card` and `ReviewLog` objects are all json-serializable via their `to_dict` and `from_dict` methods for easy database storage:
```python
# serialize before storage
scheduler_dict = scheduler.to_dict()
card_dict = card.to_dict()
review_log_dict = review_log.to_dict()
# deserialize from dict
scheduler = Scheduler(scheduler_dict)
card = Card.from_dict(card_dict)
review_log = ReviewLog.from_dict(review_log_dict)
```
### Best practices
**Re-use the same scheduler for the same cards**
```python
scheduler = Scheduler(box_intervals=[1,2,7])
card = Card()
rating = Rating.Pass
card, review_log = scheduler.review_card(card, rating)
# (...wait till next day)
different_scheduler = Scheduler(box_intervals=[1,2,3,4,5])
rating = Rating.Pass
#card, review_log = different_scheduler.review_card(card, rating) # wrong
card, review_log = scheduler.review_card(card, rating) # correct
```
In general, you should continue using the same scheduler that you first reviewed the card with. Doing otherwise could lead to scheduling issues.
**Check if a card is due before attempting to review it**
If you try to review a card that is not due, you will get an error:
```python
print(f"Card is due on {card.due}")
# => Card is due on 2024-10-22 00:00:00
print(f"Current datetime: {datetime.now()}")
# => Current datetime: 2024-10-21 21:15:23.491825
rating = Rating.Pass
card, review_log = scheduler.review_card(card, rating)
# RuntimeError: Card is not due for review until 2024-10-22 00:00:00.
```
**Be explicit about datetimes and use a local timezone**
While this package operates using [timezone-naive](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects) datetime objects, it's still recommended to provide timezone-aware datetime objects localized to where the user currently is when initializing the scheduler or reviewing cards.
```python
from leitner_box import Scheduler, Card, Rating, ReviewLog
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
# e.g., if you're in Los Angeles
start_datetime = datetime.now(ZoneInfo('America/Los_Angeles'))
scheduler = Scheduler(start_datetime=start_datetime)
card = Card()
rating = Rating.Pass
review_datetime = datetime.now(ZoneInfo('America/Los_Angeles'))
card, review_log = scheduler.review_card(card, rating, review_datetime)
```
Under the hood, these datetimes are coerced to become timezone-naive, but you still have the option of specifying timezone-aware datetime objects.
To re-iterate, cards in each box are made due at the beginning of each day, regardless of the timezone. As a consequence of this, when determining whether a user should review cards in a given box, you should know what day it is where they are.
## Versioning
This python package is currently unstable and adheres to the following versioning scheme:
- **Minor** version will increase when a backward-incompatible change is introduced.
- **Patch** version will increase when a bug is fixed, a new feature is added or when anything else backward compatible warrants a new release.
Once this package is considered stable, the **Major** version will be bumped to 1.0.0 and will follow [semver](https://semver.org/).
## Contribute
Checkout [CONTRIBUTING](https://github.com/open-spaced-repetition/leitner-box/blob/main/CONTRIBUTING.md) to help improve leitner-box!
Raw data
{
"_id": null,
"home_page": null,
"name": "leitner-box",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "leitner, spaced-repetition, flashcard",
"author": null,
"author_email": "Joshua Hamilton <hamiltonjoshuadavid@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/79/0b/54adced7c6c81762becb5c5b8ac6105a6e8c7700518d06d5baa27f6da24e/leitner_box-0.3.0.tar.gz",
"platform": null,
"description": "<div align=\"center\">\n <img src=\"https://avatars.githubusercontent.com/u/96821265?s=200&v=4\" height=\"100\" alt=\"Open Spaced Repetition logo\"/>\n</div>\n<div align=\"center\">\n\n# Leitner Box\n</div>\n\n<div align=\"center\">\n <em>\ud83d\udce6\ud83d\udd04 Build your own Leitner System in Python \ud83d\udce6\ud83d\udd04</em>\n</div>\n<br />\n<div align=\"center\" style=\"text-decoration: none;\">\n <a href=\"https://pypi.org/project/leitner-box/\"><img src=\"https://img.shields.io/pypi/v/leitner-box\"></a>\n <a href=\"https://github.com/open-spaced-repetition/leitner-box/blob/main/LICENSE\" style=\"text-decoration: none;\"><img src=\"https://img.shields.io/badge/License-MIT-brightgreen.svg\"></a>\n</div>\n<br />\n\n<div align=\"center\">\n <strong>\n Python package implementing the <a href=\"https://en.wikipedia.org/wiki/Leitner_system\">Leitner system</a> for spaced repetition scheduling.\n </strong>\n</div>\n\n<div align=\"center\">\n <img src=\"https://raw.githubusercontent.com/open-spaced-repetition/leitner-box/refs/heads/main/leitner-system.svg\" height=\"300\"/>\n</div>\n\n## Installation\n\nYou can install the leitner-box python package from [PyPI](https://pypi.org/project/leitner-box/) using pip:\n```\npip install leitner-box\n```\n\n## Quickstart\n\nImport and initialize the Leitner scheduler\n\n```python\nfrom leitner_box import Scheduler, Card, Rating, ReviewLog\n\nscheduler = Scheduler()\n```\n\nCreate a new Card object\n\n```python\ncard = Card()\n\nprint(f\"Card is in box {card.box}\")\n # => Card is in box 1\n```\n\nChoose a rating and review the card\n\n```python\n\"\"\"\nRating.Fail # (==0) forgot the card\nRating.Pass # (==1) remembered the card\n\"\"\"\n\nrating = Rating.Pass\n\ncard, review_log = scheduler.review_card(card, rating)\n\nprint(f\"Card in box {review_log.card.box} rated {review_log.rating} \\\non {review_log.review_datetime}\")\n# => Card in box 1 rated 1 on 2024-10-21 20:58:29.758259\n```\n\nSee when the card is due next\n\n```python\nprint(f\"Card in box {card.box} due on {card.due}\")\n# => Card in box 2 due on 2024-10-22 00:00:00\n```\n\n## Usage\n\n### The scheduler\n\nThe `Scheduler` has three parameters: 1) `box_intervals`, 2) `start_datetime`, and 3) `on_fail`.\n\n`box_intervals` is a list of integers corresponding to the interval lengths of each box. \n\n```python\nbox_intervals = [1,2,7] # this also the default\nscheduler = Scheduler(box_intervals=box_intervals)\n```\n\nIn this example, cards in box 1 are reviewed every day, cards in box 2 are reviewed every 2 days and cards in box 3 are reviewed every 7 days. There are only three boxes in this example.\n\nNote: in the current version of this package, the interval for box 1 must always be set to 1 day. There may be more flexible options in future versions.\n\n`start_datetime` is the datetime that you first created the Leitner System. It is an important parameter in determining when the cards in each box are reviewed. It should be noted that the important information lies in which day the Leitner System was created, not the exact hour, minute, etc. This is because the scheduler schedules cards to be due at the beginning of each day.\n\n```python\nfrom datetime import datetime\n\nstart_datetime = datetime.now() # also default datetime if not specified\n\nscheduler = Scheduler(start_datetime=start_datetime)\n\nprint(f\"Scheduler created on {scheduler.start_datetime}\")\n# => Scheduler created on 2024-10-21 21:15:23.491825\n\ncard = Card()\n\nrating = Rating.Pass\ncard, review_log = scheduler.review_card(card, rating)\n\nprint(f\"Card is due on {card.due}\")\n# => Card is due on 2024-10-22 00:00:00\n```\n\nIn the above example, even though the scheduler was created in the evening of 2024-10-21 (and the card was also reviewed late in the evening of 2024-10-21), the card becomes due first thing the next day - *not* a full 24 hours later.\n\n`on_fail` has two possible values 1) `first_box` or 2) `prev_box`.\n\nIf `on_fail='first_box'`, cards that are failed will be put back in box 1 and if `on_fail='prev_box'`, failed cards will be put in the previous box. `on_fail='first_box'` is the default value.\n\n### Serialization\n\n`Scheduler`, `Card` and `ReviewLog` objects are all json-serializable via their `to_dict` and `from_dict` methods for easy database storage:\n\n```python\n# serialize before storage\nscheduler_dict = scheduler.to_dict()\ncard_dict = card.to_dict()\nreview_log_dict = review_log.to_dict()\n\n# deserialize from dict\nscheduler = Scheduler(scheduler_dict)\ncard = Card.from_dict(card_dict)\nreview_log = ReviewLog.from_dict(review_log_dict)\n```\n\n### Best practices\n\n**Re-use the same scheduler for the same cards**\n\n```python\nscheduler = Scheduler(box_intervals=[1,2,7])\ncard = Card()\n\nrating = Rating.Pass\ncard, review_log = scheduler.review_card(card, rating)\n\n# (...wait till next day)\n\ndifferent_scheduler = Scheduler(box_intervals=[1,2,3,4,5])\n\nrating = Rating.Pass\n#card, review_log = different_scheduler.review_card(card, rating) # wrong\ncard, review_log = scheduler.review_card(card, rating) # correct\n```\n\nIn general, you should continue using the same scheduler that you first reviewed the card with. Doing otherwise could lead to scheduling issues.\n\n**Check if a card is due before attempting to review it**\n\nIf you try to review a card that is not due, you will get an error:\n```python\nprint(f\"Card is due on {card.due}\")\n# => Card is due on 2024-10-22 00:00:00\n\nprint(f\"Current datetime: {datetime.now()}\")\n# => Current datetime: 2024-10-21 21:15:23.491825\n\nrating = Rating.Pass\ncard, review_log = scheduler.review_card(card, rating)\n# RuntimeError: Card is not due for review until 2024-10-22 00:00:00.\n```\n\n**Be explicit about datetimes and use a local timezone**\n\nWhile this package operates using [timezone-naive](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects) datetime objects, it's still recommended to provide timezone-aware datetime objects localized to where the user currently is when initializing the scheduler or reviewing cards.\n\n```python\nfrom leitner_box import Scheduler, Card, Rating, ReviewLog\nfrom datetime import datetime, timezone\nfrom zoneinfo import ZoneInfo\n\n# e.g., if you're in Los Angeles\nstart_datetime = datetime.now(ZoneInfo('America/Los_Angeles'))\nscheduler = Scheduler(start_datetime=start_datetime)\n\ncard = Card()\n\nrating = Rating.Pass\nreview_datetime = datetime.now(ZoneInfo('America/Los_Angeles'))\ncard, review_log = scheduler.review_card(card, rating, review_datetime)\n```\n\nUnder the hood, these datetimes are coerced to become timezone-naive, but you still have the option of specifying timezone-aware datetime objects.\n\nTo re-iterate, cards in each box are made due at the beginning of each day, regardless of the timezone. As a consequence of this, when determining whether a user should review cards in a given box, you should know what day it is where they are.\n\n## Versioning\n\nThis python package is currently unstable and adheres to the following versioning scheme:\n\n- **Minor** version will increase when a backward-incompatible change is introduced.\n- **Patch** version will increase when a bug is fixed, a new feature is added or when anything else backward compatible warrants a new release.\n\nOnce this package is considered stable, the **Major** version will be bumped to 1.0.0 and will follow [semver](https://semver.org/).\n\n## Contribute\n\nCheckout [CONTRIBUTING](https://github.com/open-spaced-repetition/leitner-box/blob/main/CONTRIBUTING.md) to help improve leitner-box!\n",
"bugtrack_url": null,
"license": "MIT License Copyright (c) 2024 Open Spaced Repetition Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
"summary": "Leitner system spaced repetition",
"version": "0.3.0",
"project_urls": {
"homepage": "https://github.com/open-spaced-repetition/leitner-box"
},
"split_keywords": [
"leitner",
" spaced-repetition",
" flashcard"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "7c82ff6975d14cd969a04359970c3764cd2a8c83a490643276ec5e1d1ca4bee3",
"md5": "b3adea911ec43100e76a26bb1371aa1a",
"sha256": "19770b58bb16ffb24087d8981f2497e7a68a6fe44c0b6304faa8dfb5966a25f9"
},
"downloads": -1,
"filename": "leitner_box-0.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b3adea911ec43100e76a26bb1371aa1a",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 8035,
"upload_time": "2024-12-07T22:05:41",
"upload_time_iso_8601": "2024-12-07T22:05:41.329304Z",
"url": "https://files.pythonhosted.org/packages/7c/82/ff6975d14cd969a04359970c3764cd2a8c83a490643276ec5e1d1ca4bee3/leitner_box-0.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "790b54adced7c6c81762becb5c5b8ac6105a6e8c7700518d06d5baa27f6da24e",
"md5": "fe30edfce5982f8f09ac94dfc0b5ad24",
"sha256": "f3aa2d342797dc5147ba9dcedff9ae0997c56025babede1744128a5ebe9654d2"
},
"downloads": -1,
"filename": "leitner_box-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "fe30edfce5982f8f09ac94dfc0b5ad24",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 11250,
"upload_time": "2024-12-07T22:05:42",
"upload_time_iso_8601": "2024-12-07T22:05:42.725506Z",
"url": "https://files.pythonhosted.org/packages/79/0b/54adced7c6c81762becb5c5b8ac6105a6e8c7700518d06d5baa27f6da24e/leitner_box-0.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-07 22:05:42",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "open-spaced-repetition",
"github_project": "leitner-box",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "leitner-box"
}