Source

trac-dvbcronrecording-plugin / src / dvbcronrecording / programguide.py

# -*- coding: utf-8 -*-
import pkg
import re
import os.path
import sys
import datetime

from trac.core import Component, implements
# from trac.db import *
#from trac.wiki import wiki_to_html, wiki_to_oneliner
from trac.web.chrome import add_stylesheet, add_script
#from trac.util import Markup, format_datetime

from trac.web import IRequestHandler
from trac.perm import IPermissionRequestor
from trac.web.chrome import  ITemplateProvider # INavigationContributor,
from trac.util.presentation import Paginator
from translate import Translate #@UnresolvedImport

from dvbcronrecording.db.session import db_cnx, commit
from dvbcronrecording.db.schema import RecordingList #@UnresolvedImport
from dvbcronrecording.db.schema import RecordingChanges #@UnresolvedImport

def intnull(value, default=None):
    if value is None: return default
    try: return int(value)
    except Exception: return default
def lookup(lists, entry, default=None):
    if entry is None: return default
    idx = intnull(entry)
    if idx is None or idx >= len(lists): return default
    return lists[idx]
def ustr(text):
    if type(text) is unicode: return text
    if type(text) is str: return unicode(text)
    return unicode(str(text))

PACKAGE = u'dvbcronrecording'
NAV = 'recordings'
URL = 'recording'
SUBURL = 'programguide'

LIST_APPEND = 'DVBREC_LIST_APPEND'

