From efcc4fedaaa01da6cbc9fff100f34424705ff7db Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Thu, 30 Nov 2017 13:19:22 -0800 Subject: [PATCH] =?UTF-8?q?Add=20tests=20for=20Dr.=20Drang=E2=80=99s=20JSO?= =?UTF-8?q?N=20Feed.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj | 4 ++++ .../RSParser/RSParserTests/FeedParserTypeTests.swift | 9 ++++++++- .../RSParser/RSParserTests/JSONFeedParserTests.swift | 8 ++++++++ Frameworks/RSParser/RSParserTests/Resources/allthis.json | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Frameworks/RSParser/RSParserTests/Resources/allthis.json diff --git a/Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj b/Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj index d2e8843b9..9e257c60f 100644 --- a/Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj +++ b/Frameworks/RSParser/RSParser.xcodeproj/project.pbxproj @@ -63,6 +63,7 @@ 845213251FCB3C76003B6E93 /* coco.html in Resources */ = {isa = PBXBuildFile; fileRef = 845213241FCB3C75003B6E93 /* coco.html */; }; 845213281FCB4042003B6E93 /* RSHTMLTag.h in Headers */ = {isa = PBXBuildFile; fileRef = 845213261FCB4042003B6E93 /* RSHTMLTag.h */; settings = {ATTRIBUTES = (Public, ); }; }; 845213291FCB4042003B6E93 /* RSHTMLTag.m in Sources */ = {isa = PBXBuildFile; fileRef = 845213271FCB4042003B6E93 /* RSHTMLTag.m */; }; + 84566D941FD0ABFB00103322 /* allthis.json in Resources */ = {isa = PBXBuildFile; fileRef = 84566D931FD0ABFB00103322 /* allthis.json */; }; 84628AAD1FCA10AE00566A9B /* allthis.atom in Resources */ = {isa = PBXBuildFile; fileRef = 84628AAC1FCA10AE00566A9B /* allthis.atom */; }; 848674D21FCE7BF600802D1F /* macworld.rss in Resources */ = {isa = PBXBuildFile; fileRef = 848674D11FCE7BF500802D1F /* macworld.rss */; }; 849A03D01F0081EA00122600 /* DaringFireball.html in Resources */ = {isa = PBXBuildFile; fileRef = 849A03C51F0081EA00122600 /* DaringFireball.html */; }; @@ -164,6 +165,7 @@ 845213241FCB3C75003B6E93 /* coco.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = coco.html; sourceTree = ""; }; 845213261FCB4042003B6E93 /* RSHTMLTag.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RSHTMLTag.h; sourceTree = ""; }; 845213271FCB4042003B6E93 /* RSHTMLTag.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RSHTMLTag.m; sourceTree = ""; }; + 84566D931FD0ABFB00103322 /* allthis.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = allthis.json; sourceTree = ""; }; 84628AAC1FCA10AE00566A9B /* allthis.atom */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = allthis.atom; sourceTree = ""; }; 848674D11FCE7BF500802D1F /* macworld.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = macworld.rss; sourceTree = ""; }; 849A03C51F0081EA00122600 /* DaringFireball.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DaringFireball.html; sourceTree = ""; }; @@ -362,6 +364,7 @@ 845213241FCB3C75003B6E93 /* coco.html */, 849A03CE1F0081EA00122600 /* sixcolors.html */, 84628AAC1FCA10AE00566A9B /* allthis.atom */, + 84566D931FD0ABFB00103322 /* allthis.json */, 849A03CF1F0081EA00122600 /* Subs.opml */, ); path = Resources; @@ -544,6 +547,7 @@ 849A03D61F0081EA00122600 /* manton.rss in Resources */, 849A03D11F0081EA00122600 /* DaringFireball.rss in Resources */, 849A03D01F0081EA00122600 /* DaringFireball.html in Resources */, + 84566D941FD0ABFB00103322 /* allthis.json in Resources */, 84628AAD1FCA10AE00566A9B /* allthis.atom in Resources */, 848674D21FCE7BF600802D1F /* macworld.rss in Resources */, 849A03EA1F01F92B00122600 /* inessential.json in Resources */, diff --git a/Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift b/Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift index fb7bae571..774d5005d 100644 --- a/Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift +++ b/Frameworks/RSParser/RSParserTests/FeedParserTypeTests.swift @@ -119,7 +119,14 @@ class FeedParserTypeTests: XCTestCase { let type = feedType(d) XCTAssertTrue(type == .jsonFeed) } - + + func testAllThisJSONFeedType() { + + let d = parserData("allthis", "json", "http://leancrew.com/allthis/") + let type = feedType(d) + XCTAssertTrue(type == .jsonFeed) + } + // MARK: Performance func testFeedTypePerformance() { diff --git a/Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift b/Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift index f7c45d309..721cc5f3f 100644 --- a/Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift +++ b/Frameworks/RSParser/RSParserTests/JSONFeedParserTests.swift @@ -58,4 +58,12 @@ class JSONFeedParserTests: XCTestCase { XCTAssert(parsedFeed.faviconURL == "https://daringfireball.net/graphics/favicon-64.png") XCTAssert(parsedFeed.iconURL == "https://daringfireball.net/graphics/apple-touch-icon.png") } + + func testAllThis() { + + let d = parserData("allthis", "json", "http://leancrew.com/allthis/") + let parsedFeed = try! FeedParser.parse(d)! + + XCTAssertEqual(parsedFeed.items.count, 12) + } } diff --git a/Frameworks/RSParser/RSParserTests/Resources/allthis.json b/Frameworks/RSParser/RSParserTests/Resources/allthis.json new file mode 100644 index 000000000..ee6f0fcc6 --- /dev/null +++ b/Frameworks/RSParser/RSParserTests/Resources/allthis.json @@ -0,0 +1 @@ +{"description": "I just said what I said and it was wrong. Or was taken wrong.", "feed_url": "http://leancrew.com/all-this/feed.json", "title": "And now it’s all this", "items": [{"title": "Last thoughts on modifier keys", "url": "http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/", "author": {"name": "Dr. Drang"}, "summary": "The first shall be last.", "date_published": "2017-11-23T21:08:29+00:00", "id": "http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/", "content_html": "

