Guido Draheim avatar Guido Draheim committed 7fa7c36

configurable program guide elements

Comments (0)

Files changed (8)

src/dvbcronrecording/db/db5.py

+# http://trac-hacks.org/wiki/TracSqlAlchemyBridgeIntegration
+
+from sqlalchemy import (
+    # MetaData,
+    # Boolean,
+    Column,
+    DateTime,
+    # ForeignKey,
+    Integer,
+    String,
+    # Unicode,
+    )
+#from sqlalchemy.orm import relationship, backref
+#from sqlalchemy.orm.exc import NoResultFound
+
+from sqlalchemy.ext.declarative import declarative_base
+RecordingModel = declarative_base()
+
+class RecordingList(RecordingModel):
+    __tablename__ = "recording_list"
+    id = Column('id', Integer, primary_key=True)
+    channelname = Column('channelname', String)
+    newtime = Column('newtime', String)
+    endtime = Column('endtime', String)
+    extratime = Column('extratime', String)
+    onlydate = Column('onlydate', String)
+    title = Column('title', String)
+    status = Column('status', String)
+    weekday = Column('weekday', Integer)
+    priority = Column('priority', Integer)
+
+class RecordingChannels(RecordingModel):
+    __tablename__ = 'recording_channels'
+    id = Column('id', Integer, primary_key=True)
+    channelname = Column('channelname', String)
+    adapter = Column('adapter', String)
+    title = Column('title', String)
+
+class RecordingChannelsConf(RecordingModel):
+    __tablename__ = 'recording_channelsconf'
+    adapter = Column('adapter', String, primary_key = True)
+    title = Column('title', String, primary_key = True)
+    frequency = Column('frequency', Integer)
+    polarity = Column('polarity', String)
+    source = Column('source', String)
+    symbolrate = Column('symbolrate', Integer)
+    vpid = Column('vpid', Integer)
+    apid = Column('apid', Integer)
+    tpid = Column('tpid', Integer)
+
+class RecordingTuning(RecordingModel):
+    __tablename__ = 'recording_tuning'
+    id = Column("id", Integer, primary_key=True)
+    adapter = Column('adapter', String)
+    satellite = Column('satellite', String)
+    transponder = Column('transponder', String)
+    scansettings = Column('scansettings', String)
+    scansourcefile = Column('scansourcefile', String)
+    channelsconf = Column('channelsconf', String)
+
+class RecordingChanges(RecordingModel):
+    __tablename__ = 'recording_changes'
+    id = Column("id", Integer, primary_key=True)
+    tablename = Column("tablename", String)
+    changed = Column("changed", String)
+    username = Column("username", String)
+    modified = Column("modified", DateTime)
+
+class ProgramguideChannels(RecordingModel):
+    __tablename__ = 'recording_programguide'
+    id = Column("id", Integer, primary_key=True)
+    programguide = Column("programguide", String)
+    programchannel = Column("programchannel", String)
+    channelname = Column("channelname", String)
+    extratime = Column("extratime", String)
+    extrapercent = Column("extrapercent", String)
+    priority = Column("priority", Integer)
+
+metadata = RecordingModel.metadata
+
+from tsab import engine
+
+def create_all(env, cursor):
+    metadata.create_all(bind=engine(env))
+
+def upgrade(env, cursor):
+    import schemachange
+    try:
+        for sql in schemachange.migrate_AtoB(old_metadata(), metadata, env, excludeTables = None):
+            print sql
+    except Exception, e:
+        import traceback
+        traceback.print_exc(e)
+        raise
+
+def downgrade(env, cursor):
+    import schemachange
+    for sql in schemachange.migrate_BtoA(old_metadata(), metadata, env, excludeTables = None):
+        print sql
+
+def old_metadata():
+    import db4
+    return db4.metadata

src/dvbcronrecording/db/version.py

-number = 4
+number = 5

src/dvbcronrecording/htdocs/css/dvbcronrecording.css

         padding: 2px 1em;
        border: 1px outset gray; background-color: #EEE;
 }
+
+.programguide table { padding: 0 }

src/dvbcronrecording/htdocs/css/programguide.css

