Skip to content

Commit 8496251

Browse files
committed
Updates
1 parent 543e26c commit 8496251

File tree

9 files changed

+211
-68
lines changed

9 files changed

+211
-68
lines changed

db.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@
4747
checkin_id INT PRIMARY KEY,
4848
created_at TIMESTAMP,
4949
distance REAL,
50-
venue_id int references venues(venue_id)
50+
venue_id int references venues(venue_id),
51+
user_id int,
52+
rating REAL,
53+
feed TEXT references feeds(feed)
5154
)
5255
""")
5356

@@ -58,4 +61,13 @@
5861
)
5962
""")
6063

64+
cur.execute("""
65+
CREATE TABLE feeds (
66+
feed TEXT PRIMARY KEY,
67+
name TEXT,
68+
lat REAL,
69+
lon REAL
70+
);
71+
""")
72+
6173
conn.commit()

fetch.py

+77-27
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
import psycopg2
55
from datetime import datetime
66
import time
7+
import argparse
8+
9+
10+
parser = argparse.ArgumentParser()
11+
parser.add_argument("--feed", default="nyc_feed")
12+
opt = parser.parse_args()
713

814

915
conn = psycopg2.connect("postgres:///beer_feed")
@@ -26,7 +32,13 @@
2632

2733
client.set_access_token(token)
2834

29-
coords=40.714404, -73.942352
35+
36+
37+
with conn.cursor() as cur:
38+
cur.execute("SELECT lat, lon FROM feeds WHERE feed=%s", (opt.feed,))
39+
coords = cur.fetchone()
40+
if coords is None:
41+
raise ValueError(f"{opt.feed} does not exist!")
3042

3143

3244
def insert(tablename, data):
@@ -61,48 +73,86 @@ def process_checkin(checkin):
6173
})
6274

6375
with conn.cursor() as cur:
64-
cur.execute("SELECT * FROM beers WHERE beer_id=%s", (checkin["beer"]["bid"],))
76+
cur.execute("SELECT rating_count, beer_id, pic FROM beers WHERE beer_id=%s", (checkin["beer"]["bid"],))
6577
res = cur.fetchall()
6678

67-
if len(res) == 0:
68-
try:
69-
time.sleep(2)
70-
beer = client.beer.info(checkin["beer"]["bid"])
71-
except Exception as e:
72-
print(e)
73-
insert("beers", {
74-
"beer_id": beer["response"]["beer"]["bid"],
75-
"auth_rating": beer["response"]["beer"]["auth_rating"],
76-
"beer_abv": beer["response"]["beer"]["beer_abv"],
77-
"beer_name": beer["response"]["beer"]["beer_name"],
78-
"beer_style": beer["response"]["beer"]["beer_style"],
79-
"rating_count": beer["response"]["beer"]["rating_count"],
80-
"rating_score": beer["response"]["beer"]["rating_score"],
81-
"last_updated": datetime.now()
82-
})
79+
update = False
80+
if len(res) == 1:
81+
rating_count, beer_id, pic = res[0]
82+
if rating_count < 500:
83+
update = True
84+
if pic is None:
85+
update = True
86+
87+
88+
89+
if len(res) == 0 or update:
90+
time.sleep(2)
91+
beer = client.beer.info(checkin["beer"]["bid"])
92+
93+
if update:
94+
if pic is None:
95+
print("Updating beer because it doesn't have a picture")
96+
else:
97+
print(f"Updating beer, because it only has {rating_count} ratings, now has {beer['response']['beer']['rating_count']}")
98+
with conn.cursor() as cur:
99+
cur.execute("""
100+
UPDATE beers SET last_updated=now(), rating_score=%(score)s, rating_count=%(count)s
101+
WHERE beer_id=%(beer_id)s
102+
""", {
103+
"score": beer["response"]["beer"]["rating_score"],
104+
"count": beer["response"]["beer"]["rating_count"],
105+
"beer_id": beer["response"]["beer"]["bid"],
106+
})
107+
conn.commit()
108+
else:
109+
print("Adding new beer to database")
110+
insert("beers", {
111+
"beer_id": beer["response"]["beer"]["bid"],
112+
"auth_rating": beer["response"]["beer"]["auth_rating"],
113+
"beer_abv": beer["response"]["beer"]["beer_abv"],
114+
"beer_name": beer["response"]["beer"]["beer_name"],
115+
"beer_style": beer["response"]["beer"]["beer_style"],
116+
"rating_count": beer["response"]["beer"]["rating_count"],
117+
"rating_score": beer["response"]["beer"]["rating_score"],
118+
"last_updated": datetime.now(),
119+
"pic": beer["response"]["beer"]["beer_label"],
120+
"slug": beer["response"]["beer"]["beer_slug"]
121+
})
122+
else:
123+
print("Beer cache hit!")
124+
83125

84126
insert("checkins", {
85127
"beer_id": checkin["beer"]["bid"],
86128
"brewery_id": checkin["brewery"]["brewery_id"],
87129
"checkin_comment": checkin["checkin_comment"],
88130
"checkin_id": checkin["checkin_id"],
131+
"rating": checkin["rating_score"],
89132
"created_at": checkin["created_at"],
90133
"venue_id": checkin["venue"]["venue_id"],
134+
"user_id": checkin["user"]["uid"],
135+
"feed": opt.feed,
91136
})
92137

93138

94139
with conn.cursor() as cur:
95-
cur.execute("SELECT MAX(checkin_id) FROM checkins")
140+
cur.execute("SELECT MAX(checkin_id) FROM checkins WHERE feed=%s", (opt.feed,))
96141
max_id, = cur.fetchone()
97142

98-
checkins = client.thepub.local(lat=coords[0], lng=coords[1], radius=10, dist_pref="km", min_id=max_id)
99-
print(f'Fetched {len(checkins["response"]["checkins"]["items"])} new checkins!')
143+
try:
144+
checkins = client.thepub.local(lat=coords[0], lng=coords[1], radius=12, dist_pref="km", min_id=max_id)
145+
print(f'Fetched {len(checkins["response"]["checkins"]["items"])} new checkins!')
100146

101-
# iterate in reverse order to process the oldest first. That way if we crash, we won't create gaps in the feed
102-
for checkin in checkins["response"]["checkins"]["items"][::-1]:
103-
process_checkin(checkin)
147+
# iterate in reverse order to process the oldest first. That way if we crash, we won't create gaps in the feed
148+
for checkin in checkins["response"]["checkins"]["items"][::-1]:
149+
print(f"Processing {checkin['checkin_id']}")
150+
process_checkin(checkin)
104151

105152

106-
with conn.cursor() as cur:
107-
cur.execute("UPDATE tokens SET last_used=now() WHERE token=%s", (token,))
108-
conn.commit()
153+
except Exception as e:
154+
print(e)
155+
print("Rotating token")
156+
with conn.cursor() as cur:
157+
cur.execute("UPDATE tokens SET last_used=now() WHERE token=%s", (token,))
158+
conn.commit()

mobile/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"axios": "^0.18.0",
2424
"babel-preset-react-native-stage-0": "^1.0.1",
2525
"expo": "^26.0.0",
26+
"expo-cli": "^2.18.2",
2627
"immutability-helper": "^2.6.6",
2728
"react": "16.3.2",
2829
"react-native": "https://github.com/expo/react-native/archive/sdk-26.0.0.tar.gz",

package.json

