Tuesday, November 6, 2018. U.S. Election Day


Yes. Yes, they’ve done a fine job for “you”. But what about the rest of us? Moreover, what about the “us” in perpetuity, those who will have to deal with the erosion of civic norms?

I’m an atheist, but I’m familiar enough with the Christian canon that this photograph of “President” Trump with “journalist” Sean Hannity reminded me of a verse from the Gospel of Matthew (Chapter 16, verse 26):

“For what is a man profited, if he shall gain the whole world, and lose his own soul? or what shall a man give in exchange for his soul?”

It seems like we’re witnessing the end-game of winner-take-all politics, norms, democracy, decency, and truth be-damned. We’re exchanging stability and civic norms for “winning.”

Deleting cookies with AppleScript

A couple years ago I wrote about a method for deleting cookies from Safari on macOS by employing AppleScript. Now I have a new script that works on OS X 10.14 Mojave. There are ways of surgically removing cookies, but honestly most sites leave so many cookies on my machine that I have no idea what any of them do and to what extent they use them to track me.

Safari

Here’s the script for Safari. Install it in ~/Library/Scripts/Applications/Safari (or really, wherever you like.) When launched from the scripts menu in the menu bar, it will close all current tabs and delete every cookie that Safari has saved. Since it employs UI scripting, you’ll need to give it accessibility permissions when asked.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
--
-- Created by: Alan Duncan
-- Created on: 2018-10-19
--
-- Copyright (c) 2018 Ojisan Seiuchi
-- All Rights Reserved
--

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "Safari"
activate
end tell

tell application "System Events" to tell process "Safari"
click button 1 of window 1
delay 0.5
keystroke "," using command down
delay 1
click button "Privacy" of toolbar 1 of window 1
delay 0.5
click button "Manage Website Data…" of group 1 of group 1 of window "Privacy"
delay 3
if button "Remove All" of sheet 1 of window "Privacy" is enabled then
try
click button "Remove All" of sheet 1 of window "Privacy"
delay 0.5
keystroke tab
delay 0.5
keystroke return
delay 2
click button "Done" of sheet 1 of window "Privacy"
delay 0.5
on error errMsg
click button "Done" of sheet 1 of window "Privacy"
end try
else
click button "Done" of sheet 1 of window "Privacy"
end if
click button 1 of window 1
end tell

Chrome

In the case of Chrome, it seems to store its cookies in a SQLite database. There, I just delete the database using AppleScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--
-- Created by: Alan Duncan
-- Created on: 2018-10-19
--
-- Copyright (c) 2018 MyCompanyName
-- All Rights Reserved
--

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "Google Chrome" to quit
do shell script "rm '/Users/alan/Library/Application Support/Google/Chrome/Default/Cookies'"
delay 2
tell application "Google Chrome" to activate

Monday, October 9, 2018

Hello babies…

“Hello babies. Welcome to Earth. It’s hot in the summer and cold in the winter. It’s round and wet and crowded. On the outside, babies, you’ve got a hundred years here. There’s only one rule that I know of, babies-"God damn it, you’ve got to be kind.”

Kurt VonnegutGod Bless You Mr. Rosewater

Conservative statistics

I almost never agree with Bret Stephens, but this piece in the Times in one of his more Trumpian abuses of statistics. He argues that we should take false accusations of sexual misconduct seriously because “…false allegations of rape, while relatively rare, are at least five times as common as false accusations of other types of crime, according to academic literature.” In the context of false accusation of rape, who cares about the rate of false allegation of arson, for example? The relevant statistic here is the difference between the true allegation of rape and the false allegation of rape. And given that rape and attempted rape is known to be under-reported[1], an estimate of total true allegations has to be made. His statistical sleight of hand is sadly typical of conservatives.


  1. Source U.S. Department of Justice data

Scripting thumbnail image file creation on macOS