+.programguide .new { font-weight: bold; color: #040; }
+.programguide th { text-align: left; border-bottom: 1px dotted black; font-size: small; font-weight: bold; }
+.programguide .id { width: 2em; text-align: right; padding-right: 0.5em; font-size: small; }
+.programguide .programchannel { width: 10em; }
+.programguide .channelname { width: 5em; }
+.programguide .extratime { width: 5em; }
+.programguide .extrapercent { width: 5em; }
+.programguide .delaction { font-size: xx-small; padding: 0; border-spacing: 0;  }
+
+.programguide .programchannel input { width: 10em; }
+.programguide .channelname input { width: 5em; }
+.programguide .extratime input { width: 5em; text-align: right; }
+.programguide .extrapercent input { width: 5em; text-align: right; }
+.programguide .action input { width: 10em; }
+

src/dvbcronrecording/htdocs/js/dvbcronrecording_tvspielfilm.user.js

 // @require       http://SERVER/chrome/common/js/jquery.js
 // ==/UserScript==
 
-var _url = "http://SERVER/recording/programguide/tvspielfilm";
+var _url = "http://SERVER/recording/programguide/save/tvspielfilm";
 
 function url_data(data) {
 	var text = "";
 function save_error(response) {
 	GM_log("ERROR "+response.statusText);
     $("#dvbcronrecording").css("border", "2px solid red")
-    var value = $(".message", response.responseText).text();
-    $("#dvbcronrecording").text("?"+response.statusText+" | "+value);
+    var value = $(".error", response.responseText).text();
+    $("#dvbcronrecording").text("! "+response.statusText+" ! "+value);
 }
 
 function save_success(response) {
 	GM_log("STORED "+response.statusText);
     $("#dvbcronrecording").css("border", "1px solid green");
-    var value = $(".message", response.responseText).text();
-    if (value.match("OOPS")) $("#dvbcronrecording").css("border", "1px solid red");
-    $("#dvbcronrecording").text("!"+response.statusText+" | "+value);
+    $("#dvbcronrecording").css("color", "green");
+    var value = $(".error", response.responseText).text();
+    if (value.match("OOPS")) {
+    	$("#dvbcronrecording").css("border", "2px solid red");
+    	$("#dvbcronrecording").css("color", "red");
+    }
+    $("#dvbcronrecording").text("| "+response.statusText+" | "+value);
 }
 
 

src/dvbcronrecording/programguide.py

 from dvbcronrecording.db.schema import RecordingList #@UnresolvedImport
 from dvbcronrecording.db.schema import RecordingChanges #@UnresolvedImport
 
-def intnull(value, default = None):
+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):
+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
     # Public methods
     #
 
-    implements(IPermissionRequestor, ITemplateProvider,  IRequestHandler)
+    implements(IPermissionRequestor, ITemplateProvider, IRequestHandler)
+    
+    programguide_list = [ "tvspielfilm" ]
 
     # IPermissionRequestor methods
 
         message = "[%s]" % page
 
         if page and page.startswith("install/"):
-            name = page[len("install/"):]
-            filepath = os.path.join(userscripts_dir, name)
-            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"' % name)
-                    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" % name
-        elif page and page.startswith("tvspielfilm"):
-            # decode the strings from the remote page
-            date = req.args["date"]
-            time = req.args["time"]
-            channel = req.args["channel"]
-            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)
-            channel = self.map_channel(channel)
-            message = self.save(req, newdate, newtime, endtime, channel, title, page)
-
+            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/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('Userscript List')
+        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(userscripts, int(data["_pagenum"]), int(data["_pagesize"]))
+        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 ('programguidescripts.html', data, None)
+        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/"+PACKAGE+"/js/"+name)
+        return "/chrome/%s/js/%s" % (PACKAGE, name)
 
-    def save(self, req, newdate, newtime, endtime, channel, title, source):
+    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)
+        q = session.query(RecordingList).filter_by(title=title, onlydate=onlydate)
         existing = q.count()
         if existing > 0:
             return "EXISTS"
         item = RecordingList()
-        item.channelname = channel
+        item.channelname = ""
         item.newtime = newtime
         item.endtime = endtime
-        item.extratime = 9
+        item.extratime = 1
         item.weekday = weekday
         item.onlydate = onlydate
         item.status = "ok"
