Skip to content

Instantly share code, notes, and snippets.

@andras-tim
Last active August 26, 2015 15:18
Show Gist options
  • Save andras-tim/a9f14053834eccb9e2f6 to your computer and use it in GitHub Desktop.
Save andras-tim/a9f14053834eccb9e2f6 to your computer and use it in GitHub Desktop.
Quips as Screensaver

Jira Quips as Screensaver

Requirements

  • a typing style screensaver with program input (e.g. "phosphor" of xscreensaver)
  • python2
  • beautifulsoup4
  • (cowsay)

Start

  • start quip.py for multiple quips
  • start cowquip.sh for cowsay style output

check the older revision for Bugzilla Quips

#!/bin/bash -e
EXCLUDED_ANIMALS='calvin|kiss|kosh|mech-and-cow|pony|unipony'
function get_list_of_animals()
{
cowsay -l | grep -v '^Cow files in' | tr ' ' '\n' | grep -vE '^('"${EXCLUDED_ANIMALS}"')$'
}
function get_one_random_animal()
{
get_list_of_animals | shuf | head -n 1
}
function get_one_random_quip()
{
"$(dirname "$0")/quip.py" --count 1 --encode iso-8859-1
}
######
# MAIN
#
# Fetch
animal="$(get_one_random_animal)"
quip="$(get_one_random_quip)"
# Display
sleep 5
clear
echo
cowsay -f "${animal}" -- "${quip}" | iconv -t iso-8859-1 2>&1
#!/usr/bin/env python2
# -*- encoding: utf-8 -*-
"""quip.py is a useful data source for Phosphor screensaver"""
__version__ = '2.0.0'
__author__ = 'Andras Tim <[email protected]>'
import fcntl
import os
import pickle
import random
import sys
import urllib2
from bs4 import BeautifulSoup
from lxml import etree
from optparse import OptionParser
JIRA_QUIP_XML_URL = os.getenv('JIRA_URL', 'https://jira.balabit/si/jira.issueviews:issue-xml/DBO-1314/DBO-1314.xml')
COUNT_OF_QUIPS_PER_RESULT = 15
COUNT_OF_CHARACTERS_PER_RESULT = 1000
ALL_QUIPS_STORAGE = '/var/tmp/all-quips'
LEFT_QUIPS_STORAGE = '/var/tmp/left-quips'
class QuipStorage(object):
def __init__(self, storage_path):
self.__quip_storage_path = '{}.pickle'.format(storage_path)
self.__quip_lock_path = '{}.lck'.format(storage_path)
self.__lock_fd = None
self.__quips = []
self.__dirty = False
def __enter__(self):
return self.open()
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
def open(self):
self.__hold_lock()
self.__load_quips()
return self
def close(self):
if self.__dirty:
self.__save_quips()
self.__release_lock()
@property
def quips(self):
return list(self.__quips)
@quips.setter
def quips(self, new_quips):
if self.__quips == new_quips:
return
self.__quips = new_quips
self.__dirty = True
def __load_quips(self):
if not os.path.isfile(self.__quip_storage_path):
return
with open(self.__quip_storage_path, 'r') as fd:
picked_data = fd.read()
self.__quips = pickle.loads(picked_data)
def __save_quips(self):
picked_data = pickle.dumps(self.__quips, -1)
with open(self.__quip_storage_path, 'w') as fd:
fd.write(picked_data)
def __hold_lock(self):
self.lock_file = open(self.__quip_lock_path, 'a')
fcntl.lockf(self.lock_file.fileno(), fcntl.LOCK_EX)
def __release_lock(self):
fcntl.lockf(self.lock_file.fileno(), fcntl.LOCK_UN)
self.lock_file.close()
self.lock_file = None
class Jira(object):
__XPATH_OF_QUIPS = '//item/comments/comment'
def __init__(self):
self.__last_fetch_storage = QuipStorage(ALL_QUIPS_STORAGE)
self.__opener = urllib2.build_opener()
def __fetch_quips_url(self):
response = self.__opener.open(JIRA_QUIP_XML_URL)
return response.read()
def __parse_quips_from_raw_html_data(self, html_text):
parser = etree.HTMLParser(encoding='utf-8')
html = etree.HTML(html_text, parser=parser)
li_nodes = html.xpath(self.__XPATH_OF_QUIPS)
quips = map(lambda node: BeautifulSoup(node.text).get_text(), li_nodes)
return quips
def get_quips(self):
quips = []
output.print_status(' * Fetching data... ')
try:
raw_data = self.__fetch_quips_url()
quips = self.__parse_quips_from_raw_html_data(raw_data)
output.print_status('Done\n')
except urllib2.URLError:
output.print_status('Error\n')
with self.__last_fetch_storage as storage:
if quips:
storage.quips = quips
else:
output.print_status(' * Fallback to last fetch storage\n')
quips = storage.quips
if not quips:
raise Exception('Missing quips; maybe auth problem')
return quips
class ScreenSaverOutput(object):
def __init__(self, encoding=None):
self.encoding = encoding
def print_encoded_list(self, list_of_lines):
text = '\n\n'.join(list_of_lines)
print(self.__encode_for_screen_saver(text))
def print_status(self, status):
sys.stderr.write(self.__encode_for_screen_saver(status))
sys.stderr.flush()
def __encode_for_screen_saver(self, text):
if not self.encoding:
return text
if not self.encoding == 'utf-8':
text = self.__replace_long_accents(text)
return text.encode(self.encoding, 'ignore')
@classmethod
def __replace_long_accents(cls, text):
return text.replace(u'ő', u'ö').replace(u'Ő', u'Ö').replace(u'ű', u'ü').replace(u'Ű', u'Ü')
class LeftQuips(object):
def __init__(self):
self.__left_quip_storage = QuipStorage(LEFT_QUIPS_STORAGE)
self.quip_service = Jira()
def get_quips(self, max_count, max_characters):
result = []
self.__left_quip_storage.open()
quips = self.__left_quip_storage.quips
self.__fetch_new_items_if_required(quips, max_count)
quip_count = 0
character_count = 0
while (quip_count < max_count) and (character_count + len(quips[quip_count]) < max_characters):
result.append(quips[quip_count])
character_count += len(quips[quip_count])
quip_count += 1
self.__left_quip_storage.quips = quips[quip_count:]
self.__left_quip_storage.close()
return result
def __fetch_new_items_if_required(self, quips, requested_item_count):
if len(quips) >= requested_item_count:
return
fetched_quips = self.quip_service.get_quips()
while len(quips) < requested_item_count:
quips.extend(fetched_quips)
def parse_args():
app_name = os.path.basename(sys.argv[0])
parser = OptionParser(prog=app_name, description=__doc__, version='%s: %s' % (app_name, __version__))
parser.add_option('-e', '--encode', action='store', dest='encode',
help='e.g. iso-8859-1 (default: %default)')
parser.add_option('-c', '--count', action='store', dest='count', default=COUNT_OF_QUIPS_PER_RESULT,
help='Max count of printed quips (default: %default)')
parser.add_option('-C', '--chars', action='store', dest='chars', default=COUNT_OF_CHARACTERS_PER_RESULT,
help='Max characters in output (default: %default)')
opts, args = parser.parse_args()
if int(opts.count) <= 0:
parser.error('Incorrect number for count!')
opts.count = int(opts.count)
if int(opts.chars) <= 0:
parser.error('Incorrect number for chars!')
opts.chars = int(opts.chars)
return opts
def main():
global output
encoding = None
options = parse_args()
if options.encode:
encoding = options.encode
output = ScreenSaverOutput(encoding)
left_quips = LeftQuips()
quips = left_quips.get_quips(options.count, options.chars)
random.shuffle(quips)
output.print_encoded_list(quips)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment