Use datetime objects instead of timestamps in object model.

Trying to make better use of objects in this object oriented language.
master
voussoir 2022-07-19 20:03:43 -07:00
parent cb43b5d9e0
commit 4001f6f371
No known key found for this signature in database
GPG Key ID: 5F7554F8C26DACCB
9 changed files with 54 additions and 35 deletions

View File

@ -304,13 +304,11 @@ def is_xor(*args) -> bool:
'''
return [bool(a) for a in args].count(True) == 1
def now(timestamp=True):
def now():
'''
Return the current UTC timestamp or datetime object.
Return the current UTC datetime object.
'''
n = datetime.datetime.now(datetime.timezone.utc)
if timestamp:
return n.timestamp()
return n
def parse_unit_string(s) -> typing.Union[int, float, None]:
@ -435,6 +433,9 @@ def split_easybake_string(ebstring) -> tuple[str, str, str]:
tagname = tagname.strip('.')
return (tagname, synonym, rename_to)
def timestamp_to_datetime(unix):
return datetime.datetime.utcfromtimestamp(unix).replace(tzinfo=datetime.timezone.utc)
def zip_album(album, recursive=True) -> zipstream.ZipFile:
'''
Given an album, return a zipstream zipfile that contains the album's

View File

@ -4,6 +4,7 @@ but are returned by the PDB accesses.
'''
import abc
import bcrypt
import datetime
import hashlib
import os
import PIL.Image
@ -40,6 +41,8 @@ class ObjectBase(worms.Object):
self._photodb = photodb
# To be lazily retrieved by @property author.
self._author = None
# To be lazily retrieved by @property created.
self._created_dt = None
@staticmethod
def normalize_author_id(author_id) -> typing.Optional[int]:
@ -73,6 +76,13 @@ class ObjectBase(worms.Object):
self._author = user
return user
@property
def created(self) -> datetime.datetime:
if self._created_dt is not None:
return self._created_dt
self._created_dt = helpers.timestamp_to_datetime(self.created_unix)
return self._created_dt
class GroupableMixin(metaclass=abc.ABCMeta):
group_getter_many = None
group_table = None
@ -262,7 +272,7 @@ class Album(ObjectBase, GroupableMixin):
self.id = db_row['id']
self.title = self.normalize_title(db_row['title'])
self.description = self.normalize_description(db_row['description'])
self.created = db_row['created']
self.created_unix = db_row['created']
# To be lazily retrieved by @property thumbnail_photo.
self._thumbnail_photo = db_row['thumbnail_photo']
self._author_id = self.normalize_author_id(db_row['author_id'])
@ -529,7 +539,7 @@ class Album(ObjectBase, GroupableMixin):
'id': self.id,
'description': self.description,
'title': self.title,
'created': self.created,
'created': self.created_unix,
'display_name': self.display_name,
'thumbnail_photo': self.thumbnail_photo.id if self._thumbnail_photo else None,
'author': self.author.jsonify() if self._author_id else None,
@ -698,7 +708,7 @@ class Bookmark(ObjectBase):
self.id = db_row['id']
self.title = self.normalize_title(db_row['title'])
self.url = self.normalize_url(db_row['url'])
self.created = db_row['created']
self.created_unix = db_row['created']
self._author_id = self.normalize_author_id(db_row['author_id'])
def __repr__(self):
@ -786,7 +796,7 @@ class Bookmark(ObjectBase):
'id': self.id,
'author': self.author.jsonify() if self._author_id else None,
'url': self.url,
'created': self.created,
'created': self.created_unix,
'title': self.title,
'display_name': self.display_name,
}
@ -808,7 +818,7 @@ class Photo(ObjectBase):
self.real_path = pathclass.Path(self.real_path)
self.id = db_row['id']
self.created = db_row['created']
self.created_unix = db_row['created']
self._author_id = self.normalize_author_id(db_row['author_id'])
self.override_filename = db_row['override_filename']
self.extension = self.real_path.extension.no_dot
@ -828,7 +838,8 @@ class Photo(ObjectBase):
self.ratio = db_row['ratio']
self.thumbnail = self.normalize_thumbnail(db_row['thumbnail'])
self.tagged_at = db_row['tagged_at']
self.tagged_at_unix = db_row['tagged_at']
self._tagged_at_dt = None
self.searchhidden = db_row['searchhidden']
self._assign_mimetype()
@ -920,7 +931,7 @@ class Photo(ObjectBase):
self.photodb.insert(table='photo_tag_rel', data=data)
data = {
'id': self.id,
'tagged_at': helpers.now(),
'tagged_at': helpers.now().timestamp(),
}
self.photodb.update(table=Photo, pairs=data, where_key='id')
@ -1114,7 +1125,7 @@ class Photo(ObjectBase):
'duration': self.duration,
'bytes_string': self.bytes_string,
'has_thumbnail': bool(self.thumbnail),
'created': self.created,
'created': self.created_unix,
'filename': self.basename,
'mimetype': self.mimetype,
'searchhidden': bool(self.searchhidden),
@ -1245,7 +1256,7 @@ class Photo(ObjectBase):
}
self.photodb.update(table=Photo, pairs=data, where_key='id')
self._uncache()
# self._uncache()
@decorators.required_feature('photo.edit')
@worms.atomic
@ -1296,7 +1307,7 @@ class Photo(ObjectBase):
data = {
'id': self.id,
'tagged_at': helpers.now(),
'tagged_at': helpers.now().timestamp(),
}
self.photodb.update(table=Photo, pairs=data, where_key='id')
@ -1315,7 +1326,7 @@ class Photo(ObjectBase):
data = {
'id': self.id,
'tagged_at': helpers.now(),
'tagged_at': helpers.now().timestamp(),
}
self.photodb.update(table=Photo, pairs=data, where_key='id')
@ -1430,6 +1441,13 @@ class Photo(ObjectBase):
self.photodb.update(table=Photo, pairs=data, where_key='id')
self.searchhidden = searchhidden
@property
def tagged_at(self) -> datetime.datetime:
if self._tagged_at_dt is not None:
return self._tagged_at_dt
self._tagged_at_dt = helpers.timestamp_to_datetime(self.tagged_at_unix)
return self._tagged_at_dt
class Tag(ObjectBase, GroupableMixin):
'''
A Tag, which can be applied to Photos for organization.
@ -1446,7 +1464,7 @@ class Tag(ObjectBase, GroupableMixin):
# from previous character / length rules.
self.name = db_row['name']
self.description = self.normalize_description(db_row['description'])
self.created = db_row['created']
self.created_unix = db_row['created']
self._author_id = self.normalize_author_id(db_row['author_id'])
self.group_getter_many = self.photodb.get_tags_by_id
@ -1700,7 +1718,7 @@ class Tag(ObjectBase, GroupableMixin):
'type': 'tag',
'id': self.id,
'name': self.name,
'created': self.created,
'created': self.created_unix,
}
if not minimal:
j['author'] = self.author.jsonify() if self._author_id else None
@ -1818,7 +1836,7 @@ class User(ObjectBase):
self.id = db_row['id']
self.username = db_row['username']
self.created = db_row['created']
self.created_unix = db_row['created']
self.password_hash = db_row['password']
# Do not enforce maxlen here, they may be grandfathered in.
self._display_name = self.normalize_display_name(db_row['display_name'])
@ -1968,7 +1986,7 @@ class User(ObjectBase):
'type': 'user',
'id': self.id,
'username': self.username,
'created': self.created,
'created': self.created_unix,
'display_name': self.display_name,
}
return j

