added tabular view and tags

master
Anton Lydike 2 years ago
parent 3d77b2a187
commit 10be370469

1
.gitignore vendored

@ -1,2 +1,3 @@
web/index.html
reviews.json
__pycache__

@ -6,8 +6,11 @@
reviews.json: blog.md parse-md.py
python3 parse-md.py
web/index.html: reviews.json web/templates/index.html web/templates/review.html make-website.py
python3 make-website.py
web/index.html: reviews.json web/templates/index.html web/templates/review.html make_website.py
python3 make_website.py
web/table.html: reviews.json web/templates/table.html make_table.py
python3 make_table.py
web/reviews.json: reviews.json
cp reviews.json web/reviews.json
@ -15,7 +18,7 @@ web/reviews.json: reviews.json
deploy: website
rsync -a --no-i-r --info=progress2 web/ cyberbly.at:pesto-blog/
website: web/index.html web/reviews.json web/style.css
website: web/index.html web/reviews.json web/style.css web/table.html
json: reviews.json

@ -24,7 +24,9 @@ Each pesto is compared in these categories:
### Bernbacher "Pesto Calabrese"
*Date:* 2022-08-02
*Eaten with:* Gemelli.
*Eaten with:* Gemelli
*category:* ricotta, calabrese, creamy
*ingredients:* Red and yellow peppers (52%), Sunflower oil, Grana Padano Cheese (7%), Almonds (4.7%), Potato flakes, Salt, Lactic acid, spices.
@ -34,20 +36,22 @@ Each pesto is compared in these categories:
|-------------|----------------|
| taste | 1/5 |
| consistency | 2/5 |
| ingredients | 3/5 |
| ingredients | 1/5 |
| price | 2.29€ |
| size | 140g |
*notes:* This pesto lacked the spicieness I expect from a calabrese pesto. It was not spicy at all, in fact it had a very mild taste. In the context of pesto you could even say it did not taste like much at all. It also did not form the best emulsion and had a grainy texture, presumably from the shredded almonds. Adding Sriracha to it significantly improved the flavour. Make of that what you wish. This is not really surprising, as this pesto consists of around 30% sunflower oil, without a main ingredient carrying much taste (peppers). It's definitely a below-average to bad pesto.
*notes:* This pesto lacked the spicieness I expect from a calabrese pesto. It was not spicy at all, in fact it had a very mild taste. In the context of pesto you could even say it did not taste like much at all. It also did not form the best emulsion and had a grainy texture, presumably from the shredded almonds. Adding Sriracha to it significantly improved the flavour. Make of that what you wish. This is not really surprising, as this pesto consists of around 30% sunflower oil, with a main ingredient that doesn't really carry much taste (peppers). It's also missing one of the most important ingredients for a Calabrese, the ricotta! This is definitely a pretty bad pesto.
**Final verdict:** ★☆☆☆
**Final verdict:** ★☆☆☆
*images:* DSC_1189.jpg
### Barilla "Pesto Vegan" (Green)
*Date:* 2022-08-02
*Eaten with:* Gemelli.
*Eaten with:* Gemelli
*category:* basil, vegan, creamy
*ingredients:* Basil (35.6%), Sunflower oil, Cashews, Glucose syrup, water, modified cornstarch, salt, natural aroma, olive oil, sugar, lactic acid.
@ -72,6 +76,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* basil, genovese, rustic
*ingredients:* Basil (44%), Olive oil (18%), Sunflower oil, Grana Padano (6%), Pine nuts (4%), Cashews (4%), Pecorino Romano, salt, sugar, garlic, lactic acid, ascorbic acid.
@ -96,6 +102,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* ricotta, calabrese, creamy
*ingredients:* red bell peppers (35%), ricotta (26%), sunflower oil, sugar, pecorino romano, grana padano, potato flakes, tomato puree, onion, salt, olive oil (1%), carrot powder, citric acid, yeast extract, cayenne pepper, celery seeds, herbs, spices, aroma.
@ -105,7 +113,7 @@ Each pesto is compared in these categories:
| taste | 5/5 |
| consistency | 5/5 |
| ingredients | 5/5 |
| price | ??? |
| price | 2.99€ |
| size | 185g |
*notes:* This pesto has it all. Flavour, creaminess, ingredients, looks, price, etc. It's a really good pesto! Five stars!
@ -119,6 +127,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* basil, genovese, creamy
*ingredients:* sunflower oil, basil (30%), cashews, Parmiggiano Reggiano (5%), corn fiber, whey powder, salt, milk protein, olive oil, sugar, basil extract, natural aroma, lactic acid, garlic.
@ -131,7 +141,7 @@ Each pesto is compared in these categories:
| price | 3.29€ |
| size | 190g |
*notes:* This is a good pesto. It might be a little bit more salty than the vegan option, so keep that in mind when salting your noodles, but I'd argue it's comparably to the vegan pesto in all regards. In my opinion, the vegan version tastes a little bit better, but that's only marginally. It's as creamy as you'd expect from a "real" pesto alla genovese, (not as much as the vegan version, but to be honest, that's not a realistic benchmark). My only real issue with this pesto is oily it is, but luckily this doesn't really impact flavour or consistency. Of course it's missing the pine nuts, but to be hones, I didn't really notice.
*notes:* This is a good pesto. It might be a little bit more salty than the vegan option, so keep that in mind when salting your noodles, but I'd argue it's comparably to the vegan pesto in all other regards. In my opinion, the vegan version tastes a little bit better, but that's only marginally. It's a little bit more creamy than you'd expect from a "real" pesto alla genovese, (not as much as the vegan version, but to be honest, that's not a realistic benchmark). My only real issue with this pesto is how oily it is, but luckily this doesn't really impact flavour or consistency. Of course it's missing the pine nuts, but to be hones, I didn't really notice.
**Final verdict:** ★★★★★
@ -143,6 +153,8 @@ Each pesto is compared in these categories:
*Eaten with:* Gemelli
*category:* basil, genovese, rustic
*ingredients:* Sunflower oil, basil (20%), pine nuts (15%), native olive oil (6%), Parmiggiano Reggiano, salt, whey, garlic, wine vinegar, citric acid, lactic acid.
@ -153,7 +165,7 @@ Each pesto is compared in these categories:
| consistency | 3/5 |
| ingredients | 4/5 |
| price | 4.79€ |
| size | ???g |
| size | 180g |
*notes:* This seems like a good pesto on paper. Basil, real pine nuts, Parmiggiano Reggiano, olive oil. But that's only on paper. I guess that they can do this due to just low quality ingredients. It's straw-y, salty, a little to oily and has a lot of whole pine nuts in it (I know that they are in there, stop showing me them! I want them incorporated into the pesto!). Sadly, this pesto ain't it chief. Especially considering the price.
@ -165,6 +177,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* tomato, creamy
*ingredients:* tomato pulp (34%), sunflower oil, tomato puree (14%), glucose syrup, Grana Padano (5%), cashews (4%), basil (3%), salt, starch, Aceto Balsamico di Modena, aroma, Pecorino Romano (1%), sugar, bread crumbs, whey protein, garlic powder, citric acid.
@ -187,6 +201,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* tomato, creamy
*ingredients:* tomato pulp (34.9%), sunflower oil, concentrated tomato puree (15%), glucose syrup, Grana Padano, cashews, salt, basil, Aceto Balsamico di Modena, (2%), whey powder, Pecorino Romano, sugar, rice starch, garlic, lactic acid, aroma.
@ -211,6 +227,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* basil, rustic
*ingredients:* basil (45.2%), sunflower oil (25.9%), olive oil (9.7%), cashews (6%), pine nuts (4%), Parmiggiano Reggiano (4%), Pecorino Romano (2%), salt, sugar, lactic acid, garlic.
@ -235,6 +253,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* tomato, creamy
*ingredients:* dried tomatoes (39%), sunflower oil, tomato pulp (7%), basil (7%), olive oil (2.5%), sugar, salt, pine nuts (1.5%), garlic, wine acid, ascorbic acid, lactic acid.
@ -258,6 +278,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* tomato, creamy
*ingredients:* sunflower oil, tomato pulp (28.5%), concentrated tomato puree (8%), half-dried tomatoes (7.5%), whey powder, salt, sugar, concentrated carrot juice, garlic, oregano, powdered buttermilk, lactic acid, aroma.
@ -281,6 +303,8 @@ Each pesto is compared in these categories:
*Eaten with:* Girandole
*category:* ricotta, tomato, creamy
*ingredients:* sunflower oil, ricotta (20%), tomato pulp (17.3%), glucose syrup, concentrated tomato puree (5.7%), walnuts (5%), basil, Grana Padano Cheese, salt, cashews, whey powder, sugar, rice starch, lactic acid, garlic, aroma, powdered buttermilk.
@ -307,6 +331,8 @@ Each pesto is compared in these categories:
*Eaten with:* noodles.
*category:* basil, tomato, ricotta, genovese, calabrese, creamy, rustic
*ingredients:*