"""
  here we are.
"""
class DvbCronRecordingProgramguidePlugin(Component):

    #
    # Public methods
    #

    implements(IPermissionRequestor, ITemplateProvider, IRequestHandler)
    
    programguide_list = [ "tvspielfilm" ]

    # IPermissionRequestor methods

    """
      Returns list of permitions privided by this plugin.
    """
    def get_permission_actions(self):
        return [ LIST_APPEND ]

    # ITemplateProvider methods

    """
      Returns additional path where stylesheets are placed.
    """
    def get_htdocs_dirs(self):
        return [(PACKAGE, pkg.resource_filename(__name__, 'htdocs'))]

    """
      Returns additional path where templates are placed.
    """
    def get_templates_dirs(self):
        return [pkg.resource_filename(__name__, 'templates')]

    # IRequestHandler methods

    """
      Determines if request should be handled by this plugin.
    """
    def match_request(self, req):
        m = re.match(r"/%s/%ss?(?:/(.*))?$" % (URL, SUBURL), req.path_info)
        if m:
            page = m.group(1)
            req.args['page'] = page 
            return True
        return False

    """
      Handles display and download requests on this plugin.
    """
    def process_request(self, req):
        req.perm.assert_permission(LIST_APPEND)
        # ------------------------------------------------
        translate = Translate(PACKAGE, req.locale)
        userscripts_dir = self.get_scripts_dir()

        page = req.args['page']
        message = "[%s]" % page

        if page and page.startswith("install/"):
            target = page[len("install/"):]
            return self.do_install(req, page, target)
        
        if page and page.startswith("save/"):
            target = page[len("save/"):]
            return self.do_save(req, page, target)
            
        return self.do_default(req, message)
    
    def do_install(self, req, page, target):
        translate = Translate(PACKAGE, req.locale)
        userscripts_dir = self.get_scripts_dir()
        filepath = os.path.join(userscripts_dir, target)
        if os.path.exists(filepath):
            try:
                f = open(filepath)
                text = f.read()
                f.close()
                text = text.replace("http://SERVER", ustr(req.base_url).encode("utf-8"))
                req.send_header("Content-Disposition:", 'inline; filename="%s"' % target)
                req.send(text, "application/octet-stream")
                return
            except Exception, e:
                # req.send_error(sys.exc_info(), status=500, env=self.env, data = { "page" : page})
                # return
                message = "OOPS %s" % (e,)
        else:
            message = translate("userscript not found:")
            message += " %s" % target
        return self.do_default(req, message)
    
    def do_save(self, req, page, target):
        # decode the strings from the remote page
        programguide = target
        programchannel = req.args["channel"]
        date = req.args["date"]
        time = req.args["time"]
        title = req.args["title"]
        on_date = re.compile(r"\w\w\s+(\d+)[.](\d+)[.]\s*")
        on_time = re.compile(r"(\d+:\d+)\s+-\s+(\d+:\d+)")
        newdate = None
        newtime = None
        endtime = None
        m = on_date.match(date)
        if m:
            day = m.group(1)
            month = m.group(2)
            current = datetime.datetime.now()
            year = current.year
            if int(month) < current.month:
                year += 1  
            newdate = datetime.datetime(year, int(month), int(day))
        m = on_time.match(time)
        if m:
            newtime = m.group(1)
            endtime = m.group(2)
        message = self.save(req, newdate, newtime, endtime, title, programchannel, programguide)
        return self.do_default(req, message)
        
    def do_default(self, req, message):
        translate = Translate(PACKAGE, req.locale)
        userscripts_dir = self.get_scripts_dir()
        userscripts = []
        for filename in os.listdir(userscripts_dir):
            if filename.endswith(".user.js"):
                userscripts += [ filename ]
        
        programguidedata = list(self.programguidechannels_all())

        add_stylesheet(req, 'common/css/wiki.css')
        add_stylesheet(req, PACKAGE + '/css/dvbcronrecording.css')
        add_stylesheet(req, PACKAGE + '/css/programguide.css')
        add_script(req, 'common/js/trac.js')
        add_script(req, 'common/js/wikitoolbar.js')

        # passing variables to template
        data = {}
        data['message'] = message
        data['title'] = translate('Programguide List')
        data['scripts_url'] = self.get_scripts_url("")
        data['install_url'] = self.get_install_url("")
        data["_pagenum"] = req.args.get("_pagenum", "0")
        data["_pagesize"] = req.args.get("_pagesize", "10")
        data['datalist'] = Paginator(programguidedata, int(data["_pagenum"]), int(data["_pagesize"]))
        data['prioritynames'] = translate("**prioritynames")
        data['programguides'] = self.programguide_list
        data['userscriptstitle'] = translate('Userscript List')
        data['userscripts'] = userscripts
        data['author'] = req.authname or ""
        data['_'] = translate

        return ('programguide_list.html', data, None)

    def get_scripts_dir(self):
        htdocsdir = self.get_htdocs_dirs()[0][1]
        return os.path.join(str(htdocsdir), "js")

    def get_install_url(self, name):
        return "%s/%s/install/%s" % (URL, SUBURL, name)

    def get_scripts_url(self, name):
        return "/chrome/%s/js/%s" % (PACKAGE, name)

    def save(self, req, newdate, newtime, endtime, title, programchannel, programguide):
        onlydate = newdate.strftime("%d.%m.")
        weekday = newdate.weekday()
        session = db_cnx(self.env)
        q = session.query(RecordingList).filter_by(title=title, onlydate=onlydate)
        existing = q.count()
        if existing > 0:
            return "EXISTS"
        item = RecordingList()
        item.channelname = ""
        item.newtime = newtime
        item.endtime = endtime
        item.extratime = 1
        item.weekday = weekday
        item.onlydate = onlydate
        item.status = "ok"
        item.priority = 3
        item.title = title
        for entry in self.programguidechannels_all():
            if entry.programguide == programguide:
                if entry.programchannel == programchannel:
                    if entry.channelname:
                        item.channelname = entry.channelname
                    if entry.priority:
                        item.priority = entry.priority
                    extratime = 0
                    try:
                        if entry.extratime:
                            extratime = int(entry.extratime)
                        if entry.extrapercent:
                            percent = int(entry.extrapercent)
                            minutes = self.minutes(item.newtime, item.endtime)
                            extratime += (minutes * percent) / 100
                    except Exception, e:
                        item.title += "ERROR:%s" % e
                    except:
                        pass
                    item.extratime = extratime
        if not item.channelname:
            item.channelname = programchannel.lower()
        session.add(item)
        datespec = "[%s-%s%s]" % (item.newtime,
                                item.endtime,
                                item.onlydate 
                               ) 
        changed = "new recording on %s %s %s (from %s)" % (item.channelname,
                                                 datespec,
                                                 item.title,
                                                 programguide)
        session.add(RecordingChanges(tablename="RecordingList",
                                  username=req.authname,
                                  modified=datetime.datetime.now(),
                                  changed=changed))
        session.flush()
        return u"(%s)" % item.id
    
    def programguidechannels_all(self):
        """ FIXME: only hardcoded stuff here"""
        for entry in self.hardcoded_programguidechannels():
            yield entry

    def hardcoded_programguidechannels(self):
        import collections
        Entry = collections.namedtuple("ProgramguideEntry",
            ["programguide", "programchannel", "channelname",
             "extratime", "extrapercent", "priority"])
        yield Entry("tvspielfilm", "Das Erste", "1ard", 2, 2, 3)
        yield Entry("tvspielfilm", "ZDF", "2zdf", 2, 2, 3)
        yield Entry("tvspielfilm", "RTL", "rtl", 4, 4, 3)
        yield Entry("tvspielfilm", "SAT.1", "sat1", 4, 4, 3)
        yield Entry("tvspielfilm", "ProSieben", "pro7", 4, 5, 3)
        yield Entry("tvspielfilm", "RTL II", "rtl2", 4, 4, 3)
        yield Entry("tvspielfilm", "VOX", "vox", 4, 4, 3)
        yield Entry("tvspielfilm", "ARTE", "arte", 2, 2, 4)
        yield Entry("tvspielfilm", "TELE 5", "tele5", 3, 3, 2)
        yield Entry("tvspielfilm", "DMAX", "dmax", 3, 3, 2)

    def minutes(self, newtime, endtime):
        make = re.compile("(\d+):(\d+)")
        new1 = make.match(newtime)
        end1 = make.match(endtime)
        if new1 and end1:
            new = int(new1.group(1)) * 60 + int(new1.group(2))
            end = int(end1.group(1)) * 60 + int(end1.group(2))
            if end < new:
                end += 24 * 60
            if end < new:
                return 0 # unreachable?
            assert end > new
            return end - new
        return 0