PK!­9Ô–ÕÕsimplesrs/__init__.pyfrom .db import database, Settings, CardTag, Card, Tag def init(filename, **kwargs): database.init(filename, **kwargs) database.create_tables([Settings, Card, Tag, CardTag]) Settings.get_or_create() PK!ÞA-simplesrs/db.pyimport peewee as pv from playhouse import sqlite_ext from playhouse.shortcuts import model_to_dict, dict_to_model from datetime import datetime, timedelta import random import json from typing import Any from .default import DEFAULT database = sqlite_ext.SqliteDatabase(None) class BaseModel(pv.Model): class Meta: database = database class SrsField(pv.TextField): def db_value(self, value): if value: return json.dumps([v.total_seconds() for v in value]) def python_value(self, value): if value: return [timedelta(seconds=v) for v in json.loads(value)] else: return DEFAULT['srs'] class Settings(BaseModel): srs = SrsField(default=DEFAULT['srs']) type_ = pv.TextField(default=DEFAULT['type_']) info = sqlite_ext.JSONField(default=DEFAULT['info']) def to_dict(self): return model_to_dict(self) class Tag(BaseModel): name = pv.TextField(unique=True, collation='NOCASE') def __repr__(self): return f'' def __str__(self): return self.name class Card(BaseModel): item = pv.TextField(unique=True) info = sqlite_ext.JSONField(null=True) srs_level = pv.IntegerField(null=True) next_review = pv.DateTimeField(null=True) tags = pv.ManyToManyField(Tag, backref='cards', on_delete='cascade') backup = None def __repr__(self): return self.item def _repr_markdown_(self): type_ = Settings.get().type_ if type_ == 'markdown': return self.item def _repr_html_(self): type_ = Settings.get().type_ if type_ == 'html': return self.item def mark(self, tag='marked'): Tag.get_or_create(name=tag)[0].notes.add(self) def unmark(self, tag='marked'): Tag.get_or_create(name=tag)[0].notes.remove(self) def right(self): if not self.backup: self.backup = model_to_dict(self) if not self.srs_level: self.srs_level = 0 else: self.srs_level = self.srs_level + 1 srs = Settings.get()['srs'] try: self.next_review = datetime.now() + srs[self.srs_level] except IndexError: self.next_review = None self.save() correct = next_srs = right def wrong(self, next_review=timedelta(minutes=10)): if not self.backup: self.backup = model_to_dict(self) if self.srs_level and self.srs_level > 0: self.srs_level = self.srs_level - 1 self.bury(next_review) incorrect = previous_srs = wrong def bury(self, next_review=timedelta(hours=4)): if not self.backup: self.backup = model_to_dict(self) if isinstance(next_review, timedelta): self.next_review = datetime.now() + next_review else: self.next_review = next_review self.save() def undo(self): if self.backup: dict_to_model(Card, self.backup).save() @classmethod def iter_quiz(cls, **kwargs): db_cards = list(cls.search(**kwargs)) random.shuffle(db_cards) return iter(db_cards) @classmethod def iter_due(cls, **kwargs): return cls.iter_quiz(due=True, **kwargs) @classmethod def search(cls, tags=None, due=Any, offset=0, limit=None): query = cls.select() if due is True: query = query.where(Card.next_review < datetime.now()) elif due is False: query = query.where(Card.next_review >= datetime.now()) elif due is None: query = query.where(Card.next_review.is_null(True)) elif isinstance(due, timedelta): query = query.where(Card.next_review < datetime.now() + due) elif isinstance(due, datetime): query = query.where(Card.next_review < due) if tags: for tag in tags: query = query.join(CardTag).join(Tag).where(Tag.name.contains(tag)) query = query.order_by(cls.next_review.desc()) if offset: query = query.offset(offset) if limit: query = query.limit(limit) return query def to_dict(self): return model_to_dict(self, manytomany=True) @classmethod def add(cls, item, tags, **kwargs): with database.atomic(): db_card = cls.create(item=item, info=kwargs) for tag in tags: Tag.get_or_create(name=tag)[0].cards.add(db_card) return db_card CardTag = Card.tags.get_through_model() PK!&רððsimplesrs/default.pyfrom datetime import timedelta DEFAULT = { 'srs': [ timedelta(minutes=10), # 0 timedelta(hours=1), # 1 timedelta(hours=4), # 2 timedelta(hours=8), # 3 timedelta(days=1), # 4 timedelta(days=3), # 5 timedelta(weeks=1), # 6 timedelta(weeks=2), # 7 timedelta(weeks=4), # 8 timedelta(weeks=16) # 9 ], 'type_': 'markdown', 'info': { 'version': '0.1' } } PK!˜ ƒŽ::!simplesrs-0.1.1.dist-info/LICENSEMIT License Copyright (c) 2018 Pacharapol Withayasakpunt 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. PK!H‘ „ÁWXsimplesrs-0.1.1.dist-info/WHEEL ÊA € н§ð#Z´ñÑ."jmð)Afb²…·¯·~ûÚ OŽ68oF04UÑhoAÕf½ Á f©4=4¿Š’h«¾0k:ÿØ:w·žX”¸™PK!Hjêç™"simplesrs-0.1.1.dist-info/METADATAµUOoE¿Ï§xRk {„@%‹D„TˆÒÔ”˜P‰ÈJÆ»Ïë©wg63³v׉òGªrãÒ‚K/´”éÑ…¦¥ß‚7ãÄJUd{ÙÙ÷ÿýÞ›ß^CËcnyócÔF(Ù†õpux†m0"ËS4Ú°…r5\#u·È2®Ë6t½t÷ºP390njŒT"…%s0¥±˜Õ!ÃhÈ¥0pƒK×çÙ{*ÃfÎJ5´67íV+vXôÃHe­œÌ4ÏU:iU…슥!‡ýÎÕntØU,'JdžŠÕ¦±(!Gë+`Û…*݆ëœ*ðÑà¥à%7|”ÒžY41ã"mC•ôíÄI\%l'åÆˆ@H«Dó,2].“‚ʇ6ÉK #Ýi}YûðÒ’¯/kn,íñÆÒo.íq‰íáq!4šæea,¡8A„ÚÖ&)om„«uvM¤Eî¦ÙÜQÒ¢´ÍÊœVÀâ-Û¢=Åj"[¹°¯ì?-f݈§¼OœxŠZA¤ä@$…æÎ?¤l+°o¨'ÆŽŽŽrßÛÚÚÊ«´­JnÜjz½CAÔwŠûA}!Þá:yׂg¿<`yb6*n*è3°‡þ¸ô0Vï;í““²z=ÿñ{8Ð(×!E±Ñƒa‘Q[¶lùh€’#!ã ÷²¬O¿ûùôáý*±ÏáS‘z¹Ïóoîþqÿö³{_=9¹óô˯Ýù§;§w=ýöñéí{U,ƒnj¾Ø‡Õ\šÔºùÁ~ÿü 8ød8{$“röƒ‚Á gŽÝvyA:{psöŽg¿Á´˜= ¡¶RÁq5šMÁj%“z½W@"Æ`$JÍŒK vH 2Œ·˜–áÙhO§°Y! ,êC'¬Íõ I/i'kN\IÙ«[[ØÑj û”=Ál‚¶yñÿRP%û·€±Ïªž´H†¶VXktSÝm𢆿Y-Ð88~IuLÈô5òaŠcLVPdL-¿€sXÈX̓îÓ ¸σ‡•åĵð×ü^4Ïß/t ¥ ©/Rb µU Ê*,šðÅtÕ·s›Ç|ço6`¨ ºïÄ ž$®Hcy:Ÿ¤ç –‹Ä\z±Îí÷l ë\«›Y¢²&¸›{«W{õOÒÕ¡Ù„Á9Ä^TYÆ­/p›´0!G7<­Æ”â xA·SyjB¸^h<'á´ã Nl¸-™ŠÜÅmpüÄ¥ãÉ8„í1ý.=gÒšÐ?y4ÏFdû~Aüˆ:Êb_©QïÔ{¬U.ˆ›0O‰ž2"‰E¡!0ö'PK!HëõÌõT simplesrs-0.1.1.dist-info/RECORD}ѽv‚0@á½Ï‚6!ÂÐ"*ň ºä üjŧo['ÏÝ¿å ^6E&ÎâR^ñ–ÒaÓKâÈŠúy3wMwgí¹;-æä„X“tcÿ0wnZl¢U3gÓë•—òíI2DoâK·OLÀî×Ô¿,LUY'á™5¨ÿþâ˳p)œµí@y9ÔD“° •g'cÉ¥hŸ° E! nç ÎkÔƒ ]lÀz±ÂܶE¥;ežÆ°®þ[0„¿¥\´^±ú}f›Ö|i=`-,]ì‚nbÕmXÕQ‹/sÜÆl p—ùëhˉ¾1³·— ÐäòjjY³‡‹¥Í'ëP9î=êSr?ú=;F[¤¹fŒxíìpÔ½ %M{¡+0FF`<à).¨{Tœ4âÒÙÅÅÖú¨èUp6S‘iGYbçÊ8ù½#ø‚ö-ÓõG’ôöPK!­9Ô–ÕÕ¤simplesrs/__init__.pyPK!ÞA-¤simplesrs/db.pyPK!&רðð¤<simplesrs/default.pyPK!˜ ƒŽ::!¤^simplesrs-0.1.1.dist-info/LICENSEPK!H‘ „ÁWX€×simplesrs-0.1.1.dist-info/WHEELPK!Hjêç™"€ksimplesrs-0.1.1.dist-info/METADATAPK!HëõÌõT €’simplesrs-0.1.1.dist-info/RECORDPKü$