integrated responder framework

This commit is contained in:
Stefan Rupp 2020-04-05 16:00:48 +02:00
parent 4e44425ab3
commit 914e9a1f3d
14 changed files with 609 additions and 81 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
__pycache__/
venv/
.idea/

11
app_instance.py Normal file
View File

@ -0,0 +1,11 @@
import responder
# CORS wasn't demoed in the course, but is required to be used from
# external apps like movie exploder.
cors_params = {
'allow_origins': '*',
'allow_methods': '*',
}
api = responder.API(cors=True, cors_params=cors_params)

33
css/docs.css Normal file
View File

@ -0,0 +1,33 @@
.request {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-weight: bold;
border: 1px solid gray;
border-radius: 5px;
padding: 10px;
font-size: 24px;
}
.get {
color: #2b542c;
background-color: #beffbd;
}
.post {
color: #ae5900;
background-color: #ffc79d;
}
.response_formats span {
font-weight: bold;
color: darkred;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
pre {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
ul li {
font-size: 18px;
margin-bottom: 10px;
}

184
css/theme.css Normal file
View File

@ -0,0 +1,184 @@
@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
body {
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 300;
color: black;
background: white;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 300;
}
p {
font-weight: 300;
}
.font-normal {
font-weight: 400;
}
.font-semi-bold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.starter-template {
margin-top: 25px;
}
.starter-template .content {
margin-left: 10px;
}
.starter-template .content h1 {
margin-top: 10px;
font-size: 60px;
}
.starter-template .content h1 .smaller {
font-size: 40px;
}
.starter-template .content .lead {
font-size: 25px;
}
.starter-template .links {
float: right;
right: 0;
margin-top: 125px;
}
.starter-template .links ul {
display: block;
padding: 0;
margin: 0;
}
.starter-template .links ul li {
list-style: none;
display: inline;
margin: 0 10px;
}
.starter-template .links ul li:first-child {
margin-left: 0;
}
.starter-template .links ul li:last-child {
margin-right: 0;
}
.starter-template .links ul li.current-version {
font-weight: 400;
}
.starter-template .links ul li a, a {
text-decoration: underline;
}
.starter-template .links ul li a:hover, a:hover {
text-decoration: underline;
}
.starter-template .links ul li .icon-muted {
margin-right: 5px;
}
.starter-template .copyright {
margin-top: 10px;
font-size: 0.9em;
text-transform: lowercase;
float: right;
right: 0;
}
@media (max-width: 1199px) {
.starter-template .content h1 {
font-size: 45px;
}
.starter-template .content h1 .smaller {
font-size: 30px;
}
.starter-template .content .lead {
font-size: 20px;
}
}
@media (max-width: 991px) {
.starter-template {
margin-top: 0;
}
.starter-template .logo {
margin: 40px auto;
}
.starter-template .content {
margin-left: 0;
text-align: center;
}
.starter-template .content h1 {
margin-bottom: 20px;
}
.starter-template .links {
float: none;
text-align: center;
margin-top: 60px;
}
.starter-template .copyright {
float: none;
text-align: center;
}
}
@media (max-width: 767px) {
.starter-template .content h1 .smaller {
font-size: 25px;
display: block;
}
.starter-template .content .lead {
font-size: 16px;
}
.starter-template .links {
margin-top: 40px;
}
.starter-template .links ul li {
display: block;
margin: 0;
}
.starter-template .links ul li .icon-muted {
display: none;
}
.starter-template .copyright {
margin-top: 20px;
}
}
.disclaimer {
margin-top: 20px;
font-style: italic;
}

12
routes.py Normal file
View File

@ -0,0 +1,12 @@
# noinspection PyUnresolvedReferences
from app_instance import api
# noinspection PyUnresolvedReferences
from views.api_views import *
# noinspection PyUnresolvedReferences
from views.home import *
# noinspection PyUnresolvedReferences
from views.temp_css import *
# noinspection PyUnresolvedReferences
from views.player import *
api.add_route("/static", static=True)

120
server.py Normal file → Executable file
View File

@ -1,90 +1,48 @@
import time #!/usr/bin/env python3
import random import random
import socket
import responder
# Karten: 1-13 in Blau, Rot, Gelb, Gruen und 4xZ und 4xN from app_instance import api
from routes import *
import wiz_game
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i:i + n]
class Card:
def __init__(self, color, value):
self.color = color
self.value = value
def __str__(self):
sc = self.color if self.color else ' '
sv = str(self.value)
return sc+' '+sv
def __repr__(self):
return str(self)
class Player:
def __init__(self, name, player_id):
self.name = name
self.id = player_id
self.cards = []
def deal_cards(self, cards):
self.cards = cards
def show_cards(self):
print(self.cards)
class WizGame:
def __init__(self):
self.players = []
self.card_deck = []
def create_deck(self):
for color in ["blue", "red", "green", "yellow"]:
for val in range(1,14):
self.card_deck.append(Card(color, val))
for _ in range(1,5):
self.card_deck.append(Card(None, 'Z'))
for _ in range(1,5):
self.card_deck.append(Card(None, 'N'))
print("carddeck:")
print(self.card_deck)
def deal_cards(self):
random.shuffle(self.card_deck)
num_cards = int(len(self.card_deck)/len(self.players))
print(num_cards)
cs = list(chunks(self.card_deck, num_cards))
for i in range(len(self.players)):
self.players[i].deal_cards(cs[i])
self.players[i].show_cards()
def add_player(self, name: str, player_id):
if len(self.players) < 6:
self.players.append((Player(name, player_id)))
def main(): def main():
print("main started") print("main started")
w = WizGame()
w.create_deck() random.seed()
w.add_player("p1", 1)
w.add_player("p2", 2) wiz_game.the_game.create_deck()
w.add_player("p3", 3) wiz_game.the_game.add_player("p1", 1)
w.add_player("p4", 4) wiz_game.the_game.add_player("p2", 2)
w.deal_cards() wiz_game.the_game.add_player("p3", 3)
print("round 2") wiz_game.the_game.add_player("p4", 4)
w.deal_cards() wiz_game.the_game.deal_cards(4)
print("round 3")
w.deal_cards() api.run(port=19203, address="127.0.0.1")
print("round 4")
w.deal_cards()
exit(0) exit(0)
# w.create_deck()
# w.add_player("p1", 1)
# w.add_player("p2", 2)
# w.add_player("p3", 3)
# w.add_player("p4", 4)
# w.deal_cards(1)
# print("round 2")
# w.deal_cards(2)
# print("round 3")
# w.deal_cards(3)
# print("round 4")
# w.deal_cards(4)
# print("round 12")
# w.deal_cards(12)
# print("round 13")
# w.deal_cards(13)
# print("round 14")
# w.deal_cards(14)
# print("round 15")
# w.deal_cards(15)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

51
templates/home/index.html Normal file
View File

@ -0,0 +1,51 @@
{% extends "/shared/_layout.html" %}
{% block content %}
<div class="content">
<h1><span class="font-semi-bold">Gambling Foo</span> <span class="smaller">A RESTful gaming service</span></h1>
<p class="lead">
Play a nice game of cards?<br>
<br>
<strong>Endpoints</strong>
<ul>
<li>
<strong>Shuffle Card Deck</strong><br>
<a href="/api/shuffle">GET /api/shuffle</a>
</li>
<li>
<strong>Movies by director</strong><br>
<a href="/api/director/cameron">GET /api/director/{director_name}</a>
</li>
<!--
<li>
<strong>Movie by IMDB code</strong><br>
<a href="/api/movie/tt0096754">GET /api/movie/{imdb_number}</a>
</li>
<li>
<strong>Top 10 Movies (by IMDB score)</strong><br>
<a href="/api/movie/top">GET /api/movie/top</a>
</li>
<li>
<strong>All genres</strong><br>
<a href="/api/movie/genre/all">GET /api/movie/genre/all</a>
</li>
<li>
<strong>Top movies for a given genres</strong><br>
<a href="/api/movie/genre/sci-fi">GET /api/movie/genre/{genre}</a>
</li>
-->
</ul>
</p>
<p class="disclaimer">
Spam Bacon Sausage + Spam
Spam Spam Spam Spam Spam
Spam Baked Beans Spam
Spam Spam + Spam
</p>
</div>
{% endblock %}

View File

@ -0,0 +1,50 @@
{% extends "/shared/_layout.html" %}
{% block content %}
<div class="content">
<h1><span class="font-semi-bold">Gambling Foo</span> <span class="smaller">A RESTful gaming service</span></h1>
<p class="lead">
Play a nice game of cards?<br>
<br>
<strong>Player {{player}} view</strong>
<table style="width:100%">
<tr>
{% for card in cards %}
<li>
<strong>>card {{loop.index}} is: {{card}} </strong><br>
</li>
{% endfor %}
<!--
<li>
<strong>Movie by IMDB code</strong><br>
<a href="/api/movie/tt0096754">GET /api/movie/{imdb_number}</a>
</li>
<li>
<strong>Top 10 Movies (by IMDB score)</strong><br>
<a href="/api/movie/top">GET /api/movie/top</a>
</li>
<li>
<strong>All genres</strong><br>
<a href="/api/movie/genre/all">GET /api/movie/genre/all</a>
</li>
<li>
<strong>Top movies for a given genres</strong><br>
<a href="/api/movie/genre/sci-fi">GET /api/movie/genre/{genre}</a>
</li>
-->
</table>
</p>
<p class="disclaimer">
Spam Bacon Sausage + Spam
Spam Spam Spam Spam Spam
Spam Baked Beans Spam
Spam Spam + Spam
</p>
</div>
{% endblock %}

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="pyramid web application">
<meta name="author" content="Pylons Project">
<title>MovieDB Service</title>
<!-- Bootstrap core CSS -->
<link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this scaffold -->
<link href="/css/theme.css" rel="stylesheet">
<link href="/css/docs.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="starter-template">
<div class="container">
<div class="row">
<div class="col-md-2">
</div>
<div class="col-md-10">
<div>{% block content %}{% endblock %}</div>
</div>
</div>
<div class="row">
<div class="links">
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a
href="https://github.com/mikeckennedy/python-jumpstart-course-demos"
target="_blank">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://twitter.com/talkpython"
target="_blank">Twitter</a>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="copyright">
Copyright &copy; Talk Python Training
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
</body>
</html>

39
views/api_views.py Normal file
View File

@ -0,0 +1,39 @@
from responder import Response
from app_instance import api
response_count_max = 10
@api.route("/api/search/{keyword}")
def search_keyword(req, resp, keyword: str):
resp.media = {'keyword': keyword, 'hits': "movie_dicts", 'truncated_results': "limited"}
@api.route("/api/director/{director_name}")
def search_director(_, resp, director_name: str):
resp.media = {'keyword': director_name, 'hits': "movies_dicts", 'truncated_results': "limited"}
@api.route("/api/movie/genre/{genre}")
def movies_by_genre(_, resp: Response, genre: str):
resp.media = {'genre': genre, 'hits': "hits_dicts", 'truncated_results': "limited"}
@api.route("/api/movie/{imdb_number}")
def search_imdb(_, resp, imdb_number: str):
resp.media = {'foo': "bar"}
@api.route("/api/movie/top")
def top_movies(_, resp: Response):
resp.media = {'keyword': "keyword", 'hits': "hits_dicts", 'truncated_results': "limited"}
@api.route("/api/movie/genre/all")
def all_genres(_, resp: Response):
resp.media = {"all": "stuff"}
@api.route("/api/shuffle")
def cards_shuffle(_, resp: Response):
resp.media = {"shuffled": "done"}

6
views/home.py Normal file
View File

@ -0,0 +1,6 @@
from app_instance import api
@api.route("/")
def index(req, resp):
resp.content = api.template('home/index.html')

11
views/player.py Normal file
View File

@ -0,0 +1,11 @@
from app_instance import api
from wiz_game import the_game
@api.route("/player/{player}")
def index(req, resp, player: str):
p = int(player)
print(p)
print(the_game.players)
resp.content = api.template('home/player.html', player=player, cards=the_game.players[p].cards)

22
views/temp_css.py Normal file
View File

@ -0,0 +1,22 @@
import os
from app_instance import api
# This sucks. But at the moment I'm publishing this code / video
# Responder has developed a bug where it cannot serve static files
# See https://github.com/kennethreitz/responder/issues/337
#
# This is just a work around so you all can enjoy the course.
@api.route("/css/{file}")
def css(req, resp, file):
resp.headers['Content-Type'] = 'text/css'
full_file = os.path.join(
os.path.dirname(__file__),
'..',
'css',
file
)
with open(full_file, encoding='utf-8') as fin:
resp.content = fin.read()

83
wiz_game.py Normal file
View File

@ -0,0 +1,83 @@
import random
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i:i + n]
class Card:
def __init__(self, color, value):
self.color = color
self.value = value
def __str__(self):
sc = self.color if self.color else ' '
sv = str(self.value)
return sc+' '+sv
def __repr__(self):
return str(self)
class Player:
def __init__(self, name, player_id):
self.name = name
self.id = player_id
self.cards = []
def set_cards(self, cards):
self.cards = cards
def show_cards(self):
print(self.cards)
class WizGame:
def __init__(self):
self.players = []
self.card_deck = []
self.trump_color = None
def create_deck(self):
for color in ["b", "r", "g", "y"]:
for val in range(1,14):
self.card_deck.append(Card(color, val))
for _ in range(1,5):
self.card_deck.append(Card(None, 'Z'))
for _ in range(1,5):
self.card_deck.append(Card(None, 'N'))
print("carddeck:")
print(self.card_deck)
def set_trump_color(self, card: Card):
if card.color:
self.trump_color = card.color
elif card.value == 'Z':
self.trump_color = 'choose'
else:
self.trump_color = None
def deal_cards(self, cards_per_player):
random.shuffle(self.card_deck)
cs = list(chunks(self.card_deck, cards_per_player))
for idx,p in enumerate(self.players):
p.set_cards(cs[idx])
p.show_cards()
if len(cs) > len(self.players):
cc = cs[len(self.players)]
c = cc[0]
print("trump card: {}".format(c))
self.set_trump_color(c)
else:
self.trump_color = None
print("trump color: {}".format(self.trump_color))
def add_player(self, name: str, player_id):
if len(self.players) < 6:
self.players.append((Player(name, player_id)))
the_game = WizGame()