When I wrote the post about ordering Mac modifier keys a few days ago, I was thinking primarily about the proper order of the symbols when writing about a keyboard shortcut, like ⌃⌥⌘P.1. I mentioned parenthetically that this order isn’t always observed when people speak about keyboard shortcuts or when they write the names of the keys out fully, as in “Command-Shift-3 takes a screenshot.”

\n

Jason Snell, in both a post at Six Colors and in conversation with John Siracusa on the lastest episode of Upgrade, took a stand against Apple’s ordering:2

\n
\n

Command is the commander! Command is the monarch of all keys! Command always comes first, in my book.

\n
\n

Siracusa agreed, and so do I. The ⌘ key is, and has always been, the key that signals a keyboard shortcut. While other modifier keys are sometimes used without ⌘—in cursor control and text selection, for example—I can’t think of any Apple applications that don’t use ⌘ to signal a keyboard shortcut for a menu item. And that primacy in shortcuts to menu items is, I think, why Apple puts it last rather than first.

\n

Keyboard shortcuts are always presented right-justified along the right edge of the menu. The most common shortcuts are just ⌘ and a letter, like ⌘N to start a new document, for example. It’s typically the variations on the basic command that get additional modifier keys, like ⌥⌘N to start a new project. If that were presented in a menu as ⌘⌥N, the menu would look wrong because the ⌘ symbols wouldn’t line up.

\n

Here’s the File menu in Safari:

\n

\"Safari

\n

There are two different New commands and three different Close commands. This, in Apple’s opinion (and mine), wouldn’t be right:

\n

\"Altered

\n

It’s not just having the ⌘ symbols aligned. The additional modifier symbols go in front because ⌘ is king and must sit next to the N or the W. The importance of the modifier decreases as you move away from the letter.

\n

It should go without saying—but I’ll say it anyway—that the letter (or number or whatever) key is the most important because nothing happens until it’s pressed.

\n

Having said all this, and despite agreeing with Apple’s symbol ordering, my ear for shortcut ordering works just like Jason’s and John’s. The main reason I use keyboard shortcut symbols in my posts instead of words is that I can read ⌥⇧⌘W and not be bothered because I don’t “hear” it as I read the symbols. “Option-Shift-Command-W,” on the other hand, gets sounded out in my head, and it sounds wrong.

\n

I suspect that’s why Apple’s own documentation sometimes gets the order wrong when the modifiers get written out as words. In speaking out the keys, “Command” is natural to put first because it announces that what’s coming is a keyboard shortcut.

\n
\n
\n
    \n
  1. \n

    Which happens to be the shortcut I use for previewing a blog post locally before publishing it. ↩︎

    \n
  2. \n
  3. \n

    In the original post, I said I didn’t know where the order was documented. A few people pointed me to both the Human Interface Guidelines and the Style Guide, where Apple gives the proper order explicitly. ↩︎

    \n
  4. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "My next Mac", "url": "http://leancrew.com/all-this/2017/11/my-next-mac/", "author": {"name": "Dr. Drang"}, "summary": "Apple isn't making it easy to choose a Mac and hasn't for a few years.", "date_published": "2017-11-22T22:04:57+00:00", "id": "http://leancrew.com/all-this/2017/11/my-next-mac/", "content_html": "

Will probably be an iMac. I guess that spoils the suspense, doesn’t it?

\n

My iMac at work is the 27″ Late 2012 model, the one that came out one step before Retina came to the iMac. I don’t regret buying it, as my previous iMac (a 2006 model, I think) was absolutely on its last legs—constantly swapping to hard disk and running hot. I hadn’t meant to wait so long to replace it, but there was a long delay before that 2012 model came out, and I didn’t want to buy something that would be last year’s model almost as soon as I set it up.

\n

My home Mac is the venerable 2010 13″ MacBook Air, the first good Air. In the normal course of things, this would be the Mac I replace next, and I’ve been expecting to do so for a few years now. but…

\n

But Apple never came out with a Retina MacBook Air, choosing instead to go with the MacBook, which I find a little too far on the portable side of the portability/power spectrum. A couple of years ago, I had a crisis when my Air crapped out on me. It seemed wrong to put money into a five-year-old machine, but I wasn’t enthused about any of the MacBooks in the lineup at the time. I didn’t know the just-released 2015 MacBook Pro would turn out to be the best laptop ever made, I didn’t want to spend MacBook Pro money on my home/travel machine.

\n

The $280 logic board upgrade turned out to be a good investment, as I’m now 2½ years into my rejuvenated Air. Yes, it takes a while to wake up when I open the lid. Yes, its 128 GB SSD is tiny. No, it can’t take advantage of many of the iOS integration features that newer Macs can. And no, I don’t think it’s a good idea to install High Sierra on it. But it’s given me 30 months of faithful use, much more than I expected at the time.

