From ee31b9b290c7d8dabf6cc4df770249a24e617dff Mon Sep 17 00:00:00 2001 From: exprpapi <121869314+exprpapi@users.noreply.github.com> Date: Thu, 16 Feb 2023 15:16:43 +0100 Subject: [PATCH 1/2] Update melli.py --- melli.py | 151 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 63 deletions(-) diff --git a/melli.py b/melli.py index d6854d2..93c7b96 100644 --- a/melli.py +++ b/melli.py @@ -12,11 +12,17 @@ @app.get("/task1/greet/{name}", tags=["Task 1"], summary="👋🇩🇪🇬🇧🇪🇸") -async def task1_greet(name: str) -> str: +async def task1_greet(name: str, language: str = None) -> str: """Greet somebody in German, English or Spanish!""" - # Write your code below - ... - return f"Hello {name}, I am Melli." + match language: + case 'de' | None: + return f'Hallo {name}, ich bin Melli.' + case 'en': + return f'Hello {name}, I am Melli.' + case 'es': + return f'Hola {name}, soy Melli.' + case _: + return f'Hallo {name}, leider spreche ich nicht \'{language}\'!' """ @@ -28,9 +34,10 @@ async def task1_greet(name: str) -> str: def camelize(key: str): """Takes string in snake_case format returns camelCase formatted version.""" - # Write your code below - ... - return key + head, *tail = key.split('_') + titlecased = (part.title() for part in tail) + parts = (head, *titlecased) + return ''.join(parts) @app.post("/task2/camelize", tags=["Task 2"], summary="🐍➡️🐪") @@ -60,49 +67,54 @@ class ActionResponse(BaseModel): message: str -def handle_call_action(action: str): - # Write your code below - ... - return "🤙 Why don't you call them yourself!" +def handle_call_action(request: ActionRequest) -> ActionResponse: + for friend in friends[request.username]: + if friend in request.action: + return ActionResponse(message=f'🤙 Calling {friend} ...') + return ActionResponse( + message=f'{request.username}, I can\'t find this person in your contacts.' + ) + + +def handle_reminder_action(_: ActionRequest) -> ActionResponse: + return ActionResponse(message='🔔 Alright, I will remind you!') -def handle_reminder_action(action: str): - # Write your code below - ... - return "🔔 I can't even remember my own stuff!" +def handle_timer_action(_: ActionRequest) -> ActionResponse: + return ActionResponse(message='⏰ Alright, the timer is set!') -def handle_timer_action(action: str): - # Write your code below - ... - return "⏰ I don't know how to read the clock!" +def handle_unknown_action(_: ActionRequest) -> ActionResponse: + return ActionResponse(message='👀 Sorry , but I can\'t help with that!') -def handle_unknown_action(action: str): - # Write your code below - ... - return "🤬 #$!@" + +def handle_unknown_user(request: ActionRequest) -> ActionResponse: + message = f'Hi {request.username}, I don\'t know you yet. But I would love to meet you!' + return ActionResponse(message=message) @app.post("/task3/action", tags=["Task 3"], summary="🤌") def task3_action(request: ActionRequest): """Accepts an action request, recognizes its intent and forwards it to the corresponding action handler.""" - # tip: you have to use the response model above and also might change the signature - # of the action handlers - # Write your code below - ... - from random import choice - - # There must be a better way! - handler = choice( - [ - handle_call_action, - handle_reminder_action, - handle_timer_action, - handle_unknown_action, - ] - ) - return handler(request.action) + if request.username not in friends.keys(): + return handle_unknown_user(request) + + action = request.action.lower() + + if 'call' in action: + return handle_call_action(request) + + if 'remind' in action: + return handle_reminder_action(request) + + if 'timer' in action: + return handle_timer_action(request) + + if 'call' in action: + return handle_call_action(request) + + return handle_unknown_action(request) """ @@ -162,19 +174,25 @@ class Token(BaseModel): @app.post("/task4/token", response_model=Token, summary="🔒", tags=["Task 4"]) async def login(form_data: OAuth2PasswordRequestForm = Depends()): """Allows registered users to obtain a bearer token.""" - # fixme 🔨, at the moment we allow everybody to obtain a token - # this is probably not very secure 🛡️ ... - # tip: check the verify_password above - # Write your code below - ... - payload = { - "sub": form_data.username, - "exp": datetime.utcnow() + timedelta(minutes=30), - } - return { - "access_token": encode_jwt(payload), - "token_type": "bearer", - } + is_user = lambda: form_data.username in fake_users_db + + is_authorized = lambda: verify_password( + form_data.password, + fake_users_db[form_data.username]['hashed_password'] + ) + + access_token = lambda: encode_jwt(dict( + sub=form_data.username, + exp=datetime.utcnow() + timedelta(minutes=30) + )) + + if is_user() and is_authorized(): + return dict(access_token=access_token(), token_type='bearer') + + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail='Incorrect username or password', + ) def get_user(username: str) -> Optional[User]: @@ -184,15 +202,18 @@ def get_user(username: str) -> Optional[User]: async def get_current_user(token: str = Depends(oauth2_scheme)) -> User: - credentials_exception = HTTPException( + credentials_exception = lambda: HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid authentication credentials", - headers={"WWW-Authenticate": "Bearer"}, + detail='Invalid authentication credentials', + headers={'WWW-Authenticate': 'Bearer'}, ) - # check if the token 🪙 is valid and return a user as specified by the tokens payload - # otherwise raise the credentials_exception above - # Write your code below - ... + + user = decode_jwt(token)['sub'] + + if user in fake_users_db: + return get_user(user) + + raise credentials_exception() @app.get("/task4/users/{username}/secret", summary="🤫", tags=["Task 4"]) @@ -200,12 +221,16 @@ async def read_user_secret( username: str, current_user: User = Depends(get_current_user) ): """Read a user's secret.""" - # uppps 🤭 maybe we should check if the requested secret actually belongs to the user - # Write your code below - ... - if user := get_user(username): + user = get_user(username) + + if user == current_user: return user.secret + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail='Don\'t spy on other user!', + ) + """ Task and Help Routes From ec88f0829412fda7eca45aca07ae10cea37fafb4 Mon Sep 17 00:00:00 2001 From: exprpapi <121869314+exprpapi@users.noreply.github.com> Date: Thu, 16 Feb 2023 15:23:04 +0100 Subject: [PATCH 2/2] Abandon `match`-statement because of `python` version < 3.10 --- melli.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/melli.py b/melli.py index 93c7b96..bfb0394 100644 --- a/melli.py +++ b/melli.py @@ -14,15 +14,16 @@ @app.get("/task1/greet/{name}", tags=["Task 1"], summary="👋🇩🇪🇬🇧🇪🇸") async def task1_greet(name: str, language: str = None) -> str: """Greet somebody in German, English or Spanish!""" - match language: - case 'de' | None: + if language in ('de', None): return f'Hallo {name}, ich bin Melli.' - case 'en': + + if language == 'en': return f'Hello {name}, I am Melli.' - case 'es': + + if language == 'es': return f'Hola {name}, soy Melli.' - case _: - return f'Hallo {name}, leider spreche ich nicht \'{language}\'!' + + return f'Hallo {name}, leider spreche ich nicht \'{language}\'!' """