Added artist page and log page, fixed some css, added logline parse to helpers

This commit is contained in:
Remy
2011-08-05 22:20:36 -07:00
parent 5865a54fc5
commit fe1667a2e0
11 changed files with 210 additions and 148 deletions

View File

@@ -4,7 +4,7 @@
-moz-border-radius: 20px;
width: 88%;
margin: 20px auto 0 auto;
padding: 22px;
padding: 25px;
background-color: white;
position: relative;
min-height: 155px;
@@ -42,7 +42,7 @@
}
.dataTables_info {
width: 40%;
width: 50%;
float: left;
background-color: white;
font-weight: bold;

View File

@@ -66,8 +66,8 @@ input:valid, textarea:valid { }
input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; }
.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; }
::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; }
::selection { background:#FF5E99; color:#fff; text-shadow: none; }
::-moz-selection{ background: grey; color:#fff; text-shadow: none; }
::selection { background: grey; color:#fff; text-shadow: none; }
a:link { -webkit-tap-highlight-color: #FF5E99; }
button { width: auto; overflow: visible; }
@@ -110,28 +110,56 @@ header { height: 68px; width: 100%; min-width: 907px; padding: 0 10px 0 10px; ba
div#logo { float: left; }
ul#nav { margin-top: 25px; float: left; list-style-type: none; }
ul#nav li { margin: 40px 5px auto 12px; display: inline; }
ul#nav li a { font-size: 16px; font-weight: bold; color: #330000; text-decoration: none; }
ul#nav li { margin: 40px 0px auto 10px; display: inline; }
ul#nav li a { padding: 5px; font-size: 16px; font-weight: bold; color: #330000; text-decoration: none; }
ul#nav li a:hover { background-color: #a3e532; }
div#searchbar { margin: 24px auto auto 30px; float: left; }
div#subhead_container { margin-top: 68px; height: 30px; width:100%; min-width: 907px; background-color:#330000; position: fixed; float: left; list-style-type: none; top: 0; z-index: 999; overflow: hidden; }
ul#subhead_menu { margin-top: 5px; }
ul#subhead_menu li { width: 100%; height: 100%; display: inline; }
ul#subhead_menu li a { padding: 5px 15px 10px 15px; vertical-align: middle; color: white; font-size: 16px; text-decoration: none; }
ul#subhead_menu li a:hover { width: 100%; height: 100%; background-color: #grey; }
div#main { margin: 0; padding: 68px 0 0 0; }
div#searchbar { margin: 24px 30px auto auto; float: right; }
div#main { margin: 0; padding: 80px 0 0 0; }
table#artist_table { background-color: white; }
table#artist_table th#name { text-align: left; min-width: 225px; }
table#artist_table th#album { text-align: center; min-width: 250px; }
table#artist_table th#name { text-align: left; min-width: 200px; }
table#artist_table th#album { text-align: center; min-width: 300px; }
table#artist_table th#reldate { width: 175px; text-align: center; min-width: 100px; }
table#artist_table th#center { text-align: center; }
table#artist_table td#name { vertical-align: middle; text-align: left; }
table#artist_table td#album { vertical-align: middle; text-align: center; }
table#artist_table td#reldate { vertical-align: middle; width: 125px; text-align: center; }
table#artist_table td#have { vertical-align: middle; text-align: center; }
table#artist_table th#have { text-align: center; }
table#artist_table td#name { vertical-align: middle; text-align: left; min-width:200px; }
table#artist_table td#album { vertical-align: middle; text-align: center; min-width: 300px; }
table#artist_table td#reldate { vertical-align: middle; text-align: center; min-width: 100px; }
table#artist_table td#have { vertical-align: middle; }
div#artistheader { padding-top: 48px; font-size: 24px; font-weight: bold; text-align: center; }
table#album_table { background-color: white; }
table#album_table th#albumart { text-align: left; min-width: 50px; }
table#album_table th#albumname { text-align: center; min-width: 150px; }
table#album_table th#reldate { width: 175px; text-align: center; min-width: 100px; }
table#album_table th#status { width: 175px; text-align: center; min-width: 100px; }
table#album_table th#type { width: 175px; text-align: center; min-width: 100px; }
table#album_table td#albumart { vertical-align: middle; text-align: left; }
table#album_table td#albumname { vertical-align: middle; text-align: center; }
table#album_table td#reldate { vertical-align: middle; text-align: center; }
table#album_table td#status { vertical-align: middle; text-align: center; }
table#album_table td#type { vertical-align: middle; text-align: center; }
table#album_table td#have { vertical-align: middle; }
table#log_table { background-color: white; }
table#log_table th#timestamp { text-align: left; min-width: 100px; }
table#log_table th#level { text-align: left; min-width: 100px; }
table#log_table th#thread { text-align: left; min-width: 100px; }
table#log_table th#message { text-align: left; min-width: 200px; }
div.progress-container { border: 1px solid #ccc; width: 100px; height: 14px; margin: 2px 5px 2px 0; padding: 1px; float: left; background: white; }
div.progress-container > div { background-color: #a3e532; height: 14px; }
.havetracks { font-size: 13px; margin-left: 36px; }
.havetracks { font-size: 13px; margin-left: 36px; padding-bottom: 3px; vertical-align: middle; }
footer { margin: 20px auto 20px auto; }
div#version { text-align: center; font-weight: bold; }

View File

@@ -0,0 +1,68 @@
<%inherit file="base.html"/>
<%def name="subhead()">
<div id="subhead_container">
<ul id="subhead_menu">
<li><a href="refresh?ArtistID=${artist['ArtistID']}">Refresh Artist</a></li>
<li><a href="delete?ArtistID=${artist['ArtistID']}">Delete Artist</a></li>
<li><a href="pause?ArtistID=${artist['ArtistID']}">Pause Artist</a></li>
</ul>
</div>
</%def>
<%def name="body()">
<div id="artistheader">
${artist['ArtistName']}
</div>
<table class="display" id="album_table">
<thead>
<tr>
<th id="albumart"></th>
<th id="albumname">Album Name</th>
<th id="reldate">Release Date</th>
<th id="type">Release Type</th>
<th id="status">Status</th>
<th id="have">Have</th>
</tr>
</thead>
<tbody>
%for album in albums:
<tr>
<td id="albumart"><img src="http://ec1.images-amazon.com/images/P/${album['AlbumASIN']}.01.MZZZZZZZ.jpg" height="50" width="50"></td>
<td id="albumname"><a href="albumPage?AlbumID=${album['AlbumID']}">${album['AlbumTitle']}</a></td>
<td id="reldate">${album['ReleaseDate']}</td>
<td id="type">${album['Type']}</td>
<td id="status">${album['Status']}</td>
<td id="have"><div class="progress-container"><div style="width:100%"><div class="havetracks">tracks</div></div></div></td>
</tr>
%endfor
</tbody>
</table>
</%def>
<%def name="headerIncludes()">
<link rel="stylesheet" href="css/data_table.css">
</%def>
<%def name="javascriptIncludes()">
<script src="js/libs/jquery.dataTables.min.js"></script>
<script>
$(document).ready(function()
{
$('#album_table').dataTable(
{
"oLanguage": {
"sLengthMenu":"Show _MENU_ albums per page",
"sEmptyTable": "No album information available",
"sInfo":"Showing _START_ to _END_ of _TOTAL_ albums",
"sInfoEmpty":"Showing 0 to 0 of 0 albums",
"sInfoFiltered":"(filtered from _MAX_ total albums)"},
"bStateSave": true,
"iDisplayLength": 25,
"sPaginationType": "full_numbers",
"aaSorting": [[3, 'asc'],[2,'desc']]
});
});
</script>
</%def>

View File

@@ -46,7 +46,9 @@
</form>
</div>
</header>
<div id="subhead">
${next.subhead()}
</div>
<div id="main" class="main">
${next.body()}
</div>
@@ -82,3 +84,4 @@
<%def name="javascriptIncludes()"></%def>
<%def name="headerIncludes()"></%def>
<%def name="subhead()"></%def>

View File

@@ -35,8 +35,8 @@
%>
<tr>
<td id="name"><a href="artistPage?ArtistID=${artist['ArtistID']}">${artist['ArtistName']} </td>
<td id="album"><a href="albumPage?AlbumID=${artist['AlbumID']}">${artist['LatestAlbum']}</td>
<td id="name"><a href="artistPage?ArtistID=${artist['ArtistID']}">${artist['ArtistName']}</a></td>
<td id="album"><a href="albumPage?AlbumID=${artist['AlbumID']}">${artist['LatestAlbum']}</a></td>
<td id="reldate">${releasedate}</td>
<td id="have"><div class="progress-container"><div style="width:${percent}%"><div class="havetracks">${havetracks}/${totaltracks}</div></div></div></td>
</tr>
@@ -51,4 +51,18 @@
<%def name="javascriptIncludes()">
<script src="js/libs/jquery.dataTables.min.js"></script>
<script>
$(document).ready(function()
{
$('#artist_table').dataTable(
{
"aoColumnDefs": [
{ "bSOtrable": false, "aTargets": [ 1 ] } ],
"bStateSave": true,
"iDisplayLength": 50,
"sPaginationType": "full_numbers",
});
});
</script>
</%def>

View File

@@ -0,0 +1,56 @@
<%inherit file="base.html"/>
<%def name="body()">
<table class="display" id="log_table">
<thead>
<tr>
<th id="timestamp">Timestamp</th>
<th id="level">Level</th>
<th id="thread">Thread</th>
<th id="message">Message</th>
</tr>
</thead>
<tbody>
%for line in lineList:
<%
from headphones import helpers
timestamp, level, thread, message = helpers.extract_logline(line)
%>
%if timestamp and level and thread and message:
<tr>
<td id="timestamp">${timestamp}</td>
<td id="level">${level}</td>
<td id="thread">${thread}</td>
<td id="message">${message.decode('utf-8')}</td>
</tr>
%endif
%endfor
</tbody>
</table>
</%def>
<%def name="headerIncludes()">
<link rel="stylesheet" href="css/data_table.css">
</%def>
<%def name="javascriptIncludes()">
<script src="js/libs/jquery.dataTables.min.js"></script>
<script>
$(document).ready(function()
{
$('#log_table').dataTable(
{
"oLanguage": {
"sLengthMenu":"Show _MENU_ lines per page",
"sEmptyTable": "No log information available",
"sInfo":"Showing _START_ to _END_ of _TOTAL_ lines",
"sInfoEmpty":"Showing 0 to 0 of 0 lines",
"sInfoFiltered":"(filtered from _MAX_ total lines)"},
"bStateSave": true,
"iDisplayLength": 100,
"sPaginationType": "full_numbers",
});
});
</script>
</%def>

View File

@@ -33,7 +33,7 @@ g},"numeric-asc":function(g,l){return(g=="-"||g===""?0:g*1)-(l=="-"||l===""?0:l*
if(l!==null&&!isNaN(l)||typeof g=="string"&&g.length===0)return"date";return null},function(g){if(typeof g=="string"&&g.indexOf("<")!=-1&&g.indexOf(">")!=-1)return"html";return null}];o.fnVersionCheck=function(g){var l=function(x,v){for(;x.length<v;)x+="0";return x},r=o.sVersion.split(".");g=g.split(".");for(var s="",w="",y=0,G=g.length;y<G;y++){s+=l(r[y],3);w+=l(g[y],3)}return parseInt(s,10)>=parseInt(w,10)};o._oExternConfig={iNextUnique:0};i.fn.dataTable=function(g){function l(){this.fnRecordsTotal=
function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsTotal,10):this.aiDisplayMaster.length};this.fnRecordsDisplay=function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsDisplay,10):this.aiDisplay.length};this.fnDisplayEnd=function(){return this.oFeatures.bServerSide?this.oFeatures.bPaginate===false||this._iDisplayLength==-1?this._iDisplayStart+this.aiDisplay.length:Math.min(this._iDisplayStart+this._iDisplayLength,this._iRecordsDisplay):this._iDisplayEnd};this.sInstance=
this.oInstance=null;this.oFeatures={bPaginate:true,bLengthChange:true,bFilter:true,bSort:true,bInfo:true,bAutoWidth:true,bProcessing:false,bSortClasses:true,bStateSave:false,bServerSide:false,bDeferRender:false};this.oScroll={sX:"",sXInner:"",sY:"",bCollapse:false,bInfinite:false,iLoadGap:100,iBarWidth:0,bAutoCss:true};this.aanFeatures=[];this.oLanguage={sProcessing:"Processing...",sLengthMenu:"Show _MENU_ artists per page",sZeroRecords:"No matching records found",sEmptyTable:"",
sLoadingRecords:"Loading...",sInfo:"Showing _START_ to _END_ of _TOTAL_ total artists",sInfoEmpty:"Showing 0 to 0 of 0 artists",sInfoFiltered:"(filtered from _MAX_ total artists)",sInfoPostFix:"",sSearch:"Filter:",sUrl:"",oPaginate:{sFirst:"First",sPrevious:"Previous",sNext:"Next",sLast:"Last"},fnInfoCallback:null};this.aoData=[];this.aiDisplay=[];this.aiDisplayMaster=[];this.aoColumns=[];this.aoHeader=[];this.aoFooter=[];this.iNextId=0;this.asDataSearch=[];this.oPreviousSearch={sSearch:"",bRegex:false,
sLoadingRecords:"Loading...",sInfo:"Showing _START_ to _END_ of _TOTAL_ artists",sInfoEmpty:"Showing 0 to 0 of 0 artists",sInfoFiltered:"(filtered from _MAX_ total artists)",sInfoPostFix:"",sSearch:"Filter:",sUrl:"",oPaginate:{sFirst:"First",sPrevious:"Previous",sNext:"Next",sLast:"Last"},fnInfoCallback:null};this.aoData=[];this.aiDisplay=[];this.aiDisplayMaster=[];this.aoColumns=[];this.aoHeader=[];this.aoFooter=[];this.iNextId=0;this.asDataSearch=[];this.oPreviousSearch={sSearch:"",bRegex:false,
bSmart:true};this.aoPreSearchCols=[];this.aaSorting=[[0,"asc",0]];this.aaSortingFixed=null;this.asStripClasses=[];this.asDestoryStrips=[];this.sDestroyWidth=0;this.fnFooterCallback=this.fnHeaderCallback=this.fnRowCallback=null;this.aoDrawCallback=[];this.fnInitComplete=this.fnPreDrawCallback=null;this.sTableId="";this.nTableWrapper=this.nTBody=this.nTFoot=this.nTHead=this.nTable=null;this.bInitialised=this.bDeferLoading=false;this.aoOpenRows=[];this.sDom="lfrtip";this.sPaginationType="two_button";
this.iCookieDuration=7200;this.sCookiePrefix="SpryMedia_DataTables_";this.fnCookieCallback=null;this.aoStateSave=[];this.aoStateLoad=[];this.sAjaxSource=this.oLoadedState=null;this.sAjaxDataProp="aaData";this.bAjaxDataGet=true;this.jqXHR=null;this.fnServerData=function(a,b,c,d){d.jqXHR=i.ajax({url:a,data:b,success:c,dataType:"json",cache:false,error:function(f,e){e=="parsererror"&&alert("DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.")}})};
this.fnFormatNumber=function(a){if(a<1E3)return a;else{var b=a+"";a=b.split("");var c="";b=b.length;for(var d=0;d<b;d++){if(d%3===0&&d!==0)c=","+c;c=a[b-d-1]+c}}return c};this.aLengthMenu=[10,25,50,100];this.bDrawing=this.iDraw=0;this.iDrawError=-1;this._iDisplayLength=10;this._iDisplayStart=0;this._iDisplayEnd=10;this._iRecordsDisplay=this._iRecordsTotal=0;this.bJUI=false;this.oClasses=o.oStdClasses;this.bSortCellsTop=this.bSorted=this.bFiltered=false;this.oInit=null}function r(a){return function(){var b=

View File

@@ -1,12 +0,0 @@
$(document).ready(function()
{
$('#artist_table').dataTable(
{
"aoColumnDefs": [
{ "bSOtrable": false, "aTargets": [ 1 ] } ],
"bStateSave": true,
"iDisplayLength": 50,
"sPaginationType": "full_numbers",
});
});

View File

@@ -124,3 +124,16 @@ def extract_data(s):
else:
logger.info("Couldn't parse " + s + " into a valid Newbin format")
return (name, album, year)
def extract_logline(s):
# Default log format
pattern = re.compile(r'(?P<timestamp>.*?)\s\-\s(?P<level>.*?)\s*\:\:\s(?P<thread>.*?)\s\:\s(?P<message>.*)', re.VERBOSE)
match = pattern.match(s)
if match:
timestamp = match.group("timestamp")
level = match.group("level")
thread = match.group("thread")
message = match.group("message")
return (timestamp, level, thread, message)
else:
return None

View File

@@ -412,7 +412,7 @@ def displayAlbums(ArtistID, Type=None):
else:
newStatus = '%s' % (results[i][3])
page.append('''<tr>
<td align="left"><img src="http://ec1.images-amazon.com/images/P/%s.01.MZZZZZZZ.jpg" height="50" width="50"></td>
<td align="left">
<td align="left" width="240"><a href="albumPage?AlbumID=%s">%s</a>
(<A class="external" href="http://musicbrainz.org/release-group/%s.html">link</a>)</td>
<td align="center" width="160">%s</td>

View File

@@ -37,110 +37,11 @@ class WebInterface(object):
return serve_template(templatename="index.html", title="Home", artists=artists)
home.exposed = True
def homeold(self):
page = [templates._header]
if not headphones.CURRENT_VERSION:
page.append('''<div class="updatebar">You're running an unknown version of Headphones. <a class="blue" href="update">Click here to update</a></div>''')
elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.INSTALL_TYPE != 'win':
page.append('''<div class="updatebar">A <a class="blue" href="http://github.com/rembo10/headphones/compare/%s...%s">
newer version</a> is available. You're %s commits behind. <a class="blue" href="update">Click here to update</a></div>
''' % (headphones.CURRENT_VERSION, headphones.LATEST_VERSION, headphones.COMMITS_BEHIND))
page.append(templates._logobar)
page.append(templates._nav)
myDB = db.DBConnection()
results = myDB.select('SELECT ArtistName, ArtistID, Status, LatestAlbum, ReleaseDate, AlbumID, TotalTracks, HaveTracks from artists order by ArtistSortName collate nocase')
if len(results):
page.append('''<div class="table"><table border="0" cellpadding="3">
<tr>
<th align="left" width="170">Artist Name</th>
<th align="center" width="100">Status</th>
<th align="center" width="300">Upcoming Albums</th>
<th align="center">Have</th>
</tr>''')
for artist in results:
totaltracks = artist['TotalTracks']
havetracks = artist['HaveTracks']
if not havetracks:
havetracks = 0
try:
percent = (havetracks*100.0)/totaltracks
if percent > 100:
percent = 100
except (ZeroDivisionError, TypeError):
percent = 0
totaltracks = '?'
if artist['LatestAlbum']:
if artist['ReleaseDate'] > helpers.today():
newalbumName = '<a class="green" href="albumPage?AlbumID=%s"><i><b>%s</b></i>' % (artist['AlbumID'], artist['LatestAlbum'])
releaseDate = '(%s)</a>' % artist['ReleaseDate']
else:
newalbumName = '<a class="gray" href="albumPage?AlbumID=%s"><i>%s</i>' % (artist['AlbumID'], artist['LatestAlbum'])
releaseDate = ""
else:
newalbumName = '<font color="#CFCFCF">None</font>'
releaseDate = ""
if artist['Status'] == 'Paused':
newStatus = '''<font color="red"><b>%s</b></font>(<A class="external" href="resumeArtist?ArtistID=%s">resume</a>)''' % (artist['Status'], artist['ArtistID'])
elif artist['Status'] == 'Loading':
newStatus = '''<a class="gray">Loading...</a>'''
else:
newStatus = '''%s(<A class="external" href="pauseArtist?ArtistID=%s">pause</a>)''' % (artist['Status'], artist['ArtistID'])
page.append('''<tr><td align="left" width="300"><a href="artistPage?ArtistID=%s">%s</a>
(<A class="external" href="http://musicbrainz.org/artist/%s">link</a>) [<A class="externalred" href="deleteArtist?ArtistID=%s">delete</a>]</td>
<td align="center" width="160">%s</td>
<td align="center">%s %s</td>
<td><div class="progress-container"><div style="width: %s%%"><div class="smalltext3">%s/%s</div></div></div></td></tr>
''' % (artist['ArtistID'], artist['ArtistName'], artist['ArtistID'],
artist['ArtistID'], newStatus, newalbumName, releaseDate,
percent, havetracks, totaltracks))
page.append('''</table></div>''')
page.append(templates._footer % headphones.CURRENT_VERSION)
else:
have = myDB.select('SELECT ArtistName from have')
if len(have):
page.append("""<div class="datanil">Scanning...</div>""")
else:
page.append("""<div class="datanil">Add some artists to the database!</div>""")
return page
home.exposed = True
def artistPage(self, ArtistID):
page = [templates._header]
page.append(templates._logobar)
page.append(templates._nav)
myDB = db.DBConnection()
artist = myDB.select('SELECT ArtistName, IncludeExtras, Status from artists WHERE ArtistID=?', [ArtistID])
while not artist:
time.sleep(1)
page.append('''<div class="table"><table><p align="center">%s</p>
''' % artist[0][0])
if artist[0][2] == 'Loading':
page.append('<p align="center"><i>Loading...</i></p>')
if templates.displayAlbums(ArtistID, 'Album'):
page.append(templates.displayAlbums(ArtistID, 'Album'))
releasetypes = ['Compilation', 'EP', 'Single', 'Live', 'Remix']
for type in releasetypes:
if templates.displayAlbums(ArtistID, type):
page.append(templates.displayAlbums(ArtistID, type))
page.append('</table>')
if not artist[0][1]:
page.append('''<br /><div class="bluecenter"><a href="getExtras?ArtistID=%s">Get Extras for %s!</a></div>'''
% (ArtistID, artist[0][0]))
page.append(templates._footer % headphones.CURRENT_VERSION)
return page
artist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [ArtistID]).fetchone()
albums = myDB.select('SELECT * from albums WHERE ArtistID=? order by ReleaseDate DESC', [ArtistID])
return serve_template(templatename="artist.html", title=artist['ArtistName'], artist=artist, albums=albums)
artistPage.exposed = True
@@ -524,22 +425,13 @@ class WebInterface(object):
history.exposed = True
def logs(self):
page = [templates._header]
page.append(templates._logobar)
page.append(templates._nav)
page.append('''<div class="table"><p class="logtext">''')
log_file = os.path.join(headphones.LOG_DIR, 'headphones.log')
if os.path.isfile(log_file):
fileHandle = open(log_file)
lineList = fileHandle.readlines()
fileHandle.close()
lineList.reverse()
for line in lineList[1:200]:
page.append(line.decode('utf-8') + '<br /><br />')
page.append('''</p></div>''')
page.append(templates._footer % headphones.CURRENT_VERSION)
return page
return serve_template(templatename="logs.html", title="Log", lineList=lineList[0:1000])
logs.exposed = True
def clearhistory(self):