|
#!/usr/bin/env python2 |
|
# -*- encoding: utf-8 -*- |
|
|
|
"""quip.py is a useful data source for Phosphor screensaver""" |
|
|
|
__version__ = '1.1.2' |
|
__author__ = 'Andras Tim <[email protected]>' |
|
|
|
|
|
import fcntl |
|
import os |
|
import pickle |
|
import random |
|
import sys |
|
import urllib2 |
|
from lxml import etree |
|
from optparse import OptionParser |
|
|
|
BUGZILLA_URL = os.getenv('BUGZILLA_URL', 'http://bugzilla.foo/bugzilla-3.0') |
|
BUGZILLA_LOGIN_COOKIE = os.getenv('BUGZILLA_LOGIN_COOKIE', 'Bugzilla_login=76; Bugzilla_logincookie=FOobBar007') |
|
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 Bugzilla(object): |
|
__URL = '%s/quips.cgi?action=show' % BUGZILLA_URL |
|
__XPATH_OF_QUIPS = '//div[@id="bugzilla-body"]/ul/li' |
|
|
|
def __init__(self): |
|
self.__last_fetch_storage = QuipStorage(ALL_QUIPS_STORAGE) |
|
self.__opener = urllib2.build_opener() |
|
self.__opener.addheaders = [('User-agent', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0')] |
|
self.__opener.addheaders.append(('Cookie', BUGZILLA_LOGIN_COOKIE)) |
|
|
|
def __fetch_quips_url(self): |
|
response = self.__opener.open(Bugzilla.__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: node.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-cookie is bad for Bugzilla') |
|
|
|
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.bugzilla = Bugzilla() |
|
|
|
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.bugzilla.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() |