Skip to content

Commit dd826aa

Browse files
committed
added myfitnesspal plugin
1 parent e645932 commit dd826aa

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

plugins/myfitnesspal.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import locale
2+
import math
3+
import requests
4+
from bs4 import BeautifulSoup
5+
6+
from cloudbot import hook
7+
8+
scrape_url = "http://www.myfitnesspal.com/food/diary/{}"
9+
10+
11+
@hook.command('mfp')
12+
@hook.command('myfitnesspal')
13+
def mfp(text, bot):
14+
"""<user> - returns macros from the MyFitnessPal food diary of <user>"""
15+
request = requests.get(scrape_url.format(text))
16+
17+
if request.status_code != requests.codes.ok:
18+
return "Failed to fetch info ({})".format(request.status_code)
19+
20+
output = "Diary for {}: ".format(text)
21+
22+
try:
23+
soup = BeautifulSoup(request.text, 'html.parser')
24+
25+
title = soup.find('h1', {'class': 'main-title'})
26+
if title:
27+
if title.text == 'This Food Diary is Private':
28+
return "{}'s food diary is private.".format(text)
29+
if title.text == 'This Username is Invalid':
30+
return "User {} does not exist.".format(text)
31+
32+
# the output of table depends on the user's MFP profile configuration
33+
headers = get_headers(soup)
34+
totals = get_values(soup, 'total')
35+
remaining = get_values(soup, 'alt')
36+
37+
for idx, val in enumerate(headers['captions']):
38+
kwargs = {
39+
'caption': val,
40+
'total': totals[idx],
41+
'remain': remaining[idx],
42+
'units': headers['units'][idx],
43+
'pct': math.floor((totals[idx]/remaining[idx])*100)
44+
}
45+
46+
output += ("{caption}: {total}/{remain}{units} ({pct}%) "
47+
.format(**kwargs))
48+
49+
output += " ({})".format(scrape_url.format(text))
50+
51+
except Exception as e:
52+
print(e)
53+
output = "Error parsing results."
54+
55+
return output
56+
57+
58+
def get_headers(soup):
59+
"""get nutrient headers from the soup"""
60+
headers = {'captions': [], 'units': []}
61+
62+
footer = soup.find('tfoot')
63+
for cell in footer.findAll('td', {'class': 'nutrient-column'}):
64+
div = cell.find('div')
65+
headers['units'].append(div.text)
66+
headers['captions'].append(div.previous_sibling.strip())
67+
68+
return headers
69+
70+
71+
def get_values(soup, row_class):
72+
"""get values from a specific summary row based on the row class"""
73+
locale.setlocale(locale.LC_ALL, 'english_USA') # for number parsing
74+
75+
values = []
76+
77+
cells = soup.find('tr', {'class': row_class}).find_all('td')
78+
79+
for elem in cells[1:]:
80+
# if there's a child span with class "macro-value", use its value
81+
# otherwise use the cell text
82+
span = elem.find('span', {'class': 'macro-value'})
83+
if span:
84+
value = span.text
85+
else:
86+
value = elem.text
87+
88+
if value.strip() != '':
89+
values.append(locale.atoi(value))
90+
91+
return values

0 commit comments

Comments
 (0)