Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sampathweb committed Aug 20, 2016
0 parents commit 6502e52
Show file tree
Hide file tree
Showing 23 changed files with 1,848 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Virtual Env
venv/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# OSX Filesystem thing
.DS_Store

# Log Files
*.log

# Working Files - not ready to be shared
local/

# IPython NB Checkpoints
.ipynb_checkpoints/
10 changes: 10 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

The MIT License (MIT)
Copyright (c) 2016, Ramesh Sampath

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Sample IRIS API Application

Build IRIS Machine Learning Model using Scikit-Learn and deploy using Tornado Web Framework.

## Setup Environment on Local Machine

### Installation

```
cookiecutter https://github.com/sampathweb/cc-iris-api
cd <repo> # cd iris-api
# Install Packages
python env/create_env.py
source activate env/venv # Windows users: activate env/venv
python env/install_packages.py
# Build the Model
python ml_src/build_model.py
# Run the App
python run.py
````
### Test App
1. Open Browser: [http://localhost:9000](http://localhost:9000)
2. Command Line:
```
curl -i http://localhost:9000/api/iris/predict -X POST -d '{ "sepal_length": 2, "sepal_width": 5, "petal_length": 3, "petal_width": 4}'
```
3. Jupyter Notebook:
Open new terminal navigate to the new folder `iris-api`. Start `jupyter notebook`. Open ml_src -> `api_client.ipynb`. Test the API.
Api works!
## Credits:
Template from https://github.com/sampathweb/cc-iris-api
### The End.
Empty file added app/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
Appliction configuration settings
"""
import os

from tornado.options import define

define("debug", default=True, help="Debug settings")
define("port", default=9000, help="Port to run the server on")

APP_DIR = os.path.dirname(os.path.realpath(__file__))

settings = {
"template_path": os.path.join(APP_DIR, "templates"),
"static_path": os.path.join(APP_DIR, "static")
}
103 changes: 103 additions & 0 deletions app/game_managers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from board_games.tic_tac_toe import TicTacToe, InvalidMoveError


class GameManager(object):

def __init__(self):
# Record all the games
self.games = {}
self.max_game_id = 100


def new_game(self, handler):
self.max_game_id += 1
self.games[self.max_game_id] = {
"handler_a": handler
}
return self.max_game_id

def join_game(self, game_id, handler):

game = self.games.get(game_id)
if game:
game["handler_b"] = handler
return game_id
# Game ID not found
# Raise Error
return None

def end_game(self, game_id):

if game_id in self.games:
del self.games[game_id]


def get_pair(self, game_id, handler):

game = self.games.get(game_id)
if game:
if handler == game.get("handler_a"):
return game.get("handler_b")
else:
return game.get("handler_a")
return None

def get_game(self, game_id):
game = self.games.get(game_id)
if game:
return game
else:
# TODO: Raise Error
return None



class TicTacToeGameManager(GameManager):

def new_game(self, handler):
game_id = super().new_game(handler)
game = self.games[game_id]

game["tic_tac_toe"] = TicTacToe()
return game_id


def record_move(self, game_id, selection, handler):

game = self.get_game(game_id)
if handler == game.get("handler_a"):
game["tic_tac_toe"].record_player_a_move(selection)
elif handler == game.get("handler_b"):
game["tic_tac_toe"].record_player_b_move(selection)


def abort_game(self, game_id):
game = self.get_game(game_id)
tic_tac_toe = game["tic_tac_toe"]
tic_tac_toe.abort_game()

def has_game_ended(self, game_id):

game = self.get_game(game_id)
tic_tac_toe = game["tic_tac_toe"]
if tic_tac_toe.has_ended():
game["result"] = tic_tac_toe.game_result
return True
return False

def get_game_result(self, game_id, handler):

game = self.get_game(game_id)
if not game.get("result"):
# Compute game result
self.has_game_ended(game_id)

if game["result"] == "D":
return game["result"]
elif (game["result"] == "A" and game["handler_a"] == handler) or \
(game["result"] == "B" and game["handler_b"] == handler):
return "W"
elif game["result"]:
return "L"
else:
return "" # Game is still ON.
136 changes: 136 additions & 0 deletions app/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
"""
Request Handlers
"""
from builtins import super
import logging
import json

from tornado.web import RequestHandler
from tornado.websocket import WebSocketHandler, WebSocketClosedError
from tornado import concurrent
from tornado import gen

logger = logging.getLogger("app")


class IndexHandler(RequestHandler):
"""Redirect to Tic-Tac-Toe"""
def get(self):
self.redirect('/tic-tac-toe')


class TicTacToeHandler(RequestHandler):
"""APP is live"""

def get(self):
self.render("tic_tac_toe.html")


class TicTacToeSocketHandler(WebSocketHandler):

def initialize(self, game_manager, *args, **kwargs):
self.game_manager = game_manager
self.game_id = None
self.game_result = ""
super().initialize(*args, **kwargs)

def open(self):
self.send_message(action="open", message="Connected to Game Server")

def _convert_int(self, game_id):
try:
return None, int(game_id)
except (ValueError, TypeError):
return "Game numbers need to be Numeric: {}".format(game_id), None

def send_pair_message(self, action, **data):
paired_handler = self.game_manager.get_pair(self.game_id, self)
if paired_handler:
return paired_handler.send_message(action, **data)
else:
return "Could not send message to the other Player"

def on_message(self, message):
# Verify the validity of message
data = json.loads(message)
action = data.get("action", "")
error_msg = ""
if action == "move":
# Game is going on
# Set turn to False and send message to opponent
player_selection = data.get("player_move")
player_move = (int(player_selection[0]), int(player_selection[2]))
print(player_move)
if player_move:
self.game_manager.record_move(self.game_id, player_move, self)
error_msg = self.send_message(action="opp-move")
if not error_msg:
error_msg = self.send_pair_message(action="move", opp_move=player_selection)
else:
# Send Error Message that Pair Disconnected?
pass

# Check if the game is still ON
if self.game_manager.has_game_ended(self.game_id):
game_result = self.game_manager.get_game_result(self.game_id, self)
self.send_message(action="end", result=game_result)
opp_result = "L" if game_result == "W" else game_result
self.send_pair_message(action="end", result=opp_result)
self.game_manager.end_game(self.game_id)

elif action == "join":
# Get the game id
error_msg, game_id = self._convert_int(data.get("game_id"))

if game_id and self.game_manager.join_game(game_id, self):
# Joined the game.
# Send message to Player A
self.game_id = game_id
# Tell both players that they have been paired, so reset the pieces
if not error_msg:
error_msg = self.send_message(action="paired", game_id=game_id)
if not error_msg:
error_msg = self.send_pair_message(action="paired", game_id=game_id)
# One to wait, other to move
if not error_msg:
error_msg = self.send_message(action="opp-move")
if not error_msg:
error_msg = self.send_pair_message(action="move")
else:
error_msg = "Could not find the Game: {}".format(game_id)

elif action == "new":
# new Game
# Create a new game id and respond the game id
self.game_id = self.game_manager.new_game(self)
if not error_msg:
error_msg = self.send_message(action="wait-pair", game_id=self.game_id)
elif action == "abort":
self.game_manager.abort_game(self.game_id)
self.send_message(action="end", game_id=self.game_id, result="A")
self.send_pair_message(action="end", game_id=self.game_id, result="A")
self.game_manager.end_game(self.game_id)
else:
error_msg = "Unknown Action"

if error_msg:
self.send_message(action="error", message=error_msg)

def on_close(self):
"""Overwrites WebSocketHandler.close"""
self.send_pair_message(action="end", game_id=self.game_id, result="A")
self.game_manager.end_game(self.game_id)

def send_message(self, action, **data):
"""Sends the message to the connected subscriber."""
message = {
"action": action,
"data": data
}
try:
self.write_message(json.dumps(message))
except WebSocketClosedError:
logger.warning("WS_CLOSED", "Could Not send Message: " + json.dumps(message))
# Send Websocket Closed Error to Paired Opponent
self.send_pair_message(action="pair-closed")
self.close()
48 changes: 48 additions & 0 deletions app/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# !/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import logging
import logging.config

import tornado.ioloop
import tornado.web
from tornado.options import options

from app.config import settings
from app.handlers import IndexHandler
from app.handlers import TicTacToeHandler, TicTacToeSocketHandler
from app.game_managers import TicTacToeGameManager

def main():

# Get the Port and Debug mode from command line options
options.parse_command_line()

# create logger for app
logger = logging.getLogger('app')
logger.setLevel(logging.INFO)

FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
logging.basicConfig(format=FORMAT)

tic_tac_toe_game_manager = TicTacToeGameManager()

urls = [
(r"/$", IndexHandler),
(r"/tic-tac-toe$", TicTacToeHandler),
(r"/tic-tac-toe/ws$", TicTacToeSocketHandler, dict(game_manager=tic_tac_toe_game_manager))
]

# Create Tornado application
application = tornado.web.Application(
urls,
debug=options.debug,
autoreload=options.debug,
**settings)

# Start Server
logger.info("Starting App on Port: {} with Debug Mode: {}".format(options.port, options.debug))
application.listen(options.port)
tornado.ioloop.IOLoop.current().start()


4 changes: 4 additions & 0 deletions app/static/lib/jquery-3.1.0.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit 6502e52

Please sign in to comment.