\n

The announcement of the Touch Bar last year made me certain I’d be getting a MacBook Pro with it. A software-configurable set of controls seemed perfect for someone who’s always ginning up little scripts. But no one seems to like it, possibly because its configurability isn’t especially open to users. Bummer.

\n

I’ve delayed the decision on my home Mac for such a long time that now my office Mac is long in the tooth, too. Still working fine for most tasks, but just a Core 2 Duo machine that often makes me wait to scroll through long PDFs of scanned engineering and architectural drawings, something I need to do at work quite often. And no Retina.

\n

So it looks like my best bet is to buy a new iMac for work and bring my current office iMac home. This will put the power where I need it the most and will give me extra ooomph here at home. Especially with disk space (3 TB vs. 128 GB) and RAM (24GB vs. 4GB).

\n

It will be weird, though, as I haven’t had a desktop computer here at home in a dozen years. Will I enjoy being tethered to one spot in the house? And what about a travel computer?

\n

Both of these questions are made less pressing by the device I’m typing this on: a 9.7″ iPad Pro. While I agree with Gabe that it is by no means a Mac substitute, it can handle a lot of what I do at home and virtually everything I need to do on the road.1

\n

As for which iMac, I think I’ll settle on a middle-of-the-road 27″ configuration with a 3TB Fusion drive. Sort of the 2017 of what I bought in 2012.

\n
\n
\n
    \n
  1. \n

    I bought the iPad Pro last year as a sort of experiment to find out how comfortable I’d be working on it. I intend to write a full post about the results of that experiment soon, but in the meantime, you really should read Gabe’s post over at Macdrifter. I’ll probably use his post as a jumping-off point. And there may be a quiz. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Modifier key order", "url": "http://leancrew.com/all-this/2017/11/modifier-key-order/", "author": {"name": "Dr. Drang"}, "summary": "Writing about Mac keyboard shortcuts? Make sure you put them in canonical order.", "date_published": "2017-11-20T02:22:59+00:00", "id": "http://leancrew.com/all-this/2017/11/modifier-key-order/", "content_html": "

If you write about Mac keyboard shortcuts, as I did yesterday, you should know how to do it right. Just as there’s a proper order for adjectives in English, there’s a proper order for listing the modifier keys in a shortcut.

\n

I haven’t found any documentation for this, but Apple’s preferred order is clear in how they show the modifiers in menus and how they’re displayed in the Keyboard Shortcuts Setting.

\n

\"Canonical

\n

The order is similar to how you see them down at the bottom left of your keyboard.

\n

\"Modifier

\n

Control (⌃), Option (⌥), and Command (⌘) always go in that order. The oddball is the Shift(⇧) key, which sneaks in just in front of Command.

\n

Keyboard Maestro recognizes this standard order and presents its “hot key” shortcut the same way.

\n

\"Keyboard

\n

(When people speak about keyboard shortcuts, it’s not uncommon to put Command first, e.g., “Command-Shift-3 takes a screenshot.” I’ve seen it written out that way, too. Apple is usually pretty careful to use the same order when using words as when using symbols. This page, for example, uses “Shift-Command-3,” to match the ⇧⌘3 you’d see in the Keyboard Shortcut Settings. But even Apple slips up. On the grand Mac keyboard shortcut page, there are a few instances of “Command-Shift” instead of “Shift-Command.”)

\n

The last bit of standard syntax is that the letter key in the shortcut (if there is a letter) is always presented as a capital, even when the Shift key isn’t used. I suspect this was a serious topic of discussion at Bandley 3 back in the early 80s as the Mac was being developed. They got it right. When entering a keyboard shortcut, you’re not typing a letter, you’re pressing a set of physical keys on the keyboard in front of you. The symbols on the letter keys are capitals, so that’s the appropriate way to identify those keys.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Command-E", "url": "http://leancrew.com/all-this/2017/11/command-e/", "author": {"name": "Dr. Drang"}, "summary": "One of my favorite and little-known (even by Apple) Mac system level keyboard shortcuts.", "date_published": "2017-11-19T02:02:50+00:00", "id": "http://leancrew.com/all-this/2017/11/command-e/", "content_html": "

Earlier this evening, Merlin tweeted out some advice we should all heed:

\n

Per today's @thetalkshow, here's an Apple Support doc that can change your life. I dare you not to find something new here.https://t.co/37FuPPUbYK

— Merlin Mann (@hotdogsladies) November 18, 2017
\n\n

Even though I’ve been using a Mac for 32 years (albeit with an 8-year Linux hiatus) there were a few keyboard shortcuts in there I didn’t know about. And, just as important, several that I once knew but had fallen out of the habit of using.

\n

But one of my favorites—and a big convenience when writing code or prose—isn’t on the Mac keyboard shortcut page. I wonder if the folks at Apple have forgotten it.

\n

It has to do with finding text in a document. You know about ⌘F to specify a Find. That usually brings up a window or other control into which you can type (or paste) the text you’re going to be searching for. Often, there are options for controlling how the search will be performed and for replacement text. Here’s the Find window from BBEdit,

\n

\"BBEdit

\n

here’s the Find window from Pages,

\n

\"Pages

\n

and here’s TextEdit, which pops up a little Find section in the main window below the ruler,

\n

\"TextEdit

\n

