Ejemplo n.º 1
0
class Note(BaseModel):
    model = pv.ForeignKeyField(Model, backref='notes')
    data = sqlite_ext.JSONField()              # format = dict()
    constraint = ConstraintField(unique=True)  # format = list()
    _tags = pv.ManyToManyField(Tag, backref='notes')

    created = pv.DateTimeField(constraints=[pv.SQL('DEFAULT CURRENT_TIMESTAMP')])
    modified = pv.DateTimeField(constraints=[pv.SQL('DEFAULT CURRENT_TIMESTAMP')])

    info = sqlite_ext.JSONField(default=dict)

    def to_dict(self):
        return super(Note, self).to_dict(manytomany=False, backrefs=False,
                                         exclude=['_tags'], extra_attrs=['tags'])

    @property
    def tags(self):
        return [t.name for t in self._tags]

    def mark(self, tag='marked'):
        Tag.get_or_create(name=tag)[0].notes.add(self)

    add_tag = mark

    def unmark(self, tag='marked'):
        Tag.get_or_create(name=tag)[0].notes.remove(self)

    remove_tag = unmark
Ejemplo n.º 2
0
class Result(Model):
    station = ForeignKeyField(WeatherStation)
    dc_output = sqlite_ext.JSONField()
    temperature = sqlite_ext.JSONField()
    wind_speed = sqlite_ext.JSONField()

    class Meta:
        database = db
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
class Media(BaseModel):
    data = pv.BlobField()
    h = pv.TextField()
    info = sqlite_ext.JSONField(default=dict)

    class Meta:
        indexes = [
            # pv.SQL('CREATE UNIQUE INDEX media_data_hash ON media (MD5(data))'),
        ]
Ejemplo n.º 5
0
class Deck(BaseModel):
    name = pv.TextField(unique=True, collation='NOCASE')
    info = sqlite_ext.JSONField(default=dict)

    def __repr__(self):
        return f'<Deck: "{self.name}">'

    def __str__(self):
        return self.name
Ejemplo n.º 6
0
class Notes(BaseModel):
    """
    -- Notes contain the raw information that is formatted into a number of cards
    -- according to the models
    CREATE TABLE notes (
        id              integer primary key,
          -- epoch seconds of when the note was created
        guid            text not null,
          -- globally unique id, almost certainly used for syncing
        mid             integer not null,
          -- model id
        mod             integer not null,
          -- modification timestamp, epoch seconds
        usn             integer not null,
          -- update sequence number: for finding diffs when syncing.
          --   See the description in the cards table for more info
        tags            text not null,
          -- space-separated string of tags.
          --   includes space at the beginning and end, for LIKE "% tag %" queries
        flds            text not null,
          -- the values of the fields in this note. separated by 0x1f (31) character.
        sfld            text not null,
          -- sort field: used for quick sorting and duplicate check
        csum            integer not null,
          -- field checksum used for duplicate check.
          --   integer representation of first 8 digits of sha1 hash of the first field
        flags           integer not null,
          -- unused
        data            text not null
          -- unused
    );
    """
    id = pv.AutoField(primary_key=True)     # Use auto-increment instead of time in Epoch seconds to ensure uniqueness
    guid = pv.TextField(unique=True, default=shortuuid.uuid)    # Use short UUID instead of default generator
    model = pv.ForeignKeyField(Models, column_name="mid", null=True)
    mod = pv.IntegerField()     # autogenerated
    usn = pv.IntegerField(default=-1)
    tags = ArrayFieldPadded(default=list)
    flds = X1fField()
    sfld = pv.TextField()       # autogenerated
    csum = pv.IntegerField()    # autogenerated
    flags = pv.IntegerField(default=0)
    data = sqlite_ext.JSONField(default=dict)   # shouldn't that be a TextField?

    class Meta:
        indexes = [
            pv.SQL('CREATE INDEX ix_notes_usn on notes (usn)'),
            pv.SQL('CREATE INDEX ix_notes_csum on notes (csum)')
        ]

    @classmethod
    def create(cls, fields=None, **query):
        inst = cls(**query)
        inst.fields = fields
        inst.save(force_insert=True)
        return inst
