What’re you eating? Part 11
This time it’s not a huge update technically - but it’s got some pretty decent changes to the site.
I decided to finally bite the bullet and quality check my recipes - you might remember that I OCR’d them in from shaky photos I’d accumulated. Well, I absolutely knew there was more work to do on them, but seeing as how there’re more than 50, I procrastinated. After I’d digitalised the recipes, I went ahead and tried to format them using python which got functional (but not pretty) HTML pages up and running. Essentially, I separated them out into 4 parts:
Metadata
Ingredients
Notes
Steps
After I had those separated, putting them in a rough template I’d made wasn’t a problem but I made a couple of poor design decisions:
No IDs or classes to speak of (except to make my buttons work)
Each line was a <p> paragraph element but this would make it hard to edit/format/recreate
I designated that each sentence/line was a new step which has made some recipes illogical
So with that in mind, I knew I had to start some pretty major editing and for that, I turned back to python. I created a new ‘worker’ script and alternated between 2 approaches:
for line in line_list: ingredients_index = line_list.index('start_flag') len_dif = len(line_list) - len(line_list[ingredients_index:]) end_index = line_list[ingredients_index:].index('end_flag') + len_dif for element in line_list[ingredients_index:end_index]: element = element.replace('oldThing', 'newThing') table_list.append(element) final_list = \ line_list[0:ingredients_index-1] + \ table_list + \ line_list[end_index:]
This takes the document line-by-line and identifies the beginning and end of what I’m interested in before replacing strings directly. It then adds the ‘replaced’ sections to a list, adding that back into the master list of lines.
recipe = ''.join(infile.readlines()) recipe = re.sub('oldThing','newThing',recipe,flags=re.DOTALL)
This takes the entire document as a single element and used regex to replace ‘oldThing’ with ‘newThing’. It’s clean and easy, but applying it to the whole document can lead to unexpected changes and if there’s something you want to keep inside the expression, you have to use a 2nd.
<p><b>1. </b> text </p>
This sort of line was pretty common, and while regex can identify it easily, it can’t change the whole thing - meaning you have to tackle "<p><b>”, </b>, & </p> separately.
Because I was automating the formatting of so many pages, I needed a lot of different situations addressed and band-aids applied - leading to a pretty horrific end result that I don’t care to share. During testing, I would disable the ‘write to file’ function and just print to screen to let me see how the changes were applied, only switching back to ‘write’ when I was happy. I did miss a lot of inadvertent changes, however, leading to a bunch of headaches I could have avoided. So to help you out:
Remove all indentation - it’s HTML, it doesn’t need it, and you can just auto-format everything back
Add indicators - the start/end flags meant that changes outside those regions weren’t possible
Make as few changes as possible - for example, look to change <p><b> to <table><tr><td> if it’s the first one. The fewer separate commands, the better
Save regular, staggered backups - text kept going missing because of mistakes I’d made and it was good to have a ‘last good’ to see what I needed to put back
Add classes and IDs as you go - it makes future changes much easier
After all of these changes, I now have all of my sections laid out in tables wrapped in the following <div> elements:
ingredients
notes
steps
<div id="steps"> <table> <tr><td class="stepNo">1. </td><td class="stepDetail">text</td></tr> </table> </div>
Now that I have everything in tables that are appropriately classed, I can format/update/repeat the process much more easily which I’m happy with. I also did what I did for the function pages and external-ised the scripts and CSS. Ahead of me is a pretty big job that I’ll tackle without blogging about it (unless something interesting comes up):
Manually splitting the ingredients/steps/notes in the appropriate columns
Error-checking the recipes
Fixing any remaining formatting issues
That would be the end of this iteration if it weren’t for my infinite capacity for procrastination - I decided to learn more JavaScript to fix a recipe. The recipe for buttercream I have is a base recipe only and has a bit saying “and now add the flavour-specific stuff from the next page” but I wanted to incorporate it all into one page. So after a bit of fiddling, I found that my JavaScript lessons are coming along nicely. I found a really good resource for editing HTML and set out what I wanted: a drop-down menu that lets you change the ingredients based on the flavour you want.
<div id="ingredients" class="content"> <table> <tr> <td class="ingredientNo">Flavouring of choice: </td> <td class="ingredientDetail"> <select id="type" name="flavours"> <option value="chocolate">Chocolate</option> <option value="vanilla">Vanilla</option> <option value="lemon">Lemon</option> ..... </select> <button type="button" onclick="flavour()">Choose</button> </td> </tr> </table> <table id="flavour"> <tr> <td class="ingredientNo">250g</td> <td class="ingredientDetail"> unsalted butter, at room temperature (1)</td> </tr> <tr> <td class="ingredientNo">4 cups (500 g)</td> <td class="ingredientDetail"> soft icing sugar, sifted</td> </tr> </table> </div>
function flavour() { const elements = document.getElementsByClassName('ingredients'); while(elements.length > 0){ elements[0].parentNode.removeChild(elements[0]); } var type = document.getElementById("type").value; if (type.includes('chocolate')) { ingredient = '<tr class="ingredients"><td class="ingredientNo">1/2 cup (40g)</td><td class="ingredientDetail">unsweetened cocoa powder, sifted</td></tr><tr class="ingredients"><td class="ingredientNo">1 tsp</td><td class="ingredientDetail">vanilla extract</td></tr><tr class="ingredients"><td class="ingredientNo">1/8 tsp</td><td class="ingredientDetail"> table salt</td></tr><tr class="ingredients"><td class="ingredientNo">5 tbsp</td><td class="ingredientDetail">milk</td></tr>'; } else if (type.includes('vanilla')) { ..... const element = document.querySelector("#flavour"); element.innerHTML += ingredient; }
So, to work through this: the <select> and <option> tags are a dropdown menu and whatever is selected when the button is pressed will be read by the JS. The script starts by deleting any previously added elements, then reads the selection and submits the appropriate HTML which has an end result of:
For some reason the drop-down didn’t display, but you get the idea - it works! Now I can go away for countless hours and try to get these recipes all looking fab.