You probably also know about ⌘G, which is usually given the menu name Find Next. It repeats the last Find command, allowing you to step through all the occurrences of that text in the document. There are usually other ways to cycle through all the found instances—Pages and TextEdit have arrow buttons you can click on—but ⌘G is the traditional way.1

\n

A common situation, especially when looking through long documents or source code files, is to see a particular string of text and want to find other instances of it. You may, for example, see the definition of a function and want to search out where that function gets called later in the code. One way to do that would be to select the function name, copy it, ⌘F to bring up the text entry field, paste, and then ⌘G your way through the rest of the document.2 But the copy/⌘F/paste dance is a little clumsy.

\n

Which is where ⌘E, which is typically given a menu name like Use Selection for Find, comes in.

\n

\"BBEdit

\n

Select the text you want to search for and hit ⌘E. That turns the selection into the search text, and you can go straight to ⌘G to walk through all the other occurrences.

\n

I use ⌘E all the time when searching through code for function and variable names, as in the example above. It also works in Safari, Chrome, Preview, and PDF Expert, so I do similar quick searching on words I come across as I read news articles, blogs, reports, papers, etc.

\n

⌘E works in other well-behaved applications, too, so you should give it a try. Note that “well-behaved applications” excludes MS Word. In Word, ⌘E centers the paragraph of text containing the selection. Of course.

\n
\n

Update Nov 19, 2017 8:40 AM
\nA couple of things I forgot to include:

\n

First, ⌘E in the Finder has, somewhat ironically, no relationship to finding. It ejects or unmounts the selected disk, assuming there is a selected disk. This shortcut is listed on Apple’s Mac shortcut page (which is why I was going to mention it), and a couple of people reminded me of it after the post was published. I guess I forgot about it because it’s a command I never use; I eject by right-clicking and selecting Eject from the menu that pops up.

\n

Second, editors and word processors on iOS have not inherited this useful shortcut from the Mac. There is, of course, no ⌘ key on the software keyboard, but many of the traditional Mac shortcuts that use ⌘, ⌥, and ⇧ for moving the cursor and selecting text do work on iOS when you use an external keyboard. ⌘F and ⌘G (and its backward cousin, ⇧⌘G) often work in iOS, too, but ⌘E didn’t make the cut. Too bad.

\n

\"Keyboard

\n
\n
\n
\n
    \n
  1. \n

    I believe ⌘G was originally used as the shortcut for Go To Page… in MacWrite, but it’s been Find Next for ages. 

    \n
  2. \n
  3. \n

    Yes, source code editors in general, and BBEdit in particular, have other ways to find all the instances of a string of text, and those ways may be more appropriate in some situations. But stepping your way through each instance in turn is often the best way to see the string in full context. 

    \n
  4. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Converting fractions to decimal values", "url": "http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/", "author": {"name": "Dr. Drang"}, "summary": "A Keyboard Maestro macro for cleaning up numerical values.", "date_published": "2017-11-13T04:35:39+00:00", "id": "http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/", "content_html": "

Recently, I’ve been getting Excel spreadsheets from clients that contain measurements in inches and/or fractions of an inch. In some cases, the cells with these measurements actually contain numbers but are displayed as fractions using one of the many formatting options Excel has.

\n

\"Excel

\n

In these cases, it’s easy for me to change the formatting to a standard decimal representation before exporting the sheet as a CSV file.

\n
\n

Aside
\nThe only reason I own Excel is to be able to open spreadsheets that other people send me and export them into some other usable format. This wasn’t always the case. I used the hell out of Excel in the late 80s and early 90s, when it was still a proper Mac app. But somewhere there in the early to mid 90s, Microsoft turned Excel into a Windows app that ran on the Mac and ruined its user interface. I stopped using it shortly thereafter. In the quarter-century since, it’s only gotten worse. Note, for example, the absurd layout of the dialog box above.

\n

Small Excel spreadsheets usually get moved over to Numbers, a considerably less powerful spreadsheet, but one that works the way a normal Mac app works. Large Excel spreadsheets get converted to CSV (often after a short visit to Numbers, as I’ve had trouble with Excel’s CSV conversion) so I can query and manipulate the data with Pandas. Why don’t I just open the Excel file in Pandas directly? Because the spreadsheets I get are so filled with cruft, read_excel can’t be trusted.

\n
\n

Often, though, the measurements are entered as text. This is done by prefixing the data with a single quote mark, e.g.,

\n
'2 5/16\n
\n

or maybe

\n
'2-5/16\n
\n

These need to be converted into a decimal representation before exporting to CSV, so Pandas will recognize them as numbers rather than strings.1

\n

To handle this situation, I created a Keyboard Maestro called Floatize. To use Floatize, I

\n
    \n
  • Select the column of data I want to convert.
  • \n
  • Copy it to the clipboard.
  • \n
  • Run Floatize, which converts the clipboard from a fractional representation to decimal.
  • \n
  • Paste the result back into the column.
  • \n
\n

Here’s the macro:

\n

\"Floatize

\n

It takes the clipboard, sends it to a script as standard input, and then puts the output back into the clipboard. Here’s the Python script it runs:

