Month: December 2023

General Tao’s … Turkey

Ingredients

  • Four turkey thighs, deboned and cut into bite-sized pieces
  • 2 Tbsp Shaoxing wine
  • 1/2 cup stock
  • 1/2 tsp salt
  • 1/2 tsp ground pepper
  • 1 Tbsp sesame oil
  • 1/4 cup sesame seeds
  • 1/4 cup all purpose flour
  • 3/4 cup corn starch

Combine all ingredients except turkey and mix into a thick paste. Then fold in turkey pieces and coat well.

Add a handful of chili peppers to the oil before frying — the turkey is fried in chili oil. Fry, remove from oil, and drain on paper towel.

Sauce:

  • 1 cup stock
  • 1/2 cup soy sauce
  • 1/2 cup maple syrup or brown sugar
  • 2 Tbsp rice wine vinegar (or apple cider vinegar)
  • 2 Tbsp Shaoxing wine
  • 2 Tbsp corn starch

Combine all sauce ingredients in a sauce pan and heat until thickened. Serve with a veggie (roasted broccoli, garden peas) and rice. I sprinkled ground Aji Lemon Drop pepper powder over the rice, sauce, and peas. Then topped with fried turkey to keep it crispy.

 

Causes Unknown

I keep hearing that Nikki Haley failed to mention slavery as a cause of the civil war  at this New Hampshire town hall. She *did* mention slavery as a cause, she just did so in coded speech common among racists.

 

States rights!

The state’s right to do what?

Why, the right to make their own laws and do what is right for that state!

OK, but like an example of one of these laws other than forcing people into slavery?

Umm … oh, wow. Will you look at the time! Gotta go!

 

I lived in the US south for years and had some version of this conversation so frequently it was depressing.  There were other rights – South Carolina trying to nullify federal tariffs was a problem in the lead up to the war, too. But (1) hardly the biggest concern and (2) literally not something I’ve ever found a modern states rights person mention. That’s more the realm of civil war historians.

Crochet Project: Anya Scarf

Anya taught me how to knit, and I am slowly knitting her a scarf … but it’s more of a fancy going out scarf than a “keeping warm” scarf. I found the same yarn that I am using for our sofa blanket in a variegated color (Tidepool), and I used that to make a really warm scarf. I started off knitting, but the Bernat Blanket yarn doesn’t knit well for me — I managed to get about three rows in after more than an hour of working on it! Took that out, grabbed a crochet hook, and used double crochet stitches. Two days later, voila! She’s got a warm, snuggly scarf. Just in time for … 60 degree weather. It’ll get cold eventually. And her scarf is standing by!

 

Python Code — Creating Title Images

Instead of allowing YouTube to randomly pick a frame to use as the preview image, I have always made a title image for the Township meetings I post to YouTube. At first, this was a manual (and thus time consuming for a lot of videos). In the interim, I have created a script that generates the color gradient background and overlays text including the meeting type and date.

# Valid meeting types: "TrusteeRegular",  "TrusteeSpecial", "TrusteeEmer", "TrusteeHearing", "BZAReg", "BZAHearing", "ZCReg", "ZCHearing"
strMeetingListSpreadsheet = 'MeetingList.xlsx'

from PIL import Image, ImageDraw, ImageFont
import pandas as pd

BLACK= (0,0,0)
WHITE = (255,255,255)

TRUSTEE_COLOR_PALETTE = [(156,12,12), (92,7,7), (0,0,0)]
BZA_COLOR_PALETTE = [(253,139,1), (91,51,0), (0,0,0)]
ZC_COLOR_PALETTE = [(24,113,56), (8,41,20), (0,0,0)]
MISC_COLOR_PALETTE = [(175,28,195), (55,9,61), (0,0,0)]

objFontMeetingTitle = ImageFont.truetype("/usr/share/fonts/liberation-sans/LiberationSans-Regular.ttf",115)
objFontMeetingTopic = ImageFont.truetype("/usr/share/fonts/liberation-sans/LiberationSans-Regular.ttf",115)
objFontMeetingDate = ImageFont.truetype("/usr/share/fonts/liberation-sans/LiberationSans-Italic.ttf",95)

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y

class Rect(object):
    def __init__(self, x1, y1, x2, y2):
        minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
        miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
        self.min = Point(minx, miny)
        self.max = Point(maxx, maxy)

    width  = property(lambda self: self.max.x - self.min.x)
    height = property(lambda self: self.max.y - self.min.y)

def gradient_color(minval, maxval, val, color_palette):
    """ Computes intermediate RGB color of a value in the range of minval
        to maxval (inclusive) based on a color_palette representing the range.
    """
    max_index = len(color_palette)-1
    delta = maxval - minval
    if delta == 0:
        delta = 1
    v = float(val-minval) / delta * max_index
    i1, i2 = int(v), min(int(v)+1, max_index)
    (r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
    f = v - i1
    return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))

