May 2023 Update: Fixed a bug with setting the default for the "Start date" field. A bug I should have noticed years ago.
October 2020 Update: Added "Pages/day" option to form and appropriately checked CGI code to handle it.
November 2019 Update: Moved sources to GitHub, links below.
June 2018 Update: Whoa. Embarassing bugfix, caused due to my
minimal understanding of Javascript. I keep expecting it to work like
Perl.
February 2018 Update: Stylesheet/template changes, described at
end of article. Appropriate
changes made to article body.
January 2018 Update: Some major form changes, described at end of
article. Appropriate changes made to article body.
December 2017 Update: More changes, described at end of article.
October 2017 Update: I regret that I let this article get badly out of whack
with reality. Notably, most of the links to my code, which were on the
old UNH "pubpages" server, went stale when I retired. In addition, I've cleaned up the code
some and fixed a nasty Daylight Savings Time-related bug. I hope.
Further notes at the bottom of the article.
Note: this post is out of whack with normal Pun Salad
content. Only recommended for:
- computer geeks who (like me)
tend to approach everyday issues by asking: how could I write
code to make this easier?; or
- psychologists
who might be interested in whatever mental aberration causes
the behavior exhibited in (a).
A few years back I noticed I was doing a miserable job
of reading the magazines to which I was subscribed; the new
issue would show up and I would have just read few if any
articles in the previous one.
A similar problem with books: I would check them out of the
library and return them unread. And my to-be-read pile of
owned books just was getting bigger.
What worked for me was to set up a reading schedule for each "new"
book or magazine. Simply read through a reasonable, fixed number of pages
each day, until done. Lather, rinse, repeat.
I'm currently implementing this scheme by generating a calendar-format
schedule
for each item in HTML. Example for a book I read back in 2014,
Freedom™ by Daniel Suarez:
I (1) print the schedule from my web browser,
(2) cut it out, (3) attach to a card, and (4) use the
result as a bookmark
for the item. And each day, I try to meet the page goal for
each item.
(Not that it matters, but for magazines I staple the schedule
to one of the subscription cards that invariably accompany
each issue. For books, I tape it to one of the plastic
advertising cards the Yankee Candle company sends us, which are
significantly more substantial.)
Now I'm not psycho about this: it's OK to get ahead of the goals
if the material is compelling and I have the time. It's also
OK to fall behind if there's just too much other stuff going
on. (However, in my experience, just knowing that I've "fallen
behind" is just enough self-nudging motivation to carve out some
extra time to catch up later.)
Enough for the mechanics, on to the code. For a long time I ran a Perl
script from a Linux command line to generate
a schedule. But I realized that it would be
(slightly) more flexible to set up an HTML
form to get the schedule parameters, calling a CGI backend
to display the result.
The form I'm using is here. (Do I
have to mention that you can use your browser's source-viewing
capabilities to view the HTML source? Nah, probably not.) But it looks
like this [as of October 2020]:
[December 2017: removed paragraph about the jQuery datepicker, no longer
used.]
So you fill out the form, for example:
[Note: I urge you to
try it
yourself.]
… hit the submit button and the resulting page should produce
the appropriate schedule. (I'm pretty sure it would work for
you if you want to try it.)
The real work is performed by the
Perl CGI script, which relies heavily on the smarts contained in the
CGI.pm
,
HTML::Template
,
and
Date::Manip
Time::Piece
modules.
If you'd like to look at things (feel free to steal adapt to
your own preferences if you are so inclined), the
GitHub
directory contains:
-
reading_sched_gen.html
— the initial web form (also
available
here). Install
in a web-accessible location.
-
reading_sched.cgi
— the CGI script kicked off by the
form's Submit button. Install whereever you put CGI scripts, and update
the appropriate line in the form.
cal_bookmark.tmpl
— HTML::Template file used by the CGI
script to generate the bookmark. Install in a web-accessible location
and update the line setting $template_file
in the CGI
script.
reading_sched.css
— the CSS style file used by the
bookmark. Install in a web-accessible location and update the
appropriate line in the template.
Additional notes (October 2017).
-
As previously mentioned, the original version of the CGI script lived on
the UNH "pubpages" webserver, where I enjoyed
root
access.
It's now on the Pun Salad server, where I don't. This is problematic
when you need to install some random CPAN perl module.
Solution (or, at least, what I'm doing): use
cpanm
to install necessary modules "unprivileged" and
put use local::lib;
in scripts to find them. (This seems to work for CGIs, surprisingly.)
-
The most recent bug squashed was one I've struggled with for years: if
your timezone does DST, not all days are 24 hours: the "spring forward"
day has only 23; the "fall back" day has 25. This resulted in strange
behavior when a reading schedule traversed one of those days.
The fix turned out to be relatively easy: tell the script to pretend
it's in the UTC timezone. Duh. That's accomplished by (for example)
the line
$startdate->config( 'setdate', 'now,UTC' );
[Update November 2019: This bugfix seems to no longer be necessary,
removed.]
December 2017 Update: Some minor surgery on this tool to report.
-
After years, I noticed that, in many cases, I wanted to spend a specific
number of days reading a book or magazine. This involved me looking at a
calendar, and finger-counting to figure out what end-date to specify in the form.
Duh. That's an unforgivable user interface sin, is it not?
So the input form now requests either (a) the end-date for the reading
schedule, or (b) the number of days the schedule should run from the
start date.
If you enter both, the end date will be ignored, but why would you do
that?
-
The form has been otherwise (ahem) brought into the 21st century. Title,
start date, start page, and end page input fields now sport the HTML
attribute
required
. The type
attribute is used
specify that the title should be text
, the start/end
pages should be number
, and the start/end dates should be
date
.
That means the web browser's native date-picker UI will be used for
date entry instead of the previous jQuery datepicker. (Some old browsers
don't support this, but since I'm not being paid to support old
browsers, I can say "gee, that's too bad", without guilt.)
-
Some Javascript is still used for client-side verification: the start
page has to be less than the end page, either the number-of-days or the
end-date input has to be there, and if the end-date is specified, it has
to be after the start date.
-
Web Security 101 saith: your CGI must still check the validity of its
inputs. So all that code is still in the CGI. But…
-
I also modified the CGI script to use the Perl
Time::Piece
module for calendar arithmetic instead of Date::Manip
. This
greatly simplified the code, and (somewhat surprisingly) Time::Piece
seems to do the right thing when the reading schedule overlaps a
Daylight Saving time warp.
I still think Date::Manip
is vital for heavy-lifting
Date/Time calculations. But it's overkill here.
January 2018 Update: Some minor surgery on the form. Did I say
above that I had brought it into the 21st century? Not quite. My web
design skills have always been weak, since that was never my job;
I was pretty much a "get the content up" guy, and I only learned enough
CSS to make things look the way I wanted at the time. That's the case
here too. Summary:
- Rearranged the form elements so they had a more intuitive ordering.
- Switched to using an embeded stylesheet.
- Used some advanced (non-table) styles for form layout.
I think it looks prettier now.
February 2018 Update: See above about my web design skills being
weak. I can't say that the most recent changes make the design "good",
but I think I can claim "better".
-
Changed to a standalone style sheet for the generated calendar bookmark.
-
The bookmark used tables for layout. I hear that's been frowned upon
for, oh, the last 20 years or so. Layout now accomplished by CSS.
-
I changed the outer boundary of the bookmark from a dotted line to
dashes. I think that looks better.
October 2020 Update: Occasionally, mostly for magazines, I really want to read a fixed number
of pages per day. That forced me to do some math in my head. ("This month's issue has 88 pages,
I want to read four pages per day, so that's … um … 22 days.")
Not particularly challenging, but why do I have a computer, if not to offload such tasks upon?
So: you can either specify (1) a number of days; (2) an end date; or (3) pages/day. The form
should check to make sure you've specified one of those, no more, no less.
If you specify a N pages/day parameter, the final day on the schedule may be between 1 and N.
That's math. A feature, not a bug.
May 2023 Update:
I still haven't come close to learning JavaScript, it turns out. The code I snipped from somewhere
that sets the default value for the "Start date" field to today's date seemed simple and reasonable…
document.getElementById("startdate").valueAsDate = new Date();
The problem is that sometimes it filled in the field with tomorrow's date.
Specifically, if I accessed the form after 7pm EST.
Or after 6pm EDT.
Yes, it was filling in the current date in the GMT (UTC) timezone. Which for five or six hours
every day is tomorrow. That's for me in New Hampshire. That bad-default window gets larger
the further west you go. And the opposite problem occurs for browsers going east from Greenwich:
they will occasionally see a default start date of yesterday.
In my defense: have I mentioned that I haven't learned Javascript? But (mea culpa) I had no
clue that it would have been a good idea to test in different timezones.
The Google brought up long-ago debates on whether this was a feature or a bug in JavaScript.
Doesn't matter: it's the way it works, and it must be worked around.
This Stack Overflow post provided a fix I could easily patch in, a two-liner
replacing the one-liner above:
var today = new Date();
document.getElementById("startdate").valueAsDate =
new Date(today.getFullYear(), today.getMonth(), today.getDate(), 10);
One final bit of coding trivia: the Stack Overflow post used "12" instead of "10" in that
final line. At first I thought that was obviously correct: at noon UTC the entire planet is on
the same calendar page, right?
Hah. No. The entire planet is never on the same calendar page. See
Peter Moore's answer to Is there ever a time when the entire planet is experiencing the same date? The best you can do is the hour
of 10am UTC. Then the code above will give the wrong default for only the users in Kiribati (aka Christmas Island), where
it will be yesterday; and folks on a handful of different Pacific islands, where it will be tomorrow.
Or maybe it's the other way around. In any case, sorry folks.
(I will be happy to test this further and verify the assertions above if anyone wants to pay for my plane fare.
A 24-hour stop in each time zone shoud suffice. There
are
a lot of them.)