diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..029519e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.py[cod] \ No newline at end of file diff --git a/00_helloworld.py b/00_helloworld.py deleted file mode 100644 index 2459d9b..0000000 --- a/00_helloworld.py +++ /dev/null @@ -1,29 +0,0 @@ -# Clase 1 (03/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1551265068 - -### Hola Mundo ### - -# Nuestro hola mundo en Python -print("Hola Python") -print('Hola Python') - -# Esto es un comentario - -""" -Este es un -comentario -en varias líneas -""" - -''' -Este también es un -comentario -en varias líneas -''' - -# 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 diff --git a/Backend/FastAPI/db/client.py b/Backend/FastAPI/db/client.py new file mode 100644 index 0000000..bfccb67 --- /dev/null +++ b/Backend/FastAPI/db/client.py @@ -0,0 +1,26 @@ +# 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 - https://www.deta.sh/ +# Intrucciones - https://fastapi.tiangolo.com/deployment/deta/ diff --git a/Backend/FastAPI/db/models/user.py b/Backend/FastAPI/db/models/user.py new file mode 100644 index 0000000..b26dc09 --- /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] + 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 0000000..cdfdcd5 --- /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 0000000..09d72fa --- /dev/null +++ b/Backend/FastAPI/main.py @@ -0,0 +1,50 @@ +# 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 + +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 0000000..8ab5160 --- /dev/null +++ b/Backend/FastAPI/requirements.txt @@ -0,0 +1,7 @@ +# Clase en vídeo: https://youtu.be/_y9qQZXE24A?t=27335 +fastapi +python-jose +passlib +bcrypt +pymongo +python-multipart \ 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 0000000..054b991 --- /dev/null +++ b/Backend/FastAPI/routers/basic_auth_users.py @@ -0,0 +1,88 @@ +# 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@mourede.com", + "disabled": False, + "password": "123456" + }, + "mouredev2": { + "username": "mouredev2", + "full_name": "Brais Moure 2", + "email": "braismoure2@mourede.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 0000000..c942b0c --- /dev/null +++ b/Backend/FastAPI/routers/jwt_auth_users.py @@ -0,0 +1,113 @@ +# 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 + +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@mourede.com", + "disabled": False, + "password": "$2a$12$B2Gq.Dps1WYf2t57eiIKjO4DXC3IUMUXISJF62bSRiFfqMdOI2Xa6" + }, + "mouredev2": { + "username": "mouredev2", + "full_name": "Brais Moure 2", + "email": "braismoure2@mourede.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.utcnow() + 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 0000000..6d85b33 --- /dev/null +++ b/Backend/FastAPI/routers/products.py @@ -0,0 +1,22 @@ +# 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 0000000..46aca70 --- /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 0000000..7e61d75 --- /dev/null +++ b/Backend/FastAPI/routers/users_db.py @@ -0,0 +1,79 @@ +# 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 0000000..0be1c65 Binary files /dev/null and b/Backend/FastAPI/static/images/python.jpg differ diff --git a/Backend/type_hints.py b/Backend/type_hints.py new file mode 100644 index 0000000..42b7c01 --- /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 new file mode 100644 index 0000000..d2c6431 --- /dev/null +++ b/Basic/00_helloworld.py @@ -0,0 +1,29 @@ +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc + +### Hola Mundo ### + +# Nuestro hola mundo en Python +print("Hola Python") +print('Hola Python') + +# Esto es un comentario + +""" +Este es un +comentario +en varias líneas +""" + +''' +Este también es un +comentario +en varias líneas +''' + +# 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' diff --git a/01_variables.py b/Basic/01_variables.py similarity index 83% rename from 01_variables.py rename to Basic/01_variables.py index 4b6c316..682c3f7 100644 --- a/01_variables.py +++ b/Basic/01_variables.py @@ -1,4 +1,4 @@ -# Clase 1 (03/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1551265068 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=2938 ### Variables ### @@ -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/02_operators.py b/Basic/02_operators.py similarity index 79% rename from 02_operators.py rename to Basic/02_operators.py index de4b8a3..b7e7fb0 100644 --- a/02_operators.py +++ b/Basic/02_operators.py @@ -1,4 +1,4 @@ -# Clase 2 (10/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1558018826 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=5665 ### Operadores Aritméticos ### @@ -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/03_strings.py b/Basic/03_strings.py similarity index 87% rename from 03_strings.py rename to Basic/03_strings.py index 506c8c4..aca89f8 100644 --- a/03_strings.py +++ b/Basic/03_strings.py @@ -1,4 +1,4 @@ -# Clase 2 (10/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1558018826 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=8643 ### Strings ### @@ -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/04_lists.py b/Basic/04_lists.py similarity index 90% rename from 04_lists.py rename to Basic/04_lists.py index 5671bd0..d6e6d58 100644 --- a/04_lists.py +++ b/Basic/04_lists.py @@ -1,4 +1,4 @@ -# Clase 3 (17/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1564719056 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=10872 ### Lists ### @@ -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/05_tuples.py b/Basic/05_tuples.py similarity index 67% rename from 05_tuples.py rename to Basic/05_tuples.py index 8beaeca..d2cca5b 100644 --- a/05_tuples.py +++ b/Basic/05_tuples.py @@ -1,4 +1,4 @@ -# Clase 3 (17/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1564719056 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=14711 ### Tuples ### @@ -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/06_sets.py b/Basic/06_sets.py similarity index 56% rename from 06_sets.py rename to Basic/06_sets.py index 6019f38..4a6d2b1 100644 --- a/06_sets.py +++ b/Basic/06_sets.py @@ -1,4 +1,4 @@ -# Clase 4 (24/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1571410092 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=16335 ### Sets ### @@ -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/07_dicts.py b/Basic/07_dicts.py similarity index 77% rename from 07_dicts.py rename to Basic/07_dicts.py index faee74c..b76aff1 100644 --- a/07_dicts.py +++ b/Basic/07_dicts.py @@ -1,4 +1,4 @@ -# Clase 4 (24/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1571410092 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc ### Dictionaries ### @@ -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/08_conditionals.py b/Basic/08_conditionals.py similarity index 76% rename from 08_conditionals.py rename to Basic/08_conditionals.py index e6389c7..27594f4 100644 --- a/08_conditionals.py +++ b/Basic/08_conditionals.py @@ -1,4 +1,4 @@ -# Clase 5 (31/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1578036618 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=21442 ### Conditionals ### @@ -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/09_loops.py b/Basic/09_loops.py similarity index 77% rename from 09_loops.py rename to Basic/09_loops.py index 96f3d43..6db69d9 100644 --- a/09_loops.py +++ b/Basic/09_loops.py @@ -1,4 +1,4 @@ -# Clase 5 (31/08/22) en directo desde Twitch: https://www.twitch.tv/videos/1578036618 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=23822 ### Loops ### @@ -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/10_functions.py b/Basic/10_functions.py similarity index 73% rename from 10_functions.py rename to Basic/10_functions.py index 3d1fecf..7360466 100644 --- a/10_functions.py +++ b/Basic/10_functions.py @@ -1,21 +1,24 @@ -# Clase 6 (08/09/22) en directo desde Twitch: https://www.twitch.tv/videos/1585369113 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=26619 ### Functions ### # 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/11_classes.py b/Basic/11_classes.py similarity index 57% rename from 11_classes.py rename to Basic/11_classes.py index 91c157d..28e8469 100644 --- a/11_classes.py +++ b/Basic/11_classes.py @@ -1,28 +1,31 @@ -# Clase 6 (08/09/22) en directo desde Twitch: https://www.twitch.tv/videos/1585369113 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=29327 ### Classes ### # 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/12_exceptions.py b/Basic/12_exceptions.py similarity index 89% rename from 12_exceptions.py rename to Basic/12_exceptions.py index 4edf9d6..b0ee410 100644 --- a/12_exceptions.py +++ b/Basic/12_exceptions.py @@ -1,4 +1,4 @@ -# Clase 7 (15/09/22) en directo desde Twitch: https://www.twitch.tv/videos/1591757464 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=32030 ### Exception Handling ### @@ -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/13_modules.py b/Basic/13_modules.py similarity index 78% rename from 13_modules.py rename to Basic/13_modules.py index ddc7e11..c6c2657 100644 --- a/13_modules.py +++ b/Basic/13_modules.py @@ -1,20 +1,22 @@ +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=34583 + ### 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/my_module.py b/Basic/my_module.py similarity index 59% rename from my_module.py rename to Basic/my_module.py index 7058b21..1b65d2b 100644 --- a/my_module.py +++ b/Basic/my_module.py @@ -1,9 +1,10 @@ -# Clase 7 (15/09/22) en directo desde Twitch: https://www.twitch.tv/videos/1591757464 +# Clase en vídeo: https://youtu.be/Kp4Mvapo5kc?t=34583 ### Módulo para pruebas ### 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 0000000..c5522dd 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 0000000..986b770 Binary files /dev/null and b/Images/pro.jpg differ diff --git a/Intermediate/00_dates.py b/Intermediate/00_dates.py new file mode 100644 index 0000000..26b1411 --- /dev/null +++ b/Intermediate/00_dates.py @@ -0,0 +1,75 @@ +# 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) + print(date.day) + print(date.hour) + print(date.minute) + print(date.second) + print(date.timestamp()) + + +print_date(now) + +year_2023 = datetime(2023, 1, 1) + +print_date(year_2023) + +# Time + + +current_time = time(21, 6, 0) + +print(current_time.hour) +print(current_time.minute) +print(current_time.second) + +# Date + + +current_date = date.today() + +print(current_date.year) +print(current_date.month) +print(current_date.day) + +current_date = date(2022, 10, 6) + +print(current_date.year) +print(current_date.month) +print(current_date.day) + +current_date = date(current_date.year, + current_date.month + 1, current_date.day) + +print(current_date.month) + +# Operaciones con fechas + +diff = year_2023 - now +print(diff) + +diff = year_2023.date() - current_date +print(diff) + +# Timedelta + + +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) diff --git a/Intermediate/01_list_comprehension.py b/Intermediate/01_list_comprehension.py new file mode 100644 index 0000000..eb84749 --- /dev/null +++ b/Intermediate/01_list_comprehension.py @@ -0,0 +1,28 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=3239 + +### List Comprehension ### + +my_original_list = [0, 1, 2, 3, 4, 5, 6, 7] +print(my_original_list) + +my_range = range(8) +print(list(my_range)) + +# Definición + +my_list = [i + 1 for i in range(8)] +print(my_list) + +my_list = [i * 2 for i in range(8)] +print(my_list) + +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) diff --git a/Intermediate/02_challenges.py b/Intermediate/02_challenges.py new file mode 100644 index 0000000..a816e67 --- /dev/null +++ b/Intermediate/02_challenges.py @@ -0,0 +1,115 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=4142 + +### Challenges ### + +""" +EL FAMOSO "FIZZ BUZZ”: +Escribe un programa que muestre por consola (con un print) los +números de 1 a 100 (ambos incluidos y con un salto de línea entre +cada impresión), sustituyendo los siguientes: +- Múltiplos de 3 por la palabra "fizz". +- Múltiplos de 5 por la palabra "buzz". +- Múltiplos de 3 y de 5 a la vez por la palabra "fizzbuzz". +""" + + +def fizzbuzz(): + for index in range(1, 101): + if index % 3 == 0 and index % 5 == 0: + print("fizzbuzz") + elif index % 3 == 0: + print("fizz") + elif index % 5 == 0: + print("buzz") + else: + print(index) + + +fizzbuzz() + +""" +¿ES UN ANAGRAMA? +Escribe una función que reciba dos palabras (String) y retorne +verdadero o falso (Bool) según sean o no anagramas. +- Un Anagrama consiste en formar una palabra reordenando TODAS + las letras de otra palabra inicial. +- NO hace falta comprobar que ambas palabras existan. +- Dos palabras exactamente iguales no son anagrama. +""" + + +def is_anagram(word_one, word_two): + if word_one.lower() == word_two.lower(): + return False + return sorted(word_one.lower()) == sorted(word_two.lower()) + + +print(is_anagram("Amor", "Roma")) + +""" +LA SUCESIÓN DE FIBONACCI +Escribe un programa que imprima los 50 primeros números de la sucesión +de Fibonacci empezando en 0. +- La serie Fibonacci se compone por una sucesión de números en + la que el siguiente siempre es la suma de los dos anteriores. + 0, 1, 1, 2, 3, 5, 8, 13... +""" + + +def fibonacci(): + + prev = 0 + next = 1 + + for index in range(0, 50): + print(prev) + fib = prev + next + prev = next + next = fib + + +fibonacci() + +""" +¿ES UN NÚMERO PRIMO? +Escribe un programa que se encargue de comprobar si un número es o no primo. +Hecho esto, imprime los números primos entre 1 y 100. +""" + + +def is_prime(): + + for number in range(1, 101): + + if number >= 2: + + is_divisible = False + + for index in range(2, number): + if number % index == 0: + is_divisible = True + break + + if not is_divisible: + print(number) + + +is_prime() + +""" +INVIRTIENDO CADENAS +Crea un programa que invierta el orden de una cadena de texto +sin usar funciones propias del lenguaje que lo hagan de forma automática. +- Si le pasamos "Hola mundo" nos retornaría "odnum aloH" +""" + + +def reverse(text): + text_len = len(text) + reversed_text = "" + for index in range(0, text_len): + reversed_text += text[text_len - index - 1] + return reversed_text + + +print(reverse("Hola mundo")) diff --git a/Intermediate/03_lambdas.py b/Intermediate/03_lambdas.py new file mode 100644 index 0000000..d51f8fd --- /dev/null +++ b/Intermediate/03_lambdas.py @@ -0,0 +1,14 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=9145 + +### Lambdas ### + +sum_two_values = lambda first_value, second_value: first_value + second_value +print(sum_two_values(2, 4)) + +multiply_values = lambda first_value, second_value: first_value * second_value - 3 +print(multiply_values(2, 4)) + +def sum_three_values(value): + return lambda first_value, second_value: first_value + second_value + value + +print(sum_three_values(5)(2, 4)) \ No newline at end of file diff --git a/Intermediate/04_higher_order_functions.py b/Intermediate/04_higher_order_functions.py new file mode 100644 index 0000000..4c0c6e0 --- /dev/null +++ b/Intermediate/04_higher_order_functions.py @@ -0,0 +1,69 @@ +# 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)) + +### Built-in Higher Order Functions ### + +numbers = [2, 5, 10, 21, 3, 30] + +# 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)) diff --git a/Intermediate/05_error_types.py b/Intermediate/05_error_types.py new file mode 100644 index 0000000..62566c8 --- /dev/null +++ b/Intermediate/05_error_types.py @@ -0,0 +1,51 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=12721 + +### Error Types ### + +# SyntaxError +# print "¡Hola comunidad!" # Descomentar para Error +from math import pi +import math +print("¡Hola comunidad!") + +# NameError +language = "Spanish" # Comentar para Error +print(language) + +# IndexError +my_list = ["Python", "Swift", "Kotlin", "Dart", "JavaScript"] +print(my_list[0]) +print(my_list[4]) +print(my_list[-1]) +# print(my_list[5]) # Descomentar para Error + +# ModuleNotFoundError +# import maths # Descomentar para Error + +# AttributeError +# print(math.PI) # Descomentar para Error +print(math.pi) + +# KeyError +my_dict = {"Nombre": "Brais", "Apellido": "Moure", "Edad": 35, 1: "Python"} +print(my_dict["Edad"]) +# print(my_dict["Apelido"]) # Descomentar para Error +print(my_dict["Apellido"]) + +# TypeError +# print(my_list["0"]) # Descomentar para Error +print(my_list[0]) +print(my_list[False]) + +# ImportError +# from math import PI # Descomentar para Error +print(pi) + +# ValueError +# 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) diff --git a/Intermediate/06_file_handling.py b/Intermediate/06_file_handling.py new file mode 100644 index 0000000..3bf072b --- /dev/null +++ b/Intermediate/06_file_handling.py @@ -0,0 +1,99 @@ +# Clase en vídeo: https://youtu.be/TbcEqkabAWU?t=15524 + +### File Handling ### + +import xml +import csv +import json +import os + +# .txt file + +# 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") + +# 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") + +# 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() + +# 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") + +# .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 0000000..086d79b --- /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 0000000..e0e7410 --- /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 0000000..eeb0e38 --- /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 0000000..dec5a3c --- /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/my_file.txt b/Intermediate/my_file.txt new file mode 100644 index 0000000..e8fb110 --- /dev/null +++ b/Intermediate/my_file.txt @@ -0,0 +1,6 @@ +Mi nombre es Brais +Mi apellido es Moure +35 años +Y mi lenguaje preferido es Python +Aunque también me gusta Kotlin +Y Swift \ No newline at end of file diff --git a/Intermediate/mypackage/__init__.py b/Intermediate/mypackage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Intermediate/mypackage/arithmetics.py b/Intermediate/mypackage/arithmetics.py new file mode 100644 index 0000000..3ed8294 --- /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 0e0bd85..c93afa6 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,23 @@ # 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: Miércoles 5 de Octubre a las 20:00 (hora España)** -> -> Nos tomamos un pequeño descanso ya que estaré de viaje. ¡Aprovecha para practicar unos [retos de programación](https://retosdeprogramacion.com/semanales2022)! -> -> En [Discord](https://discord.gg/U3KjjfUfUJ?event=1020321837353287740) tienes creado un [evento](https://discord.gg/U3KjjfUfUJ?event=1020321837353287740) 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! + +## ¡NUEVO! Curso de Python para web + +[![Curso Python Web](https://img.shields.io/github/stars/mouredev/python-web?label=Curso%20Python%20web&style=social)](https://github.com/mouredev/python-web) + + ## Clases en vídeo @@ -24,6 +25,8 @@ Curso que agrupa todas las clases en directo que hacen referencia a los fundamentos de Python. +> Código: Directorio "Basic" en el proyecto + * [Introducción](https://youtu.be/Kp4Mvapo5kc) @@ -36,7 +39,7 @@ Curso que agrupa todas las clases en directo que hacen referencia a los fundamen * [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 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) @@ -45,45 +48,129 @@ Curso que agrupa todas las clases en directo que hacen referencia a los fundamen * [Lección 15 - Módulos](https://youtu.be/Kp4Mvapo5kc?t=34583) * [Próximos pasos](https://youtu.be/Kp4Mvapo5kc?t=36390) -## Información importante y preguntas frecuentes +### 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 + + + +* [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) + +### Backend desde cero + +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. + +> Código: Directorio "Backend" en el proyecto + + + +* [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) + +***ACTUALIZACIÓN Sobre la lección 18:** Deta ha actualizado ligeramente su servicio de despliegue de aplicaciones con FastAPI. Tienes toda la documentación [aquí](https://deta.space/docs/en/quickstart-guides/python#fastapi). También han creado una [guía de migración](https://deta.space/migration/guides/migrate-a-micro/). + +### Frontend desde cero + +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. + + + + -* **¿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 Python Web](https://img.shields.io/github/stars/mouredev/python-web?label=Curso%20Python%20web&style=social)](https://github.com/mouredev/python-web) -* **¿Se subirá a YouTube?** - * No te preocupes, antes de que se cumplan los 60 días de Twitch, iré publicando las clases en YouTube editadas. + -* **¿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). +[![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) -* **¿Hasta dónde llegará el curso?** - * Mi idea es repasar los conceptos básicos hasta llegar a crear un backend (en principio). +### Aprende a integrar ChatGPT en tu proyecto desde cero -* **¿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. + ---- +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 -### 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 +* [Deta](https://www.deta.sh/): Para desplegar nuestra API en la nube ---- - -#### Puedes apoyar mi trabajo haciendo "☆ Star" en el repo o nominarme a "GitHub Star". ¡Gracias! +## Únete al campus de programación de la comunidad -[![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/) +![https://mouredev.pro](./Images/pro.jpg) -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: +#### 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. -[![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) +> **¿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 @@ -95,8 +182,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 0d63bb6..0000000 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 ca5d74d..0000000 Binary files a/__pycache__/my_module.cpython-310.pyc and /dev/null differ