diff --git a/.gitignore b/.gitignore
index 64c6955..85ce4c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
web/index.html
-reviews.json
\ No newline at end of file
+reviews.json
+__pycache__
diff --git a/Makefile b/Makefile
index 198ee53..5476a69 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/blog.md b/blog.md
index f54ff86..6874255 100644
--- a/blog.md
+++ b/blog.md
@@ -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:*
diff --git a/make_table.py b/make_table.py
new file mode 100644
index 0000000..25c915f
--- /dev/null
+++ b/make_table.py
@@ -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 ("
" + ("
{}
" * 12) + "
").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 '{}'.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)
\ No newline at end of file
diff --git a/make-website.py b/make_website.py
similarity index 64%
rename from make-website.py
rename to make_website.py
index cb744e0..f70e486 100644
--- a/make-website.py
+++ b/make_website.py
@@ -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(''.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('{}'.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('{}'.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')
\ No newline at end of file
diff --git a/parse-md.py b/parse-md.py
index 86ec876..4d4d25f 100644
--- a/parse-md.py
+++ b/parse-md.py
@@ -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):
diff --git a/web/style.css b/web/style.css
index 0048635..e0536c7 100644
--- a/web/style.css
+++ b/web/style.css
@@ -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;
diff --git a/web/table.html b/web/table.html
new file mode 100644
index 0000000..e52928b
--- /dev/null
+++ b/web/table.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+ Blog of Pesto - Table
+
+
+
+
+
+ Blog of Pesto
+ - reviewing all sorts of pesto
+
* 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.
diff --git a/web/templates/table.html b/web/templates/table.html
new file mode 100644
index 0000000..7d7cf47
--- /dev/null
+++ b/web/templates/table.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+ Blog of Pesto - Table
+
+
+
+
+
+ Blog of Pesto
+ - reviewing all sorts of pesto
+
+
+
+
Tabular Overview
+
Tabular overview over all reviews on this blog:
+
+
+
+
+
+
Brand
+
Name
+
Type
+
Rating
+
Taste
+
Consistency
+
Ingredient
+
Weight
+
Price
+
100g Price
+
Value *
+
Creamy / Rustic
+
+
+
+ {TABLE_ROWS}
+
+
+
+
* 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.