@ -0,0 +1,66 @@
from make_website import populate_template_str, datetime, get_json, review_id
with open('web/templates/table.html', 'r') as f:
TABLE_TEMPLATE = f.read()
def generate_table_row(review: dict) -> str:
return ("<tr>" + ("<td>{}</td>" * 12) + "</tr>").format(
review['company'],
make_link(
review['name'] + (' ({})'.format(review['variant']) if review['variant'] else ''),
'index.html#' + review_id(review)
),
get_type_from_categories(review),
review['final_verdict']['string'],
review['rating_value']['taste'],
review['rating_value']['consistency'],
review['rating_value']['ingredients'],
review['rating_value']['size'],
review['rating_value']['price'],
price_per_100g(review),
taste_grams_per_price(review),
", ".join(cat for cat in review['category'] if cat in ('Creamy', 'Rustic'))
)
def make_link(name, url):
return '<a href="{}">{}</a>'.format(
url, name
)
def get_type_from_categories(review: dict) -> str:
cats = review['category']
for name in ('Genovese', 'Calabrese', 'Ricotta', 'Tomato'):
if name in cats:
return name
def price_per_100g(review: dict, raw=False):
weight, price = review['rating_value'].get('size_value', None), review['rating_value'].get('price_value', None)
if None in (weight, price):
return "???"
if raw:
return price / weight * 100
return "{:.2f}".format(price / weight * 100)
def taste_grams_per_price(review):
price_per_100g_val = price_per_100g(review, True)
if price_per_100g_val == '???':
return '???'
return "{:.1f}".format(
review['rating_value']['taste_percent'] / price_per_100g_val * 10
)
if __name__ == '__main__':
reviews = get_json()['reviews']
html = populate_template_str(
TABLE_TEMPLATE,
{
'table_rows': "\n".join(generate_table_row(review) for review in reviews),
'current_year': str(datetime.datetime.now().year)
}
)
with open("web/table.html", 'w') as f:
f.write(html)