def horz_gradient(draw, rect, color_func, color_palette):
    minval, maxval = 1, len(color_palette)
    delta = maxval - minval
    width = float(rect.width)  # Cache.
    for x in range(rect.min.x, rect.max.x+1):
        f = (x - rect.min.x) / width
        val = minval + f * delta
        color = color_func(minval, maxval, val, color_palette)
        draw.line([(x, rect.min.y), (x, rect.max.y)], fill=color)

def vert_gradient(draw, rect, color_func, color_palette):
    minval, maxval = 1, len(color_palette)
    delta = maxval - minval
    height = float(rect.height)  # Cache.
    for y in range(rect.min.y, rect.max.y+1):
        f = (y - rect.min.y) / height
        val = minval + f * delta
        color = color_func(minval, maxval, val, color_palette)
        draw.line([(rect.min.x, y), (rect.max.x, y)], fill=color)


if __name__ == '__main__':
    df = pd.read_excel(strMeetingListSpreadsheet, sheet_name="Sheet1")

    df = df.reset_index()  # make sure indexes pair with number of rows

    for index, row in df.iterrows():
        strGraphicName = f"{row['Date'].strftime('%Y%d%m')}-{row['Type']}.png"
        strMeetingType = row['Type']

        # Draw a three color horizontal gradient.
        region = Rect(0, 0, 1920, 1080)
        width, height = region.max.x+1, region.max.y+1
        image = Image.new("RGB", (width, height), BLACK)
        draw = ImageDraw.Draw(image)

        # Add meeting title
        if strMeetingType == "TrusteeRegular":
            horz_gradient(draw, region, gradient_color, TRUSTEE_COLOR_PALETTE)
            draw.text((1670, 525),"Trustee Regular Meeting",WHITE,font=objFontMeetingTopic, anchor="rm")
        elif strMeetingType == "TrusteeSpecial":
            horz_gradient(draw, region, gradient_color, TRUSTEE_COLOR_PALETTE)
            draw.text((1670, 525),"Trustee Special Meeting",WHITE,font=objFontMeetingTopic, anchor="rm")
        elif strMeetingType == "TrusteeEmer":
            horz_gradient(draw, region, gradient_color, TRUSTEE_COLOR_PALETTE)
            draw.text((1670, 525),"Trustee Emergency Meeting",WHITE,font=objFontMeetingTopic, anchor="rm")
        elif strMeetingType == "TrusteeHearing":
            horz_gradient(draw, region, gradient_color, TRUSTEE_COLOR_PALETTE)
            draw.text((1670, 525),"Trustee Public Hearing",WHITE,font=objFontMeetingTopic, anchor="rm")
        elif strMeetingType == "BZAReg":
            horz_gradient(draw, region, gradient_color, BZA_COLOR_PALETTE)
            draw.text((1670, 525),"BZA Regular Meeting",WHITE,font=objFontMeetingTopic, anchor="rm")
        elif strMeetingType == "BZAHearing":
            horz_gradient(draw, region, gradient_color, BZA_COLOR_PALETTE)
            draw.text((1670, 525),"BZA Public Hearing",WHITE,font=objFontMeetingTopic, anchor="rm")
        elif strMeetingType == "ZCReg":
            horz_gradient(draw, region, gradient_color, ZC_COLOR_PALETTE)
            draw.text((1670, 525),"Zoning Commission Meeting",WHITE,font=objFontMeetingTopic, anchor="rm")
        elif strMeetingType == "ZCHearing":
            horz_gradient(draw, region, gradient_color, ZC_COLOR_PALETTE)
            draw.text((1670, 525),"Zoning Commission Hearing",WHITE,font=objFontMeetingTopic, anchor="rm")
        else:
            horz_gradient(draw, region, gradient_color, MISC_COLOR_PALETTE)
            draw.text((1670, 525),"Township Meeting",WHITE,font=objFontMeetingTopic, anchor="rm")

        # Add township and date
        draw.text((1070, 225),"Hinckley Township",WHITE,font=objFontMeetingTitle, anchor="rm")
        draw.text((1770, 825),row['Date'].strftime('%B %d, %Y'),WHITE,font=objFontMeetingDate, anchor="rm")

        image.save(strGraphicName, "PNG")
        print(f"image saved as {strGraphicName}")


I have an Excel file which contains the meeting type code, a long meeting title that is used as the second line of the image, a date (and a MeetingDate that I use in my concat formulae that create the title and description for YouTube). To use an Excel date in concat, you need to use a TEXT formula with the text formatting string.

This allows me to have a consistent preview image for all of our postings without actually making dozens of files by hand.

Christmas Cookies

We made lots of maple cookies for Christmas — two different types, each of which has a full cup of maple syrup in it!

Anya cut shapes for the gingerbread.

We have lots of maple leaves, 5’s, cats, and seahorses. And some gingerbread people.

 

