forked from retoor/snek
feat: add initial project structure with snek chat server and registration form
Set up the Python project skeleton including pyproject.toml, setup.cfg, Makefile, .gitignore, and README. Implement the core snek application with aiohttp-based web server, model validation framework, and a registration form with username, email, and password fields.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
from app.app import Application as BaseApplication
|
||||
from snek.forms import RegisterForm
|
||||
from aiohttp import web
|
||||
|
||||
class Application(BaseApplication):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.router.add_get("/register", self.handle_register)
|
||||
self.router.add_post("/register", self.handle_register)
|
||||
|
||||
async def handle_register(self, request):
|
||||
if request.method == "GET":
|
||||
return web.json_response({"form": RegisterForm().to_json()})
|
||||
elif request.method == "POST":
|
||||
return self.render("register.html")
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = Application()
|
||||
web.run_app(app,port=8081,host="0.0.0.0")
|
||||
@@ -0,0 +1,40 @@
|
||||
from snek import models
|
||||
|
||||
class FormElement(models.ModelField):
|
||||
|
||||
def __init__(self,html_type, place_holder=None, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.place_holder = place_holder
|
||||
self.html_type = html_type
|
||||
|
||||
def to_json(self):
|
||||
data = super().to_json()
|
||||
data["html_type"] = self.html_type
|
||||
data["place_holder"] = self.place_holder
|
||||
return data
|
||||
|
||||
class Form(models.BaseModel):
|
||||
pass
|
||||
|
||||
class RegisterForm(Form):
|
||||
|
||||
username = FormElement(
|
||||
name="username",
|
||||
required=True,
|
||||
min_length=2,
|
||||
max_length=20,
|
||||
regex=r"^[a-zA-Z0-9_]+$",
|
||||
place_holder="Username",
|
||||
html_type="text"
|
||||
)
|
||||
email = FormElement(
|
||||
name="email",
|
||||
required=True,
|
||||
regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
|
||||
place_holder="Email address",
|
||||
html_type="email"
|
||||
)
|
||||
password = FormElement(name="password",required=True,regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",html_type="password")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user