feat(pets): adds register view and validations wip

This commit is contained in:
2024-12-08 23:07:45 -03:00
parent ff9d94bb72
commit a41a929ca5
8 changed files with 309 additions and 13 deletions

View File

@@ -1,6 +1,7 @@
from flask import request, render_template
from app.pets import bp
from app.services.pet_service import PetService
from app.utils.helpers import login_required
@bp.route('/')
def index():
@@ -8,4 +9,11 @@ def index():
"pets/index.html",
options=PetService.get_options(request),
pagination_result=PetService.get_pets(request)
)
)
@bp.route('register', methods=["GET", "POST"])
def register():
if request.method == 'POST':
PetService.register_pet(request)
types = PetService.get_pets_kind()
return render_template("pets/register.html", types=types)

View File

@@ -1,22 +1,34 @@
from flask import Request
from flask import flash, Request
from app.extensions import db
from app.models.pet import Pet
from app.models.pet_kind import PetKind
from app.utils.alert_type import AlertType
from app.utils.errors.pets.pet_register_errors import PetRegisterError
from app.utils.flash_message import FlashMessage
from app.utils.helpers import pet_sex_id_to_str
from app.utils.validators.pet_validators import PetValidators
class PetService:
@staticmethod
def get_pets(request: Request,results_per_page=8):
def sex_n_to_str(sex: str):
if sex == '1':
return 'F'
elif sex == '2':
return 'M'
else:
return None
def register_pet(request: Request):
"""Validations"""
try:
name = PetValidators.is_valid_name(request.form.get('name'))
age = PetValidators.is_valid_age(request.form.get('age'))
sex = PetValidators.is_valid_sex(request.form.get('sex'))
heigth = PetValidators.is_valid_height(request.form.get('height'))
weight = PetValidators.is_valid_weight(request.form.get('weight'))
kind = PetValidators.is_valid_type(request.form.get('type'))
location = PetValidators.is_valid_location(request.form.get('location'))
"""uploading image to cloudinary"""
"""saving the data in the db"""
except PetRegisterError as e:
flash(FlashMessage(e.message, AlertType.DANGER.value ))
@staticmethod
def get_pets(request: Request,results_per_page=8):
type = request.args.get('type')
sex = sex_n_to_str(request.args.get('sex'))
sex = pet_sex_id_to_str(request.args.get('sex'))
age_from = request.args.get('age-from')
age_to = request.args.get('age-to')
query = db.select(Pet)
@@ -61,6 +73,10 @@ class PetService:
options["age_from"] = PetService.get_age_from_options(age_from)
return options
@staticmethod
def get_pets_kind():
return db.session.execute(db.select(PetKind)).scalars()
@staticmethod
def get_pets_kind_options(selected_kind=None):
pet_kinds = db.session.execute(db.select(PetKind)).scalars()

View File

@@ -0,0 +1,12 @@
.pets-register {
max-width: 480px;
}
.pets-register__form-img-container {
max-height: 150px;
max-width: 150px;
overflow: hidden;
}
.pets-register__form-img:hover {
cursor: pointer;
}

View File

@@ -0,0 +1,30 @@
(() => {
'use strict'
function readImage(file) {
if (!file.type || !file.type.match('image\/(png|jpeg|jpg|webp)')) {
alert("Invalid image format!");
return;
}
if(file.size > 2097152) {
alert("File is too big!");
return;
}
const img = document.querySelector('#img-card');
const reader = new FileReader();
reader.addEventListener('load', (event) => {
const imgObj = new Image();
imgObj.src = event.target.result;
imgObj.onload = function(ev) {
if(ev.target.naturalWidth > 512 || ev.target.naturalHeight > 512) {
alert("Image max dimesions allowed is 512x512");
return;
}
img.src = event.target.result;
}
});
reader.readAsDataURL(file);
}
document.querySelector('#img-selector')
.addEventListener('change', function(ev){readImage(ev.target.files[0])})
})()

View File