Ejemplo n.º 7
0
def upgrade():
    settings = db.Settings.get()
    version = settings.info['version']
    migrator = SqliteMigrator(db.database)

    if version < '0.2':
        migrate(
            migrator.add_column('deck', 'info',
                                sqlite_ext.JSONField(default=dict)),
            migrator.add_column('media', 'info',
                                sqlite_ext.JSONField(default=dict)),
            migrator.add_column('model', 'info',
                                sqlite_ext.JSONField(default=dict)),
            migrator.add_column('template', 'info',
                                sqlite_ext.JSONField(default=dict)),
            migrator.add_column('note', 'info',
                                sqlite_ext.JSONField(default=dict)),
            migrator.add_column('card', 'info',
                                sqlite_ext.JSONField(default=dict)),
            migrator.add_column('card', 'last_review', pv.TimestampField()),
        )
        settings.info['version'] = '0.2'
        settings.save()

    if version < '0.2.1':
        migrate(
            migrator.drop_column('card', 'last_review'),
            migrator.add_column('card', 'last_review',
                                pv.DateTimeField(default=datetime.now)),
            migrator.drop_column('note', 'modified'),
            migrator.add_column('note', 'modified',
                                pv.DateTimeField(default=datetime.now)))
        settings.info['version'] = '0.2.1'
        settings.save()
Ejemplo n.º 8
0
class Template(BaseModel):
    model = pv.ForeignKeyField(Model, backref='templates')
    name = pv.TextField()
    front = pv.TextField()
    back = pv.TextField(null=True)
    info = sqlite_ext.JSONField(default=dict)

    def test_front(self, d):
        text = self.front
        for k, v in d.items():
            text = text.replace('{{%s}}' % k, str(v))

        return text

    class Meta:
        indexes = [
            (('model_id', 'name'), True),
            (('model_id', 'front'), True),
        ]
Ejemplo n.º 9
0
class Model(BaseModel):
    name = pv.TextField(unique=True)
    key_fields = sqlite_ext.JSONField(default=list)
    css = pv.TextField(null=True)
    js = pv.TextField(null=True)
    info = sqlite_ext.JSONField(default=dict)
Ejemplo n.º 10
0
class Settings(BaseModel):
    srs = SrsField(default=DEFAULT['srs'])
    info = sqlite_ext.JSONField(default=DEFAULT['info'])