\n
python:\n 1:  #!/usr/bin/env python\n 2:  \n 3:  from fractions import Fraction\n 4:  import re\n 5:  import sys\n 6:  \n 7:  def floatize(s):  \n 8:    if s in ['', 'n/a', 'none']:\n 9:      return ''\n10:    else:\n11:      try:\n12:        num = float(s)\n13:      except ValueError:\n14:        t = re.sub(r'(.*\\d) *- *(\\d.*)', r'\\1 \\2', s)\n15:        if t[0] == '-':\n16:          num = -float(sum(Fraction(x) for x in t[1:].split(None, 1)))\n17:        else:\n18:          num = float(sum(Fraction(x) for x in t.split(None, 1)))\n19:      return \"{:f}\".format(num)\n20:    \n21:  for line in sys.stdin.read().splitlines():\n22:    item = line.strip().lower()\n23:    print floatize(item)\n
\n

The bulk of the fraction parsing is done by the fractions module in either Line 16 or Line 18. The rest of the script is an attempt to manipulate the oddball types of data I get into a format that fractions understands. Here’s what it can handle:

\n
    \n
  • Blank, n/a or none entries get turned into an empty string (Lines 8–9). Note that entries are converted to lower case in Line 22, before being passed to the floatize function, so this handles N/A and None, too.
  • \n
  • Entries already in decimal format get converted to a float (Line 12) and then output in decimal format (Line 19). This is basically a pass-through.
  • \n
  • Entries with a dash between the whole and fractional parts get the dash removed (Line 14).
  • \n
  • Negative entries (Line 15) have their whole and fractional parts added together with the sign reversed (Line 16), and then output in decimal format (Line 19).
  • \n
  • Positive entries have their whole and fractional parts added together (Line 18), and then output in decimal format (Line 19).
  • \n
  • Entries that don’t look like any of the above cause an error.
  • \n
\n

The macro works perfectly in Numbers and BBEdit, but not in Excel. For some reason, nothing happens to the clipboard if I run the Floatize macro while Excel is the active application. I can, however, copy the column of fractions, activate another app, run Floatize, switch back to Excel and paste the converted clipboard. Another example of Excel not acting like a proper Mac app and another reason I move data out of Excel as quickly as I can.

\n

Despite its inability to work inside Excel, this macro has been very handy over the past couple of weeks. I’ve used it to clean up several data sets that were loaded with fractions.

\n

By the way, I will block anyone who tweets that I wouldn’t have this problem if I used the metric system.

\n
\n

Update Nov 13, 2017 10:04 PM  \nThe Twitter consensus is my problem in using this macro in Excel stems from Excel’s clipboard dumbassery. It apparently uses a private clipboard instead of the system clipboard until you bring another application to the front. Hence the macro’s inability to convert the clipboard unless I switched away from Excel.

\n

Jimmy Hartington suggested this workaround:

\n
\n
\n@drdrang Excel does something weird with the clipboard as you noticed.
Try this. Copy in Excel. Press Escape. Run the macro.
\nBy pressing Escape I think Excel stops messing with the clipboard.\n
Jimmy Hartington (@jimmyhartington) Nov 12 2017 11:06 PM
\n
\n
\n

This works perfectly and pointed me in the direction to make a better version of the macro, one that does all the copying, converting, and pasting:

\n

\"New

\n

Note the conditional action that simulates the pressing of the Escape key only if Excel is the front application. This is necessary because Escape causes Numbers to deselect the column, which screws up the paste action at the end of the macro.

\n

With this macro I can now select the column that needs converting and just press ⌃⌥⌘F to do the conversion in a single step. Thanks, Jimmy!

\n
\n
\n
\n
    \n
  1. \n

    There are probably ways to get Pandas to do the conversion as it imports the data, but I feel more comfortable—more in control—if I do it ahead of time. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "A modest proposal", "url": "http://leancrew.com/all-this/2017/11/a-modest-proposal/", "author": {"name": "Dr. Drang"}, "summary": "MacStories should think about cleaning up some of its Apple sales charts.", "date_published": "2017-11-07T03:27:11+00:00", "id": "http://leancrew.com/all-this/2017/11/a-modest-proposal/", "content_html": "

With Apple sales and graphing on my mind, I’d like to make a small complaint and suggestion to the folks at MacStories: some of your quarterly graphs could use a little scrubbing; they’d be much easier on the eyes if they were cleaned up a bit.

\n

I’m talking about the ones that look like this:

\n

\"MacStories

\n

My complaint is with the clutter along the x-axis. That uniform gray mass of text detracts from the data above it. And although putting a label at each quarter seems to provide more information to the reader, the reader actually gets less out of it because it’s too hard to read. The axis would be improved if it had just yearly Q1 labels. Maybe even one label every other year. Fewer labels would mean they could be turned back horizontal, which would be easier to read and would give more vertical space for the data.

\n

I suspect every data point is labeled because that’s the way Numbers wants to do it, and MacStories’ automated system for generating these graphs follows the Numbers defaults. But if you were making these graphs by hand, you’d never label the x-axis this way. An automated system shouldn’t, either.

\n

On the Mac, Numbers has a way to skip labels on the x-axis by selecting Custom Category Intervals from the Category Labels popup menu and then choosing how often to put the labels.

\n

\"Mac

\n

Sadly, I can’t find that option in the iOS version. Maybe it’s tucked away in a less obvious place.

\n

\"iOS

\n

Personally, I’m not a fan of either version of Numbers when it comes to making graphs because I like way more control than they give. But Numbers (like most graphing programs and libraries) can make nice graphs if you’re willing to break away from its defaults.

\n

I don’t really want to pick on MacStories. Their graphs are as good as, if not better than, what you typically find in blogs and news sites. Most graphs, unfortunately, are based on the default settings of the software that made them, and come out looking a little clumsy.