+30-30
Original file line numberDiff line numberDiff line change
@@ -26,50 +26,50 @@
2626
"fsevents": "*"
2727
},
2828
"dependencies": {
29-
"axios": "^0.18.0",
30-
"bluebird": "^3.5.1",
31-
"body-parser": "^1.18.2",
29+
"axios": "^0.18.1",
30+
"bluebird": "^3.7.2",
31+
"body-parser": "^1.20.1",
3232
"dotenv": "^5.0.1",
33-
"express": "^4.16.3",
34-
"history": "^4.7.2",
35-
"immutability-helper": "^2.6.6",
36-
"lodash": "^4.17.5",
37-
"mongoose": "^5.0.15",
33+
"express": "^4.18.2",
34+
"history": "^4.10.1",
35+
"immutability-helper": "^2.9.1",
36+
"lodash": "^4.17.21",
37+
"mongoose": "^5.13.15",
3838
"node-untappd": "git+https://github.com/lematt1991/node-untappd.git",
39-
"pg": "^7.4.1",
40-
"react": "^16.3.2",
41-
"react-bootstrap": "^0.32.1",
42-
"react-cookie": "^2.1.4",
43-
"react-dom": "^16.3.2",
39+
"pg": "^7.18.2",
40+
"react": "^16.14.0",
41+
"react-bootstrap": "^0.32.4",
42+
"react-cookie": "^2.2.0",
43+
"react-dom": "^16.14.0",
4444
"react-google-maps": "^9.4.5",
45-
"react-redux": "^5.0.7",
46-
"react-router": "^4.2.0",
45+
"react-redux": "^5.1.2",
46+
"react-router": "^4.3.1",
4747
"react-router-bootstrap": "^0.24.4",
48-
"react-router-dom": "^4.2.2",
49-
"react-router-redux": "^5.0.0-alpha.6",
50-
"react-scripts": "^1.1.4",
48+
"react-router-dom": "^4.3.1",
49+
"react-router-redux": "^5.0.0-alpha.8",
50+
"react-scripts": "^1.1.5",
5151
"react-search-input": "^0.11.3",
52-
"react-select": "^1.2.1",
52+
"react-select": "^1.3.0",
5353
"react-spinkit": "^3.0.0",
54-
"redux": "^4.0.0",
54+
"redux": "^4.2.1",
5555
"redux-logger": "^3.0.6",
56-
"request": "^2.85.0",
57-
"twit": "^2.2.9",
58-
"universal-cookie": "^2.1.2",
56+
"request": "^2.88.2",
57+
"twit": "^2.2.11",
58+
"universal-cookie": "^2.2.0",
5959
"untappd-js": "^1.0.3",
6060
"url": "^0.11.0"
6161
},
6262
"devDependencies": {
63-
"babel-preset-env": "^1.6.1",
63+
"babel-preset-env": "^1.7.0",
6464
"babel-preset-stage-0": "^6.24.1",
65-
"enzyme": "^3.3.0",
66-
"enzyme-adapter-react-16": "^1.1.1",
67-
"enzyme-to-json": "^3.3.3",
65+
"enzyme": "^3.11.0",
66+
"enzyme-adapter-react-16": "^1.15.7",
67+
"enzyme-to-json": "^3.6.2",
6868
"flow-bin": "^0.70.0",
69-
"jest": "^22.4.3",
70-
"jest-cli": "^22.4.3",
69+
"jest": "^22.4.4",
70+
"jest-cli": "^22.4.4",
7171
"react-addons-test-utils": "^15.6.2",
72-
"react-test-renderer": "^16.3.2",
72+
"react-test-renderer": "^16.14.0",
7373
"sinon": "^4.5.0"
7474
},
7575
"proxy": "http://localhost:3001",

