hydrus/hydrus/client/ClientTime.py

330 lines
9.8 KiB
Python
Raw Normal View History

2022-10-12 20:18:22 +00:00
import datetime
import typing
2023-04-12 20:34:43 +00:00
from hydrus.core import HydrusConstants as HC
2023-04-19 20:38:13 +00:00
from hydrus.core import HydrusGlobals as HG
2023-04-12 20:34:43 +00:00
from hydrus.core import HydrusSerialisable
2023-04-19 20:38:13 +00:00
from hydrus.client import ClientConstants as CC
2024-02-14 21:20:24 +00:00
from hydrus.client import ClientGlobals as CG
2023-04-19 20:38:13 +00:00
try:
from dateutil.relativedelta import relativedelta
DATEUTIL_OK = True
except:
DATEUTIL_OK = False
2023-04-19 20:38:13 +00:00
from hydrus.core import HydrusTime
2022-10-12 20:18:22 +00:00
2023-09-13 18:26:31 +00:00
try:
import dateparser
DATEPARSER_OK = True
except:
DATEPARSER_OK = False
2022-10-12 20:18:22 +00:00
def CalendarDelta( dt: datetime.datetime, month_delta = 0, day_delta = 0 ) -> datetime.datetime:
if DATEUTIL_OK:
delta = relativedelta( months = month_delta, days = day_delta )
return dt + delta
else:
total_days = ( 30 * month_delta ) + day_delta
return dt + datetime.timedelta( days = total_days )
2022-10-12 20:18:22 +00:00
2023-09-13 18:26:31 +00:00
def ParseDate( date_string: str ):
if not DATEPARSER_OK:
raise Exception( 'Sorry, you need the dateparse library for this, please try reinstalling your venv!' )
dt = dateparser.parse( date_string )
if dt is None:
raise Exception( 'Sorry, could not parse that date!' )
return HydrusTime.DateTimeToTimestamp( dt )
def MergeModifiedTimes( existing_timestamp: typing.Optional[ int ], new_timestamp: typing.Optional[ int ] ) -> typing.Optional[ int ]:
2023-04-12 20:34:43 +00:00
if ShouldUpdateModifiedTime( existing_timestamp, new_timestamp ):
return new_timestamp
else:
return existing_timestamp
2022-06-22 20:43:12 +00:00
2023-04-12 20:34:43 +00:00
def ShouldUpdateModifiedTime( existing_timestamp: int, new_timestamp: typing.Optional[ int ] ) -> bool:
2022-10-12 20:18:22 +00:00
2023-04-19 20:38:13 +00:00
if new_timestamp is None:
2022-10-12 20:18:22 +00:00
return False
2023-04-19 20:38:13 +00:00
if existing_timestamp is None:
2022-10-12 20:18:22 +00:00
return True
# only go backwards, in general
if new_timestamp >= existing_timestamp:
return False
return True
2022-06-22 20:43:12 +00:00
def TimestampIsSensible( timestamp: typing.Optional[ int ] ) -> bool:
if timestamp is None:
return False
# assume anything too early is a meme and a timestamp parsing conversion error
if timestamp <= 86400 * 7:
return False
return True
2023-04-12 20:34:43 +00:00
2023-04-19 20:38:13 +00:00
def TimestampToPrettyTimeDelta( timestamp, just_now_string = 'just now', just_now_threshold = 3, history_suffix = ' ago', show_seconds = True, no_prefix = False ):
2024-02-14 21:20:24 +00:00
if CG.client_controller.new_options.GetBoolean( 'always_show_iso_time' ):
2023-04-19 20:38:13 +00:00
return HydrusTime.TimestampToPrettyTime( timestamp )
else:
return HydrusTime.BaseTimestampToPrettyTimeDelta( timestamp, just_now_string = just_now_string, just_now_threshold = just_now_threshold, history_suffix = history_suffix, show_seconds = show_seconds, no_prefix = no_prefix )
HydrusTime.TimestampToPrettyTimeDelta = TimestampToPrettyTimeDelta
2023-04-12 20:34:43 +00:00
REAL_SIMPLE_TIMESTAMP_TYPES = {
HC.TIMESTAMP_TYPE_ARCHIVED,
HC.TIMESTAMP_TYPE_MODIFIED_FILE
}
SIMPLE_TIMESTAMP_TYPES = {
HC.TIMESTAMP_TYPE_ARCHIVED,
HC.TIMESTAMP_TYPE_MODIFIED_FILE,
HC.TIMESTAMP_TYPE_MODIFIED_AGGREGATE
}
FILE_SERVICE_TIMESTAMP_TYPES = {
HC.TIMESTAMP_TYPE_IMPORTED,
HC.TIMESTAMP_TYPE_DELETED,
HC.TIMESTAMP_TYPE_PREVIOUSLY_IMPORTED
}
class TimestampData( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TIMESTAMP_DATA
SERIALISABLE_NAME = 'Timestamp Data'
2024-01-17 18:57:00 +00:00
SERIALISABLE_VERSION = 2
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
def __init__( self, timestamp_type = None, location = None, timestamp_ms: typing.Optional[ typing.Union[ int, float ] ] = None ):
2023-04-12 20:34:43 +00:00
HydrusSerialisable.SerialisableBase.__init__( self )
self.timestamp_type = timestamp_type
self.location = location
2024-01-17 18:57:00 +00:00
self.timestamp_ms = None if timestamp_ms is None else int( timestamp_ms )
self.timestamp = HydrusTime.SecondiseMS( self.timestamp_ms ) # TODO: pretty sure I can delete this variable now, but I am currently attempting to fold space using the spice melange and don't want to make a foolish mistake
2023-04-12 20:34:43 +00:00
def __eq__( self, other ):
if isinstance( other, TimestampData ):
return self.__hash__() == other.__hash__()
return NotImplemented
def __hash__( self ):
2024-01-17 18:57:00 +00:00
return ( self.timestamp_type, self.location, self.timestamp_ms ).__hash__()
2023-04-12 20:34:43 +00:00
2023-04-19 20:38:13 +00:00
def __repr__( self ):
return self.ToString()
2023-04-12 20:34:43 +00:00
def _GetSerialisableInfo( self ):
if self.timestamp_type in FILE_SERVICE_TIMESTAMP_TYPES:
serialisable_location = self.location.hex()
else:
serialisable_location = self.location # str, int, or None
2024-01-17 18:57:00 +00:00
return ( self.timestamp_type, serialisable_location, self.timestamp_ms )
2023-04-12 20:34:43 +00:00
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
2024-01-17 18:57:00 +00:00
( self.timestamp_type, serialisable_location, self.timestamp_ms ) = serialisable_info
self.timestamp = HydrusTime.SecondiseMS( self.timestamp_ms )
2023-04-12 20:34:43 +00:00
if self.timestamp_type in FILE_SERVICE_TIMESTAMP_TYPES:
self.location = bytes.fromhex( serialisable_location )
else:
self.location = serialisable_location
2024-01-17 18:57:00 +00:00
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
( timestamp_type, serialisable_location, timestamp ) = old_serialisable_info
timestamp_ms = HydrusTime.MillisecondiseS( timestamp )
new_serialisable_info = ( timestamp_type, serialisable_location, timestamp_ms )
return ( 2, new_serialisable_info )
2023-04-19 20:38:13 +00:00
def ToString( self ) -> str:
if self.timestamp_type in SIMPLE_TIMESTAMP_TYPES:
type_base = HC.timestamp_type_str_lookup[ self.timestamp_type ]
else:
if self.timestamp_type in FILE_SERVICE_TIMESTAMP_TYPES:
try:
2024-02-14 21:20:24 +00:00
service_string = CG.client_controller.services_manager.GetName( self.location )
2023-04-19 20:38:13 +00:00
except:
service_string = 'unknown service'
type_base = '"{}" {}'.format( service_string, HC.timestamp_type_str_lookup[ self.timestamp_type ] )
elif self.timestamp_type == HC.TIMESTAMP_TYPE_LAST_VIEWED:
type_base = '{} {}'.format( CC.canvas_type_str_lookup[ self.location ], HC.timestamp_type_str_lookup[ self.timestamp_type ] )
elif self.timestamp_type == HC.TIMESTAMP_TYPE_MODIFIED_DOMAIN:
type_base = '"{}" {}'.format( self.location, HC.timestamp_type_str_lookup[ self.timestamp_type ] )
else:
type_base = 'unknown timestamp type'
2024-01-17 18:57:00 +00:00
if self.timestamp_ms is None:
2023-04-19 20:38:13 +00:00
# we are a stub, type summary is appropriate
return type_base
else:
2024-01-17 18:57:00 +00:00
return '{}: {}'.format( type_base, HydrusTime.TimestampMSToPrettyTime( self.timestamp_ms ) )
2023-04-19 20:38:13 +00:00
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICArchivedTime( timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_ARCHIVED, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICAggregateModifiedTime( timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_MODIFIED_AGGREGATE, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICDeletedTime( service_key: bytes, timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_DELETED, location = service_key, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICDomainModifiedTime( domain: str, timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_MODIFIED_DOMAIN, location = domain, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICFileModifiedTime( timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_MODIFIED_FILE, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICImportedTime( service_key: bytes, timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_IMPORTED, location = service_key, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICLastViewedTime( canvas_type: int, timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_LAST_VIEWED, location = canvas_type, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
2024-01-17 18:57:00 +00:00
def STATICPreviouslyImportedTime( service_key: bytes, timestamp_ms: int ) -> "TimestampData":
2023-04-12 20:34:43 +00:00
2024-01-17 18:57:00 +00:00
return TimestampData( timestamp_type = HC.TIMESTAMP_TYPE_PREVIOUSLY_IMPORTED, location = service_key, timestamp_ms = timestamp_ms )
2023-04-12 20:34:43 +00:00
@staticmethod
def STATICSimpleStub( timestamp_type: int ) -> "TimestampData":
return TimestampData( timestamp_type = timestamp_type )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_TIMESTAMP_DATA ] = TimestampData