Skip to content

Commit

Permalink
Improving (#26)
Browse files Browse the repository at this point in the history
* Encapsulating code

* Trying assert False

* Trying zoneinfo

* Replacing datetime convertions with cast
  • Loading branch information
turulomio committed Dec 4, 2023
1 parent 0bc590b commit d97789d
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 97 deletions.
6 changes: 5 additions & 1 deletion pydicts/casts.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ def str2date(iso, format="YYYY-MM-DD"):
raise exceptions.CastException(_("I can't convert this format '{}'. I only support this {}").format(format, allowed))

def str2dtnaive(s, format):
allowed=["%Y%m%d%H%M","%Y-%m-%d %H:%M:%S","%d/%m/%Y %H:%M","%d %m %H:%M %Y","%Y-%m-%d %H:%M:%S.","%H:%M:%S", '%b %d %H:%M:%S']
allowed=["%Y%m%d%H%M","%Y-%m-%d %H:%M:%S","%d/%m/%Y %H:%M","%d %m %H:%M %Y","%Y-%m-%d %H:%M:%S.","%H:%M:%S", '%b %d %H:%M:%S', "JsIso"]
if format in allowed:
if format=="%Y%m%d%H%M":
dat=datetime.strptime( s, format )
Expand All @@ -346,6 +346,10 @@ def str2dtnaive(s, format):
if format=='%b %d %H:%M:%S': #Apr 26 07:50:44. Year is missing so I set to current
s=f"{date.today().year} {s}"
return datetime.strptime(s, '%Y %b %d %H:%M:%S')
if format=="JsIso": #2021-08-21T06:27:38.294
s=s.replace("T"," ")
dtnaive=str2dtnaive(s,"%Y-%m-%d %H:%M:%S.")
return dtnaive
else:
raise exceptions.CastException(_("I can't convert this format '{}'. I only support this {}").format(format, allowed))

Expand Down
206 changes: 113 additions & 93 deletions pydicts/myjsonencoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from decimal import Decimal
from json import JSONEncoder, dumps, loads
from base64 import b64encode, b64decode
from pydicts import casts

# Forma en que debe parsearse los Decimals
class DecimalsWay:
Expand Down Expand Up @@ -94,107 +95,133 @@ def MyJSONEncoderDecimalsAsString_dumps(r, indent=4):
def MyJSONEncoderDecimalsAsFloat_dumps(r, indent=4):
return dumps(r, cls=MyJSONEncoderDecimalsAsFloat, indent=indent)

def get_date(s):
try:
return date.fromisoformat(s)
except:
return None

def get_datetime(s):
try:
return datetime.fromisoformat(s)
except:
return None

# Parse the ISO8601 duration string as hours, minutes, seconds
def get_timedelta(str):
# try:
## https://stackoverflow.com/questions/36976138/is-there-an-easy-way-to-convert-iso-8601-duration-to-timedelta
## Parse the ISO8601 duration as years,months,weeks,days, hours,minutes,seconds
## Returns: milliseconds
## Examples: "PT1H30M15.460S", "P5DT4M", "P2WT3H"
def get_isosplit(str, split):
if split in str:
n, str = str.split(split, 1)
else:
n = '0'
return n.replace(',', '.'), str # to handle like "P0,5Y"

str = str.split('P', 1)[-1] # Remove prefix
s_yr, str = get_isosplit(str, 'Y') # Step through letter dividers
s_mo, str = get_isosplit(str, 'M')
s_wk, str = get_isosplit(str, 'W')
s_dy, str = get_isosplit(str, 'D')
_, str = get_isosplit(str, 'T')
s_hr, str = get_isosplit(str, 'H')
s_mi, str = get_isosplit(str, 'M')
s_sc, str = get_isosplit(str, 'S')
n_yr = float(s_yr) * 365 # approx days for year, month, week
n_mo = float(s_mo) * 30.4
n_wk = float(s_wk) * 7
dt = datetime.timedelta(days=n_yr+n_mo+n_wk+float(s_dy), hours=float(s_hr), minutes=float(s_mi), seconds=float(s_sc))
print(dt)
return int(dt.total_seconds()*1000) ## int(dt.total_seconds()) | dt
# except:
# return None

def get_time(s):
try:
if not ":" in s:
return None
return time.fromisoformat(s)
except:
return None

def get_bytes(s):
try:
return b64decode(s)
except:
return None

def get_Decimal(s):
try:
return eval(s)
except:
return None

def MyJSONEncoder_loads(s):
def hooks_MyJSONEncoder(iter_value):
return hooks(iter_value, DecimalsWay.Decimal)
##############################
return loads(s, object_hook=hooks_MyJSONEncoder)

def MyJSONEncoderDecimalsAsFloat_loads(s):

def hooks_MyJSONEncoderAsFloat(iter_value):
return hooks(iter_value, DecimalsWay.Float)
####################################
return loads(s, object_hook=hooks_MyJSONEncoderAsFloat)

def MyJSONEncoderDecimalsAsString_loads(s):
def hooks_MyJSONEncoderAsString(iter_value):
return hooks(iter_value, DecimalsWay.String)
######################################
return loads(s, object_hook=hooks_MyJSONEncoderAsString)

def guess_cast(o, decimal_way):
if decimal_way==DecimalsWay.Decimal:
r=get_Decimal(o)
if r is not None:
return r

r=get_date(o)
if r is not None:
return r

r=get_datetime(o)
if r is not None:
return r

r=get_time(o)
if r is not None:
return r

r=get_bytes(o)
if r is not None:
return r

return o

def hooks(iter_value, decimals_way):
"""
Iterates a dict or list to cast decimals and dtaware in json.loads using objeck_hook
"""

def get_date(s):
try:
return date.fromisoformat(s)
except:
return None

def get_dtaware(s):
try:
print(s, casts.str2dtaware(s,"JsUtcIso"))
return casts.str2dtaware(s,"JsUtcIso")
except:
return None

def get_dtnaive(s):
try:
print(s, casts.str2dtnaive(s,"JsIso"))
return casts.str2dtnaive(s,"JsIso")
except:
return None

# Parse the ISO8601 duration string as hours, minutes, seconds
def get_timedelta(str):
# try:
## https://stackoverflow.com/questions/36976138/is-there-an-easy-way-to-convert-iso-8601-duration-to-timedelta
## Parse the ISO8601 duration as years,months,weeks,days, hours,minutes,seconds
## Returns: milliseconds
## Examples: "PT1H30M15.460S", "P5DT4M", "P2WT3H"
def get_isosplit(str, split):
if split in str:
n, str = str.split(split, 1)
else:
n = '0'
return n.replace(',', '.'), str # to handle like "P0,5Y"

str = str.split('P', 1)[-1] # Remove prefix
s_yr, str = get_isosplit(str, 'Y') # Step through letter dividers
s_mo, str = get_isosplit(str, 'M')
s_wk, str = get_isosplit(str, 'W')
s_dy, str = get_isosplit(str, 'D')
_, str = get_isosplit(str, 'T')
s_hr, str = get_isosplit(str, 'H')
s_mi, str = get_isosplit(str, 'M')
s_sc, str = get_isosplit(str, 'S')
n_yr = float(s_yr) * 365 # approx days for year, month, week
n_mo = float(s_mo) * 30.4
n_wk = float(s_wk) * 7
dt = datetime.timedelta(days=n_yr+n_mo+n_wk+float(s_dy), hours=float(s_hr), minutes=float(s_mi), seconds=float(s_sc))
print(dt)
return int(dt.total_seconds()*1000) ## int(dt.total_seconds()) | dt
# except:
# return None

def get_time(s):
try:
if not ":" in s:
return None
return time.fromisoformat(s)
except:
return None

def get_bytes(s):
try:
return b64decode(s)
except:
return None

def get_Decimal(s):
try:
return eval(s)
except:
return None


def guess_cast(o, decimal_way):
if decimal_way==DecimalsWay.Decimal:
r=get_Decimal(o)
if r is not None:
return r

r=get_date(o)
if r is not None:
return r

r=get_dtnaive(o)
if r is not None:
return r

r=get_dtaware(o)
if r is not None:
return r

r=get_time(o)
if r is not None:
return r

r=get_bytes(o)
if r is not None:
return r

return o
########################################################
if isinstance(iter_value, dict):
for k, v in iter_value.items():
if isinstance(v, dict):
Expand All @@ -209,12 +236,5 @@ def hooks(iter_value, decimals_way):
i=hooks(i, decimals_way)
return iter_value

def hooks_MyJSONEncoder(iter_value):
return hooks(iter_value, DecimalsWay.Decimal)

def hooks_MyJSONEncoderAsFloat(iter_value):
return hooks(iter_value, DecimalsWay.Float)

def hooks_MyJSONEncoderAsString(iter_value):
return hooks(iter_value, DecimalsWay.String)

3 changes: 2 additions & 1 deletion pydicts/tests/test_casts.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ def test_str2dtnaive():
# assert casts.str2dtnaive("2023-11-26 17:05:05", "%Y-%m-%d %H:%M:%S")==datetime(2023, 11, 26, 17, 5, 5)
# assert casts.str2dtnaive("20231126 1705", "%Y%m%d %H%M")==datetime(2023, 11, 26, 17, 5, 5)
assert casts.str2dtnaive("202311261705", "%Y%m%d%H%M")==datetime(2023, 11, 26, 17, 5)
# assert casts.str2dtnaive("2023-11-26T17:05:05Z", "JsUtcIso")==datetime(2023, 11, 26, 17, 5, 5)
assert casts.str2dtnaive("2023-11-26T17:05:05.123456", "JsIso")==datetime(2023, 11, 26, 17, 5, 5, 123456)
assert casts.str2dtnaive("2023-11-26T17:05:05", "JsIso")==datetime(2023, 11, 26, 17, 5, 5)


def test_str2dtaware():
Expand Down
5 changes: 3 additions & 2 deletions pydicts/tests/test_myjsonencoder.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from decimal import Decimal
from datetime import timezone, date, datetime, time, timedelta
from datetime import date, datetime, time, timedelta
from zoneinfo import ZoneInfo
from pydicts import myjsonencoder
d={}
d["None"]=None
Expand All @@ -9,7 +10,7 @@
d["Datetime"]=datetime.now()
d["Timedelta"]=timedelta(hours=4, days=2, minutes=12, seconds=12)
d["Time"]=time(12, 12, 12, 123456)
d["Datetime aware"]=d["Datetime"].replace(tzinfo=timezone.utc)
d["Datetime aware"]=d["Datetime"].replace(tzinfo=ZoneInfo("UTC"))
d["Bytes"]=b"Byte array"
d["Decimal"]=Decimal("12.12123414")

Expand Down

0 comments on commit d97789d

Please sign in to comment.