\n

I read MacStories because it has stylish writing. I’d like its graphs to be just as stylish.

\n
\n

Update Nov 7, 2017 8:55 PM
\nI was informed on Twitter by two authoritative sources (this one and this one) that the MacStories graphs are made with Excel, not Numbers. I haven’t used Excel for graphing since the mid-90s, but even then it had more customization options than Numbers has now. So I’m crossing my fingers for cleaner x-axes in January’s graphs.

\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Apple sales graphs and the iPhone 7", "url": "http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/", "author": {"name": "Dr. Drang"}, "summary": "Obligatory quarterly post.", "date_published": "2017-11-06T02:53:19+00:00", "id": "http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/", "content_html": "

Last week Apple released its 2017 Q4 (which everyone else’s calendar says is 2017 Q3) sales and revenue figures, which means a boatload of graphs from all your fave rave Apple-oriented bloggers. I’m a few days late, as usual, despite having long ago written scripts to generate these graphs.

\n

I don’t make as many graphs as everyone else, partly because I’m lazy, but mainly because don’t care about revenue. My concern is the popularity of the devices I use, not how expensive they are. I want third-party developers to keep writing apps for them, and that means there has to be a market for them out there.

\n

Here’s the summary graph, with the sales figures for the three main Apple products. The dots are the quarterly sales and the lines are the four-quarter moving averages.

\n

\"Apple

\n

The iPhone dominates the scale of this graphs, so it’s helpful to also see the iPad and Mac on their own graphs.

\n

\"iPad

\n

\"Mac

\n

The good news is the second straight quarter of year-over-year increase in iPad sales after its long, well-documented slide since 2013. Additional good news is the Mac’s slow but consistent rise over the past year—four straight quarters of year-over-year sales increases. Imagine how much better the figures would be if people really liked the Touch Bar.

\n

I think its fair to say, though, that the iPhone 7 was something of a dud. It had a good start, but its non-intro quarters had almost exactly the same sales as the 6S had. Look at the final three quarters of each.

\n

\"Apple

\n

As I’ve argued before, the iPhone 6S sales looked lackluster only in comparison to the gangbusters popularity of the iPhone 6. If one ignored the year of the 6, the 6S’s sales were more or less on the same upward trend as the previous three editions. The iPhone 7 was on that same trend for its first quarter but went dead flat after that.

\n

Apple’s report in January will be very interesting.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Another one-off Keyboard Maestro macro", "url": "http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/", "author": {"name": "Dr. Drang"}, "summary": "I should do more of these.", "date_published": "2017-11-03T02:02:41+00:00", "id": "http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/", "content_html": "

Do you use Keyboard Maestro (or AppleScript or whatever) for one-time, throwaway macros as often as you should? I know I don’t, but I did put one together a couple of days ago and used a feature I’d never tried before.

\n

I had one of those recalcitrant PDFs that I often get from clients. This one was 25–30 pages long, each an E-sized floor plan drawing for a building. The drawings were all black-and-white, but the PDF had color annotations added. I needed to add my own annotations to most of the pages, but something about the format of the file made it very cumbersome to work with. I tried Preview, PDF Expert, and PDfpen Pro, and they all were glacially slow when panning, zooming, and switching pages.

\n

So I broke the file up into individual pages using PDFtk:

\n
pdftk drawings.pdf burst\n
\n

The single-page files didn’t make me wait for the spinning beach ball, so I was able to add my annotations quickly in PDF Expert. Then came an impasse.

\n

I wanted to email the drawings with my annotations back to the client and to some other parties, but they were too big to fit in an email. I could use multiple emails, but that’s a recipe for losing some of the files. I could use a Dropbox link, but I had a sense that one of the other parties wouldn’t understand how that worked. What seemed best was to convert the files to JPEGs at the lowest legible resolution, zip them together, and send the zipped file in a single email.1

\n

My normal practice would be to use sips for this, because I can issue a single command to convert any number of files. But I soon learned that sips doesn’t handle annotations properly when converting the format of a file from PDF to JPEG. In my brief testing, I found that neither my annotations or the ones that came from the client were visible in the converted JPEGs.

\n

Preview, though, can export a PDF as a JPEG with the annotations intact and visible. Which presumably means that sips and Preview are using different code bases for the conversion. Whatever.

\n

The problem with using Preview is I’d have to convert every file by hand. Not the most burdensome job I’ve ever had, but one that’s boring and susceptible to error. Enter Keyboard Maestro.

\n

Here’s the macro that exports the current file in Preview to a JPEG and closes it:

\n

\"Convert

\n

You’ll note there’s no step for setting the resolution for the exported file. That’s because once it’s set, it doesn’t change from one export to the next.

\n

I didn’t try to have the macro open each PDF in turn, because I didn’t trust myself to do that right. I just opened all of them and then ran the macro by pressing ⌃⌥⌘J repeatedly. The macro closed each file after exporting it, leaving the next window ready to be operated on.

\n

The new (for me) thing with this macro was the “and drag to” part at the bottom of the third step. That’s what moved the popup menu selection from PDF up to JPEG.

\n

\"Preview

\n

I figured out how much to drag by taking a screenshot like the one above and using a selection in Acorn to measure the vertical distance from the center of PDF to the center of JPEG. No trial and error.

\n