Ejemplo n.º 11
0
class Card(BaseModel):
    template = pv.ForeignKeyField(Template, backref='cards')
    note = pv.ForeignKeyField(Note, backref='cards')
    _front = pv.TextField(unique=True)
    srs_level = pv.IntegerField(null=True)
    next_review = pv.DateTimeField(null=True)
    _decks = pv.ManyToManyField(Deck, backref='cards')

    last_review = pv.DateTimeField(constraints=[pv.SQL('DEFAULT CURRENT_TIMESTAMP')])
    info = sqlite_ext.JSONField(default=dict)

    backup = None

    def to_dict(self, max_depth=2, **kwargs):
        d = super(Card, self).to_dict(manytomany=False, backrefs=False,
                                      exclude=['_decks', '_front', 'note'],
                                      extra_attrs=['decks', 'front', 'back'])
        d['note'] = self.note.to_dict()
        return d

    @property
    def decks(self):
        return [d.name for d in self._decks]

    @property
    def front(self):
        text = self.template.front
        for k, v in self.note.data.items():
            text = text.replace('{{%s}}' % k, str(v))

        return text

    @property
    def back(self):
        text = self.template.back
        if not text:
            return '\n'.join(' ' * 4 + line for line in json.dumps(
                self.note.data,
                indent=2, ensure_ascii=False
            ).split('\n'))

        for k, v in self.note.data.items():
            text = text.replace('{{%s}}' % k, str(v))

        return text

    def __repr__(self):
        return self.front

    @property
    def data(self):
        return self.note.data

    def add_deck(self, deck_name):
        Deck.get_or_create(name=deck_name)[0].cards.add(self)

    def remove_deck(self, deck_name):
        Deck.get_or_create(name=deck_name)[0].cards.remove(self)

    def mark(self, tag='marked'):
        return self.note.mark(tag)

    def unmark(self, tag='marked'):
        return self.note.unmark(tag)

    def right(self, step=1):
        self.undo()

        if not self.backup:
            self.backup = model_to_dict(self)

        print(self.srs_level)

        if self.srs_level is None:
            self.srs_level = 0
        else:
            self.srs_level = self.srs_level + step

        srs = Settings.get().srs
        try:
            self.next_review = datetime.now() + srs[self.srs_level]
        except IndexError:
            self.next_review = None

        assert isinstance(self.info, dict)

        self.info['lapse'] = 0
        self.info['streak'] = self.info.get('streak', 0) + 1
        self.info['total_right'] = self.info.get('total_right', 0) + 1

        self.save()

    correct = next_srs = right

    def easy(self, max_srs_level_enabled=3):
        if self.srs_level < max_srs_level_enabled:
            return self.right(step=2)
        else:
            raise ValueError

    def wrong(self, next_review=timedelta(minutes=10)):
        self.undo()

        if not self.backup:
            self.backup = model_to_dict(self)

        if self.srs_level is not None and self.srs_level > 0:
            self.srs_level = self.srs_level - 1

        assert isinstance(self.info, dict)

        self.info['streak'] = 0
        self.info['lapse'] = self.info.get('lapse', 0) + 1
        self.info['total_wrong'] = self.info.get('total_wrong', 0) + 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 reset(self):
        self.srs_level = None
        self.next_review = None
        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, q_str='', deck=None, tags=None, due=None, offset=0, limit=None):
        """

        :param q_str:
        :param deck:
        :param tags:
        :param bool|None|timedelta|datetime due:
        :param offset:
        :param limit:
        :return:
        """
        query = cls.select()
        due_is_set = False
        note_keys = None

        result = parse_query(q_str)
        if result:
            for seg in result:
                if len(seg) == 1:
                    if note_keys is None:
                        note_keys = set()
                        for srs_note in Note.select(Note.data):
                            note_keys.update(srs_note.data.keys())

                        note_keys = tuple(note_keys)
                    q_note = Note.data[note_keys[0]].contains(seg[0])

                    for k in note_keys[1:]:
                        q_note |= Note.data[k].contains(seg[0])

                    query = query.switch(cls).join(Note).where(q_note)
                else:
                    if seg[0] == 'due':
                        due_is_set = True
                        if seg[2].lower() == 'true':
                            query = query.switch(cls).where(cls.next_review < datetime.now())
                        elif seg[2].lower() == 'false':
                            query = query.switch(cls).where(cls.next_review.is_null(True))
                        else:
                            dur_sec = pytimeparse.parse(seg[2])
                            if dur_sec:
                                _due = datetime.now() + timedelta(seconds=dur_sec)
                            else:
                                _due = dateutil.parser.parse(seg[2])

                            query = query.switch(cls).where(cls.next_review < _due)
                    elif seg[0] == 'deck':
                        deck_q = (Deck.name == seg[2])
                        if seg[1] != '=':
                            deck_q = (deck_q | Deck.name.startswith(seg[2] + '::'))

                        query = query.switch(cls).join(CardDeck).join(Deck).where(deck_q)
                    elif seg[0] == 'tag':
                        if seg[1] == '=':
                            query = query.switch(cls).join(Note).join(NoteTag).join(Tag)\
                                .where(Tag.name == seg[2])
                        else:
                            query = query.switch(cls).join(Note).join(NoteTag).join(Tag)\
                                .where(Tag.name.contains(seg[2]))
                    else:
                        if seg[1] == '=':
                            query = query.switch(cls).join(Note).where(Note.data[seg[0]] == seg[2])
                        elif seg[1] == '>':
                            query = query.switch(cls).join(Note).where(Note.data[seg[0]] > seg[2])
                        elif seg[1] == '<':
                            query = query.switch(cls).join(Note).where(Note.data[seg[0]] < seg[2])
                        else:
                            query = query.switch(cls).join(Note).where(Note.data[seg[0]].contains(seg[2]))

        if due is True:
            query = query.switch(cls).where(cls.next_review < datetime.now())
        elif due is False:
            query = query.switch(cls).where(cls.next_review.is_null(True))
        elif isinstance(due, timedelta):
            query = query.switch(cls).where(cls.next_review < datetime.now() + due)
        elif isinstance(due, datetime):
            query = query.switch(cls).where(cls.next_review < due)
        else:
            if not due_is_set:
                query = query.where((cls.next_review < datetime.now()) | cls.next_review.is_null(True))

        if deck:
            query = query.switch(cls).join(CardDeck).join(Deck).where(Deck.name.startswith(deck + '::')
                                                                      | (Deck.name == deck))

        if tags:
            for tag in tags:
                query = query.switch(cls).join(Note).join(NoteTag).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
