Creating year and month groups in DEVONthink Pro Office using AppleScript

In DEVONthink Pro Office I often organize certain content by year and month. To do that, I created a simple AppleScript to build year and month groups in the following format:

To use the script, you select the parent group in which the year will reside.

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
43
44
--
-- Created by: Alan Duncan
-- Created on: 2018-12-14
--
-- Copyright (c) 2018 Ojisan Seiuchi
-- All Rights Reserved
--

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

set theMonths to {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}

tell application id "DNtp"
try
set theSelection to selection
if theSelection is {} then error "Please select some contents."
set theYear to display name editor "Year:"
set theYearGroup to create record with {name:theYear, type:group} in current group
repeat with i from 1 to 12
set theMonth to item i of theMonths

set theMonth to (my add_leading_zeros(i, 1)) & " - " & theMonth
set theMonthGroup to create record with {name:theMonth, type:group} in theYearGroup
end repeat
on error
display dialog "Error"
end try
end tell

on add_leading_zeros(this_number, max_leading_zeros)
set the threshold_number to (10 ^ max_leading_zeros) as integer
if this_number is less than the threshold_number then
set the leading_zeros to ""
set the digit_count to the length of ((this_number div 1) as string)
set the character_count to (max_leading_zeros + 1) - digit_count
repeat character_count times
set the leading_zeros to (the leading_zeros & "0") as string
end repeat
return (leading_zeros & (this_number as text)) as string
else
return this_number as text
end if
end add_leading_zeros

Duncan's Law

Trump lawyer and all-around whackadoodle Rudy Guiliani claims he’s the most ethical person ever. Of course, his association with one of the least ethical people ever suggests otherwise. Thus, it prompts me to articulate “Duncan’s Law.” Succinctly stated, if someone claims absolute superiority in some particular characteristic, his actual performance in that characteristic is actually somewhere between average and the least performant.

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.