@ -17,11 +17,17 @@ def review_title(review):
'({})'.format(review['variant']) if review['variant'] else ''
)
def generate_website(website_source: str, json_source: str, dest: str):
with open(website_source, 'r') as f:
def get_json():
with open("reviews.json", 'r') as f:
return json.load(f)
def generate_website(template_dir: str, dest: str):
with open(template_dir + 'index.html', 'r') as f:
website_content = f.read()
with open(json_source, 'r') as f:
data = json.load(f)
with open(template_dir + 'table.html', 'r') as f:
table_template = f.read()
data = get_json()
website = populate_template_str(website_content, {
'index': generate_index(data['reviews']),
@ -30,10 +36,27 @@ def generate_website(website_source: str, json_source: str, dest: str):
'css_hash': stylesheet_hash(),
})
with open(dest, 'w') as f:
with open(dest + 'index.html', 'w') as f:
f.write(website)
f.write('<!-- auto generated on the {} -->'.format(datetime.datetime.now()))
def generate_review_html(review: dict) -> str:
return populate_template_str(REVIEW_TEMPLATE, {
'review_id': review_id(review)
, 'title': review_title(review)
, 'date': review['date']
, 'notes': review['notes']
, 'ingredients': ', '.join(review['ingredients'])
, 'rating_taste': review['rating_value']['taste']
, 'rating_consistency': review['rating_value']['consistency']
, 'rating_ingredients': review['rating_value']['ingredients']
, 'rating_price': review['rating_value']['price']
, 'rating_size': review['rating_value']['size']
, 'rating': review['final_verdict']['string']
, 'image_items': generate_image_items(review)
, 'categories': " ".join('<span class="category">{}</span>'.format(cat[0].upper() + cat[1:]) for cat in review['category'])
})
def generate_review_html(review: dict) -> str:
return populate_template_str(REVIEW_TEMPLATE, {
@ -49,6 +72,7 @@ def generate_review_html(review: dict) -> str:
, 'rating_size': review['rating_value']['size']
, 'rating': review['final_verdict']['string']
, 'image_items': generate_image_items(review)
, 'categories': " ".join('<span class="category">{}</span>'.format(cat[0].upper() + cat[1:]) for cat in review['category'])
})
def generate_index(reviews):
@ -82,5 +106,5 @@ def stylesheet_hash():
if __name__ == '__main__':
generate_website('web/templates/index.html', 'reviews.json', 'web/index.html')
generate_website('web/templates/', 'web/')
shutil.copy('reviews.json', 'web/reviews.json')

