Skip to content

Instantly share code, notes, and snippets.

@willmarkley
Forked from loisaidasam/README.md
Last active April 10, 2024 05:09
Show Gist options
  • Save willmarkley/ae3898e92825e1244ef936f13a24e1b7 to your computer and use it in GitHub Desktop.
Save willmarkley/ae3898e92825e1244ef936f13a24e1b7 to your computer and use it in GitHub Desktop.
Masters Web Application
AWSTemplateFormatVersion: "2010-09-09"
Description: "Masters WebApp"
Resources:
MastersWebAppRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- apigateway.amazonaws.com
Action:
- 'sts:AssumeRole'
Description: "Masters WebApp Role"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole
RoleName: Masters-WebApp-Role
MastersWebAppLambda:
Type: AWS::Lambda::Function
DependsOn:
- MastersWebAppRole
Properties:
Code:
ZipFile: |
output = '''
<!DOCTYPE html>
<html>
<head>
<title>Hello World HTML</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
'''
def handle(event, context):
return output
Description: "Masters WebApp Lambda"
Environment:
Variables:
envVar: testVal
FunctionName: MastersLambda
Handler: masters.handle
Role: !GetAtt MastersWebAppRole.Arn
Runtime: python3.9
Timeout: 8
MastersWebAppApi:
Type: AWS::ApiGatewayV2::Api
Properties:
CredentialsArn: !GetAtt MastersWebAppRole.Arn
Description: "Masters WebApp Api"
Name: MastersApi
ProtocolType: HTTP
Target: !GetAtt MastersWebAppLambda.Arn

Masters-Web-App

This simple web application is intended to faciliate a masters pick-em pool. It takes a CSV with the names of the competitors in the pool and their top 6 golfer picks. The program then pulls golfer scores from ESPN and displays the standings of the pool, via the templated masters.html.

Setup

  • Create a cloud formation stack in AWS using masters-cf-template.yml

  • Create a Zip Archive for Lambda by:

## download and unzip the files in this gist

## get jinja and requests dependency
#### assumes virtualenv is installed
virtualenv env
source env/bin/activate
pip install jinja2 -t .
deactivate
rm -rf env

## edit masters.csv with final entries

## zip for lambda
zip -r lambda.zip *

## upload lambda.zip to lambda

License

MIT License

Copyright (c) 2016 - 2023 Will Markley

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.

