From 914e9a1f3d32c705e1288e0859bbc20debd720a4 Mon Sep 17 00:00:00 2001 From: Stefan Rupp Date: Sun, 5 Apr 2020 16:00:48 +0200 Subject: [PATCH] integrated responder framework --- .gitignore | 4 + app_instance.py | 11 ++ css/docs.css | 33 ++++++ css/theme.css | 184 ++++++++++++++++++++++++++++++++++ routes.py | 12 +++ server.py | 120 +++++++--------------- templates/home/index.html | 51 ++++++++++ templates/home/player.html | 50 +++++++++ templates/shared/_layout.html | 64 ++++++++++++ views/api_views.py | 39 +++++++ views/home.py | 6 ++ views/player.py | 11 ++ views/temp_css.py | 22 ++++ wiz_game.py | 83 +++++++++++++++ 14 files changed, 609 insertions(+), 81 deletions(-) create mode 100644 .gitignore create mode 100644 app_instance.py create mode 100644 css/docs.css create mode 100644 css/theme.css create mode 100644 routes.py mode change 100644 => 100755 server.py create mode 100644 templates/home/index.html create mode 100644 templates/home/player.html create mode 100644 templates/shared/_layout.html create mode 100644 views/api_views.py create mode 100644 views/home.py create mode 100644 views/player.py create mode 100644 views/temp_css.py create mode 100644 wiz_game.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19f4038 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +venv/ +.idea/ + diff --git a/app_instance.py b/app_instance.py new file mode 100644 index 0000000..c773f22 --- /dev/null +++ b/app_instance.py @@ -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) \ No newline at end of file diff --git a/css/docs.css b/css/docs.css new file mode 100644 index 0000000..aca49e8 --- /dev/null +++ b/css/docs.css @@ -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; +} \ No newline at end of file diff --git a/css/theme.css b/css/theme.css new file mode 100644 index 0000000..d913390 --- /dev/null +++ b/css/theme.css @@ -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; + +} \ No newline at end of file diff --git a/routes.py b/routes.py new file mode 100644 index 0000000..66432f9 --- /dev/null +++ b/routes.py @@ -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) diff --git a/server.py b/server.py old mode 100644 new mode 100755 index a949963..a715376 --- a/server.py +++ b/server.py @@ -1,90 +1,48 @@ -import time +#!/usr/bin/env python3 + import random - - -# Karten: 1-13 in Blau, Rot, Gelb, Gruen und 4xZ und 4xN - - -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))) - - +import socket +import responder +from app_instance import api +from routes import * +import wiz_game def main(): print("main started") - w = WizGame() - 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() - print("round 2") - w.deal_cards() - print("round 3") - w.deal_cards() - print("round 4") - w.deal_cards() + + random.seed() + + wiz_game.the_game.create_deck() + wiz_game.the_game.add_player("p1", 1) + wiz_game.the_game.add_player("p2", 2) + wiz_game.the_game.add_player("p3", 3) + wiz_game.the_game.add_player("p4", 4) + wiz_game.the_game.deal_cards(4) + + api.run(port=19203, address="127.0.0.1") 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__': main() diff --git a/templates/home/index.html b/templates/home/index.html new file mode 100644 index 0000000..ee1c4fb --- /dev/null +++ b/templates/home/index.html @@ -0,0 +1,51 @@ +{% extends "/shared/_layout.html" %} + +{% block content %} + +
+

Gambling Foo A RESTful gaming service

+

+ Play a nice game of cards?
+
+ Endpoints +

+

+ +

+ Spam Bacon Sausage + Spam + Spam Spam Spam Spam Spam + Spam Baked Beans Spam + Spam Spam + Spam + +

+
+ + +{% endblock %} \ No newline at end of file diff --git a/templates/home/player.html b/templates/home/player.html new file mode 100644 index 0000000..116824f --- /dev/null +++ b/templates/home/player.html @@ -0,0 +1,50 @@ +{% extends "/shared/_layout.html" %} + +{% block content %} + +
+

Gambling Foo A RESTful gaming service

+

+ Play a nice game of cards?
+
+ Player {{player}} view + + + {% for card in cards %} +
  • + >card {{loop.index}} is: {{card}}
    +
  • + {% endfor %} + + +
    +

    + +

    + Spam Bacon Sausage + Spam + Spam Spam Spam Spam Spam + Spam Baked Beans Spam + Spam Spam + Spam + +

    +
    + + +{% endblock %} \ No newline at end of file diff --git a/templates/shared/_layout.html b/templates/shared/_layout.html new file mode 100644 index 0000000..b30976f --- /dev/null +++ b/templates/shared/_layout.html @@ -0,0 +1,64 @@ + + + + + + + + + + MovieDB Service + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    {% block content %}{% endblock %}
    +
    +
    +
    + +
    +
    + +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/views/api_views.py b/views/api_views.py new file mode 100644 index 0000000..862e389 --- /dev/null +++ b/views/api_views.py @@ -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"} \ No newline at end of file diff --git a/views/home.py b/views/home.py new file mode 100644 index 0000000..87636b5 --- /dev/null +++ b/views/home.py @@ -0,0 +1,6 @@ +from app_instance import api + + +@api.route("/") +def index(req, resp): + resp.content = api.template('home/index.html') diff --git a/views/player.py b/views/player.py new file mode 100644 index 0000000..6e2da52 --- /dev/null +++ b/views/player.py @@ -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) diff --git a/views/temp_css.py b/views/temp_css.py new file mode 100644 index 0000000..35ee358 --- /dev/null +++ b/views/temp_css.py @@ -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() diff --git a/wiz_game.py b/wiz_game.py new file mode 100644 index 0000000..30c1e1f --- /dev/null +++ b/wiz_game.py @@ -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() +