From 7ee73caf3cf18382a5fba62c936849229b56ee73 Mon Sep 17 00:00:00 2001 From: Bastian Kleineidam Date: Mon, 11 Mar 2013 17:33:59 +0100 Subject: [PATCH] Allow multiple event output and improve HTML output. --- doc/changelog.txt | 3 +- doc/dosage.1 | 3 +- doc/dosage.1.html | 3 +- doc/dosage.txt | 5 ++-- dosage | 7 +++-- dosagelib/events.py | 71 ++++++++++++++++++++++++++++++++++---------- tests/test_dosage.py | 4 +-- 7 files changed, 71 insertions(+), 25 deletions(-) diff --git a/doc/changelog.txt b/doc/changelog.txt index f5d2f29e6..856af784f 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -7,7 +7,8 @@ Features: Changes: - cmdline: Comic lists are displayed one page at a time. -- events: HTML output embeds the images in the page. +- output: HTML output embeds the images in the page and show the page URLs. +- output: The --output option can be given multiple times. Fixes: - cmdline: Catch error when piping output to another diff --git a/doc/dosage.1 b/doc/dosage.1 index 725879441..fd47055bd 100644 --- a/doc/dosage.1 +++ b/doc/dosage.1 @@ -15,7 +15,7 @@ for updating and maintaining collections. Specifies a base path to put comic subdirectories. The default is \fBComics\fP. .TP \fB\-\-baseurl=\fP\fIPATH\fP -Specifies the base URL for output events. The default is a local file URI. +Specifies the base URL for output handlers. The default is a local file URI. .TP \fB\-a\fP, \fB\-\-all\fP Traverses all available strips backwards from the current one. @@ -61,6 +61,7 @@ current run, named by date (ala dailystrips). The files can be found in the Writes out an RSS feed detailing what strips were downloaded in the last 24 hours. The feed can be found in \fBComics/dailydose.xml\fP. .RE +This option can be given multiple times. .TP \fB\-t\fP, \fB\-\-timestamps\fP Print timestamps for all output at any level. diff --git a/doc/dosage.1.html b/doc/dosage.1.html index 6f04bde3d..2d784dde5 100644 --- a/doc/dosage.1.html +++ b/doc/dosage.1.html @@ -30,7 +30,7 @@ for updating and maintaining collections.
-b PATH, --basepath=PATH
Specifies a base path to put comic subdirectories. The default is Comics.
--baseurl=PATH
-Specifies the base URL for output events. The default is a local file URI. +Specifies the base URL for output handlers. The default is a local file URI.
-a, --all
Traverses all available strips backwards from the current one. This can be useful you want a full collection of a new comic strip, @@ -77,6 +77,7 @@ Writes out an RSS feed detailing what strips were downloaded in the last 24 hours. The feed can be found in Comics/dailydose.xml. +This option can be given multiple times.
-t, --timestamps
Print timestamps for all output at any level. diff --git a/doc/dosage.txt b/doc/dosage.txt index 28adfdfa5..5064aa88f 100644 --- a/doc/dosage.txt +++ b/doc/dosage.txt @@ -20,8 +20,8 @@ OPTIONS default is Comics. --baseurl=PATH - Specifies the base URL for output events. The default is - a local file URI. + Specifies the base URL for output handlers. The default + is a local file URI. -a, --all Traverses all available strips backwards from the cur‐ @@ -61,6 +61,7 @@ OPTIONS rss - Writes out an RSS feed detailing what strips were downloaded in the last 24 hours. The feed can be found in Comics/dailydose.xml. + This option can be given multiple times. -t, --timestamps Print timestamps for all output at any level. diff --git a/dosage b/dosage index 5ae4f83b5..b5bf9e677 100755 --- a/dosage +++ b/dosage @@ -82,7 +82,7 @@ def setupOptions(): parser.add_argument('--version', action='store_true', help='display the version number') parser.add_argument('-m', '--modulehelp', action='store_true', help='display help for comic modules') parser.add_argument('-t', '--timestamps', action='store_true', help='print timestamps for all output at any info level') - parser.add_argument('-o', '--output', action='store', choices=events.getHandlers(), help='output formatting for downloaded comics') + parser.add_argument('-o', '--output', action='append', dest='handler', choices=events.getHandlerNames(), help='sets output handlers for downloaded comics') parser.add_argument('--adult', action='store_true', help='confirms that you are old enough to view adult content') parser.add_argument('--multimatch', action='store_true', help=argparse.SUPPRESS) parser.add_argument('comic', nargs='*', help='comic module name (including case insensitive substrings)') @@ -150,8 +150,9 @@ def displayComicHelp(scraperobj): def getComics(options): """Retrieve comics.""" errors = 0 - if options.output: - events.installHandler(options.output, options.basepath, options.baseurl) + if options.handler: + for name in options.handler: + events.addHandler(name, options.basepath, options.baseurl) events.getHandler().start() try: for scraperobj in getScrapers(options.comic, options.basepath, options.adult, options.multimatch): diff --git a/dosagelib/events.py b/dosagelib/events.py index 1201f1d66..602c8a373 100644 --- a/dosagelib/events.py +++ b/dosagelib/events.py @@ -46,6 +46,8 @@ class EventHandler(object): class RSSEventHandler(EventHandler): """Output in RSS format.""" + name = 'rss' + def getFilename(self): """Return RSS filename.""" return os.path.abspath(os.path.join(self.basepath, 'dailydose.rss')) @@ -95,6 +97,8 @@ class RSSEventHandler(EventHandler): class HtmlEventHandler(EventHandler): """Output in HTML format.""" + name = 'html' + def fnFromDate(self, date): """Get filename from date.""" fn = time.strftime('comics-%Y%m%d.html', date) @@ -135,8 +139,10 @@ class HtmlEventHandler(EventHandler):
    ''' % (configuration.App, time.strftime('%Y/%m/%d', today), yesterdayUrl, tomorrowUrl)) - + # last comic name (eg. CalvinAndHobbes) self.lastComic = None + # last comic strip URL (eg. http://example.com/page42) + self.lastUrl = None def comicDownloaded(self, comic, filename): """Write HTML entry for downloaded comic.""" @@ -144,44 +150,79 @@ class HtmlEventHandler(EventHandler): self.newComic(comic) imageUrl = self.getUrlFromFilename(filename) pageUrl = comic.referrer - self.html.write(u'
  • \n' % (pageUrl, imageUrl)) + if pageUrl != self.lastUrl: + self.html.write(u'
  • %s\n' % (pageUrl, pageUrl)) + self.html.write(u'
    \n' % imageUrl) + self.lastComic = comic.name + self.lastUrl = pageUrl def newComic(self, comic): """Start new comic list in HTML.""" + if self.lastUrl is not None: + self.html.write(u'
  • \n') if self.lastComic is not None: self.html.write(u'
\n') - self.lastComic = comic.name self.html.write(u'
  • %s
  • \n' % comic.name) self.html.write(u'
      \n') def end(self): """End HTML output.""" + if self.lastUrl is not None: + self.html.write(u'\n') if self.lastComic is not None: - self.html.write(u'
    \n') + self.html.write(u'\n') self.html.write(u''' ''') self.html.close() -handlers = { - 'html': HtmlEventHandler, - 'rss': RSSEventHandler, -} +_handler_classes = {} -def getHandlers(): +def addHandlerClass(clazz): + if not issubclass(clazz, EventHandler): + raise ValueError("%s must be subclassed from %s" % (clazz, EventHandler)) + _handler_classes[clazz.name] = clazz + +addHandlerClass(HtmlEventHandler) +addHandlerClass(RSSEventHandler) + + +def getHandlerNames(): """Get sorted handler names.""" - return sorted(handlers.keys()) + return sorted(_handler_classes.keys()) -_handler = EventHandler(".", None) -def installHandler(name, basepath=None, baseurl=None): +_handlers = [] + +def addHandler(name, basepath=None, baseurl=None): """Install a global handler with given name.""" - global _handler if basepath is None: basepath = '.' - _handler = handlers[name](basepath, baseurl) + _handlers.append(_handler_classes[name](basepath, baseurl)) + + +class MultiHandler(object): + """Encapsulate a list of handlers.""" + + def start(self): + """Emit a start event. Should be overridden in subclass.""" + for handler in _handlers: + handler.start() + + def comicDownloaded(self, comic, filename): + """Emit a comic downloaded event. Should be overridden in subclass.""" + for handler in _handlers: + handler.comicDownloaded(comic, filename) + + def end(self): + """Emit an end event. Should be overridden in subclass.""" + for handler in _handlers: + handler.end() + + +multihandler = MultiHandler() def getHandler(): """Get installed event handler.""" - return _handler + return multihandler diff --git a/tests/test_dosage.py b/tests/test_dosage.py index 6b6ceea64..eb80bcd22 100644 --- a/tests/test_dosage.py +++ b/tests/test_dosage.py @@ -53,10 +53,10 @@ class TestDosage (unittest.TestCase): self.assertRaises(OSError, run_with_options, ['--imadoofus']) def test_fetch_html(self): - run_with_options(["-n", "2", "-b", self.tmpdir, "-o", "html", "calvinandhobbes"]) + run_with_options(["-n", "2", "-b", self.tmpdir, "-o", "html", "-o", "rss", "calvinandhobbes"]) def test_fetch_rss(self): - run_with_options(["--numstrips", "2", "--baseurl", "bla", "--basepath", self.tmpdir, "--output", "rss", "--adult", "sexyloser"]) + run_with_options(["--numstrips", "2", "--baseurl", "bla", "--basepath", self.tmpdir, "--output", "rss", "--output", "html", "--adult", "sexyloser"]) def test_fetch_indexed(self): run_with_options(["-n", "2", "-b", self.tmpdir, "calvinandhobbes:2012/02/02"])