With 25–30 files to convert, it’s possible I did save time with this macro. But the main reason I made it was to avoid the tedium and the likelihood of error on my part. These are not independent—I’m far more prone to make errors when the task is repetitive and doesn’t maintain my attention.

\n
\n
\n
    \n
  1. \n

    No, a Dropbox link is really no more complicated than a zip file, but zip files are more familiar to more people. And although zipping JPEGs doesn’t make them smaller, it does package them up in a way that naive Windows users are usually comfortable with. It would be lovely if I always worked with people whose computer skills were trustworthy, but that’s not the world I live in. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Binomial baseball", "url": "http://leancrew.com/all-this/2017/10/binomial-baseball/", "author": {"name": "Dr. Drang"}, "summary": "Teams with a 3–2 lead in the World Series win less often than you might think.", "date_published": "2017-10-30T18:14:47+00:00", "id": "http://leancrew.com/all-this/2017/10/binomial-baseball/", "content_html": "

While reading a recap of last night’s World Series games, I saw this statistic: of the 65 Series that have had a sixth game, the team with the 3–2 lead has won the Series 43 times. This was, I think, intended to show us that the Astros have a strong chance to beat out the Dodgers for the title. And they do. But not as strong as you might expect.

\n

If the teams were evenly balanced and each game independent of the others, we would expect the team with the 3–2 lead to win 75% of the time. 50% of the time they’d win the sixth game and the Series would be over; 25% of the time (50% of the other 50%) they’d win in the seventh game. So the leading team “should” have won the Series 48 or 49 times out of 65, not 43 times.

\n

Is this 5 or 6 game difference meaningful? For that we need to do some calculations using the binomial distribution. Python’s SciPy set of libraries has a subsection of statistical modules, including one for binomial distribution calculations. We can import it this way:

\n
python:\nfrom scipy.stats import binom\n
\n

Let’s start by figuring out the probability that the leading team would win 43 times in 65 trials. With a 75% probability of winning the Series in each trial, the probability of 43 Series wins in 65 chances is calculated through

\n
python:\nbinom.pmf(43, 65, .75)\n
\n

where the pmf function gets its name from the standard abbreviation for “probability mass function.” The answer is 0.029 or just under 3%. This makes it seem very unlikely that our assumption of 50–50 games would lead to only 43 Series wins for the leading team.

\n

But that isn’t the way these sorts of calculations are normally done. If we want to find out if a seemingly out-of-whack result is “statistically significant,” we should look at the probability of results that are at least as far away from our expectations as the actual result was. In our case, that means looking not only at the probability of 43 Series wins out of 65 chances, but also 42 wins, 41 wins, and so on. We then add up all of these “at least as weird” probabilities.

\n

The usual terminology for this sort of summation is “cumulative distribution function,” and the binom module has a function for it:

\n
python:\nbinom.cdf(43, 65, .75)\n
\n

The result is 0.0695, or about 7%. Another way of looking at this is that if our assumption of 50–50 games were correct, there’s a 93% chance that the leading team would win the Series more than 43 times in 65 chances.

\n

In hypothesis testing, the value 0.0695 is called the p-value,\nand it’s common in many fields to consider a result statistically significant if its p-value is less than 0.05. Using that criterion, we would not take the difference between our “null hypothesis” of 50–50 games and the World Series history as statistically significant.1

\n

But it’s something for Dodgers fans to cling to.

\n
\n
\n
    \n
  1. \n

    Yes, I’ve been a little breezy here with my definition of null and alternate hypotheses and one-sided vs. two-sided rejection areas, but it’s just baseball. ↩︎

    \n
  2. \n
\n

\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Judas", "url": "http://leancrew.com/all-this/2017/10/judas/", "author": {"name": "Dr. Drang"}, "summary": "I don't believe you.", "date_published": "2017-10-28T14:33:47+00:00", "id": "http://leancrew.com/all-this/2017/10/judas/", "content_html": "

I can’t say I wasn’t warned. The concert’s promotional artwork (especially those fonts) and the previous two albums, Shadows in the Night and Fallen Angels, were strong clues that I was going to be seeing a Bob Dylan infected by the Great American Songbook.

\n

\"Dylan

\n

So yes, we got “Melancholy Mood” and “Autumn Leaves” and “The September of My Years” with Bob crooning into a big microphone and holding its stand at a jaunty angle. But that’s not what I disliked about the concert. In fact, those songs were done pretty well, and Dylan’s voice sounded better than any previous concert I’ve been to. It was what he did to his own songs that was frustrating.

\n

No one in his right mind goes to a Dylan concert expecting to hear his classics played or sung the way they were done originally. But the treatment they’re given is always interesting and usually fun. Not this time. Apart from “It Ain’t Me, Babe” and “Highway 61 Revisited,” the Dylan songs suffered from arrangements that were, I guess, meant to match the style of the non-Dylan songs. Under these conditions, you can’t expect the songs to rock, but you can expect them to swing. And the band just didn’t swing.

\n

There was a sameness to Charlie Sexton’s guitar work throughout the night. Despite changing instruments several times, his sound was constrained and repetitive. But when a band doesn’t swing, it’s mainly because of the rhythm section. These are talented musicians who can play in any style, so if they didn’t find a groove it must have been because Bob didn’t want them to.

\n

As a result, he lost the audience. Many left early and those who hung in there stayed glued to their seats until the encores. Can you imagine a Dylan audience not standing to “Tangled Up in Blue”?

\n