-        item.priority = "3"
+        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, 
+        datespec = "[%s-%s%s]" % (item.newtime,
+                                item.endtime,
                                 item.onlydate 
                                ) 
         changed = "new recording on %s %s %s (from %s)" % (item.channelname,
                                                  datespec,
                                                  item.title,
-                                                 source)
+                                                 programguide)
         session.add(RecordingChanges(tablename="RecordingList",
                                   username=req.authname,
                                   modified=datetime.datetime.now(),
                                   changed=changed))
         session.flush()
-        return u"OK %s" % item.id
-    def map_channel(self, channel):
-        """ FIXME: hardcoded """
-        if channel in ["Das Erste"]:
-            return "1ard"
-        if channel in ["ZDF"]:
-            return "2zdf"
-        if channel in [ "RTL" ]:
-            return "rtl"
-        if channel in [ "SAT.1" ]:
-            return "sat1"
-        if channel in [ "ProSieben" ]:
-            return "pro7"
-        if channel in [ "kabel eins" ]:
-            return "kabel1"
-        if channel in [ "RTL II" ]:
-            return "rtl2"
-        if channel in [ "VOX" ]:
-            return "vox"
-        if channel in [ "ARTE" ]:
-            return "arte"
-        if channel in [ "TELE 5" ]:
-            return "tele5"
-        return channel
+        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
+
+        

src/dvbcronrecording/templates/programguide_list.html

+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+  <xi:include href="layout.html" />
+  <xi:include href="macros.html" />
+<head><title>${title}</title></head>
+<body>
+<div id="ctxtnav" class="nav">
+</div>
+  <xi:include href="channels_nav.html" />
+  <xi:include href="channelsconf_nav.html" />
+
+<div id="content" class="programguide">
+<h2>${title}</h2>
+ 
+<py:if test="message">
+  <div class="error">
+    ${message}
+  </div>
+</py:if>
+
+<table>
+<tr>
+<th>#</th>
+<th>programguide</th>
+<th>programchannel</th>
+<th>channelname</th>
+<th>extratime</th>
+<th>extrapercent</th>
+<th>priority</th>
+</tr>
+<tr py:for="item in datalist">
+  <td>${item.id}</td>
+  <td class="programguide"><select name="programguide">
+        <option py:for="name in sorted(programguides)" 
+                value="${name}" selected="${ (name == item.programguide) or None}">
+                ${name}
+        </option>
+      </select></td>
+  <td class="programchannel"><input type="text" name="programchannel" value="${item.programchannel}"></input></td>
+  <td class="channelname"><input type="text" name="channelname" value="${item.channelname}"></input></td>
+  <td class="extratime"><input type="text" name="extratime" value="${item.extratime}"></input></td>
+  <td class="extrapercent"><input type="text" name="extrapercent" value="${item.extrapercent}"></input></td>
+  <td class="priority"><select name="priority">
+        <option py:for="name in sorted(prioritynames)" 
+                value="${name}" selected="${ (name == item.priority) or None}">
+                ${prioritynames[name]}
+        </option>
+      </select>
+   </td>
+</tr>
+</table>
+  <xi:include href="paginator_datalist.html" />
+
+<h2>${userscriptstitle}</h2>
+<table>
+<tr>
+<th>#</th>
+<th>Installation</th>
+</tr>
+<tr py:for="item in userscripts">
+  <td><a href="${href(scripts_url)+'/'+item}">*</a></td>
+  <td><a href="${href(install_url)+'/'+item}">${item}</a></td>
+</tr>
+</table>
+</div>
+
+</body>
+</html>

src/dvbcronrecording/templates/programguidescripts.html

-<!DOCTYPE html
-    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
-  <xi:include href="layout.html" />
-  <xi:include href="macros.html" />
-<head><title>${title}</title></head>
-<body>
-<div id="ctxtnav" class="nav">
-</div>
-  <xi:include href="channels_nav.html" />
-  <xi:include href="recording_nav.html" />
-
-<div id="content">
-<h2>${title}</h2>
- 
-<py:if test="message">
-  <div class="error">
-    ${message}
-  </div>
-</py:if>
-
-<table>
-<tr>
-<th>Original</th>
-<th>Installation</th>
-</tr>
-<tr py:for="item in datalist">
-  <td><a href="${href(scripts_url)+'/'+item}">${item}</a></td>
-  <td><a href="${href(install_url)+'/'+item}">${item}</a></td>
-</tr>
-</table>
-  <xi:include href="paginator_datalist.html" />
-</div>
-
-</body>
-</html>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.