Cakey Maple Cookies

  • 1 cup all-purpose flour
  • 1 tsp baking soda
  • 1 tsp baking powder
  • 3 cup rolled oats
  • 1 large egg
  • 1 cup maple syrup
  • 1 1/2 cup Greek yogurt

Preheat oven to 350F. Sift together the dry ingredients excluding the oats. In a different bowl, combine eggs, maple, and yogurt. Mix into the dry ingredients, then stir in oats. Put about a tablespoon of batter per cookie on a sheet, and bake for about 25 minutes. Remove from tray and allow to cool.

Maple Gingerbread (Needs more ginger and cinnamon!)

  • 3 1/2 cups all purpose flour
  • 1 tsp baking powder
  • 1/2 tsp salt
  • 1 Tbps ground cinnamon
  • Tbps ground ginger
  • 1 cup dark maple syrup
  • 1/2 cup unsalted butter, melted
  • 1 egg

Sift dry ingredients together. Combine butter, maple, and egg. Stir into dry ingredients. Form into a ball. Refrigerate for a few hours.

Preheat oven to 350. Roll out dough, cut shapes, and cook for about 15 minutes. Remove from tray and allow to cool

ZFS Compression Ratio

We’ve got several PostgreSQL servers using ZFS file system for the database, and I needed to know how compressed the data is. Fortunately, there appears to be a zfs command that does exactly that: report the compression ratio for a zfs file system. Use zfs get compressratio /path/to/mount

Web Proxy Auto Discovery (WPAD) DNS Failure

I wanted to set up automatic proxy discovery on our home network — but it just didn’t work. The website is there, it looks fine … but it doesn’t work. Turns out Microsoft introduced some security idea in Windows 2008 that prevents Windows DNS servers from serving specific names. They “banned” Web Proxy Auto Discovery (WPAD) and Intra-site Automatic Tunnel Addressing Protocol (ISATAP). Even if you’ve got a valid wpad.example.com host recorded in your domain, Windows DNS server says “Nope, no such thing!”. I guess I can appreciate the logic — some malicious actor can hijack all of your connections by tunnelling or proxying your traffic. But … doesn’t the fact I bothered to manually create a hostname kind of clue you into the fact I am trying to do this?!?

I gave up and added the proxy config to my group policy — a few computers, then, needed to be manually configured. It worked. Looking in the event log for a completely different problem, I saw the following entry:

Event ID 6268

The global query block list is a feature that prevents attacks on your  network by blocking DNS queries for specific host names. This feature has caused the DNS server to fail a query with error code NAME ERROR for wpad.example.com. even though data for this DNS name exists in the DNS database. Other queries in all locally authoritative zones for other names
that begin with labels in the block list will also fail, but no event will be logged when further queries are blocked until the DNS server service on this computer is restarted. See product documentation for information about this feature and instructions on how to configure it.

The oddest bit is that this appears to be a substring ‘starts with’ query — like wpadlet or wpadding would also fail? A quick search produced documentation on this Global Query Blocklist … and two quick ways to resolve the issue.

(1) Change the block list to contain only the services you don’t want to use. I don’t use ISATAP, so blocking isatap* hostnames isn’t problematic:

dnscmd /config /globalqueryblocklist isatap

View the current blocklist with:

dnscmd /info /globalqueryblocklist

– Or –

(2) Disable the block list — more risk, but it avoids having to figure this all out again in a few years when a hostname starting with isatap doesn’t work for no reason!

dnscmd /config /enableglobalqueryblocklist 0

 

Chocolate Crepe Rolled Cake

This was another video recipe that I tried to transcribe into a list of ingredients

Cake Base

Whip together 2 eggs and 70g sugar
Add 1 tablespoon honey, and whip some more

Stir in 20g butter and 30ml milk

Sift together 65g baking flour and 10g unsweetened cocoa, then fold into egg mixture. Pour thin layer in bottom of cake pan and bake — this is the base of the crepe rolled cake.

Crepes

Whisk together 180g flour and 35g unsweetened cocoa
Add a pinch salt and 10g sugar. Whisk to combine.

Blend in 3 eggs, 260g warm milk, and 160g boiling water. Strain to remove any lumps, then mix in 60g of liquid honey and 25 (g? ml?) vegetable oil.

Cover with cling film and rest for 20 mins, then make crepes in a pan.

 

To make cream cheese filling:

Blend 250g cream cheese, 45g powdered sugar, and 150g Greek yogurt

In a separate bowl, mix 250g cream 33% and 45g powdered sugar. Whip to firm peak state.
whip to firm peak

Fold whipped cream into cream cheese mixture.

Fold in 20g liquid honey.

Assembly:

Lay out a crepe, coat with whip, and fold in thirds toward center. Repeat with all crepes.

Put cake base on plate, coat with cream mixture. Roll crepes into a big circle, then place on cake layer. Wrap additional crepes around. Chill in fridge for 3-4 hours, preferably overnight.

Dust with powdered sugar. Cut and serve.