Ejemplo n.º 12
0
Archivo: db.py Proyecto: t-web/ankix
class Note(BaseModel):
    data = sqlite_ext.JSONField()
    model = pv.ForeignKeyField(Model, backref='notes')
    media = pv.ManyToManyField(Media, backref='notes', on_delete='cascade')
    tags = pv.ManyToManyField(Tag, backref='notes', on_delete='cascade')
    h = pv.TextField(unique=True)

    def mark(self, tag):
        Tag.get_or_create(name=tag)[0].notes.add(self)

    def unmark(self, tag):
        Tag.get_or_create(name=tag)[0].notes.remove(self)

    def rename_field(self, old_name, new_name):
        for db_note in Note.select(Note.data, Note.model_id).where(model_id=self.model_id):
            if old_name in db_note.data.keys():
                db_note.data[new_name] = db_note.data.pop(old_name)
                db_note.save()

    @classmethod
    def add(cls, data, model, card_to_decks: dict, media: dict=None, tags: list=None):
        if media is None:
            media = dict()
        if tags is None:
            tags = list()

        with database.atomic():
            if isinstance(model, int) or (isinstance(model, str) and model.isdigit()):
                db_model = Model.get(id=int(model))
            elif isinstance(model, Model):
                db_model = model
            else:
                db_model = Model.get(name=model)

            db_note = cls.create(
                data=data,
                model_id=db_model.id
            )

            for template, deck in card_to_decks.items():
                if isinstance(deck, int) or (isinstance(deck, str) and deck.isdigit()):
                    db_deck = Deck.get(id=int(deck))
                elif isinstance(deck, Deck):
                    db_deck = deck
                else:
                    db_deck = Deck.get_or_create(name=deck)[0]

                if isinstance(template, int) or (isinstance(template, str) and template.isdigit()):
                    db_template = Template.get(id=int(template))
                elif isinstance(template, Template):
                    db_template = template
                else:
                    db_template = Template.get(model_id=db_model.id, name=template)

                Card.create(
                    note_id=db_note.id,
                    deck_id=db_deck.id,
                    template_id=db_template.id
                )

            for media_name, media_path in media.items():
                type_ = {
                    'audio': MediaType.audio,
                    'font': MediaType.font
                }.get(magic.from_file(media_path, mime=True).split('/')[0], MediaType.image)

                with open(media_path, 'rb') as f:
                    Media.create(
                        name=media_name,
                        data=f.read(),
                        type_=type_
                    )

            for tag_name in tags:
                Tag.get_or_create(name=tag_name)[0].notes.add(db_note)

        return db_note

    @classmethod
    def search(cls, model_name=None, deck_name=None, tags=None, data=None, **kwargs):
        db_query = cls.select()
        if deck_name:
            db_query = db_query \
                .join(Card).join(Deck) \
                .where(Deck.name.contains(deck_name))

        db_query = cls._build_query(db_query, model_name=model_name, tags=tags, data=data, **kwargs)

        return db_query

    @classmethod
    def _build_query(cls, db_query, model_name=None, tags=None, data=None, **kwargs):
        if data is None:
            data = dict()
        data.update(kwargs)

        db_query = db_query.switch(cls)

        if data:
            for k, v in data.items():
                db_query = db_query.where(cls.data[k].contains(v))
        if model_name:
            db_query = db_query \
                .join(Model) \
                .where(Model.name.contains(model_name))
        if tags:
            db_query = db_query\
                .join(NoteTag).join(Tag)\
                .where(~Tag.name.not_in(tags))

        return db_query

    def to_viewer(self):
        d = model_to_dict(self)
        d['cards'] = '<br/>'.join(c.html for c in self.cards)
        d['tags'] = [t.name for t in self.tags]

        return d

    viewer_config = {
        'renderer': {
            'cards': 'html'
        },
        'colWidth': {
            'cards': 400
        }
    }
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
class Word(BaseModel):
    word = sqlite.CharField(index=True,
                            constraints=[peewee.SQL("COLLATE NOCASE")])
    vector = sqlite.JSONField()
    stoplisted = sqlite.BooleanField(index=True, default=False)
Ejemplo n.º 15
0
class Gene(BaseModel):
    gene = peewee.CharField(index=True,
                            constraints=[peewee.SQL("COLLATE NOCASE")])
    vector = sqlite.JSONField()