One of the sites that I manage uses a jQuery-based image gallery to display images in a grid. The script decides which thumbnail to use based on how large and image is needed. A series of suffixes à la Flickr[1] is used to signify classes of image size. I wrote the following script to automate the process of scanning a source folder and creating four thumbnail sizes to an output directory.

It’s all pretty self-explanatory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/python
# encoding=utf8

import os
import argparse
import glob
import subprocess

# instantiate argument parser
parser = argparse.ArgumentParser(description='Create thumbnails for YAPCA site')
# arguments
parser.add_argument('srcdir', help='Source directory to search')
parser.add_argument('outdir', help='Thumbnail output directory')
# parse
args = parser.parse_args()

extensions = [ ('t',99), ('m',239), ('n',319), ('q', 499)]

for imgpath in glob.glob(os.path.join(args.srcdir,"*.jpg")):
imgname = os.path.basename(imgpath)
imgbase = os.path.splitext(imgname)[0]
for e in extensions:
thumbbase = imgbase + '_' + e[0]
thumbname = thumbbase + ".jpg"
thumbpath = os.path.join(args.outdir,thumbname)
if os.path.isfile(thumbpath):
print "File: {0} exists".format(thumbname)
else:
# this thumbsize doesn't exist, so we must create it
dimension = e[1]
print "File: {0} does NOT exist. Creating for dimension {1}\n".format(thumbname,dimension)
cmd = "sips --resampleHeightWidthMax {0} '{1}' --out '{2}'".format(dimension, imgpath,thumbpath)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
p_status = p.wait()

  1. Well, sort of. I don't think this is exactly what Flickr uses; and I made up the _q suffix for the less than 500px image.

Friday, October 5, 2018

This article by Christopher Browning published in The New York Review of Books puts the unprecedented polarization of American political life in an eery historical context. As he puts it, “Trump is no Hitler and Trumpism is not Nazism” but certain parallels are inescapable. The article surfaces an old question that I’ve harboured for almost a decade - Is the chaotic concentration of all power the intentional end-game of the Republican party or did they simply provoke the worst darkest tendencies of voters, only later finding they couldn’t control what they started? Whatever the answer to that question, the piece is worth reading if only to observe how beautiful sentences are crafted. The essay is that well-written.


“If the US has someone whom historians will look back on as the gravedigger of American democracy, it is Mitch McConnell. He stoked the hyperpolarization of American politics to make the Obama presidency as dysfunctional and paralyzed as he possibly could. As with parliamentary gridlock in Weimar, congressional gridlock in the US has diminished respect for democratic norms, allowing McConnell to trample them even more.”[1]


As much as Trump’s base is to blame for the chaotic abdication of American leadership, the moderate Republican voter standing in the penumbra of Trump’s vile idiocy must share some of the blame. Almost never, in a political movement, does the base have enough mass to effect change. It requires consenting moderates. As Christopher Browning puts it:

“There seems to be nothing for which the demonization of Hillary Clinton does not serve as sufficient justification…” [2]


Collins to announce Kavanaugh position on Friday afternoon. (source) - seriously, how hard can this be?

Grassley: ‘We won’t know how it’s going to go until everybody casts their vote’ (source) - such erudition from a U.S. Senator. Wow.


  1. "The Suffocation of Democracy", Browning, Christopher, October 25, 2018 issue, The New York Review of Books.

  2. Ibid.

Thursday, October 4, 2018

This piece in The Atlantic by Adam Serwer is a reminder that for Trump’s supporters, cruelty isn’t just a side-effect of Trumpism. It’s a feature. That the current U.S. president chose to mock the victim of a sexual assault by a Supreme Court nominee is hardly surprising. But what little hope I had for Trump supporters, particularly those at the Mississippi rally who joined Trump in his mockery, is gone.

“Trump’s only true skill is the con; his only fundamental belief is that the United States is the birthright of straight, white, Christian men, and his only real, authentic pleasure is in cruelty. It is that cruelty, and the delight it brings them, that binds his most ardent supporters to him, in shared scorn for those they hate and fear: immigrants, black voters, feminists, and treasonous white men who empathize with any of those who would steal their birthright.”