server.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from typing import Union
2+
from fastapi import FastAPI, Depends
3+
from fastapi.staticfiles import StaticFiles
4+
from fastapi.templating import Jinja2Templates
5+
from fastapi import Request
6+
import psycopg2
7+
import psycopg2.extras
8+
9+
templates = Jinja2Templates(directory="build")
10+
11+
app = FastAPI()
12+
13+
14+
conn = psycopg2.connect("postgres:///beer_feed")
15+
16+
@app.get("/items/{item_id}")
17+
def read_item(item_id: int, q: Union[str, None] = None):
18+
return {"item_id": item_id, "q": q}
19+
20+
21+
app.mount("/static", StaticFiles(directory="build/static"), name="static")
22+
app.mount("/js", StaticFiles(directory="build/static/js"), name="js")
23+
24+
25+
@app.get("/Feed/{location}/{id}",)
26+
def feed_limit(location: str, id: int):
27+
28+
threshold = {
29+
"nyc_feed": 4,
30+
}.get(location, 3.75)
31+
32+
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
33+
# cur.execute("SELECT * FROM checkins ORDER BY checkin_id DESC LIMIT 25")
34+
cur.execute(f"""
35+
SELECT
36+
COALESCE(beers.pic, 'https://assets.untappd.com/site/assets/images/temp/badge-beer-default.png') AS pic,
37+
checkin_id,
38+
venue_id,
39+
checkins.created_at AS created,
40+
beer_id AS bid,
41+
beer_name,
42+
venues.lat,
43+
breweries.brewery_name AS brewery,
44+
venues.lng as lon,
45+
rating_count AS checkin_count,
46+
venue_name AS venue,
47+
rating_score AS beer_rating,
48+
beer_style AS beer_style
49+
FROM checkins
50+
JOIN venues USING(venue_id)
51+
JOIN breweries USING (brewery_id)
52+
JOIN beers USING(beer_id)
53+
WHERE created_at > now() - INTERVAL '2 day' AND rating_score >= {threshold} AND feed=%s
54+
""", (location,))
55+
rows = cur.fetchall()
56+
last_id = rows[0]["checkin_id"]
57+
return {"lastID": last_id, "checkins": rows}
58+
59+
@app.get("/Feed/{location}")
60+
def feed(location: str):
61+
return feed_limit(location, 0)
62+
63+
@app.get("/Feeds")
64+
def feeds():
65+
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
66+
cur.execute("SELECT feed AS name, feed AS id, lat, lon, name AS city FROM feeds")
67+
return cur.fetchall()
68+
69+
return [{"name": "nyc", "id": "nyc_feed", "lat": 40.713342, "lon": -73.945931, "city": "New York"}]
70+
# return [{"name": "nyc", "lat": 40.714404, "lng": -73.942352, "city": "New York"}]
71+
72+
@app.get("/{path_name:path}")
73+
def feed(request: Request, path_name: str):
74+
return templates.TemplateResponse("index.html", {"request": request})
75+
76+
77+
if __name__ == "__main__":
78+
import uvicorn
79+
uvicorn.run(app, host="0.0.0.0", port=3000, log_level="debug")

src/actions/DataActions.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const updateData = (feed, lastID) => ({
1313
type : UPDATE_DATA,
1414
payload : {
1515
method : 'GET',
16-
url : `/FEED/${feed}/${lastID}`
16+
url : `/Feed/${feed}/${lastID}`
1717
},
1818
meta : 'API'
1919
})

src/components/FeedRow.jsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default class FeedRow extends Component{
77
constructor(props){
88
super(props);
99
this.state = {
10-
picSrc : props.beer.pic,
10+
picSrc : props.pic,
1111
error : false
1212
}
1313
}
@@ -21,7 +21,7 @@ export default class FeedRow extends Component{
2121
handleError = () => {
2222
if(!this.state.error){
2323
this.setState(_.extend({}, this.state, {
24-
picSrc : 'https://untappd.akamaized.net/site/assets/images/temp/badge-beer-default.png',
24+
picSrc : 'https://assets.untappd.com/site/assets/images/temp/badge-beer-default.png',
2525
error : true
2626
}))
2727
}
@@ -37,7 +37,7 @@ export default class FeedRow extends Component{
3737
}
3838
var date = new Date(this.props.created)
3939
date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
40-
var beerLink = `https://untappd.com/b/${this.props.beer.slug}/${this.props.bid}`
40+
var beerLink = `https://untappd.com/b/${this.props.beer_slug}/${this.props.bid}`
4141
return(
4242
<div className="media">
4343
<div className="media-left media-middle">
@@ -57,10 +57,10 @@ export default class FeedRow extends Component{
5757
Brewery: {this.props.brewery}
5858
</h4>
5959
<h4>
60-
Beer: <a target="_blank" href={beerLink}>{this.props.beer.name}</a>
60+
Beer: <a target="_blank" href={beerLink}>{this.props.beer_name}</a>
6161
</h4>
6262
<h4>
63-
Score: {this.props.beer.rating}
63+
Score: {this.props.beer_rating}
6464
</h4>
6565
<h4>
6666
Found at: <SafeAnchor id='goto-map'
@@ -70,7 +70,7 @@ export default class FeedRow extends Component{
7070
Number of checkins: {this.props.checkin_count}
7171
</h4>
7272
<h4>
73-
Style: {this.props.beer.style}
73+
Style: {this.props.beer_style}
7474
</h4>
7575
</div>
7676
</div>

0 commit comments

Comments
 (0)