|
import re
|
|
import uuid
|
|
import json
|
|
from datetime import datetime , timezone
|
|
from collections import OrderedDict
|
|
import copy
|
|
|
|
TIMESTAMP_REGEX = r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}\+\d{2}:\d{2}$"
|
|
|
|
def now():
|
|
return str(datetime.now(timezone.utc))
|
|
|
|
def add_attrs(**kwargs):
|
|
def decorator(func):
|
|
for key, value in kwargs.items():
|
|
setattr(func, key, value)
|
|
|
|
return func
|
|
return decorator
|
|
|
|
def validate_attrs(required=False,min_length=None,max_length=None,regex=None,**kwargs):
|
|
def decorator(func):
|
|
return add_attrs(required=required,min_length=min_length,max_length=max_length,regex=regex,**kwargs)(func)
|
|
|
|
class Validator:
|
|
|
|
@property
|
|
def value(self):
|
|
return self._value
|
|
|
|
@value.setter
|
|
def value(self,val):
|
|
self._value = json.loads(json.dumps(val,default=str))
|
|
|
|
@property
|
|
def initial_value(self):
|
|
return None
|
|
|
|
def custom_validation(self):
|
|
return True
|
|
|
|
def __init__(self,required=False,min_num=None,max_num=None,min_length=None,max_length=None,regex=None,value=None,kind=None,help_text=None,**kwargs):
|
|
self.required = required
|
|
self.min_num = min_num
|
|
self.max_num = max_num
|
|
self.min_length = min_length
|
|
self.max_length = max_length
|
|
self.regex = regex
|
|
self._value = None
|
|
self.value = value
|
|
self.type = kind
|
|
self.help_text = help_text
|
|
self.__dict__.update(kwargs)
|
|
@property
|
|
def errors(self):
|
|
error_list = []
|
|
if self.value is None and self.required:
|
|
error_list.append("Field is required.")
|
|
return error_list
|
|
|
|
if self.value is None:
|
|
return error_list
|
|
|
|
if self.type == float or self.type == int:
|
|
if self.min_num is not None and self.value < self.min_num:
|
|
error_list.append("Field should be minimal {}.".format(self.min_num))
|
|
if self.max_num is not None and self.value > self.max_num:
|
|
error_list.append("Field should be maximal {}.".format(self.max_num))
|
|
if self.min_length is not None and len(self.value) < self.min_length:
|
|
error_list.append("Field should be minimal {} characters long.".format(self.min_length))
|
|
if self.max_length is not None and len(self.value) > self.max_length:
|
|
error_list.append("Field should be maximal {} characters long.".format(self.max_length))
|
|
if not self.regex is None and not self.value is None and not re.match(self.regex, self.value):
|
|
error_list.append("Invalid value.".format(self.regex))
|
|
if not self.type is None and type(self.value) != self.type:
|
|
error_list.append("Invalid type. It is supposed to be {}.".format(self.type))
|
|
return error_list
|
|
|
|
def validate(self):
|
|
if self.errors:
|
|
raise ValueError("\n", self.errors)
|
|
return True
|
|
|
|
@property
|
|
def is_valid(self):
|
|
try:
|
|
self.validate()
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
def to_json(self):
|
|
return {
|
|
"required": self.required,
|
|
"min_num": self.min_num,
|
|
"max_num": self.max_num,
|
|
"min_length": self.min_length,
|
|
"max_length": self.max_length,
|
|
"regex": self.regex,
|
|
"value": self.value,
|
|
"type": self.type,
|
|
"help_text": self.help_text,
|
|
"errors": self.errors,
|
|
"is_valid": self.is_valid
|
|
}
|
|
|
|
class ModelField(Validator):
|
|
def __init__(self,name=None,save=True, *args, **kwargs):
|
|
self.name = name
|
|
|
|
self.save = save
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
class CreatedField(ModelField):
|
|
|
|
@property
|
|
def initial_value(self):
|
|
return now()
|
|
|
|
def update(self):
|
|
if not self.value:
|
|
self.value = now()
|
|
|
|
class UpdatedField(ModelField):
|
|
|
|
def update(self):
|
|
self.value = now()
|
|
|
|
class DeletedField(ModelField):
|
|
|
|
def update(self):
|
|
self.value = now()
|
|
|
|
class UUIDField(ModelField):
|
|
|
|
@property
|
|
def initial_value(self):
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
class BaseModel:
|
|
|
|
uid = UUIDField(name="uid",required=True)
|
|
created_at = CreatedField(name="created_at",required=True, regex=TIMESTAMP_REGEX, place_holder="Created at")
|
|
updated_at = UpdatedField(name="updated_at",regex=TIMESTAMP_REGEX,place_holder="Updated at")
|
|
deleted_at = DeletedField(name="deleted_at",regex=TIMESTAMP_REGEX, place_holder="Deleted at")
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
print(self.__dict__)
|
|
print(dir(self.__class__))
|
|
for key in dir(self.__class__):
|
|
obj = getattr(self.__class__,key)
|
|
|
|
if isinstance(obj,Validator):
|
|
self.__dict__[key] = copy.deepcopy(obj)
|
|
print("JAAA")
|
|
self.__dict__[key].value = kwargs.pop(key,self.__dict__[key].initial_value)
|
|
|
|
def __setitem__(self, key, value):
|
|
obj = self.__dict__.get(key)
|
|
if isinstance(obj,Validator):
|
|
obj.value = value
|
|
|
|
def __getattr__(self, key):
|
|
obj = self.__dict__.get(key)
|
|
if isinstance(obj,Validator):
|
|
print("HPAPP")
|
|
return obj.value
|
|
return obj
|
|
|
|
|
|
def __getitem__(self, key):
|
|
obj = self.__dict__.get(key)
|
|
if isinstance(obj,Validator):
|
|
return obj.value
|
|
|
|
def __setattr__(self, key, value):
|
|
obj = getattr(self,key)
|
|
if isinstance(obj,Validator):
|
|
obj.value = value
|
|
else:
|
|
setattr(self,key,value)
|
|
#def __getattr__(self, key):
|
|
# obj = self.__dict__.get(key)
|
|
# if isinstance(obj,Validator):
|
|
# return obj.value
|
|
@property
|
|
def record(self):
|
|
obj = self.to_json()
|
|
record = {}
|
|
for key,value in obj.items():
|
|
if getattr(self,key).save:
|
|
record[key] = value.get('value')
|
|
return record
|
|
|
|
def to_json(self,encode=False):
|
|
model_data = OrderedDict({
|
|
"uid": self.uid.value,
|
|
"created_at": self.created_at.value,
|
|
"updated_at": self.updated_at.value,
|
|
"deleted_at": self.deleted_at.value
|
|
})
|
|
for key,value in self.__dict__.items():
|
|
if key == "record":
|
|
continue
|
|
value = self.__dict__[key]
|
|
if hasattr(value,"value"):
|
|
model_data[key] = value.to_json()
|
|
if encode:
|
|
return json.dumps(model_data,indent=2)
|
|
return model_data
|
|
|
|
class FormElement(ModelField):
|
|
|
|
def __init__(self, place_holder=None, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.place_holder = place_holder
|
|
|
|
|
|
|
|
class FormElement(ModelField):
|
|
|
|
def __init__(self,place_holder=None, *args, **kwargs):
|
|
self.place_holder = place_holder
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
def to_json(self):
|
|
data = super().to_json()
|
|
data["name"] = self.name
|
|
data["place_holder"] = self.place_holder
|
|
return data
|
|
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
first_name = FormElement(name="first_name",required=True,min_length=3,max_length=20,regex=r"^[a-zA-Z0-9_]+$",place_holder="First name")
|
|
last_name = FormElement(name="last_name",required=True,min_length=3,max_length=20,regex=r"^[a-zA-Z0-9_]+$",place_holder="Last name")
|
|
email = FormElement(name="email",required=True,regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",place_holder="Email address")
|
|
password = FormElement(name="password",required=True,regex=r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$",place_holder="Password")
|
|
|
|
class Form:
|
|
username = FormElement(required=True,min_length=3,max_length=20,regex=r"^[a-zA-Z0-9_]+$",place_holder="Username")
|
|
email = FormElement(required=True,regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",place_holder="Email address")
|
|
def __init__(self, *args, **kwargs):
|
|
self.place_holder = kwargs.pop("place_holder",None)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
model = TestModel(first_name="John",last_name="Doe",email="n9K9p@example.com",password="Password123")
|
|
model2 = TestModel(first_name="John",last_name="Doe",email="ddd",password="zzz")
|
|
model.first_name = "AAA"
|
|
print(model.first_name)
|
|
print(model.first_name.value)
|
|
|
|
print(model.first_name)
|
|
print(model.first_name.value)
|
|
print(model.to_json(True))
|
|
print(model2.to_json(True))
|
|
print(model2.record)
|