diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..029519e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.py[cod] \ No newline at end of file diff --git a/Backend/FastAPI/db/client.py b/Backend/FastAPI/db/client.py new file mode 100644 index 00000000..d92a3616 --- /dev/null +++ b/Backend/FastAPI/db/client.py @@ -0,0 +1,31 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=20480) + +### MongoDB client ### + +# Descarga versión community: https://www.mongodb.com/try/download +# Instalación:https://www.mongodb.com/docs/manual/tutorial +# Módulo conexión MongoDB: pip install pymongo +# Ejecución: sudo mongod --dbpath "/path/a/la/base/de/datos/" +# Conexión: mongodb://localhost + +from pymongo import MongoClient + +# Descomentar el db_client local o remoto correspondiente + +# Base de datos local MongoDB +db_client = MongoClient().local + +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=25470 + +# Base de datos remota MongoDB Atlas (https://mongodb.com) +# db_client = MongoClient( +# "mongodb+srv://:@/?retryWrites=true&w=majority").test + +# Despliegue API en la nube: +# Deta (deprecado) - https://www.deta.sh/ +# Vercel - https://www.vercel.com +# Instrucciones - https://cleverzone.medium.com/fastapi-deployment-into-vercel-0fa4e6478014 +# MUY IMPORTANTE - Al desplegar en producción, preparar el proyecto para trabajar con variables de entorno que hagan referencia a datos sensibles: +# - Nunca subas a un repositorio público el valor de las variables +# - Puedes usar dotenv en Python +# - Añade el valor de las variables desde el proveedor de hosting diff --git a/Backend/FastAPI/db/models/user.py b/Backend/FastAPI/db/models/user.py new file mode 100644 index 00000000..73d57665 --- /dev/null +++ b/Backend/FastAPI/db/models/user.py @@ -0,0 +1,12 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=20480 + +### User model ### + +from pydantic import BaseModel +from typing import Optional + + +class User(BaseModel): + id: Optional[str] = None + username: str + email: str diff --git a/Backend/FastAPI/db/schemas/user.py b/Backend/FastAPI/db/schemas/user.py new file mode 100644 index 00000000..cdfdcd56 --- /dev/null +++ b/Backend/FastAPI/db/schemas/user.py @@ -0,0 +1,12 @@ +# Clase en vídeo (22/12/2022): https://www.twitch.tv/videos/1686104006 + +### User schema ### + +def user_schema(user) -> dict: + return {"id": str(user["_id"]), + "username": user["username"], + "email": user["email"]} + + +def users_schema(users) -> list: + return [user_schema(user) for user in users] diff --git a/Backend/FastAPI/main.py b/Backend/FastAPI/main.py new file mode 100644 index 00000000..c8ba0830 --- /dev/null +++ b/Backend/FastAPI/main.py @@ -0,0 +1,51 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A + +### Hola Mundo ### + +# Documentación oficial: https://fastapi.tiangolo.com/es/ + +# Instala FastAPI: pip install "fastapi[all]" + +from fastapi import FastAPI +from routers import products, users, basic_auth_users, jwt_auth_users, users_db +from fastapi.staticfiles import StaticFiles +import os + +app = FastAPI() + +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=12475 +app.include_router(products.router) +app.include_router(users.router) + +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=14094 +app.include_router(basic_auth_users.router) + +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=17664 +app.include_router(jwt_auth_users.router) + +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=20480 +app.include_router(users_db.router) + +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=13618 +app.mount("/static", StaticFiles(directory="static"), name="static") + + +# Url local: http://127.0.0.1:8000 + + +@app.get("/") +async def root(): + return "Hola FastAPI!" + +# Url local: http://127.0.0.1:8000/url + + +@app.get("/url") +async def url(): + return {"url": "https://mouredev.com/python"} + +# Inicia el server: uvicorn main:app --reload +# Detener el server: CTRL+C + +# Documentación con Swagger: http://127.0.0.1:8000/docs +# Documentación con Redocly: http://127.0.0.1:8000/redoc diff --git a/Backend/FastAPI/requirements.txt b/Backend/FastAPI/requirements.txt new file mode 100644 index 00000000..8fa41bc8 --- /dev/null +++ b/Backend/FastAPI/requirements.txt @@ -0,0 +1,5 @@ +fastapi[standard] +python-jose +passlib +bcrypt +pymongo \ No newline at end of file diff --git a/Backend/FastAPI/routers/basic_auth_users.py b/Backend/FastAPI/routers/basic_auth_users.py new file mode 100644 index 00000000..da1b0f20 --- /dev/null +++ b/Backend/FastAPI/routers/basic_auth_users.py @@ -0,0 +1,90 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=14094 + +### Users API con autorización OAuth2 básica ### + +from fastapi import APIRouter, Depends, HTTPException, status +from pydantic import BaseModel +from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm + +router = APIRouter( + prefix="/basicauth", + tags=["basicauth"], + responses={status.HTTP_404_NOT_FOUND: {"message": "No encontrado"}} +) + +oauth2 = OAuth2PasswordBearer(tokenUrl="login") + + +class User(BaseModel): + username: str + full_name: str + email: str + disabled: bool + + +class UserDB(User): + password: str + + +users_db = { + "mouredev": { + "username": "mouredev", + "full_name": "Brais Moure", + "email": "braismoure@mouredev.com", + "disabled": False, + "password": "123456" + }, + "mouredev2": { + "username": "mouredev2", + "full_name": "Brais Moure 2", + "email": "braismoure2@mouredev.com", + "disabled": True, + "password": "654321" + } +} + + +def search_user_db(username: str): + if username in users_db: + return UserDB(**users_db[username]) + + +def search_user(username: str): + if username in users_db: + return User(**users_db[username]) + + +async def current_user(token: str = Depends(oauth2)): + user = search_user(token) + if not user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Credenciales de autenticación inválidas", + headers={"WWW-Authenticate": "Bearer"}) + + if user.disabled: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Usuario inactivo") + + return user + + +@router.post("/login") +async def login(form: OAuth2PasswordRequestForm = Depends()): + user_db = users_db.get(form.username) + if not user_db: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="El usuario no es correcto") + + user = search_user_db(form.username) + if not form.password == user.password: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="La contraseña no es correcta") + + return {"access_token": user.username, "token_type": "bearer"} + + +@router.get("/users/me") +async def me(user: User = Depends(current_user)): + return user diff --git a/Backend/FastAPI/routers/jwt_auth_users.py b/Backend/FastAPI/routers/jwt_auth_users.py new file mode 100644 index 00000000..dd266882 --- /dev/null +++ b/Backend/FastAPI/routers/jwt_auth_users.py @@ -0,0 +1,115 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=17664 + +### Users API con autorización OAuth2 JWT ### + +from fastapi import APIRouter, Depends, HTTPException, status +from pydantic import BaseModel +from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm +from jose import jwt, JWTError +from passlib.context import CryptContext +from datetime import datetime, timedelta, timezone + +ALGORITHM = "HS256" +ACCESS_TOKEN_DURATION = 1 +SECRET = "201d573bd7d1344d3a3bfce1550b69102fd11be3db6d379508b6cccc58ea230b" + +router = APIRouter( + prefix="/jwtauth", + tags=["jwtauth"], + responses={status.HTTP_404_NOT_FOUND: {"message": "No encontrado"}} +) + +oauth2 = OAuth2PasswordBearer(tokenUrl="login") + +crypt = CryptContext(schemes=["bcrypt"]) + + +class User(BaseModel): + username: str + full_name: str + email: str + disabled: bool + + +class UserDB(User): + password: str + + +users_db = { + "mouredev": { + "username": "mouredev", + "full_name": "Brais Moure", + "email": "braismoure@mouredev.com", + "disabled": False, + "password": "$2a$12$B2Gq.Dps1WYf2t57eiIKjO4DXC3IUMUXISJF62bSRiFfqMdOI2Xa6" + }, + "mouredev2": { + "username": "mouredev2", + "full_name": "Brais Moure 2", + "email": "braismoure2@mouredev.com", + "disabled": True, + "password": "$2a$12$SduE7dE.i3/ygwd0Kol8bOFvEABaoOOlC8JsCSr6wpwB4zl5STU4S" + } +} + + +def search_user_db(username: str): + if username in users_db: + return UserDB(**users_db[username]) + + +def search_user(username: str): + if username in users_db: + return User(**users_db[username]) + + +async def auth_user(token: str = Depends(oauth2)): + + exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Credenciales de autenticación inválidas", + headers={"WWW-Authenticate": "Bearer"}) + + try: + username = jwt.decode(token, SECRET, algorithms=[ALGORITHM]).get("sub") + if username is None: + raise exception + + except JWTError: + raise exception + + return search_user(username) + + +async def current_user(user: User = Depends(auth_user)): + if user.disabled: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Usuario inactivo") + + return user + + +@router.post("/login") +async def login(form: OAuth2PasswordRequestForm = Depends()): + + user_db = users_db.get(form.username) + if not user_db: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="El usuario no es correcto") + + user = search_user_db(form.username) + + if not crypt.verify(form.password, user.password): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="La contraseña no es correcta") + + access_token = {"sub": user.username, + "exp": datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_DURATION)} + + return {"access_token": jwt.encode(access_token, SECRET, algorithm=ALGORITHM), "token_type": "bearer"} + + +@router.get("/users/me") +async def me(user: User = Depends(current_user)): + return user diff --git a/Backend/FastAPI/routers/products.py b/Backend/FastAPI/routers/products.py new file mode 100644 index 00000000..4562224c --- /dev/null +++ b/Backend/FastAPI/routers/products.py @@ -0,0 +1,23 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=12475 + +### Products API ### + +from fastapi import APIRouter + +router = APIRouter( + prefix="/products", + tags=["products"], + responses={404: {"message": "No encontrado"}} +) + +products_list = ["Producto 1", "Producto 2", "Producto 3", "Producto 4", "Producto 5"] + + +@router.get("/") +async def products(): + return products_list + + +@router.get("/{id}") +async def products(id: int): + return products_list[id] diff --git a/Backend/FastAPI/routers/users.py b/Backend/FastAPI/routers/users.py new file mode 100644 index 00000000..46aca70c --- /dev/null +++ b/Backend/FastAPI/routers/users.py @@ -0,0 +1,97 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=5382 + +### Users API ### + +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel + +# Inicia el server: uvicorn users:app --reload + +router = APIRouter() + + +class User(BaseModel): + id: int + name: str + surname: str + url: str + age: int + + +users_list = [User(id=1, name="Brais", surname="Moure", url="https://moure.dev", age=35), + User(id=2, name="Moure", surname="Dev", + url="https://mouredev.com", age=35), + User(id=3, name="Brais", surname="Dahlberg", url="https://haakon.com", age=33)] + + +@router.get("/usersjson") +async def usersjson(): # Creamos un JSON a mano + return [{"name": "Brais", "surname": "Moure", "url": "https://moure.dev", "age": 35}, + {"name": "Moure", "surname": "Dev", + "url": "https://mouredev.com", "age": 35}, + {"name": "Haakon", "surname": "Dahlberg", "url": "https://haakon.com", "age": 33}] + + +@router.get("/users") +async def users(): + return users_list + + +@router.get("/user/{id}") # Path +async def user(id: int): + return search_user(id) + + +@router.get("/user/") # Query +async def user(id: int): + return search_user(id) + + +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=8529 + + +@router.post("/user/", response_model=User, status_code=201) +async def user(user: User): + if type(search_user(user.id)) == User: + raise HTTPException(status_code=404, detail="El usuario ya existe") + + users_list.append(user) + return user + + +@router.put("/user/") +async def user(user: User): + + found = False + + for index, saved_user in enumerate(users_list): + if saved_user.id == user.id: + users_list[index] = user + found = True + + if not found: + return {"error": "No se ha actualizado el usuario"} + + return user + + +@router.delete("/user/{id}") +async def user(id: int): + + found = False + + for index, saved_user in enumerate(users_list): + if saved_user.id == id: + del users_list[index] + found = True + + if not found: + return {"error": "No se ha eliminado el usuario"} + + +def search_user(id: int): + users = filter(lambda user: user.id == id, users_list) + try: + return list(users)[0] + except: + return {"error": "No se ha encontrado el usuario"} diff --git a/Backend/FastAPI/routers/users_db.py b/Backend/FastAPI/routers/users_db.py new file mode 100644 index 00000000..1b5645ff --- /dev/null +++ b/Backend/FastAPI/routers/users_db.py @@ -0,0 +1,80 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=20480 + +### Users DB API ### + +from fastapi import APIRouter, HTTPException, status +from db.models.user import User +from db.schemas.user import user_schema, users_schema +from db.client import db_client +from bson import ObjectId + +router = APIRouter( + prefix="/userdb", + tags=["userdb"], + responses={status.HTTP_404_NOT_FOUND: {"message": "No encontrado"}}) + + +@router.get("/", response_model=list[User]) +async def users(): + return users_schema(db_client.users.find()) + + +@router.get("/{id}") # Path +async def user(id: str): + return search_user("_id", ObjectId(id)) + + +@router.get("/") # Query +async def user(id: str): + return search_user("_id", ObjectId(id)) + + +@router.post("/", response_model=User, status_code=status.HTTP_201_CREATED) +async def user(user: User): + if type(search_user("email", user.email)) == User: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="El usuario ya existe") + + user_dict = dict(user) + del user_dict["id"] + + id = db_client.users.insert_one(user_dict).inserted_id + + new_user = user_schema(db_client.users.find_one({"_id": id})) + + return User(**new_user) + + +@router.put("/", response_model=User) +async def user(user: User): + + user_dict = dict(user) + del user_dict["id"] + + try: + db_client.users.find_one_and_replace( + {"_id": ObjectId(user.id)}, user_dict) + except: + return {"error": "No se ha actualizado el usuario"} + + return search_user("_id", ObjectId(user.id)) + + +@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT) +async def user(id: str): + + found = db_client.users.find_one_and_delete({"_id": ObjectId(id)}) + + if not found: + return {"error": "No se ha eliminado el usuario"} + +# Helper + + +def search_user(field: str, key): + + try: + user = db_client.users.find_one({field: key}) + return User(**user_schema(user)) + except: + return {"error": "No se ha encontrado el usuario"} diff --git a/Backend/FastAPI/static/images/python.jpg b/Backend/FastAPI/static/images/python.jpg new file mode 100644 index 00000000..0be1c65e Binary files /dev/null and b/Backend/FastAPI/static/images/python.jpg differ diff --git a/Backend/FastAPI/vercel.json b/Backend/FastAPI/vercel.json new file mode 100644 index 00000000..10bc3cd6 --- /dev/null +++ b/Backend/FastAPI/vercel.json @@ -0,0 +1,14 @@ +{ + "builds": [ + { + "src": "main.py", + "use": "@vercel/python" + } + ], + "routes": [ + { + "src": "/(.*)", + "dest": "main.py" + } + ] +} \ No newline at end of file diff --git a/Backend/type_hints.py b/Backend/type_hints.py new file mode 100644 index 00000000..42b7c01a --- /dev/null +++ b/Backend/type_hints.py @@ -0,0 +1,19 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=1810 + +### Type Hints ### + +my_string_variable = "My String variable" +print(my_string_variable) +print(type(my_string_variable)) + +my_string_variable = 5 +print(my_string_variable) +print(type(my_string_variable)) + +my_typed_variable: str = "My typed String variable" +print(my_typed_variable) +print(type(my_typed_variable)) + +my_typed_variable = 5 +print(my_typed_variable) +print(type(my_typed_variable)) diff --git a/Basic/00_helloworld.py b/Basic/00_helloworld.py index ab9825e6..d2c64317 100644 --- a/Basic/00_helloworld.py +++ b/Basic/00_helloworld.py @@ -21,9 +21,9 @@ ''' # Cómo consultar el tipo de dato -print(type("Soy un dato str")) # Tipo 'str' -print(type(5)) # Tipo 'int' -print(type(1.5)) # Tipo 'float' -print(type(3 + 1j)) # Tipo 'complex' -print(type(True)) # Tipo 'bool' -print(type(print("Mi cadena de texto"))) # Tipo 'NoneType' \ No newline at end of file +print(type("Soy un dato str")) # Tipo 'str' +print(type(5)) # Tipo 'int' +print(type(1.5)) # Tipo 'float' +print(type(3 + 1j)) # Tipo 'complex' +print(type(True)) # Tipo 'bool' +print(type(print("Mi cadena de texto"))) # Tipo 'NoneType' diff --git a/Basic/01_variables.py b/Basic/01_variables.py index fd5de7ca..682c3f73 100644 --- a/Basic/01_variables.py +++ b/Basic/01_variables.py @@ -24,7 +24,8 @@ # Variables en una sola línea. ¡Cuidado con abusar de esta sintaxis! name, surname, alias, age = "Brais", "Moure", 'MoureDev', 35 -print("Me llamo:", name, surname, ". Mi edad es:", age, ". Y mi alias es:", alias) +print("Me llamo:", name, surname, ". Mi edad es:", + age, ". Y mi alias es:", alias) # Inputs name = input('¿Cuál es tu nombre? ') @@ -43,4 +44,4 @@ address = True address = 5 address = 1.2 -print(type(address)) \ No newline at end of file +print(type(address)) diff --git a/Basic/02_operators.py b/Basic/02_operators.py index 673f27d5..b7e7fb07 100644 --- a/Basic/02_operators.py +++ b/Basic/02_operators.py @@ -26,7 +26,7 @@ ### Operadores Comparativos ### # Operaciones con enteros -print(3 > 4) +print(3 > 4) print(3 < 4) print(3 >= 4) print(4 <= 4) @@ -34,10 +34,10 @@ print(3 != 4) # Operaciones con cadenas de texto -print("Hola" > "Python") +print("Hola" > "Python") print("Hola" < "Python") -print("aaaa" >= "abaa") # Ordenación alfabética por ASCII -print(len("aaaa") >= len("abaa")) # Cuenta caracteres +print("aaaa" >= "abaa") # Ordenación alfabética por ASCII +print(len("aaaa") >= len("abaa")) # Cuenta caracteres print("Hola" <= "Python") print("Hola" == "Hola") print("Hola" != "Python") @@ -50,4 +50,4 @@ print(3 < 4 and "Hola" < "Python") print(3 < 4 or "Hola" > "Python") print(3 < 4 or ("Hola" > "Python" and 4 == 4)) -print(not(3 > 4)) \ No newline at end of file +print(not (3 > 4)) diff --git a/Basic/03_strings.py b/Basic/03_strings.py index 25562322..aca89f8a 100644 --- a/Basic/03_strings.py +++ b/Basic/03_strings.py @@ -22,7 +22,7 @@ name, surname, age = "Brais", "Moure", 35 print("Mi nombre es {} {} y mi edad es {}".format(name, surname, age)) -print("Mi nombre es %s %s y mi edad es %d" %(name, surname, age)) +print("Mi nombre es %s %s y mi edad es %d" % (name, surname, age)) print("Mi nombre es " + name + " " + surname + " y mi edad es " + str(age)) print(f"Mi nombre es {name} {surname} y mi edad es {age}") @@ -62,4 +62,4 @@ print(language.lower()) print(language.lower().isupper()) print(language.startswith("Py")) -print("Py" == "py") # No es lo mismo \ No newline at end of file +print("Py" == "py") # No es lo mismo diff --git a/Basic/04_lists.py b/Basic/04_lists.py index 7a0c383a..d6e6d58b 100644 --- a/Basic/04_lists.py +++ b/Basic/04_lists.py @@ -26,8 +26,8 @@ print(my_other_list[-1]) print(my_other_list[-4]) print(my_list.count(30)) -#print(my_other_list[4]) IndexError -#print(my_other_list[-5]) IndexError +# print(my_other_list[4]) IndexError +# print(my_other_list[-5]) IndexError print(my_other_list.index("Brais")) diff --git a/Basic/05_tuples.py b/Basic/05_tuples.py index dfbf0bcf..d2cca5b5 100644 --- a/Basic/05_tuples.py +++ b/Basic/05_tuples.py @@ -17,14 +17,14 @@ print(my_tuple[0]) print(my_tuple[-1]) -#print(my_tuple[4]) IndexError -#print(my_tuple[-6]) IndexError +# print(my_tuple[4]) IndexError +# print(my_tuple[-6]) IndexError print(my_tuple.count("Brais")) print(my_tuple.index("Moure")) print(my_tuple.index("Brais")) -#my_tuple[1] = 1.80 'tuple' object does not support item assignment +# my_tuple[1] = 1.80 'tuple' object does not support item assignment # Concatenación @@ -48,7 +48,7 @@ # Eliminación -#del my_tuple[2] TypeError: 'tuple' object doesn't support item deletion +# del my_tuple[2] TypeError: 'tuple' object doesn't support item deletion del my_tuple -#print(my_tuple) NameError: name 'my_tuple' is not defined \ No newline at end of file +# print(my_tuple) NameError: name 'my_tuple' is not defined diff --git a/Basic/06_sets.py b/Basic/06_sets.py index ee435367..4a6d2b19 100644 --- a/Basic/06_sets.py +++ b/Basic/06_sets.py @@ -8,9 +8,9 @@ my_other_set = {} print(type(my_set)) -print(type(my_other_set)) # Inicialmente es un diccionario +print(type(my_other_set)) # Inicialmente es un diccionario -my_other_set = {"Brais","Moure", 35} +my_other_set = {"Brais", "Moure", 35} print(type(my_other_set)) print(len(my_other_set)) @@ -19,9 +19,9 @@ my_other_set.add("MoureDev") -print(my_other_set) # Un set no es una estructura ordenada +print(my_other_set) # Un set no es una estructura ordenada -my_other_set.add("MoureDev") # Un set no admite repetidos +my_other_set.add("MoureDev") # Un set no admite repetidos print(my_other_set) @@ -39,19 +39,19 @@ print(len(my_other_set)) del my_other_set -#print(my_other_set) NameError: name 'my_other_set' is not defined +# print(my_other_set) NameError: name 'my_other_set' is not defined # Transformación -my_set = {"Brais","Moure", 35} +my_set = {"Brais", "Moure", 35} my_list = list(my_set) print(my_list) print(my_list[0]) -my_other_set = {"Kotlin","Swift", "Python"} +my_other_set = {"Kotlin", "Swift", "Python"} # Otras operaciones my_new_set = my_set.union(my_other_set) print(my_new_set.union(my_new_set).union(my_set).union({"JavaScript", "C#"})) -print(my_new_set.difference(my_set)) \ No newline at end of file +print(my_new_set.difference(my_set)) diff --git a/Basic/07_dicts.py b/Basic/07_dicts.py index 7a42bee2..b76aff1f 100644 --- a/Basic/07_dicts.py +++ b/Basic/07_dicts.py @@ -10,15 +10,16 @@ print(type(my_dict)) print(type(my_other_dict)) -my_other_dict = {"Nombre":"Brais", "Apellido":"Moure", "Edad":35, 1:"Python"} +my_other_dict = {"Nombre": "Brais", + "Apellido": "Moure", "Edad": 35, 1: "Python"} my_dict = { - "Nombre":"Brais", - "Apellido":"Moure", - "Edad":35, - "Lenguajes": {"Python","Swift", "Kotlin"}, - 1:1.77 - } + "Nombre": "Brais", + "Apellido": "Moure", + "Edad": 35, + "Lenguajes": {"Python", "Swift", "Kotlin"}, + 1: 1.77 +} print(my_other_dict) print(my_dict) @@ -72,4 +73,4 @@ print(my_new_dict.values()) print(list(dict.fromkeys(list(my_new_dict.values())).keys())) print(tuple(my_new_dict)) -print(set(my_new_dict)) \ No newline at end of file +print(set(my_new_dict)) diff --git a/Basic/08_conditionals.py b/Basic/08_conditionals.py index ae370958..27594f4f 100644 --- a/Basic/08_conditionals.py +++ b/Basic/08_conditionals.py @@ -6,7 +6,7 @@ my_condition = False -if my_condition: # Es lo mismo que if my_condition == True: +if my_condition: # Es lo mismo que if my_condition == True: print("Se ejecuta la condición del if") my_condition = 5 * 5 @@ -33,4 +33,4 @@ print("Mi cadena de texto es vacía") if my_string == "Mi cadena de textoooooo": - print("Estas cadenas de texto coinciden") \ No newline at end of file + print("Estas cadenas de texto coinciden") diff --git a/Basic/09_loops.py b/Basic/09_loops.py index 58ba19eb..6db69d9b 100644 --- a/Basic/09_loops.py +++ b/Basic/09_loops.py @@ -9,7 +9,7 @@ while my_condition < 10: print(my_condition) my_condition += 2 -else: # Es opcional +else: # Es opcional print("Mi condición es mayor o igual que 10") print("La ejecución continúa") @@ -35,19 +35,19 @@ for element in my_tuple: print(element) -my_set = {"Brais","Moure", 35} +my_set = {"Brais", "Moure", 35} for element in my_set: print(element) -my_dict = {"Nombre":"Brais", "Apellido":"Moure", "Edad":35, 1:"Python"} +my_dict = {"Nombre": "Brais", "Apellido": "Moure", "Edad": 35, 1: "Python"} for element in my_dict: print(element) if element == "Edad": break else: - print("El bluce for para diccionario ha finalizado") + print("El bucle for para el diccionario ha finalizado") print("La ejecución continúa") @@ -57,4 +57,4 @@ continue print("Se ejecuta") else: - print("El bluce for para diccionario ha finalizado") \ No newline at end of file + print("El bluce for para diccionario ha finalizado") diff --git a/Basic/10_functions.py b/Basic/10_functions.py index 3f8d78dd..73604666 100644 --- a/Basic/10_functions.py +++ b/Basic/10_functions.py @@ -4,18 +4,21 @@ # Definición -def my_function (): +def my_function(): print("Esto es una función") + my_function() my_function() my_function() # Función con parámetros de entrada/argumentos -def sum_two_values (first_value: int, second_value): + +def sum_two_values(first_value: int, second_value): print(first_value + second_value) + sum_two_values(5, 7) sum_two_values(54754, 71231) sum_two_values("5", "7") @@ -23,10 +26,12 @@ def sum_two_values (first_value: int, second_value): # Función con parámetros de entrada/argumentos y retorno -def sum_two_values_with_return (first_value, second_value): + +def sum_two_values_with_return(first_value, second_value): my_sum = first_value + second_value return my_sum + my_result = sum_two_values(1.4, 5.2) print(my_result) @@ -35,21 +40,26 @@ def sum_two_values_with_return (first_value, second_value): # Función con parámetros de entrada/argumentos por clave -def print_name (name, surname): + +def print_name(name, surname): print(f"{name} {surname}") -print_name(surname = "Moure", name = "Brais") + +print_name(surname="Moure", name="Brais") # Función con parámetros de entrada/argumentos por defecto -def print_name_with_default (name, surname, alias = "Sin alias"): + +def print_name_with_default(name, surname, alias="Sin alias"): print(f"{name} {surname} {alias}") + print_name_with_default("Brais", "Moure") print_name_with_default("Brais", "Moure", "MoureDev") # Función con parámetros de entrada/argumentos arbitrarios + def print_upper_texts(*texts): print(type(texts)) for text in texts: @@ -57,4 +67,4 @@ def print_upper_texts(*texts): print_upper_texts("Hola", "Python", "MoureDev") -print_upper_texts("Hola") \ No newline at end of file +print_upper_texts("Hola") diff --git a/Basic/11_classes.py b/Basic/11_classes.py index f1b611c8..28e84692 100644 --- a/Basic/11_classes.py +++ b/Basic/11_classes.py @@ -5,24 +5,27 @@ # Definición class MyEmptyPerson: - pass # Para poder dejar la clase vacía + pass # Para poder dejar la clase vacía + print(MyEmptyPerson) print(MyEmptyPerson()) -# Clase con constructor, funciones y popiedades privadas y públicas +# Clase con constructor, funciones y propiedades privadas y públicas + class Person: - def __init__ (self, name, surname, alias = "Sin alias"): - self.full_name = f"{name} {surname} ({alias})" # Propiedad pública - self.__name = name # Propiedad privada + def __init__(self, name, surname, alias="Sin alias"): + self.full_name = f"{name} {surname} ({alias})" # Propiedad pública + self.__name = name # Propiedad privada - def get_name (self): + def get_name(self): return self.__name - def walk (self): + def walk(self): print(f"{self.full_name} está caminando") + my_person = Person("Brais", "Moure") print(my_person.full_name) print(my_person.get_name()) diff --git a/Basic/12_exceptions.py b/Basic/12_exceptions.py index 8f9bbb7c..b0ee4102 100644 --- a/Basic/12_exceptions.py +++ b/Basic/12_exceptions.py @@ -22,10 +22,10 @@ print("No se ha producido un error") except: print("Se ha producido un error") -else: # Opcional +else: # Opcional # Se ejecuta si no se produce una excepción print("La ejecución continúa correctamente") -finally: # Opcional +finally: # Opcional # Se ejecuta siempre print("La ejecución continúa") diff --git a/Basic/13_modules.py b/Basic/13_modules.py index f6f87e3f..c6c2657f 100644 --- a/Basic/13_modules.py +++ b/Basic/13_modules.py @@ -2,21 +2,21 @@ ### Modules ### +from math import pi as PI_VALUE +import math +from my_module import sumValue, printValue import my_module my_module.sumValue(5, 3, 1) my_module.printValue("Hola Python!") -from my_module import sumValue, printValue sumValue(5, 3, 1) printValue("Hola python") -import math print(math.pi) print(math.pow(2, 8)) -from math import pi as PI_VALUE -print(PI_VALUE) \ No newline at end of file +print(PI_VALUE) diff --git a/Basic/my_module.py b/Basic/my_module.py index 62c49daf..1b65d2bd 100644 --- a/Basic/my_module.py +++ b/Basic/my_module.py @@ -5,5 +5,6 @@ def sumValue(numberOne, numberTwo, numberThree): print(numberOne + numberTwo + numberThree) + def printValue(value): - print(value) \ No newline at end of file + print(value) diff --git a/Images/header.jpg b/Images/header.jpg new file mode 100644 index 00000000..c5522ddb Binary files /dev/null and b/Images/header.jpg differ diff --git a/Images/pro.jpg b/Images/pro.jpg new file mode 100644 index 00000000..986b7705 Binary files /dev/null and b/Images/pro.jpg differ diff --git a/Intermediate/00_dates.py b/Intermediate/00_dates.py index b9ca1f21..26b14113 100644 --- a/Intermediate/00_dates.py +++ b/Intermediate/00_dates.py @@ -1,13 +1,17 @@ -# Clase en vídeo (06/10/2022): https://www.twitch.tv/videos/1611014007 +# Clase en vídeo: https://youtu.be/TbcEqkabAWU ### Dates ### # Date time +from datetime import timedelta +from datetime import date +from datetime import time from datetime import datetime now = datetime.now() + def print_date(date): print(date.year) print(date.month) @@ -17,6 +21,7 @@ def print_date(date): print(date.second) print(date.timestamp()) + print_date(now) year_2023 = datetime(2023, 1, 1) @@ -25,7 +30,6 @@ def print_date(date): # Time -from datetime import time current_time = time(21, 6, 0) @@ -35,7 +39,6 @@ def print_date(date): # Date -from datetime import date current_date = date.today() @@ -49,7 +52,8 @@ def print_date(date): print(current_date.month) print(current_date.day) -current_date = date(current_date.year, current_date.month + 1, current_date.day) +current_date = date(current_date.year, + current_date.month + 1, current_date.day) print(current_date.month) @@ -63,10 +67,9 @@ def print_date(date): # Timedelta -from datetime import timedelta -start_timedelta = timedelta(200, 100, 100, weeks = 10) -end_timedelta = timedelta(300, 100, 100, weeks = 13) +start_timedelta = timedelta(200, 100, 100, weeks=10) +end_timedelta = timedelta(300, 100, 100, weeks=13) print(end_timedelta - start_timedelta) -print(end_timedelta + start_timedelta) \ No newline at end of file +print(end_timedelta + start_timedelta) diff --git a/Intermediate/01_list_comprehension.py b/Intermediate/01_list_comprehension.py index 7406327b..eb847494 100644 --- a/Intermediate/01_list_comprehension.py +++ b/Intermediate/01_list_comprehension.py @@ -1,8 +1,8 @@ -# Clase en vídeo (06/10/2022): https://www.twitch.tv/videos/1611014007 +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=3239 ### List Comprehension ### -my_original_list = [0, 1, 2, 3, 4, 5, 6, 7] +my_original_list = [0, 1, 2, 3, 4, 5, 6, 7] print(my_original_list) my_range = range(8) @@ -19,8 +19,10 @@ my_list = [i * i for i in range(8)] print(my_list) + def sum_five(number): return number + 5 + my_list = [sum_five(i) for i in range(8)] -print(my_list) \ No newline at end of file +print(my_list) diff --git a/Intermediate/02_challenges.py b/Intermediate/02_challenges.py index 32537b34..a816e676 100644 --- a/Intermediate/02_challenges.py +++ b/Intermediate/02_challenges.py @@ -1,4 +1,4 @@ -# Clase en vídeo (13/10/2022): https://www.twitch.tv/videos/1623225956 +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=4142 ### Challenges ### @@ -12,14 +12,11 @@ - Múltiplos de 3 y de 5 a la vez por la palabra "fizzbuzz". """ -from itertools import count -from tokenize import String - def fizzbuzz(): for index in range(1, 101): if index % 3 == 0 and index % 5 == 0: - print("fizzbuz") + print("fizzbuzz") elif index % 3 == 0: print("fizz") elif index % 5 == 0: @@ -27,6 +24,7 @@ def fizzbuzz(): else: print(index) + fizzbuzz() """ @@ -39,11 +37,13 @@ def fizzbuzz(): - Dos palabras exactamente iguales no son anagrama. """ + def is_anagram(word_one, word_two): if word_one.lower() == word_two.lower(): - return False + return False return sorted(word_one.lower()) == sorted(word_two.lower()) + print(is_anagram("Amor", "Roma")) """ @@ -55,6 +55,7 @@ def is_anagram(word_one, word_two): 0, 1, 1, 2, 3, 5, 8, 13... """ + def fibonacci(): prev = 0 @@ -66,6 +67,7 @@ def fibonacci(): prev = next next = fib + fibonacci() """ @@ -74,6 +76,7 @@ def fibonacci(): Hecho esto, imprime los números primos entre 1 y 100. """ + def is_prime(): for number in range(1, 101): @@ -86,10 +89,11 @@ def is_prime(): if number % index == 0: is_divisible = True break - + if not is_divisible: print(number) + is_prime() """ @@ -99,6 +103,7 @@ def is_prime(): - Si le pasamos "Hola mundo" nos retornaría "odnum aloH" """ + def reverse(text): text_len = len(text) reversed_text = "" @@ -106,4 +111,5 @@ def reverse(text): reversed_text += text[text_len - index - 1] return reversed_text -print(reverse("Hola mundo")) \ No newline at end of file + +print(reverse("Hola mundo")) diff --git a/Intermediate/03_lambdas.py b/Intermediate/03_lambdas.py index f96ec5c8..d51f8fda 100644 --- a/Intermediate/03_lambdas.py +++ b/Intermediate/03_lambdas.py @@ -1,4 +1,4 @@ -# Clase en vídeo (19/10/22): https://www.twitch.tv/videos/1628654998 +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=9145 ### Lambdas ### diff --git a/Intermediate/04_higher_order_functions.py b/Intermediate/04_higher_order_functions.py index 6f4f16c0..4c0c6e04 100644 --- a/Intermediate/04_higher_order_functions.py +++ b/Intermediate/04_higher_order_functions.py @@ -1,28 +1,34 @@ -# Clase en vídeo (19/10/22): https://www.twitch.tv/videos/1628654998 +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=10172 ### Higher Order Functions ### from functools import reduce + def sum_one(value): return value + 1 + def sum_five(value): return value + 5 + def sum_two_values_and_add_value(first_value, second_value, f_sum): return f_sum(first_value + second_value) + print(sum_two_values_and_add_value(5, 2, sum_one)) print(sum_two_values_and_add_value(5, 2, sum_five)) ### Closures ### + def sum_ten(original_value): def add(value): return value + 10 + original_value return add + add_closure = sum_ten(1) print(add_closure(5)) print((sum_ten(5))(1)) @@ -33,25 +39,31 @@ def add(value): # Map + def multiply_two(number): return number * 2 + print(list(map(multiply_two, numbers))) print(list(map(lambda number: number * 2, numbers))) # Filter + def filter_greater_than_ten(number): if number > 10: return True return False + print(list(filter(filter_greater_than_ten, numbers))) print(list(filter(lambda number: number > 10, numbers))) # Reduce + def sum_two_values(first_value, second_value): return first_value + second_value -print(reduce(sum_two_values, numbers)) \ No newline at end of file + +print(reduce(sum_two_values, numbers)) diff --git a/Intermediate/05_error_types.py b/Intermediate/05_error_types.py index 7706aebc..62566c8d 100644 --- a/Intermediate/05_error_types.py +++ b/Intermediate/05_error_types.py @@ -1,13 +1,15 @@ -# Clase en vídeo (25/10/22): https://www.twitch.tv/videos/1634818287 +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=12721 ### Error Types ### # SyntaxError -#print "¡Hola comunidad!" # Descomentar para Error +# print "¡Hola comunidad!" # Descomentar para Error +from math import pi +import math print("¡Hola comunidad!") # NameError -language = "Spanish" # Comentar para Error +language = "Spanish" # Comentar para Error print(language) # IndexError @@ -15,37 +17,35 @@ print(my_list[0]) print(my_list[4]) print(my_list[-1]) -#print(my_list[5]) # Descomentar para Error +# print(my_list[5]) # Descomentar para Error # ModuleNotFoundError -#import maths # Descomentar para Error -import math +# import maths # Descomentar para Error # AttributeError -#print(math.PI) # Descomentar para Error +# print(math.PI) # Descomentar para Error print(math.pi) # KeyError -my_dict = {"Nombre":"Brais", "Apellido":"Moure", "Edad":35, 1:"Python"} +my_dict = {"Nombre": "Brais", "Apellido": "Moure", "Edad": 35, 1: "Python"} print(my_dict["Edad"]) -#print(my_dict["Apelido"]) # Descomentar para Error +# print(my_dict["Apelido"]) # Descomentar para Error print(my_dict["Apellido"]) # TypeError -#print(my_list["0"]) # Descomentar para Error +# print(my_list["0"]) # Descomentar para Error print(my_list[0]) print(my_list[False]) # ImportError -#from math import PI # Descomentar para Error -from math import pi +# from math import PI # Descomentar para Error print(pi) # ValueError -#my_int = int("10 Años") -my_int = int("10") # Descomentar para Error +# my_int = int("10 Años") # Descomentar para Error +my_int = int("10") print(type(my_int)) # ZeroDivisionError -#print(4/0) # Descomentar para Error -print(4/2) \ No newline at end of file +# print(4/0) # Descomentar para Error +print(4/2) diff --git a/Intermediate/06_file_handling.py b/Intermediate/06_file_handling.py index 4adb0608..3bf072b0 100644 --- a/Intermediate/06_file_handling.py +++ b/Intermediate/06_file_handling.py @@ -1,29 +1,99 @@ -# Clase en vídeo (25/10/22): https://www.twitch.tv/videos/1634818287 +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=15524 ### File Handling ### +import xml +import csv +import json import os # .txt file -txt_file = open("Intermediate/my_file.txt", "w+") # Leer, escribir y sobrescribir si ya existe +# Leer, escribir y sobrescribir si ya existe +txt_file = open("my_file.txt", "w+") -txt_file.write("Mi nombre es Brais\nMi apellido es Moure\n35 años\nY mi lenguaje preferido es Python") +txt_file.write( + "Mi nombre es Brais\nMi apellido es Moure\n35 años\nY mi lenguaje preferido es Python") -#print(txt_file.read()) +# Posiciona el cursor al inicio del fichero +txt_file.seek(0) + +# Lee e imprime todo el contenido del fichero +print(txt_file.read()) + +# Lee e imprime 10 caracteres desde el inicio del fichero +txt_file.seek(0) print(txt_file.read(10)) + +# Lee e imprime el resto de la línea actual desde la posición 11 print(txt_file.readline()) + +# Lee e imprime la siguiente línea print(txt_file.readline()) + +# Lee e imprime las líneas restantes del fichero for line in txt_file.readlines(): print(line) +# Escribe una nueva línea en el fichero txt_file.write("\nAunque también me gusta Kotlin") -print(txt_file.readline()) +# Posiciona el cursor al inicio del fichero, lee e imprime todo su contenido +txt_file.seek(0) +print(txt_file.read()) + +# Cierra el fichero txt_file.close() -with open("Intermediate/my_file.txt", "a") as my_other_file: +# Agrega una nueva línea en el fichero +with open("my_file.txt", "a") as my_other_file: my_other_file.write("\nY Swift") -#os.remove("Intermediate/my_file.txt") +# os.remove("Intermediate/my_file.txt") + +# .json file + +json_file = open("Intermediate/my_file.json", "w+") + +json_test = { + "name": "Brais", + "surname": "Moure", + "age": 35, + "languages": ["Python", "Swift", "Kotlin"], + "website": "https://moure.dev"} + +json.dump(json_test, json_file, indent=2) + +json_file.close() + +with open("Intermediate/my_file.json") as my_other_file: + for line in my_other_file.readlines(): + print(line) + +json_dict = json.load(open("Intermediate/my_file.json")) +print(json_dict) +print(type(json_dict)) +print(json_dict["name"]) + +# .csv file + + +csv_file = open("Intermediate/my_file.csv", "w+") + +csv_writer = csv.writer(csv_file) +csv_writer.writerow(["name", "surname", "age", "language", "website"]) +csv_writer.writerow(["Brais", "Moure", 35, "Python", "https://moure.dev"]) +csv_writer.writerow(["Roswell", "", 2, "COBOL", ""]) + +csv_file.close() + +with open("Intermediate/my_file.csv") as my_other_file: + for line in my_other_file.readlines(): + print(line) + +# .xlsx file +# import xlrd # Debe instalarse el módulo + +# .xml file +# ¿Te atreves a practicar cómo trabajar con este tipo de ficheros? diff --git a/Intermediate/07_regular_expressions.py b/Intermediate/07_regular_expressions.py new file mode 100644 index 00000000..086d79bd --- /dev/null +++ b/Intermediate/07_regular_expressions.py @@ -0,0 +1,78 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=19762 + +### Regular Expressions ### + +import re + +# match + +my_string = "Esta es la lección número 7: Lección llamada Expresiones Regulares" +my_other_string = "Esta no es la lección número 6: Manejo de ficheros" + +match = re.match("Esta es la lección", my_string, re.I) +print(match) +start, end = match.span() +print(my_string[start:end]) + +match = re.match("Esta no es la lección", my_other_string) +# if not(match == None): # Otra forma de comprobar el None +# if match != None: # Otra forma de comprobar el None +if match is not None: + print(match) + start, end = match.span() + print(my_other_string[start:end]) + +print(re.match("Expresiones Regulares", my_string)) + +# search + +search = re.search("lección", my_string, re.I) +print(search) +start, end = search.span() +print(my_string[start:end]) + +# findall + +findall = re.findall("lección", my_string, re.I) +print(findall) + +# split + +print(re.split(":", my_string)) + +# sub + +print(re.sub("[l|L]ección", "LECCIÓN", my_string)) +print(re.sub("Expresiones Regulares", "RegEx", my_string)) + +### Regular Expressions Patterns ### + +# Para aprender y validar expresiones regulares: https://regex101.com + +pattern = r"[lL]ección" +print(re.findall(pattern, my_string)) + +pattern = r"[lL]ección|Expresiones" +print(re.findall(pattern, my_string)) + +pattern = r"[0-9]" +print(re.findall(pattern, my_string)) +print(re.search(pattern, my_string)) + +pattern = r"\d" +print(re.findall(pattern, my_string)) + +pattern = r"\D" +print(re.findall(pattern, my_string)) + +pattern = r"[l].*" +print(re.findall(pattern, my_string)) + +email = "mouredev@mouredev.com" +pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z-.]+$" +print(re.match(pattern, email)) +print(re.search(pattern, email)) +print(re.findall(pattern, email)) + +email = "mouredev@mouredev.com.mx" +print(re.findall(pattern, email)) diff --git a/Intermediate/08_python_package_manager.py b/Intermediate/08_python_package_manager.py new file mode 100644 index 00000000..e0e7410e --- /dev/null +++ b/Intermediate/08_python_package_manager.py @@ -0,0 +1,39 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=24010 + +### Python Package Manager ### + +# PIP https://pypi.org + +# pip install pip +# pip --version + +# pip install numpy +import pandas +from mypackage import arithmetics +import requests +import numpy + +print(numpy.version.version) + +numpy_array = numpy.array([35, 24, 62, 52, 30, 30, 17]) +print(type(numpy_array)) + +print(numpy_array * 2) + +# pip install pandas + +# pip list +# pip uninstall pandas +# pip show numpy + +# pip install requests + +response = requests.get("https://pokeapi.co/api/v2/pokemon?limit=151") +print(response) +print(response.status_code) +print(response.json()) + +# Arithmetics Package + + +print(arithmetics.sum_two_values(1, 4)) diff --git a/Intermediate/my_file.csv b/Intermediate/my_file.csv new file mode 100644 index 00000000..eeb0e38e --- /dev/null +++ b/Intermediate/my_file.csv @@ -0,0 +1,3 @@ +name,surname,age,language,website +Brais,Moure,35,Python,https://moure.dev +Roswell,,2,COBOL, diff --git a/Intermediate/my_file.json b/Intermediate/my_file.json new file mode 100644 index 00000000..dec5a3cc --- /dev/null +++ b/Intermediate/my_file.json @@ -0,0 +1,11 @@ +{ + "name": "Brais", + "surname": "Moure", + "age": 35, + "languages": [ + "Python", + "Swift", + "Kotlin" + ], + "website": "https://moure.dev" +} \ No newline at end of file diff --git a/Intermediate/mypackage/__init__.py b/Intermediate/mypackage/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Intermediate/mypackage/arithmetics.py b/Intermediate/mypackage/arithmetics.py new file mode 100644 index 00000000..3ed8294a --- /dev/null +++ b/Intermediate/mypackage/arithmetics.py @@ -0,0 +1,6 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=24010 + +### Arithmetics ### + +def sum_two_values(first_value, second_value): + return first_value + second_value diff --git a/README.md b/README.md index 0ce8ace8..c423682a 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,18 @@ # Hello Python -[![Python](https://img.shields.io/badge/Python-3.9+-yellow?style=for-the-badge&logo=python&logoColor=white&labelColor=101010)](https://python.org) +[![Python](https://img.shields.io/badge/Python-3.10+-yellow?style=for-the-badge&logo=python&logoColor=white&labelColor=101010)](https://python.org) +[![FastAPI](https://img.shields.io/badge/FastAPI-0.88.0+-00a393?style=for-the-badge&logo=fastapi&logoColor=white&labelColor=101010)](https://fastapi.tiangolo.com) +[![MongoDB](https://img.shields.io/badge/MongoDB-6.0+-00684A?style=for-the-badge&logo=mongodb&logoColor=white&labelColor=101010)](https://www.mongodb.com) +[![ChatGPT](https://img.shields.io/badge/ChatGPT-GPT--4-7CF178?style=for-the-badge&logo=openai&logoColor=white&labelColor=101010)](https://platform.openai.com) +[![Reflex](https://img.shields.io/badge/Reflex-0.4.6+-5646ED?style=for-the-badge&logo=reflex&logoColor=white&labelColor=101010)](https://reflex.dev) -## Proyecto realizado durante las emisiones en directo desde [Twitch](https://twitch.tv/mouredev) para aprender Python desde cero +## Curso para aprender el lenguaje de programación Python desde cero y para principiantes -### 🐍 CADA SEMANA UNA NUEVA CLASE EN DIRECTO 🐍 -##### Si consideras útil esta actividad, apóyala haciendo "★ Star" en el repositorio. ¡Gracias! +![](./Images/header.jpg) -> --- -> **🔴 PRÓXIMA CLASE: Jueves 3 de Noviembre a las 20:00 (hora España)** -> -> Mientras, aprovecha para practicar unos [retos de programación](https://retosdeprogramacion.com/semanales2022) y así ir mejorando poco a poco. -> -> En [Discord](https://discord.gg/U3KjjfUfUJ?event=1034770962127790142) tienes creado un [evento](https://discord.gg/U3KjjfUfUJ?event=1034770962127790142) para que consultes la hora de tu país y añadas un recordatorio. -> -> *Finalizada la clase, se actualizará el repositorio con los nuevos recursos* -> -> --- +### Proyecto realizado durante emisiones en directo desde [Twitch](https://twitch.tv/mouredev) + +> ##### Si consideras útil el curso, apóyalo haciendo "★ Star" en el repositorio. ¡Gracias! ## Clases en vídeo @@ -28,80 +24,151 @@ Curso que agrupa todas las clases en directo que hacen referencia a los fundamen -* [Introducción](https://youtu.be/Kp4Mvapo5kc) -* [Contexto](https://youtu.be/Kp4Mvapo5kc?t=244) -* [Lección 1 - Configuración](https://youtu.be/Kp4Mvapo5kc?t=850) -* [Lección 2 - Hola Mundo](https://youtu.be/Kp4Mvapo5kc?t=1518) -* [Lección 3 - Variables](https://youtu.be/Kp4Mvapo5kc?t=2938) -* [Lección 4 - Operadores](https://youtu.be/Kp4Mvapo5kc?t=5665) -* [Lección 5 - Strings](https://youtu.be/Kp4Mvapo5kc?t=8643) -* [Lección 6 - Listas](https://youtu.be/Kp4Mvapo5kc?t=10872) -* [Lección 7 - Tuplas](https://youtu.be/Kp4Mvapo5kc?t=14711) -* [Lección 8 - Sets](https://youtu.be/Kp4Mvapo5kc?t=16335) -* [Lección 9 - Diccionarios](https://youtu.be/Kp4Mvapo5kc) -* [Lección 10 - Condicionales](https://youtu.be/Kp4Mvapo5kc?t=21442) -* [Lección 11 - Bucles/Loops/Ciclos](https://youtu.be/Kp4Mvapo5kc?t=23822) -* [Lección 12 - Funciones](https://youtu.be/Kp4Mvapo5kc?t=26619) -* [Lección 13 - Clases](https://youtu.be/Kp4Mvapo5kc?t=29327) -* [Lección 14 - Excepciones](https://youtu.be/Kp4Mvapo5kc?t=32030) -* [Lección 15 - Módulos](https://youtu.be/Kp4Mvapo5kc?t=34583) -* [Próximos pasos](https://youtu.be/Kp4Mvapo5kc?t=36390) - -### Curso intermedio - -Curso en el que continuamos aprendiendo Python desde sus bases, siguiendo la ruta de aprendizaje desde la última lección del curso de fundamentos. +- [Introducción](https://youtu.be/Kp4Mvapo5kc) +- [Contexto](https://youtu.be/Kp4Mvapo5kc?t=244) +- [Lección 1 - Configuración](https://youtu.be/Kp4Mvapo5kc?t=850) +- [Lección 2 - Hola Mundo](https://youtu.be/Kp4Mvapo5kc?t=1518) +- [Lección 3 - Variables](https://youtu.be/Kp4Mvapo5kc?t=2938) +- [Lección 4 - Operadores](https://youtu.be/Kp4Mvapo5kc?t=5665) +- [Lección 5 - Strings](https://youtu.be/Kp4Mvapo5kc?t=8643) +- [Lección 6 - Listas](https://youtu.be/Kp4Mvapo5kc?t=10872) +- [Lección 7 - Tuplas](https://youtu.be/Kp4Mvapo5kc?t=14711) +- [Lección 8 - Sets](https://youtu.be/Kp4Mvapo5kc?t=16335) +- [Lección 9 - Diccionarios](https://youtu.be/Kp4Mvapo5kc?t=18506) +- [Lección 10 - Condicionales](https://youtu.be/Kp4Mvapo5kc?t=21442) +- [Lección 11 - Bucles/Loops/Ciclos](https://youtu.be/Kp4Mvapo5kc?t=23822) +- [Lección 12 - Funciones](https://youtu.be/Kp4Mvapo5kc?t=26619) +- [Lección 13 - Clases](https://youtu.be/Kp4Mvapo5kc?t=29327) +- [Lección 14 - Excepciones](https://youtu.be/Kp4Mvapo5kc?t=32030) +- [Lección 15 - Módulos](https://youtu.be/Kp4Mvapo5kc?t=34583) +- [Próximos pasos](https://youtu.be/Kp4Mvapo5kc?t=36390) + +### Curso intermedio de fundamentos desde cero + +Curso en el que continuamos aprendiendo Python desde sus bases, siguiendo la ruta de aprendizaje desde la última lección del curso de inicial. > Código: Directorio "Intermediate" en el proyecto -* [Clase 06/10/22 - Dates y List Comprehension](https://www.twitch.tv/videos/1611014007) -* [Clase 13/10/22 - Resolución retos de programación](https://www.twitch.tv/videos/1623225956) -* [Clase 19/10/22 - Lambdas y Funciones de orden superior](https://www.twitch.tv/videos/1628654998) -* [Clase 25/10/22 - Tipos de error y manejo de ficheros .txt](https://www.twitch.tv/videos/1634818287) + -## Información importante y preguntas frecuentes +- [Introducción](https://youtu.be/TbcEqkabAWU) +- [Lección 1 - Dates](https://youtu.be/TbcEqkabAWU?t=202) +- [Lección 2 - List Comprehension](https://youtu.be/TbcEqkabAWU?t=3239) +- [Lección 3 - Resolución de retos de programación](https://youtu.be/TbcEqkabAWU?t=4142) +- [Lección 4 - Lambdas](https://youtu.be/TbcEqkabAWU?t=9145) +- [Lección 5 - Funciones de orden superior](https://youtu.be/TbcEqkabAWU?t=10172) +- [Lección 6 - Tipos de error](https://youtu.be/TbcEqkabAWU?t=12721) +- [Lección 7 - Manejo de ficheros](https://youtu.be/TbcEqkabAWU?t=15524) +- [Lección 8 - Expresiones regulares](https://youtu.be/TbcEqkabAWU?t=19762) +- [Lección 9 - Manejo de paquetes](https://youtu.be/TbcEqkabAWU?t=24010) +- [Próximos pasos](https://youtu.be/TbcEqkabAWU?t=26228) -* **¿Cómo está estructurado el proyecto y el código?** - * Actualmente tienes dos directorios, "Basic" e "Intermediate", correspondientes a cómo están agrupadas las clases. +### Backend desde cero -* **¿Las clases quedan grabadas?** - * Todos los directos de Twitch están disponibles 60 días en la sección [vídeos](https://twitch.tv/mouredev/videos). +Curso en el que aprenderemos a utilizar Python para backend e implementaremos un API REST con autenticación, base de datos y desplegaremos el proyecto en un servidor real. -* **¿Se subirá a YouTube?** - * No te preocupes, antes de que se cumplan los 60 días de Twitch, iré publicando las clases agrupadas en YouTube. +> Código: Directorio "Backend" en el proyecto -* **¿Harás un curso?** - * Agruparé lecciones en YouTube para crear cursos por nivel. Actualmente ya existe el de [fundamentos desde cero](https://youtu.be/Kp4Mvapo5kc). + -* **¿Hasta dónde llegará el curso?** - * Mi idea es repasar los conceptos básicos hasta llegar a crear un backend (en principio). +- [Introducción](https://youtu.be/_y9qQZXE24A) +- [Lección 01 - ¿Qué es un backend?](https://youtu.be/_y9qQZXE24A?t=125) +- [Lección 02 - API y FastAPI](https://youtu.be/_y9qQZXE24A?t=834) +- [Lección 03 - Type Hints](https://youtu.be/_y9qQZXE24A?t=1810) +- [Lección 04 - Configuración FastAPI](https://youtu.be/_y9qQZXE24A?t=2629) +- [Lección 05 - Hola mundo](https://youtu.be/_y9qQZXE24A?t=3504) +- [Lección 06 - Operación GET](https://youtu.be/_y9qQZXE24A?t=5382) +- [Lección 07 - Peticiones HTTP](https://youtu.be/_y9qQZXE24A?t=5925) +- [Lección 08 - Creación API](https://youtu.be/_y9qQZXE24A?t=6099) +- [Lección 09 - Path y Query](https://youtu.be/_y9qQZXE24A?t=7510) +- [Lección 10 - Operaciones POST, PUT y DELETE](https://youtu.be/_y9qQZXE24A?t=8529) +- [Lección 11 - HTTP status codes](https://youtu.be/_y9qQZXE24A?t=11072) +- [Lección 12 - Routers](https://youtu.be/_y9qQZXE24A?t=12475) +- [Lección 13 - Recursos estáticos](https://youtu.be/_y9qQZXE24A?t=13618) +- [Lección 14 - Autorización OAuth2](https://youtu.be/_y9qQZXE24A?t=14094) +- [Lección 15 - OAuth2 JWT](https://youtu.be/_y9qQZXE24A?t=17664) +- [Lección 16 - MongoDB](https://youtu.be/_y9qQZXE24A?t=20480) +- [Lección 17 - MongoDB Atlas](https://youtu.be/_y9qQZXE24A?t=25470) +- [Lección 18 - Despliegue en Deta \*](https://youtu.be/_y9qQZXE24A?t=27335) +- [Próximos pasos](https://youtu.be/_y9qQZXE24A?t=28484) -* **¿Cuándo será la próxima clase?** - * Te recomiendo que me sigas en redes, sobre todo en [Discord](https://discord.gg/mouredev) e [Instagram](https://instagram.com/mouredev) donde creo eventos a diario con la hora de emisión (así podrás ver qué hora es en tu país). - * También he creado en el Discord un canal "🐍curso-python" para que puedas comentar lo que quieras. - * Una vez finalizada la clase subiré los ficheros de código a este repositorio. +**\*ACTUALIZACIÓN Sobre la lección 18:** Deta, el servicio utilizado para el despliegue durante el curso, ya no existe. Te recomiendo revisar la documentación oficial de FastAPI sobre [despliegue](https://fastapi.tiangolo.com/deployment/). Puedes utilizar alguno de los [proveedores gratuitos](https://fastapi.tiangolo.com/deployment/cloud/) recomendados. En mi caso, te dejo el [fichero de configuración](./Backend/FastAPI/vercel.json) y el de [dependencias](./Backend/FastAPI/requirements.txt) para hacerlo desde [Vercel](https://vercel.com/) (al crear el proyecto en la plataforma selecciona que el directorio "Backend/FastAPI" es el root). ---- +### Frontend desde cero -### Enlaces de interés -* [Web oficial de Python](https://www.python.org/) -* [Tutorial oficial de Python en Español](https://docs.python.org/es/3/tutorial/index.html) -* [Repo 30 días de Python](https://github.com/Asabeneh/30-Days-Of-Python) -* [Juego Codédex para aprender Python](https://www.codedex.io/) -* [Visual Studio Code](https://code.visualstudio.com/): El editor que estoy usando +Cursos en los que aprenderemos a utilizar Python para desarrollo web con dos proyectos reales desplegados en producción. Tutoriales en vídeo paso a paso con 9 horas de contenido. ---- + -#### Puedes apoyar mi trabajo haciendo "☆ Star" en el repo o nominarme a "GitHub Star". ¡Gracias! + -[![GitHub Star](https://img.shields.io/badge/GitHub-Nominar_a_star-yellow?style=for-the-badge&logo=github&logoColor=white&labelColor=101010)](https://stars.github.com/nominate/) +[![Curso Python Web](https://img.shields.io/github/stars/mouredev/python-web?label=Curso%20Python%20web&style=social)](https://github.com/mouredev/python-web) -Si quieres unirte a nuestra comunidad de desarrollo, aprender programación de Apps, mejorar tus habilidades y ayudar a la continuidad del proyecto, puedes encontrarnos en: + -[![Twitch](https://img.shields.io/badge/Twitch-Programación_en_directo-9146FF?style=for-the-badge&logo=twitch&logoColor=white&labelColor=101010)](https://twitch.tv/mouredev) -[![Discord](https://img.shields.io/badge/Discord-Servidor_de_la_comunidad-5865F2?style=for-the-badge&logo=discord&logoColor=white&labelColor=101010)](https://mouredev.com/discord) -[![Link](https://img.shields.io/badge/Links_de_interés-moure.dev-39E09B?style=for-the-badge&logo=Linktree&logoColor=white&labelColor=101010)](https://moure.dev) +[![Curso Python Web](https://img.shields.io/github/stars/mouredev/adeviento-web?label=Tutorial%20Python%20web%20extra&style=social)](https://github.com/mouredev/adeviento-web) + +### Aprende a integrar ChatGPT en tu proyecto desde cero + + + +Clase de una hora de duración donde aprenderás a interactuar con ChatGPT desde tu aplicación, mantener conversaciones y establecer el contexto de la IA para potenciar tu proyecto. + +Con todo el código publicado [aquí](https://gist.github.com/mouredev/58abfbcef017efaf3852e8821564c011). + +### Traductor de Voz con IA + + + +Aprende a desarrollar un traductor de voz a varios idiomas utilizando con IA. Creando su Web y todo en 100 líneas. + +Con todo el código publicado [aquí](https://gist.github.com/mouredev/0ea42112751f0187d90d5403d1f333e2). + +### Introducción al Testing + +Taller de introducción a testing con Python creado junto a [Carlos Blé](https://www.carlosble.com) y [Miguel A. Gómez](https://softwarecrafters.io), expertos en la materia. + + + +### Extra: 15 curiosidades sobre Python + +Y para finalizar... ¿Quieres saber aun más sobre él? Aquí tienes 15 curiosidades que quizás no conozcas sobre el lenguaje. + + + +## Información importante y preguntas frecuentes + +Actualmente el curso está en pausa. Se han finalizados los bloques básico, intermedio y backend, y ese era el objetivo inicial del proyecto. +No descarto añadir nuevas lecciones a futuro, pero creo que por el momento puede servir de base a cualquier persona que quiera empezar a aprender este lenguaje. + +- Recuerda que he creado en el [Discord](https://discord.gg/mouredev) un canal "🐍python" para que puedas comentar lo que quieras. +- En el momento que el curso continúe, actualizaré el repositorio y avisaré en redes. + +¡Muchísimas gracias por todo el apoyo mostrado! + +## Enlaces de interés + +- [Web oficial de Python](https://www.python.org/) +- [Tutorial oficial de Python en Español](https://docs.python.org/es/3/tutorial/index.html) +- [Repo 30 días de Python](https://github.com/Asabeneh/30-Days-Of-Python) +- [Juego Codédex para aprender Python](https://www.codedex.io/) +- [Visual Studio Code](https://code.visualstudio.com/): El editor que estoy usando +- [FastAPI](https://fastapi.tiangolo.com/es/): El framework para crear nuestra API Backend +- [MongoDB](https://www.mongodb.com/): La base de datos que utiliza nuestro backend +- [Vercel](https://vercel.com/): Para desplegar nuestra API en la nube + +## Únete al campus de programación de la comunidad + +![https://mouredev.pro](./Images/pro.jpg) + +#### Te presento [mouredev pro](https://mouredev.pro), mi proyecto más importante para ayudarte a estudiar programación y desarrollo de software de manera diferente. + +> **¿Buscas un extra?** Aquí encontrarás este y otros cursos editados por lecciones individuales, para avanzar a tu ritmo y guardar el progreso. También dispondrás de ejercicios y correcciones, test para validar tus conocimientos, examen y certificado público de finalización, soporte, foro de estudiantes, reunionnes grupales, cursos exclusivos y mucho más. +> +> Entra en **[mouredev.pro](https://mouredev.pro)** y utiliza el cupón **"PYTHON"** con un 10% de descuento en tu primera suscripción. ## ![https://mouredev.com](https://raw.githubusercontent.com/mouredev/mouredev/master/mouredev_emote.png) Hola, mi nombre es Brais Moure. + ### Freelance full-stack iOS & Android engineer [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UCxPD7bsocoAMq8Dj18kmGyQ?style=social)](https://youtube.com/mouredevapps?sub_confirmation=1) @@ -111,8 +178,10 @@ Si quieres unirte a nuestra comunidad de desarrollo, aprender programación de A ![GitHub Followers](https://img.shields.io/github/followers/mouredev?style=social) ![GitHub Followers](https://img.shields.io/github/stars/mouredev?style=social) -Soy ingeniero de software desde hace más de 12 años. Desde hace 4 años combino mi trabajo desarrollando Apps con creación de contenido formativo sobre programación y tecnología en diferentes redes sociales como **[@mouredev](https://moure.dev)**. +Soy ingeniero de software desde 2010. Desde 2018 combino mi trabajo desarrollando Apps con la creación de contenido formativo sobre programación y tecnología en diferentes redes sociales como **[@mouredev](https://moure.dev)**. -### En mi perfil de GitHub tienes más información +Si quieres unirte a nuestra comunidad de desarrollo, aprender programación, mejorar tus habilidades y ayudar a la continuidad del proyecto, puedes encontrarnos en: -[![Web](https://img.shields.io/badge/GitHub-MoureDev-14a1f0?style=for-the-badge&logo=github&logoColor=white&labelColor=101010)](https://github.com/mouredev) +[![Twitch](https://img.shields.io/badge/Twitch-Programación_en_directo-9146FF?style=for-the-badge&logo=twitch&logoColor=white&labelColor=101010)](https://twitch.tv/mouredev) +[![Discord](https://img.shields.io/badge/Discord-Servidor_de_la_comunidad-5865F2?style=for-the-badge&logo=discord&logoColor=white&labelColor=101010)](https://mouredev.com/discord) [![Pro](https://img.shields.io/badge/Cursos-mouredev.pro-FF5500?style=for-the-badge&logo=gnometerminal&logoColor=white&labelColor=101010)](https://moure.dev) +[![Link](https://img.shields.io/badge/Links_de_interés-moure.dev-14a1f0?style=for-the-badge&logo=Linktree&logoColor=white&labelColor=101010)](https://moure.dev) [![Web](https://img.shields.io/badge/GitHub-MoureDev-087ec4?style=for-the-badge&logo=github&logoColor=white&labelColor=101010)](https://github.com/mouredev) diff --git a/__pycache__/module.cpython-310.pyc b/__pycache__/module.cpython-310.pyc deleted file mode 100644 index 0d63bb63..00000000 Binary files a/__pycache__/module.cpython-310.pyc and /dev/null differ diff --git a/__pycache__/my_module.cpython-310.pyc b/__pycache__/my_module.cpython-310.pyc deleted file mode 100644 index ca5d74d7..00000000 Binary files a/__pycache__/my_module.cpython-310.pyc and /dev/null differ