How can the educational system have failed so miserably than to allow the ignorant to wallow in their own ignorance and bigotry? The South is the home of millions of adherents, faithful in word, at least, to a religion of kindness, meekness and tolerance. Yet the Venn circles of evangelicals and Trump supporters is near complete. The hypocrisy is nearly palpable.

Searching the Russian National Corpus

The Russian language has a vast and nuanced vocabulary. One approach to learning the vocabulary is to approach it in frequency order. The Nicholas Brown book seems dated and the frequency ordering methodology is not clear to me. Some words seem to be clustered by the beginning letter, which seems statistically unlikely. However, it’s a convenient list and I’m slowly building a table that cross-correlates the Nicholas Brown list with the methodologically-superior Russian National Corpus. To do that I harvested the data from the Corpus and built a Python application to search the database and report the rank and frequency data from it.

Creating a sqlite3 version of the Russian National Corpus

There is a CSV version of the Corpus, but the data is not useful for ordering in a meaningful way. Instead, I took the rank ordered tabular data from the page Частотный список лемм (Frequency list of lemmas) and simply pasted it into a Numbers spreadsheet. Since Numbers is extremely slow even on a fast-performing machine, it beachballed for nearly a minute during the paste operation. After that, I exported it as CSV. To get the CSV file into a sqlite3 database, I created a new table with the following schema:

After mapping the column names to those in the CSV, the import was simple.

Accessing the sqlite3 version of the corpus using Python

Next I wrote a little Python application to access the data and return the rank or frequency of any Russian word. The only trick is that the Russian letter ë is rendered as e in the database; so any word containing ë must be altered before the search. Regular expressions to the rescue! To use the application, just launch it with -h help flag and you’ll see the calling format.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python
# encoding=utf8

import sqlite3
import sys
import re
import argparse

# '/Users/alan/torrential/russian/vocabulary/RussianNationalCorpus'

# instantiate argument parser
parser = argparse.ArgumentParser(description='Search the Russian National Corpus')
# arguments
parser.add_argument('word', help='Russian word to search for')
parser.add_argument('db_path', help='Path to the sqlite db')
group = parser.add_mutually_exclusive_group()
group.add_argument('--r', action='store_true',help='Show rank order')
group.add_argument('--f', action='store_false',help='Show frequency in instances/million')
# parse
args = parser.parse_args()

word = args.word
replaced = re.sub('ё','е',word)

conn = sqlite3.connect(args.db_path)

col = "rank" if args.r else "frequency"
sql = "SELECT " + col + " FROM corpus WHERE word LIKE '" + replaced + "'"
curs = conn.cursor()
curs.execute(sql)
print curs.fetchone()[0]

And a little AppleScript

Finally, I wrote an AppleScript wrapper that I can launch with a Quicksilver keystroke trigger. The wrapper takes the word off the clipboard and calls the Python app above, replacing the contents of the clipboard with the rank order of the word. For a little fun, it speaks the rank order number in Russian! Here’s the code for the AppleScript wrapper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
--
-- Created by: Alan Duncan
-- Created on: 2018-09-22
--
-- Copyright (c) 2018 Ojisan Seiuchi
-- Use to your heart's content; just give me a little credit
--

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set dbPath to "/Users/alan/torrential/russian/vocabulary/RussianNationalCorpus"

set errorFlag to 0
set w to the clipboard
set cmd to "python /Users/alan/Documents/dev/scripts+tools/getRussianRank.py " & w & " " & dbPath & " --r"

try
set rank to do shell script cmd
set the clipboard to rank
on error errMsg
say "плохо"
set errorFlag to 1
end try
if errorFlag is 0 then
set saying to "Готово " & (rank as string)
say saying
end if

If you want a pre-built sqlite3 version of the Russian National Corpus, here it is.