Merge remote-tracking branch 'origin/main'
This commit is contained in:
3
.env.example
Normal file
3
.env.example
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
API_URI=
|
||||||
|
API_KEY=
|
||||||
|
API_TEAM=
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
9
app.py
9
app.py
@ -1,5 +1,9 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
|
from dto.requests import GameStartRequest
|
||||||
|
from services.julius_baer_api_client import JuliusBaerApiClient
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -9,4 +13,9 @@ def hello_world(): # put application's code here
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
jb_client = JuliusBaerApiClient()
|
||||||
|
game_start_request = GameStartRequest("Welch")
|
||||||
|
res = jb_client.start_game(game_start_request)
|
||||||
|
print(res)
|
||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
|
8
config.py
Normal file
8
config.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
API_URI = str(os.getenv("API_URI") or "")
|
||||||
|
API_KEY = str(os.getenv("API_KEY") or "")
|
||||||
|
API_TEAM = str(os.getenv("API_TEAM") or "")
|
0
dto/__init__.py
Normal file
0
dto/__init__.py
Normal file
15
dto/errors.py
Normal file
15
dto/errors.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ValidationError:
|
||||||
|
"""Model for validation errors."""
|
||||||
|
loc: list
|
||||||
|
msg: str
|
||||||
|
type: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HTTPValidationError:
|
||||||
|
"""Model for HTTP validation errors."""
|
||||||
|
detail: list[ValidationError] = field(default_factory=list)
|
17
dto/requests.py
Normal file
17
dto/requests.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Literal
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GameStartRequest:
|
||||||
|
"""Request model for starting a new game."""
|
||||||
|
player_name: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GameDecisionRequest:
|
||||||
|
"""Request model for making a game decision."""
|
||||||
|
decision: Literal["Accept", "Reject"]
|
||||||
|
session_id: UUID
|
||||||
|
client_id: UUID
|
23
dto/responses.py
Normal file
23
dto/responses.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Dict, Optional, Any
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GameStartResponse:
|
||||||
|
"""Response model for a new game started."""
|
||||||
|
message: str
|
||||||
|
session_id: UUID
|
||||||
|
player_id: str
|
||||||
|
client_id: UUID
|
||||||
|
client_data: Dict[str, Any]
|
||||||
|
score: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GameDecisionResponse:
|
||||||
|
"""Response model for a game decision result."""
|
||||||
|
status: str
|
||||||
|
score: int
|
||||||
|
client_id: Optional[UUID] = None
|
||||||
|
client_data: Optional[Dict[str, Any]] = None
|
1
openapi.json
Normal file
1
openapi.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"openapi":"3.1.0","info":{"title":"Master","version":"0.1.0"},"paths":{"/game/start":{"post":{"summary":"Game Start","description":"Start a new game session for the player.","operationId":"game_start_game_start_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameStartRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameStartResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/game/decision":{"post":{"summary":"Game Decision","description":"Log decision, update the score and proceed with game.","operationId":"game_decision_game_decision_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameDecisionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameDecisionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"GameDecisionRequest":{"properties":{"decision":{"type":"string","enum":["Accept","Reject"],"title":"Decision","description":"Decision of the player.","example":"Accept"},"session_id":{"type":"string","format":"uuid","title":"Session Id","description":"Unique session ID for the game.","example":"6e8af95a-1819-4ac4-82b7-88846cf907cc"},"client_id":{"type":"string","format":"uuid","title":"Client Id","description":"Unique client ID for the game.","example":"2eb62743-b5e9-4225-b4b6-1ef4aaaf7813"}},"type":"object","required":["decision","session_id","client_id"],"title":"GameDecisionRequest"},"GameDecisionResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Status of the game after the decision.","example":"gameover"},"score":{"type":"integer","title":"Score","description":"Current score of the player.","example":1},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id","description":"Unique client ID for the game.","example":"71737c82-8123-47a1-bc22-4ecf47ac5d7d"},"client_data":{"type":"object","title":"Client Data","description":"Client data for the player.","example":{"passport":"123456789"}}},"type":"object","required":["status","score"],"title":"GameDecisionResponse"},"GameStartRequest":{"properties":{"player_name":{"type":"string","title":"Player Name","description":"Name of the player.","example":"Haligali"}},"type":"object","required":["player_name"],"title":"GameStartRequest"},"GameStartResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Message indicating the game has started.","example":"Game started successfully."},"session_id":{"type":"string","format":"uuid","title":"Session Id","description":"Unique session ID for the game.","example":"92ab2b1a-a3b5-4e36-af59-2d4083a18ee6"},"player_id":{"type":"string","title":"Player Id","description":"Unique player ID for the game.","example":"some_key"},"client_id":{"type":"string","format":"uuid","title":"Client Id","description":"Unique client ID for the game.","example":"42048bf6-5947-4797-bac9-348e23dcc904"},"client_data":{"type":"object","title":"Client Data","description":"Client data for the player.","example":{"data":{}}},"score":{"type":"integer","title":"Score","description":"Starting score of the player.","example":0}},"type":"object","required":["message","session_id","player_id","client_id","client_data","score"],"title":"GameStartResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}
|
@ -1,8 +1,25 @@
|
|||||||
|
annotated-types==0.7.0
|
||||||
|
blinker==1.9.0
|
||||||
certifi==2025.1.31
|
certifi==2025.1.31
|
||||||
charset-normalizer==3.4.1
|
charset-normalizer==3.4.1
|
||||||
|
click==8.1.8
|
||||||
|
Flask==3.1.0
|
||||||
idna==3.10
|
idna==3.10
|
||||||
|
itsdangerous==2.2.0
|
||||||
|
Jinja2==3.1.6
|
||||||
|
MarkupSafe==3.0.2
|
||||||
|
openapi-client==1.1.7
|
||||||
packaging==24.2
|
packaging==24.2
|
||||||
pillow==11.1.0
|
pillow==11.1.0
|
||||||
|
pydantic==2.11.3
|
||||||
|
pydantic_core==2.33.1
|
||||||
pytesseract==0.3.13
|
pytesseract==0.3.13
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
|
six==1.17.0
|
||||||
|
typing-inspection==0.4.0
|
||||||
|
typing_extensions==4.13.2
|
||||||
urllib3==2.4.0
|
urllib3==2.4.0
|
||||||
|
Werkzeug==3.1.3
|
||||||
|
Flask==3.1.0
|
||||||
|
python-dotenv=1.1.0
|
0
services/__init__.py
Normal file
0
services/__init__.py
Normal file
87
services/julius_baer_api_client.py
Normal file
87
services/julius_baer_api_client.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
import config
|
||||||
|
from dto.requests import GameStartRequest, GameDecisionRequest
|
||||||
|
|
||||||
|
|
||||||
|
class JuliusBaerApiClient:
|
||||||
|
"""
|
||||||
|
Client for interacting with the Julius Baer API service.
|
||||||
|
|
||||||
|
Provides methods to start a game and make game decisions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.api_uri = config.API_URI
|
||||||
|
self.api_key = config.API_KEY
|
||||||
|
self.api_team = config.API_TEAM
|
||||||
|
|
||||||
|
def start_game(self, game_start_request: GameStartRequest) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Start a new game session.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_name: Name of the player.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing the game start response with session_id, player_id, etc.
|
||||||
|
"""
|
||||||
|
url = f"{self.api_uri}/game/start"
|
||||||
|
payload = {"player_name": game_start_request.player_name}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"x-api-key": self.api_key,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, json=payload, headers=headers)
|
||||||
|
response.raise_for_status() # Raise exception for HTTP errors
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Store session_id and client_id for convenience in future calls
|
||||||
|
self.session_id = data.get("session_id")
|
||||||
|
self.client_id = data.get("client_id")
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def make_decision(self, game_decision_request: GameDecisionRequest) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Make a game decision (Accept or Reject).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
decision: Either "Accept" or "Reject".
|
||||||
|
session_id: Unique session ID for the game. If None, uses the stored session_id.
|
||||||
|
client_id: Unique client ID for the game. If None, uses the stored client_id.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing the game decision response with status, score, etc.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If decision is not "Accept" or "Reject".
|
||||||
|
ValueError: If session_id and client_id are not provided or stored from a previous start_game call.
|
||||||
|
"""
|
||||||
|
if game_decision_request.decision not in ["Accept", "Reject"]:
|
||||||
|
raise ValueError('Decision must be either "Accept" or "Reject"')
|
||||||
|
|
||||||
|
# Use stored values if not provided
|
||||||
|
session_id = game_decision_request.session_id or self.session_id
|
||||||
|
client_id = game_decision_request.client_id or self.client_id
|
||||||
|
|
||||||
|
if not session_id or not client_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Session ID and Client ID are required. Either provide them explicitly or call start_game first.")
|
||||||
|
|
||||||
|
url = f"{self.base_url}/game/decision"
|
||||||
|
payload = {
|
||||||
|
"decision": game_decision_request.decision,
|
||||||
|
"session_id": session_id,
|
||||||
|
"client_id": client_id
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, json=payload)
|
||||||
|
response.raise_for_status() # Raise exception for HTTP errors
|
||||||
|
|
||||||
|
return response.json()
|
Reference in New Issue
Block a user