@ -308,6 +308,11 @@ class ReviewPostprocessor:
x.strip() for x in images.split(',')
]
def category(self, images: str):
return [
x.strip()[0].upper() + x.strip()[1:] for x in images.split(',')
]
def rating_value(self, table: Dict[str, str]):
new = dict()
for key, value in table.items():
@ -315,6 +320,10 @@ class ReviewPostprocessor:
if '/' in value:
x,y = value.split('/')
new[key + '_percent'] = float(x) / float(y)
if '' in value:
new[key + '_value'] = float(value.replace('', ''))
if 'g' in value:
new[key + '_value'] = float(value.replace('g', ''))
return new
def final_verdict(self, verdict: str):

@ -13,6 +13,7 @@ body {
--text-color: #111;
color: var(--text-color);
min-height: 100vh;
}
body > main {
@ -33,6 +34,17 @@ p {
text-align: justify;
}
.category-container {
margin-top: -24px;
}
.category {
background: rgba(128,128,128, 0.2);
padding: 3px 6px;
border-radius: 3px;
margin: 0 4px;
}
header {
text-align: center;
@ -101,6 +113,23 @@ table tr:nth-child(2n+1) td {
background-color: var(--table-background-color);
}
.large-table-wrapper {
width: 90vw;
overflow: auto;
margin-left: calc(512px - 45vw);
}
.large-table-wrapper table {
width: 100%;
min-width: 1524px;
}
@media (max-width: 1150px) {
.large-table-wrapper {
width: 100%;
margin-left: 0;
}
}
body.dark-theme {
--table-border-color: #222;
--table-background-color: #333;

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<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">
<link rel="stylesheet" href="style.css?v=Unknown field CSS_HASH"/>
<title>Blog of Pesto - Table</title>
</head>
<body>
<header>
<h1>
Blog of Pesto
<span class="thin"> - reviewing all sorts of pesto</span>
</h1>
</header>
<main>
<h1>Tabular Overview</h1>
<p>Tabular overview over all reviews on this blog:</p>
<div class="large-table-wrapper">
<table>
<thead>
<tr>
<th>Brand</th>
<th>Name</th>
<th>Type</th>
<th>Rating</th>
<th>Taste</th>
<th>Consistency</th>
<th>Ingredient</th>
<th>Weight</th>
<th>Price</th>
<th>100g Price</th>
<th>Value *</th>
<th>Creamy / Rustic</th>
</tr>
</thead>
<tbody>
<tr><td>Barilla</td><td><a href="index.html#barilla_pesto-alla-genovese_">Pesto alla Genovese</a></td><td>Genovese</td><td>★★★★★</td><td>5/5</td><td>5/5</td><td>4/5</td><td>190g</td><td>3.29€</td><td>1.73€</td><td>5.8</td><td>Creamy</td></tr>
<tr><td>Barilla</td><td><a href="index.html#barilla_pesto-con-pomodori-secchi_">Pesto con Pomodori Secchi</a></td><td>Tomato</td><td>★★☆☆☆</td><td>4/5</td><td>5/5</td><td>3/5</td><td>200g</td><td>3.29€</td><td>1.65€</td><td>4.9</td><td>Creamy</td></tr>
<tr><td>Barilla</td><td><a href="index.html#barilla_pesto-ricotta-e-noci_">Pesto Ricotta e Noci</a></td><td>Ricotta</td><td>★★★★☆</td><td>4/5</td><td>5/5</td><td>3/5</td><td>190g</td><td>3.29€</td><td>1.73€</td><td>4.6</td><td>Creamy</td></tr>
<tr><td>Barilla</td><td><a href="index.html#barilla_pesto-rosso_">Pesto Rosso</a></td><td>Tomato</td><td>★★★★☆</td><td>5/5</td><td>5/5</td><td>5/5</td><td>200g</td><td>3.29€</td><td>1.65€</td><td>6.1</td><td>Creamy</td></tr>
<tr><td>Barilla</td><td><a href="index.html#barilla_pesto-vegan_green">Pesto Vegan (Green)</a></td><td>None</td><td>★★★★★</td><td>5/5</td><td>5/5</td><td>3/5</td><td>195g</td><td>3.59€</td><td>1.84€</td><td>5.4</td><td>Creamy</td></tr>
<tr><td>Bernbacher</td><td><a href="index.html#bernbacher_pesto-calabrese_">Pesto Calabrese</a></td><td>Calabrese</td><td>★☆☆☆☆</td><td>1/5</td><td>2/5</td><td>1/5</td><td>140g</td><td>2.29€</td><td>1.64€</td><td>1.2</td><td>Creamy</td></tr>
<tr><td>Bertolli</td><td><a href="index.html#bertolli_pesto-calabrese_">Pesto Calabrese</a></td><td>Calabrese</td><td>★★★★★</td><td>5/5</td><td>5/5</td><td>5/5</td><td>185g</td><td>2.99€</td><td>1.62€</td><td>6.2</td><td>Creamy</td></tr>
<tr><td>Cucina</td><td><a href="index.html#cucina_premium-pesto-alla-genovese_">Premium Pesto alla Genovese</a></td><td>Genovese</td><td>★★★★☆</td><td>4/5</td><td>4/5</td><td>4/5</td><td>190g</td><td>???</td><td>???</td><td>???</td><td>Rustic</td></tr>
<tr><td>Gut & Günstig</td><td><a href="index.html#gut-&-günstig_pesto-rosso_">Pesto Rosso</a></td><td>Tomato</td><td>★★★★★</td><td>5/5</td><td>5/5</td><td>5/5</td><td>190g</td><td>1.19€</td><td>0.63€</td><td>16.0</td><td>Creamy</td></tr>
<tr><td>Ja!</td><td><a href="index.html#ja!_pesto-rosso_">Pesto Rosso</a></td><td>Tomato</td><td>★★★★☆</td><td>4/5</td><td>4/5</td><td>4/5</td><td>190g</td><td>1.19€</td><td>0.63€</td><td>12.8</td><td>Creamy</td></tr>
<tr><td>Ja!</td><td><a href="index.html#ja!_pesto-verde_">Pesto Verde</a></td><td>None</td><td>★★★★☆</td><td>3/5</td><td>4/5</td><td>4/5</td><td>190g</td><td>1.19€</td><td>0.63€</td><td>9.6</td><td>Rustic</td></tr>
<tr><td>Terre di Liguiria</td><td><a href="index.html#terre-di-liguiria_pesto-alla-genovese_">Pesto alla Genovese</a></td><td>Genovese</td><td>★★☆☆☆</td><td>4/5</td><td>3/5</td><td>4/5</td><td>180g</td><td>4.79€</td><td>2.66€</td><td>3.0</td><td>Rustic</td></tr>
</tbody>
</table>
</div>
<p>* Value is defined as the taste rating divided by the price of 10 grams of pesto. This is an arbitrary metric, but you can interpret it as "taste per normalized price". Higher is better.</p>
</main>
<footer>
<p>&copy; copyright 2022 by Anton Lydike</p> <p>Download raw review data: <a href="reviews.json">reviews.json</a></p>
</footer>
</body>
</html>

@ -35,10 +35,14 @@
<li><strong>Size:</strong> how much is in the glass</li>
</ul>
<p>I also list the ingredients and add some notes why I rated the pasta the way I did. I then give a final grade.</p>
<p>I also list the ingredients and add some notes why I rated the pasta the way I did. I then give a final grade.</p>
<h2>Index</h2>
<p>New: There is also a <a href="table.html">Tabular view</a> available.</p>
{INDEX}
<hr/>

@ -1,9 +1,14 @@
<h1 id="{REVIEW_ID}">{TITLE}</h1>
<p class="category-container">{CATEGORIES}<p>
<div class="review-body">
<div class="image-container">
{IMAGE_ITEMS}
</div>
<p><em>Date:</em> {DATE}</p>
<p><em>Notes:</em> {NOTES}</p>

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<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">
<link rel="stylesheet" href="style.css?v={CSS_HASH}"/>
<title>Blog of Pesto - Table</title>
</head>
<body>
<header>
<h1>
Blog of Pesto
<span class="thin"> - reviewing all sorts of pesto</span>
</h1>
</header>
<main>
<h1>Tabular Overview</h1>
<p>Tabular overview over all reviews on this blog:</p>
<div class="large-table-wrapper">
<table>
<thead>
<tr>
<th>Brand</th>
<th>Name</th>
<th>Type</th>
<th>Rating</th>
<th>Taste</th>
<th>Consistency</th>
<th>Ingredient</th>
<th>Weight</th>
<th>Price</th>
<th>100g Price</th>
<th>Value *</th>
<th>Creamy / Rustic</th>
</tr>
</thead>
<tbody>
{TABLE_ROWS}
</tbody>
</table>
</div>
<p>* Value is defined as the taste rating divided by the price of 10 grams of pesto. This is an arbitrary metric, but you can interpret it as "taste per normalized price". Higher is better.</p>
</main>
<footer>
<p>&copy; copyright {CURRENT_YEAR} by Anton Lydike</p> <p>Download raw review data: <a href="reviews.json">reviews.json</a></p>
</footer>
</body>
</html>
Loading…
Cancel
Save