The optimist in me notes that Dylan never sticks with anything for long, and after two albums of standards he may be ready for the next thing. I hope he comes back to the notion that that it’s his songs, and the blues and country/folk they came out of, that are truly the Great American Songbook and gives them the treatment they deserve.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Icons", "url": "http://leancrew.com/all-this/2017/10/icons/", "author": {"name": "Dr. Drang"}, "summary": "Brett Terpstra's little app has a strangely familiar icon.", "date_published": "2017-10-27T01:39:06+00:00", "id": "http://leancrew.com/all-this/2017/10/icons/", "content_html": "

I have some code sitting around here somewhere for extracting the images (app icons, book covers, album art, etc.) from the various iTunes and App Stores. But I think it’ll be easier to use Brett Terpstra’s iTunesIcon Automator app, which just got an update.

\n

Apart from its appeal to my laziness, Brett’s little app has a cool icon that I took an instant liking to.

\n

\"iTunesIcon

\n

If you work around industrial and construction equipment, Brett’s icon should bring a smile of familiarity. It’s highly reminiscent of the hands in safety signs and decals that are in dire straits.

\n

\"Safety

\n

Images from SafetySign.com

\n

I’m sure your fingers are safe when using iTunesIcon.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}, {"title": "Feed reading", "url": "http://leancrew.com/all-this/2017/10/feed-reading/", "author": {"name": "Dr. Drang"}, "summary": "My feed reading setup is basically complete", "date_published": "2017-10-23T00:10:42+00:00", "id": "http://leancrew.com/all-this/2017/10/feed-reading/", "content_html": "

Gabe Weatherhead has a nice article today on how he uses Feedbin to handle his reading on the web. If you follow Gabe—and if not, why don’t you?—it will not surprise you to learn that he’s formidably organized. I don’t see myself following in his footsteps, but it’s always useful to learn how smart people do things. His article also reminded me that I’ve been meaning to write about my feed reading setup.

\n

A couple of years ago, I let my subscription to Feed Wrangler lapse and started using a homemade, web-based RSS reading system. The heart of the system is still the script described in this post, but with a some changes as I thought of better ways of doing things.

\n

The biggest change came in the past few months. Initially, I created a single static web page with all of today’s articles from the feeds I subscribe to. A cron task updated the page a few times an hour throughout the day. In this system, “today” was defined as “from 10:00 pm last night until now,” and the page would grow in size from morning to night.

\n

The advantage of this temporal arrangement from a programming point of view was that I didn’t have to write any code to keep track of whether I’d already read an article or not, and there were no external dependencies. If it was published “today,” it got on the page.

\n

The disadvantage was from the reading point of view. As I visited the page throughout the day, it became more and more filled with article I’d already read. This wasn’t as terrible as you might think. The articles were arranged in reverse chronological order by publication time, so the ones I’d read were typically at the bottom of the page. I say “typically” because some feeds—XKCD comes to mind—are very bad at providing accurate publication times and their articles would sometimes end up at the bottom despite being recently published.

\n

Eliminating the reading disadvantage meant keeping track of what I’d read and showing only what I hadn’t—eliminating the programming advantage. I decided to keep track of read articles in an SQLite database and to add items to that database through a button placed at the bottom of each article on my RSS page.

\n

\"RSS

\n

This meant

\n
    \n
  1. Building a database that would uniquely identify every article. This was pretty simple. Each record has just two fields: the name of the website and the unique article ID (which is often just the article’s URL but is sometimes a long alphanumeric string generated by the site’s blogging software).
  2. \n
  3. Altering the existing script that builds the RSS page to filter out feed items that are in the database. Because Python has an SQLite module as part of its standard library and the syntax of SQL commands is straightforward, this wasn’t as tricky as I thought it would be. In fact, the new code is easier to read than the time-based filtering code I removed.
  4. \n
  5. Writing a server script (basically just a CGI script) to add an article to the database when given the blog name and article ID via the POST method. It’s been a while since I last wrote a CGI script, but it was like riding a bicycle.
  6. \n
  7. Adding some JavaScript with XMLHttpRequest to the RSS page to call the server script when a button is pressed. This took the most time, mainly because everyone in the world (except me) knows how to do AJAX now, and finding references written at an appropriately low level was harder than I expected. I found this Stack Overflow discussion helpful.
  8. \n
\n

So now I usually tap the Mark as read button when I get to the end of an article. If it’s a long article that I want to read later, I don’t mark it as read, and it’ll be there the next time I bring up the RSS page.

\n

Fearing I’d forget how to use XMLHttpRequest, I quickly included another form at the end of each article for adding that article to my Pinboard account. I didn’t bother adding labels to the text field, because I’m the only one who uses this and I know the field is for tags. I did, however, include some DOM stuff to make it obvious when I’d marked an article as read or added it to Pinboard.

\n

\"RSS

\n

What I like about this system is how portable and (I hope) future-proof it is. I’ve been reluctant to sign on with Feedbin or Feedly or BazQux or any of the other Google Reader replacements because I worry they’ll write a Medium post and disappear with my subscription list and whatever organization scheme I’ve created. My system can run on any web server with Python, SQLite, and a cgi-bin directory. I think that’ll mean “any server, anywhere” for a very long time.


\n

[If the formatting looks odd in your feed reader, visit the original article]

"}], "home_page_url": "http://leancrew.com/all-this/", "version": "https://jsonfeed.org/version/1", "icon": "http://leancrew.com/all-this/resources/snowman-200.png"}