View File

@ -113,7 +113,7 @@ class PDBAlbumMixin:
'id': album_id,
'title': title,
'description': description,
'created': helpers.now(),
'created': helpers.now().timestamp(),
'thumbnail_photo': None,
'author_id': author_id,
}
@ -205,7 +205,7 @@ class PDBBookmarkMixin:
'id': bookmark_id,
'title': title,
'url': url,
'created': helpers.now(),
'created': helpers.now().timestamp(),
'author_id': author_id,
}
self.insert(table=objects.Bookmark, data=data)
@ -360,7 +360,7 @@ class PDBPhotoMixin:
'basename': filepath.basename,
'override_filename': None,
'extension': filepath.extension.no_dot,
'created': helpers.now(),
'created': helpers.now().timestamp(),
'tagged_at': None,
'author_id': author_id,
'searchhidden': searchhidden,
@ -980,7 +980,7 @@ class PDBTagMixin:
'id': tag_id,
'name': tagname,
'description': description,
'created': helpers.now(),
'created': helpers.now().timestamp(),
'author_id': author_id,
}
self.insert(table=objects.Tag, data=data)
@ -1150,7 +1150,7 @@ class PDBUserMixin:
'username': username,
'password': hashed_password,
'display_name': display_name,
'created': helpers.now(),
'created': helpers.now().timestamp(),
}
self.insert(table=objects.User, data=data)