@@ -0,0 +1,152 @@
{% extends "layout/layout.html" %}
{% from "forms/submit-btn.html" import form_submit_button %}
{% from "layout/inner_header.html" import inner_header%}
{% block title %} Home {% endblock %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename='css/pets-register.css') }}">
{% endblock %}
{% block content %}
<div class="container">
<div class="pets-register mx-auto my-auto">
<div class="my-3">
{{ inner_header("Pet Details") }}
</div>
<div class="row px-2">
{% include 'message.html' %}
</div>
<div class="row mt-2">
<form class="needs-validation" enctype="multipart/form-data" method="post" novalidate>
<div class="row">
<input
accept="image/png, image/jpeg, image/webp"
class="d-none"
id="img-selector"
name="img"
type="file"
/>
<div class="col-12 col-sm-4 mb-3 mb-sm-0">
<div class="card pets-register__form-img-container">
<label for="img-selector">
<img
alt="pet image"
class="d-block pets-register__form-img"
height="150"
id="img-card"
src="{{ url_for('static', filename='img/pet-placeholder.webp') }}"
title="Max file size 2MB, max dimensions 512x512"
width="150"
>
</label>
</div>
</div>
<div class="col-12 col-sm-8">
<div class="row mb-3">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="pet-name-wrapping">Name</span>
<input
aria-describedby="pet-name-wrapping"
aria-label="Pet's name"
class="form-control"
name="name"
pattern="[A-Za-z ]{2,255}"
placeholder="Pet's name"
type="text"
required
>
</div>
</div>
<div class="row mb-3">
<div class="col-6">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="pet-age-wrapping">Age</span>
<input
aria-describedby="pet-age-wrapping"
aria-label="Pet's age"
class="form-control"
name="age"
pattern="^[0-9]+$"
placeholder="yrs"
required
>
</div>
</div>
<div class="col-6">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="pet-sex-wrapping">Sex</span>
<select aria-label="type select field for pet's sex" class="form-select" name="sex">
<option value="1">F</option>
<option value="2">M</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="pet-height-wrapping">height</span>
<input
aria-describedby="pet-height-wrapping"
aria-label="Pet's height"
class="form-control"
name="height"
pattern="^\d+[\.]?[\d]*$"
placeholder="ft"
>
</div>
</div>
<div class="col-6">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="pet-weight-wrapping">Weight</span>
<input
aria-describedby="pet-weight-wrapping"
aria-label="Pet's weight"
class="form-control"
name="weight"
pattern="^\d+[\.]?[\d]*$"
placeholder="lbs"
>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-3 ps-sm-0">
<div class="col-5">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="pet-type-wrapping">Type</span>
<select aria-label="type select field" class="form-select" name="type">
{% for type in types %}
<option value="{{type.id}}">{{type.name}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-7">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="pet-location-wrapping">Location</span>
<input
aria-describedby="pet-location-wrapping"
aria-label="Pet's location"
class="form-control"
name="location"
pattern="[A-Za-z ]{2,255}"
placeholder="Pet's location"
type="text"
required
>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12 d-flex justify-content-end">
{{ form_submit_button("Submit") }}
</div>
</div>
</form>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/validate-form.js') }}"></script>
<script src="{{ url_for('static', filename='js/pet-form-img-updater.js') }}"></script>
{% endblock %}

View File

@@ -0,0 +1,4 @@
class PetRegisterError(Exception):
def __init__(self, message):
self.message = message
super().__init__(self.message)

View File

@@ -15,3 +15,11 @@ def only_guests(html: str):
if not session["id"]:
return html
return None
def pet_sex_id_to_str(sex: str):
if sex == '1':
return 'F'
elif sex == '2':
return 'M'
else:
return None

View File

@@ -0,0 +1,66 @@
from app.extensions import db
from app.models.pet_kind import PetKind
from app.utils.errors.pets.pet_register_errors import PetRegisterError
from app.utils.helpers import pet_sex_id_to_str
from app.utils.validators.validators import Validators
class PetValidators:
@staticmethod
def is_valid_name(name: str|None):
if Validators.is_valid_str_and_pattern(name,'^[A-Za-z ]{2,255}$'):
return name
raise PetRegisterError("Invalid pet name")
@staticmethod
def is_valid_age(age: str|None):
try:
if int(age) >= 0:
return age
except:
raise PetRegisterError("Invalid age")
raise PetRegisterError("Invalid age")
@staticmethod
def is_valid_weight(w: str|None):
if w is None:
return None
if Validators.is_valid_decimal(w) and float(w) >= 0:
return w
raise PetRegisterError("Invalid pet weight")
@staticmethod
def is_valid_height(h: str|None):
if h is None:
return None
if Validators.is_valid_decimal(h) and float(h) >= 0:
return h
raise PetRegisterError("Invalid pet height")
@staticmethod
def is_valid_sex(sex: str|None):
pet_sex = pet_sex_id_to_str(sex)
if pet_sex is None:
raise PetRegisterError("Invalid pet sex")
return pet_sex
@staticmethod
def is_valid_type(pet_type: str|None):
INVALID_MSG = "Invalid pet type"
if pet_type is None:
raise PetRegisterError(INVALID_MSG)
types = db.session.execute(db.select(PetKind)).scalars()
try:
for type in types:
if int(pet_type) == type.id:
return pet_type
except:
raise PetRegisterError(INVALID_MSG)
raise PetRegisterError(INVALID_MSG)
@staticmethod
def is_valid_location(location: str|None):
if location is not None:
if Validators.is_valid_str_and_pattern(location,'^[A-Za-z ]{2,255}$'):
return location
raise PetRegisterError("Invalid location")