diff --git a/Pipfile b/Pipfile index 630ad9874f..a8598b45df 100644 --- a/Pipfile +++ b/Pipfile @@ -22,6 +22,8 @@ flask-jwt-extended = "*" wtforms = "==3.1.2" flask-bcrypt = "*" flask-mail = "*" +unicode = "*" +unidecode = "*" [requires] python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock index dbf8d0dae4..8caaf9a468 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "622091408571d30ecefdd6f7a96bce85eef0b10b8468f5110c00adb6d86cfdf4" + "sha256": "995b2d788a368b8f99dcf586201ccac6832bfda4599eaf935ecff47ed18a38f2" }, "pipfile-spec": 6, "requires": { @@ -107,11 +107,11 @@ }, "cloudinary": { "hashes": [ - "sha256:ba223705409b2aaddd5196c2184d65f50a83dffcba3b94f3727658ff6a0172a3", - "sha256:e4191b470c5bae55542b64e0a78659af42971880294456dca480bc974fa9280a" + "sha256:cdfe53473fd9564cfb3ff99f93fc6d3a1928f81e7e81920d1b3373bc9768cbd4", + "sha256:d2b4ce247e0fff558fd8326df5db7c94bce15521c5ff53edafbe1bb2e8128cef" ], "index": "pypi", - "version": "==1.42.2" + "version": "==1.43.0" }, "flask": { "hashes": [ @@ -583,6 +583,22 @@ "index": "pypi", "version": "==4.12.2" }, + "unicode": { + "hashes": [ + "sha256:39d67799f4795823122192a1f8dd58d476bf8b80c863fb7d530ad990dd2ebc9a", + "sha256:42b6c3b953dce84a25bc9e5d11942c7d34b4d70d8e2d54b455c074770eb753b2" + ], + "index": "pypi", + "version": "==2.9" + }, + "unidecode": { + "hashes": [ + "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", + "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39" + ], + "index": "pypi", + "version": "==1.3.8" + }, "urllib3": { "hashes": [ "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000000..0e04844159 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 0000000000..ec9d45c26a --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,50 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic,flask_migrate + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000000..4c9709271b --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,113 @@ +import logging +from logging.config import fileConfig + +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + + +def get_engine(): + try: + # this works with Flask-SQLAlchemy<3 and Alchemical + return current_app.extensions['migrate'].db.get_engine() + except (TypeError, AttributeError): + # this works with Flask-SQLAlchemy>=3 + return current_app.extensions['migrate'].db.engine + + +def get_engine_url(): + try: + return get_engine().url.render_as_string(hide_password=False).replace( + '%', '%%') + except AttributeError: + return str(get_engine().url).replace('%', '%%') + + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option('sqlalchemy.url', get_engine_url()) +target_db = current_app.extensions['migrate'].db + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def get_metadata(): + if hasattr(target_db, 'metadatas'): + return target_db.metadatas[None] + return target_db.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=get_metadata(), literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + conf_args = current_app.extensions['migrate'].configure_args + if conf_args.get("process_revision_directives") is None: + conf_args["process_revision_directives"] = process_revision_directives + + connectable = get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=get_metadata(), + **conf_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000000..2c0156303a --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/6c3ebc20c297_.py b/migrations/versions/6c3ebc20c297_.py new file mode 100644 index 0000000000..658f07c95d --- /dev/null +++ b/migrations/versions/6c3ebc20c297_.py @@ -0,0 +1,96 @@ +"""empty message + +Revision ID: 6c3ebc20c297 +Revises: +Create Date: 2025-03-18 23:08:42.843785 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6c3ebc20c297' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('accessories', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('brand', sa.String(length=100), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('animal_type', sa.String(length=50), nullable=False), + sa.Column('pathologies', sa.Text(), nullable=True), + sa.Column('price', sa.Float(), nullable=False), + sa.Column('url', sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('foods', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('brand', sa.String(length=100), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('ingredients', sa.Text(), nullable=False), + sa.Column('weight', sa.Float(), nullable=False), + sa.Column('price', sa.Float(), nullable=False), + sa.Column('age', sa.String(), nullable=False), + sa.Column('animal_type', sa.String(length=50), nullable=False), + sa.Column('size', sa.String(length=30), nullable=True), + sa.Column('pathologies', sa.Text(), nullable=True), + sa.Column('url', sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('password_reset', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('email', sa.String(length=120), nullable=False), + sa.Column('token', sa.String(length=255), nullable=False), + sa.Column('expires_at', sa.DateTime(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('token') + ) + op.create_table('user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=80), nullable=False), + sa.Column('email', sa.String(length=120), nullable=False), + sa.Column('password', sa.String(length=80), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + op.create_table('order', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('ordered_food', sa.String(), nullable=True), + sa.Column('ordered_accessories', sa.String(), nullable=True), + sa.Column('status', sa.String(), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('pet', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('size', sa.String(length=100), nullable=True), + sa.Column('breed', sa.String(length=100), nullable=True), + sa.Column('age', sa.String(), nullable=False), + sa.Column('animal_type', sa.String(), nullable=False), + sa.Column('pathologies', sa.Text(), nullable=True), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('url', sa.String(length=255), nullable=True), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('pet') + op.drop_table('order') + op.drop_table('user') + op.drop_table('password_reset') + op.drop_table('foods') + op.drop_table('accessories') + # ### end Alembic commands ### diff --git a/src/api/commands.py b/src/api/commands.py index 3023075155..1e6cd874d7 100644 --- a/src/api/commands.py +++ b/src/api/commands.py @@ -62,7 +62,7 @@ def insert_data_catfood(): catfood.price = 6.99 catfood.animal_type = "gato" catfood.age = "adulto" - catfood.pathologies = "diabético" + catfood.pathologies = "diabetes" catfood.url = "https://bixoto.com/media/catalog/product/cache/2e45ba69d70625e0fc47dbc2d34862e1/M/Y/MYNKvuY-920-2042_webp.png" db.session.add(catfood) db.session.commit() @@ -76,7 +76,7 @@ def insert_data_catfood(): catfood.price = 10.70 catfood.animal_type = "gato" catfood.age = "cachorro" - catfood.pathologies = "" + catfood.pathologies = "ninguna" catfood.url = "https://www.tiendanimal.es/dw/image/v2/BDLQ_PRD/on/demandware.static/-/Sites-kiwoko-master-catalog/default/dw55de3cf2/images/ownat_prime_gf_kitten_OWN31463.jpg?sw=780&sh=780&sm=fit&q=85" db.session.add(catfood) db.session.commit() @@ -123,7 +123,7 @@ def insert_data_food(): dogfood.pathologies = "renal" dogfood.animal_type = "perro" dogfood.age = "senior" - dogfood.size = "medium" + dogfood.size = "medio" dogfood.weight = 1. dogfood.url = "https://media.zooplus.com/bilder/9/400/cfl_dog_renal_1kg_1000x1000_9.jpg" db.session.add(dogfood) @@ -154,7 +154,7 @@ def insert_data_food(): dogfood.pathologies = "diabetes" dogfood.animal_type = "perro" dogfood.age = "adulto" - dogfood.size = "" + dogfood.size = "medio" dogfood.weight = 1.5 dogfood.url = "https://www.tiendanimal.es/dw/image/v2/BDLQ_PRD/on/demandware.static/-/Sites-kiwoko-master-catalog/default/dw6af74272/images/virbac_pienso_perros_hpm_weight_loss_diabetes_VIRAD360106.jpg?sw=780&sh=780&sm=fit&q=85" db.session.add(dogfood) @@ -166,7 +166,7 @@ def insert_data_food(): dogfood.description = "Briantos ofrece el alimento adecuado para todas las necesidades. Nuestras recetas sin cereales cuentan con una alta tolerancia entre los perros. El mayor contenido de humedad convence incluso a los perros más quisquillosos. La alta calidad de los ingredientes utilizados en la elaboración de este alimento y todos los controles de calidad durante la producción garantizan el mejor producto para tu perro." dogfood.ingredients = "5 por ciento de proteínas de pavo (deshidratadas), (22 %) de patatas (deshidratadas), fécula de patata (deshidratada), celulosa, 5 % de boniatos (deshidratados), pulpa de remolacha deshidratada (desazucarada), grasa de ave, proteínas de ave (deshidratadas), hidrolizado de proteínas, guisantes (deshidratados), fosfato monocálcico, aceite de pescado, achicoria (deshidratada), levadura (deshidratada), psilio, cloruro sódico, apio (deshidratado), zanahorias (deshidratadas)." dogfood.price = 33.99 - dogfood.pathologies = "esterilizado" + dogfood.pathologies = "ninguna" dogfood.animal_type = "perro" dogfood.age = "adulto" dogfood.size = "grande" @@ -181,19 +181,16 @@ def insert_data_food(): dogfood.description = "Un mayor contenido de proteínas combinadas con la L-carnitina, así como un ajustado contenido energético y en grasas (-11 % en comparación con Concept for Life Mini Adult) puede influir ayudar a mantener el peso ideal mediante una alimentación adecuada. De este modo, se puede evitar el aumento de peso causado por la alteración del metabolismo tras la castración. Además, las fibras dietéticas seleccionadas de psilio y lignocelulosa proporcionan una sensación de saciedad, lo que puede favorecer una ingesta menor de energía." dogfood.ingredients = "proteína de ave (31 % parcialmente deshidratada e hidrolizada), carne de pollo fresca (20 %), copos de patata, guisantes, fécula de patata, lignocelulosa (5,4 %), pulpa de remolacha seca, grasa de ave, linaza, pulpa de manzana, aceite de salmón (0,25 %), cloruro de sodio, aceite de girasol (0,15 %), inulina de achicoria (0,2 %), psilio (0,1 %)." dogfood.price = 5.49 - dogfood.pathologies = "esterilizado" + dogfood.pathologies = "ninguna" dogfood.animal_type = "perro" dogfood.age = "adulto" - dogfood.size = "pequeño" + dogfood.size = "pequeña" dogfood.weight = 1. dogfood.url = "https://media.zooplus.com/bilder/7/400/pla_163496_cfl_dog_sterilised_mini_1kg_7.jpg" db.session.add(dogfood) db.session.commit() - - - @app.cli.command("insert_data_exoticfood") def insert_data_exoticfood(): exoticfood = Food() @@ -202,8 +199,8 @@ def insert_data_exoticfood(): exoticfood.description = "bunny KaninchenTraum Special Edition Harmony es el alimento ideal para tu conejo enano. Su mezcla única de 63 plantas y hierbas de praderas no tratadas ofrece una alimentación natural y variada. Los ingredientes cuidadosamente seleccionados, sobre todo las flores de aciano y siempreviva, garantizan una experiencia aromática y llena sabor que le encantará a tu amiguito." exoticfood.ingredients = "Hierbas de pasto permanente (fleo de los prados, festuca de los prados, festuca roja, hierba de cola de zorra, pasto azul de Kentucky, dactilo, perifollo silvestre, falso diente de león, arveja de campo, milenrama común, alquimila, cerrillo, trébol amarillo, llantén menor, salvia de los prados, briza media, bromo erguido, campanilla silvestre, alcaravea, cártamo silvestre, cerastio común, cardo borriquero, bromo blando, diente de león común, hierba capilar, ulmaria, cerastio de campo, fresa silvestre, arveja silvestre, gallineta blanca, crepis de los prados, galio amarillo, avénula pelosa, lotus común, cola de caballo, galio blanco, geranio blanco de campo, margarita de campo, viuda silvestre, flor de cuclillo, arroyuela, verónica de los campos, hierba triguera, poa de los prados, llantén mediano, pimpinela mayor, alverjilla, nomeolvides silvestre, saxifraga blanca, pimpinela mayor, cincoenrama, primavera común, conejera cabizbaja, pimpinela menor, estelaria, barba de cabra, ortiga mayor, ontineta, trébol rojo, arveja de los setos, búgula de Ginebra, egopodio, margarita), cáscaras de avena, harina de extracción de semilla de girasol, harina de extracción de lino, pulpa de manzana, bagazo de zanahoria, salvado de trigo, harina de extracción de semillas de colza, flor de aciano rojo (2 %), flores de siempreviva (1 %), semillas de hinojo (0,5 %), papaya deshidratada, flores de camomila." exoticfood.price = 14.69 - exoticfood.pathologies = "" - exoticfood.animal_type = "exótico" + exoticfood.pathologies = "ninguna" + exoticfood.animal_type = "exotico" exoticfood.age = "adulto" exoticfood.weight = 1.5 exoticfood.url = "https://media.zooplus.com/bilder/7/400/547507_pla_bunny_kaninchentraum_special_edition_harmony_1_5k_hs_01_7.jpg" @@ -216,8 +213,8 @@ def insert_data_exoticfood(): exoticfood.description = "Fórmula todo en uno: la comida beaphar Care+ está constituida por gránulos de composición uniforme. Esto permite evitar las carencias nutricionales que se pueden dar por la alimentación selectiva de tu conejo. Su fórmula equilibrada y sin azúcar ha sido elaborada por veterinarios y científicos expertos. Contiene un 25 % de extruido de fibra cruda y una cantidad elevada de nutrientes y otras sustancias vitales. La dureza de los gránulos favorece la abrasión dental y la higiene bucal." exoticfood.price = 26.49 exoticfood.ingredients = "Subproductos vegetales (Yucca Schidigera 0,1 %, Echinacea 0,03 %, extracto de té verde 0,03 %, FOS 0,01 %) , cereales, verduras, minerales, levadura (MOS 0,1 %), semillas, algas (espirulina 0,01 %)." - exoticfood.pathologies = "diabético" - exoticfood.animal_type = "exótico" + exoticfood.pathologies = "diabetes" + exoticfood.animal_type = "exotico" exoticfood.age = "senior" exoticfood.weight = 5. exoticfood.url = "https://media.zooplus.com/bilder/0/400/73777_pla_beaphar_care_kaninchen_5kg_hs_1_2_0.jpg" @@ -230,8 +227,8 @@ def insert_data_exoticfood(): exoticfood.description = "Alimento variado y rico en fibra como base para el crecimiento óptimo de los conejos y conejos enanos hasta los 8 meses de edad. Nature Cuni Junior contiene una gran cantidad de hierbas, semillas y hierbas aromáticas sabrosas, combinadas con verduras y frutos que suministran altas dosis de vitaminas, minerales y oligoelementos. Sin cereales añadidos." exoticfood.price = 9.99 exoticfood.ingredients = "Subproductos vegetales (hierba timotea, 20 % hierbas y hierbas aromáticas), verdura (18,1 % guisantes, 5,4 % zanahorias, 4 % frijol chícharo, 4 % remolacha), frutos, semillas, extracto de proteína vegetal, minerales, aceites y grasas, FOS, caléndula, MOS, algas, yuca." - exoticfood.pathologies = "" - exoticfood.animal_type = "exótico" + exoticfood.pathologies = "ninguna" + exoticfood.animal_type = "exotico" exoticfood.age = "cachorro" exoticfood.weight = 2.3 exoticfood.url = "https://d7rh5s3nxmpy4.cloudfront.net/CMP9365/8/18407_347074-D.jpg" @@ -245,7 +242,7 @@ def insert_data_exoticfood(): exoticfood.price = 8.69 exoticfood.ingredients = "Fleo, dáctilo, hierbas, perejil, copos de guisantes, dados de zanahoria, habas, chirivía, hojas de menta, hinojo, dados de manzana, semilla de lino, hojas de diente de león, remolacha, hojas de ortiga, flor de manzanilla, comino y vitaminas." exoticfood.pathologies = "escorbuto" - exoticfood.animal_type = "exótico" + exoticfood.animal_type = "exotico" exoticfood.age = "senior" exoticfood.weight = 2.3 exoticfood.url = "https://media.zooplus.com/bilder/5/400/28140_pla_jrfarm_grainless_complete_zwergkaninchen_5.jpg" @@ -292,7 +289,7 @@ def insert_data_accessories(): accessories.brand="TIAKI" accessories.description = "El juguete de inteligencia TIAKI para pequeños animales es un juguete maravilloso que estimula el comportamiento natural de búsqueda de comida y juego de conejos, hámsters y cobayas. Este juguete interactivo tiene una serie de zanahorias de madera que ofrecen a tus mascotas un divertido reto , por ejemplo, arrancarlas y roerlas. La zona de juegos no solo es entretenida, sino también segura para que tus animales la roan, ya que está hecha de papel, madera de pino y hojas de maíz." accessories.price = 7.99 - accessories.animal_type = "exótico" + accessories.animal_type = "exotico" accessories.pathologies = "" accessories.url = "https://media.zooplus.com/bilder/7/400/451140_pla_tiaki_intelligence_toy_carrot_patch_fg_9772_7.jpg" db.session.add(accessories) @@ -304,7 +301,7 @@ def insert_data_accessories(): accessories.brand="Bayer Vetriderm" accessories.description = "Gracias a la loción tópica Vetriderm, de Bayer, es posible reducir de manera efectiva la causa de las reacciones alérgicas a los animales domésticos. Simplemente, aplica la solución sobre un paño y, con ayuda de este, extiende el producto sobre el pelo de tu perro o gato; primero en la dirección del crecimiento del pelo y después en el sentido opuesto. De este modo, eliminarás las partículas que quedan en el pelaje como los epitelios o los restos de saliva o de orina, que pueden producir reacciones alérgicas." accessories.price = 21.99 - accessories.animal_type = "exótico" + accessories.animal_type = "exotico" accessories.pathologies = "" accessories.url = "https://media.zooplus.com/bilder/7/400/73160_pla_vetriderm_350ml_hs_01_7.jpg" db.session.add(accessories) @@ -325,7 +322,7 @@ def insert_data_pet(): pet= Pet() pet.name = "recoleto" - pet.size="medium" + pet.size="medio" pet.breed= "asd" pet.age="senior" pet.animal_type = "perro" diff --git a/src/api/models.py b/src/api/models.py index 8cb0a08067..90595b6b1f 100644 --- a/src/api/models.py +++ b/src/api/models.py @@ -62,17 +62,6 @@ class Food(db.Model): animal_type = db.Column(db.String(50), nullable=False) size = db.Column(db.String(30), nullable=True) pathologies = db.Column(db.Text, nullable=True) - # is_hypoallergenic = db.Column(db.Boolean, default=False) - # is_gluten_free = db.Column(db.Boolean, default=False) - # protein_source = db.Column(db.String(100), nullable=False) - # fat_content = db.Column(db.Float, nullable=True) - # omega3_content = db.Column(db.Float, nullable=True) - # taurine_content = db.Column(db.Float, nullable=True) - # suitable_for_senior = db.Column(db.Boolean, default=False) - # suitable_for_sterilized = db.Column(db.Boolean, default=False) - # croquette_shape = db.Column(db.String(50), nullable=True) - # weight_kg = db.Column(db.Float, nullable=False) - # price_eur = db.Column(db.Float, nullable=False) url = db.Column(db.String(255), nullable=True) def __repr__(self): @@ -83,6 +72,7 @@ def serialize(self): "id": self.id, "name": self.name, "brand": self.brand, + "age": self.age, "description": self.description, "ingredients": self.ingredients, "weight": self.weight, #peso en kilos diff --git a/src/api/routes.py b/src/api/routes.py index 2df4de377e..3e84249d0a 100644 --- a/src/api/routes.py +++ b/src/api/routes.py @@ -11,6 +11,7 @@ from flask_bcrypt import Bcrypt from flask_mail import Message import random, string +from unidecode import unidecode api = Blueprint('api', __name__) @@ -121,31 +122,23 @@ def get_pet(pet_id): @api.route('/foods/suggestions/', methods=['GET']) @jwt_required() def get_pet_suggestions(pet_id): - pet = Pet.query.filter_by(id=pet_id).first() - print(pet) - if pet is None: - return jsonify({"msg": "Pet no exist"}), 404 - # pet = Pet.query.get(pet_id).serialize() - # Problema: Un animal puede tener varias patologias en su campo, habría que co - # ger este campo y tratarlo, + pet = Pet.query.get(pet_id).serialize() + # Problema: Un animal puede tener varias patologias en su campo, habría que coger este campo y tratarlo, # separar las patologias en una lista y hacer la query para cada patologia. # Solucion simple: limitar a 1 patologia cada animal por ahora - # if para pet# anymal_type == perro, animal size #si no no hace falta size - if pet.animal_type == "perro": - food_suggestions = db.session.execute(select(Food).where(and_(Food.animal_type==pet.animal_type), - Food.size==pet.size, - Food.age==pet.age, - Food.pathologies==pet.pathologies)).all() + #if para pet# anymal_type == perro, animal size #si no no hace falta size + if pet["animal_type"] == "perro": + food_suggestions = db.session.execute(select(Food).where(and_(Food.animal_type==pet["animal_type"]), + Food.size==pet["size"], + Food.age==pet["age"], + Food.pathologies==pet["pathologies"])).all() else: - food_suggestions = db.session.execute(select(Food).where(Food.animal_type==pet.animal_type), - Food.age==pet.age, - Food.pathologies==pet.pathologies).all() + food_suggestions = db.session.execute(select(Food).where(and_(Food.animal_type==pet["animal_type"]), + Food.age==pet["age"], + Food.pathologies==pet["pathologies"])).all() if not food_suggestions : - return "no suggestions found", 404 + return "no suggestions found", 404 return [food[0].serialize() for food in food_suggestions], 200 - return jsonify("okey"), 200 - - diff --git a/src/front/js/pages/RegistroMascota.js b/src/front/js/pages/RegistroMascota.js index 1dcededf46..1b4d2447ab 100644 --- a/src/front/js/pages/RegistroMascota.js +++ b/src/front/js/pages/RegistroMascota.js @@ -94,7 +94,7 @@ export const RegistroMascota = () => { - + @@ -121,9 +121,9 @@ export const RegistroMascota = () => { required > - - - + + +
@@ -154,13 +154,12 @@ export const RegistroMascota = () => { required > - + + - - - +
diff --git a/src/front/js/pages/vistaMascota.js b/src/front/js/pages/vistaMascota.js index 887d973759..206952ff07 100644 --- a/src/front/js/pages/vistaMascota.js +++ b/src/front/js/pages/vistaMascota.js @@ -57,8 +57,9 @@ export const VistaMascota = () => { setFoodSuggestions(data); }); } - }, [petDetails, id, actions]); + }, [petDetails, id]); + useEffect(() =>{console.log(foodSuggestions)},[foodSuggestions]) useEffect(() => { if (!loading && petDetails === null) { navigate("/not-found"); @@ -187,9 +188,9 @@ export const VistaMascota = () => { onChange={(e) => setEditedPet({ ...editedPet, size: e.target.value })} > - - - + + + @@ -214,10 +215,8 @@ export const VistaMascota = () => { > - + - -