View File

@ -34,7 +34,7 @@ def get_dbdump():
with common.P.transaction:
binary = common.P.database_filepath.read('rb')
now = etiquette.helpers.now(timestamp=False).strftime('%Y-%m-%d_%H-%M-%S')
now = etiquette.helpers.now().strftime('%Y-%m-%d_%H-%M-%S')
download_as = f'etiquette {now}.db'
outgoing_headers = {
'Content-Type': 'application/octet-stream',

View File

@ -58,16 +58,15 @@ def join_and_trail(l, s):
@filter_function
def timestamp_to_8601(timestamp):
return datetime.datetime.utcfromtimestamp(timestamp).isoformat(' ') + ' UTC'
return timestamp.isoformat(' ') + ' UTC'
@filter_function
def timestamp_to_string(timestamp, format):
date = datetime.datetime.utcfromtimestamp(timestamp)
return date.strftime(format)
def timestamp_strftime(timestamp, format):
return timestamp.strftime(format)
@filter_function
def timestamp_to_naturaldate(timestamp):
return timestamp_to_string(timestamp, '%B %d, %Y')
return timestamp_strftime(timestamp, '%B %d, %Y')
@filter_function
def users_to_usernames(users):

View File

@ -118,7 +118,7 @@ class Session:
def expired(self):
now = etiquette.helpers.now()
age = now - self.last_activity
return age > SESSION_MAX_AGE
return age.seconds > SESSION_MAX_AGE
def maintain(self):
self.last_activity = etiquette.helpers.now()

View File

@ -247,7 +247,7 @@ const ALBUM_ID = undefined;
<p>Author: <a href="/userid/{{author.id}}">{{author.display_name}}</a></p>
{% endif %}
<p>Created on <span title="{{album.created|int|timestamp_to_8601}}">{{album.created|timestamp_to_naturaldate}}.</span></p>
<p>Created on <span title="{{album.created|timestamp_to_8601}}">{{album.created|timestamp_to_naturaldate}}.</span></p>
<button class="green_button editor_toolbox_placeholder">Edit</button>
</div> <!-- #album_metadata -->
<div id="album_thumbnail">

View File

@ -190,6 +190,7 @@
<li>Duration: {{photo.duration_string}}</li>
<li>Overall Bitrate: {{photo.bitrate|int}} kbps</li>
{% endif %}
<li>Created {{photo.created|timestamp_to_naturaldate}}</li>
<li><button id="refresh_metadata_button" class="green_button button_with_spinner" onclick="return refresh_metadata_form();">Refresh metadata</button></li>
{% if request.is_localhost %}
<li><button id="show_in_folder_button" onclick="return show_in_folder_form();">Show in folder</button></li>
@ -223,9 +224,9 @@
<!-- BEFORE & AFTER SEARCH LINKS -->
<div id="before_after_links">
<a href="/search?created=-{{photo.created}}">&larr;Before</a>
<a href="/search?created=-{{photo.created_unix}}">&larr;Before</a>
<span> | </span>
<a href="/search?created={{photo.created}}-&orderby=created-asc">After&rarr;</a>
<a href="/search?created={{photo.created_unix}}-&orderby=created-asc">After&rarr;</a>
</div>
</div>

View File

@ -31,7 +31,7 @@
<div id="hierarchy_self" class="panel">
<h1 id="display_name">{{user.display_name}}</h1>
<p>ID: <a href="/userid/{{user.id}}">{{user.id}}</a></p>
<p>User since <span title="{{user.created|int|timestamp_to_8601}}">{{user.created|timestamp_to_naturaldate}}.</span></p>
<p>User since <span title="{{user.created|timestamp_to_8601}}">{{user.created|timestamp_to_naturaldate}}.</span></p>
</div>
{% set photos = user.get_photos(direction='desc')|islice(0, 15)|list %}