diff --git a/src/grung/examples.py b/src/grung/examples.py index f93905c..6b4a364 100644 --- a/src/grung/examples.py +++ b/src/grung/examples.py @@ -1,4 +1,4 @@ -from grung.types import BackReference, Collection, Field, Integer, Password, Record +from grung.types import BackReference, Collection, DateTime, Field, Integer, Password, Record, Timestamp class User(Record): @@ -10,6 +10,8 @@ class User(Record): Integer("number", default=0), Field("email", unique=True), Password("password"), + DateTime("created"), + Timestamp("last_updated"), BackReference("groups", Group), ] diff --git a/src/grung/types.py b/src/grung/types.py index 34e2653..c6ea2a9 100644 --- a/src/grung/types.py +++ b/src/grung/types.py @@ -6,6 +6,7 @@ import os import re from collections import namedtuple from dataclasses import dataclass, field +from datetime import datetime from typing import Dict, List import nanoid @@ -37,7 +38,7 @@ class Field: if value is not None: return str(value) - def deserialize(self, value: value_type, db: TinyDB, recurse: bool = False) -> value_type: + def deserialize(self, value: str, db: TinyDB, recurse: bool = False) -> value_type: return value @@ -50,6 +51,30 @@ class Integer(Field): return int(value) +@dataclass +class DateTime(Field): + value_type: datetime + default: datetime = datetime.utcfromtimestamp(0) + + def serialize(self, value: value_type) -> str: + return (value - datetime.utcfromtimestamp(0)).total_seconds() + + def deserialize(self, value: str, db: TinyDB, recurse: bool = False) -> value_type: + return datetime.utcfromtimestamp(int(value)) + + def before_insert(self, value: value_type, db: TinyDB, record: Record) -> None: + if not value: + record[self.name] = datetime.utcnow().replace(microsecond=0) + + +@dataclass +class Timestamp(DateTime): + value_type: datetime + + def before_insert(self, value: value_type, db: TinyDB, record: Record) -> None: + super().before_insert(None, db, record) + + @dataclass class Password(Field): value_type = str @@ -67,7 +92,7 @@ class Password(Field): try: if passwd[offset] != ":": return False - digest = passwd[offset + 1 :] + digest = passwd[(offset + 1) :] if len(digest) != cls.digest_size * 2: return False return re.match(r"^[0-9a-f]+$", digest) diff --git a/test/test_db.py b/test/test_db.py index efc538d..68d549c 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -1,4 +1,6 @@ +from datetime import datetime from pprint import pprint as print +from time import sleep import pytest from tinydb import Query @@ -133,3 +135,14 @@ def test_password(db): assert check("fnord", user.password) assert not check("wrong password", user.password) assert not check("", user.password) + + +def test_datetime(db): + user = db.save(examples.User(name="john", email="john@foo", password="fnord", created=datetime.utcnow())) + assert user.created > datetime.utcfromtimestamp(0) + assert user.created < datetime.utcnow() + assert user.last_updated == user.created + + sleep(1) + user = db.save(user) + assert user.last_updated >= user.created