name p1 p2 p3 p4 p5 p6 paid
<!doctype html>
<html>
<head>
<title>Masters Pool</title>
<style>
table {
border-collapse:collapse;
width:97%;
font-size:90%;
text-align:center;
margin:auto;
margin-top:15px;
}
table, th, td {
border: 1px solid #ccc;
}
th, td {
height:40px;
width:14%;
}
th {
background-color:#009933;
}
</style>
<meta name="author" content="Will Markley">
</head>
<body style="font-family:Courier New, Courier, monospace">
<div style="padding-top:20px;font-size:350%;padding-bottom:30px;text-align:center;">Masters Pool Leaderboard</div>
<table>
<th>Name</th>
<th>P1</th>
<th>P2</th>
<th>P3</th>
<th>P4</th>
<th>P5</th>
<th>P6</th>
<th>Par</th>
{% for entry in entries %}
<tr>
<td style="width:14%" >{{ entry['name'] }}</td>
<td>{{ entry['p1'] }} ({{ golfers[entry['p1']] }})</td>
<td>{{ entry['p2'] }} ({{ golfers[entry['p2']] }})</td>
<td>{{ entry['p3'] }} ({{ golfers[entry['p3']] }})</td>
<td>{{ entry['p4'] }} ({{ golfers[entry['p4']] }})</td>
<td>{{ entry['p5'] }} ({{ golfers[entry['p5']] }})</td>
<td>{{ entry['p6'] }} ({{ golfers[entry['p6']] }})</td>
<td style="width:10%" >{{ entry['score'] }}</td>
</tr>
{% endfor %}
</table>
<div style="padding-top:20px;font-size:350%;padding-bottom:30px;text-align:center;">Pot: ${{ pot }}</div>
<div style="padding-top:20px;font-size:350%;padding-bottom:30px;text-align:center;">Masters Leaderboard: <a href="https://www.espn.com/golf/leaderboard">www.espn.com/golf/leaderboard</a></div>
<div style="font-size:350%;">Rules:</div>
<div style="font-size:150%;">Picks:</div>
<ul>
<li>2 golfers from top 10, 2 from 11-50, 2 from 50+ (Site: <a href="http://www.owgr.com/ranking">http://www.owgr.com/ranking</a> )</li>
<li>It is your responsibility to make sure your golfers are actually playing in the Masters this year</li>
<li>Golfers who withdraw are treated as missing the cut with the cut score</li>
</ul>
<div style="font-size:150%;">Scoring:</div>
<ul>
<li>Combined final score to par (lowest score wins)</li>
<li>Player's that miss the cut get their score multiplied by 2</li>
<li>Master's Champion gets -4 strokes added to score</li>
</ul>
<div style="font-size:150%;">Betting:</div>
<ul>
<li>$20 buy-in</li>
<li>3rd gets money back</li>
<li>2nd gets 20% of remaining pot</li>
<li>Winner takes 80% of remaining pot</li>
</ul>
<div style="text-align:right">Updated: {{ timestamp }}</div>
</body>
</html>
import csv
import json
import operator
import subprocess
from jinja2 import Environment, FileSystemLoader
from datetime import datetime
import re
import urllib.request
entries = [] # list of dictionaries; item is dict; key = name/p1 value = text
golfers = {} # golfer with topar value
# import CSV
with open('masters.csv') as csvfile:
entriesDR = csv.DictReader(csvfile)
entries = list(entriesDR)
# setup player and golfer scores
for entry in entries:
entry['score'] = 0
golfers[ entry['p1'] ] = 100
golfers[ entry['p2'] ] = 100
golfers[ entry['p3'] ] = 100
golfers[ entry['p4'] ] = 100
golfers[ entry['p5'] ] = 100
golfers[ entry['p6'] ] = 100
pot = str(20*len(entries))
pattern = re.compile("competitors.*rawText")
URL = "https://www.espn.com/golf/leaderboard"
def fetchLeaderboard():
req = urllib.request.Request(URL)
r = urllib.request.urlopen(req).read().decode('utf-8')
return r
def transformLeaderboard(input_html):
matched = pattern.search(input_html).group()
matched_cleaned = matched.replace(',"rawText', '').replace('competitors":', '')
leaderboard_json_list = json.loads(matched_cleaned)
leaders = []
for item in leaderboard_json_list:
leader = {
'cut': item['pos'],
'topar': item['toPar'],
'player': item['name']
}
leaders.append(leader)
return leaders
def handle(event, context):
# Call API
json_data_list = transformLeaderboard(fetchLeaderboard())
# Update golfers
for golfer in json_data_list:
if golfer["player"] in golfers:
if golfer["topar"]!="E":
golfers[golfer["player"]] = int(golfer["topar"])
else:
golfers[golfer["player"]] = 0
if golfer["cut"]=="-":
golfers[golfer["player"]] = 2*golfers[golfer["player"]]
# Update scores
for entry in entries:
entry['score'] = golfers[entry['p1']] + golfers[entry['p2']] + golfers[entry['p3']] + golfers[entry['p4']] + golfers[entry['p5']] + golfers[entry['p6']]
env = Environment(loader = FileSystemLoader('.'))
template = env.get_template('masters.html')
return {
"statusCode": 200,
"body": template.render(timestamp=str(datetime.now()), entries=sorted(entries, key=lambda k: k['score']), pot=pot, golfers=golfers),
"headers": {
'Content-Type': 'text/html',
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment