Merge branch 'devel' of dev.sourcefabric.org:airtime into devel

This commit is contained in:
james 2012-09-19 12:51:13 -04:00
commit b2e452acbb
9 changed files with 112 additions and 46 deletions

View File

@ -396,7 +396,6 @@ SQL;
return $possible_ext;
}
// We fallback to guessing the extension from the mimetype if we
// cannot extract it from the file name
@ -993,15 +992,18 @@ SQL;
/**
*
* Enter description here ...
* @param $dir_id - if this is not provided, it returns all files with full path constructed.
* @param $dir_id - if this is not provided, it returns all files with full
* path constructed.
*/
public static function listAllFiles($dir_id=null, $all)
{
$con = Propel::getConnection();
$sql = "SELECT filepath as fp"
." FROM CC_FILES as f"
." WHERE f.directory = :dir_id";
$sql = <<<SQL
SELECT filepath AS fp
FROM CC_FILES AS f
WHERE f.directory = :dir_id
SQL;
if (!$all) {
$sql .= " AND f.file_exists = 'TRUE'";

View File

@ -38,6 +38,7 @@ var AIRTIME = (function(AIRTIME) {
"track_num" : "n",
"year" : "n",
"owner_id" : "s",
"info_url" : "s",
"replay_gain" : "n"
};
@ -1041,6 +1042,7 @@ function addQtipToSCIcons(){
*/
function validateAdvancedSearch(divs) {
var valid = true,
allValid = true,
fieldName,
fields,
searchTerm = Array(),
@ -1053,7 +1055,6 @@ function validateAdvancedSearch(divs) {
searchTerm[0] = "";
searchTerm[1] = "";
$.each(divs, function(i, div){
fieldName = $(div).children(':nth-child(2)').attr('id');
fields = $(div).children().find('input');
@ -1085,6 +1086,7 @@ function validateAdvancedSearch(divs) {
//string fields do not need validation
if (searchTermType !== "s") {
valid = regExpr.test(searchTerm[i]);
if (!valid) allValid = false;
}
addRemoveValidationIcons(valid, $(field));
@ -1107,13 +1109,9 @@ function validateAdvancedSearch(divs) {
return false;
}
});
if (!valid) {
return false;
}
});
return valid;
return allValid;
}
function addRemoveValidationIcons(valid, field) {

View File

@ -2,6 +2,45 @@ from media.monitor.log import Loggable
from media.monitor.events import DeleteFile
class EventContractor(Loggable):
def __init__(self):
self.store = {}
def event_registered(self, evt):
"""
returns true if the event is registered which means that there is
another "unpacked" event somewhere out there with the same path
"""
return evt.path in self.store
def get_old_event(self, evt):
"""
get the previously registered event with the same path as 'evt'
"""
return self.store[ evt.path ]
def register(self, evt):
if self.event_registered(evt):
ev_proxy = self.get_old_event(evt)
if ev_proxy.same_event(evt):
ev_proxy.merge_proxy(evt)
return False
# delete overrides any other event
elif evt.is_event(DeleteFile):
ev_proxy.merge_proxy(evt)
return False
else:
ev_proxy.run_hook()
ev_proxy.reset_hook()
self.store[ evt.path ] = evt
evt.set_pack_hook( lambda : self.__unregister(evt) )
return True
def __unregister(self, evt):
del self.store[evt.path]
# Delete this class when done using
class EventContractor2(Loggable):
"""
This class is responsible for "contracting" events together to ease the
load on airtime. It does this by morphing old events into newer ones

View File

@ -42,6 +42,43 @@ class EventRegistry(object):
raise Exception("You can instantiate this class. Must only use class \
methods")
class EventProxy(object):
"""
A container object for instances of BaseEvent (or it's subclasses) used for
event contractor
"""
def __init__(self, orig_evt):
self.orig_evt = orig_evt
self.evt = orig_evt
self.reset_hook()
if hasattr(orig_evt, 'path'): self.path = orig_evt.path
def set_pack_hook(self, l):
self._pack_hook = l
def reset_hook(self):
self._pack_hook = lambda : None
def run_hook(self):
self._pack_hook()
def safe_pack(self):
self.run_hook()
# make sure that cleanup hook is never called twice for the same event
self.reset_hook()
return self.evt.safe_pack()
def merge_proxy(self, proxy):
self.evt = proxy.evt
def is_event(self, real_event):
return isinstance(self.evt, real_event)
def same_event(self, proxy):
return self.evt.__class__ == proxy.evt.__class__
class HasMetaData(object):
"""
Any class that inherits from this class gains the metadata attribute that
@ -91,6 +128,9 @@ class BaseEvent(Loggable):
"""
self._pack_hook = k
def proxify(self):
return EventProxy(self)
# As opposed to unsafe_pack...
def safe_pack(self):
"""

View File

@ -490,7 +490,6 @@ def file_playable(pathname):
command = ("airtime-liquidsoap -c 'output.dummy" + \
"(audio_to_stereo(single(\"%s\")))' > /dev/null 2>&1") % \
pathname.replace("'", "'\\''")
print(command)
return True
return_code = subprocess.call(command, shell=True)
return (return_code == 0)

View File

@ -8,6 +8,7 @@ from media.monitor.log import Loggable
from media.monitor.exceptions import BadSongFile
from media.monitor.pure import LazyProperty
from media.monitor.eventcontractor import EventContractor
from media.monitor.events import EventProxy
import api_clients.api_client as ac
@ -125,7 +126,7 @@ class WatchSyncer(ReportHandler,Loggable):
try:
# If there is a strange bug anywhere in the code the next line
# should be a suspect
if self.contractor.register(event): self.push_queue( event )
if self.contractor.register(EventProxy(event)): self.push_queue( event )
#self.push_queue( event )
except BadSongFile as e:
self.fatal_exception("Received bas song file '%s'" % e.path, e)

View File

@ -1,19 +1,15 @@
import shutil
import subprocess
# The tests rely on a lot of absolute paths and other garbage so this file
# configures all of that
music_folder = u'/home/rudi/music'
o_path = u'/home/rudi/throwaway/ACDC_-_Back_In_Black-sample-64kbps.ogg'
watch_path = u'/home/rudi/throwaway/fucking_around/watch/',
real_path1 = u'/home/rudi/throwaway/fucking_around/watch/unknown/unknown/ACDC_-_Back_In_Black-sample-64kbps-64kbps.ogg'
opath = u"/home/rudi/Airtime/python_apps/media-monitor2/tests/"
ppath = u"/home/rudi/Airtime/python_apps/media-monitor2/media/"
sample_config = u'/home/rudi/Airtime/python_apps/media-monitor2/tests/api_client.cfg'
real_config = u'/home/rudi/Airtime/python_apps/media-monitor2/tests/live_client.cfg'
api_client_path = '/etc/airtime/api_client.cfg'
if __name__ == "__main__":
shutil.copy(api_client_path, real_config)
# TODO : fix this to use liberal permissions
subprocess.call(["chown","rudi",real_config])
# holdover from the time we had a special config for testing
sample_config = api_client_path
real_config = api_client_path

View File

@ -9,7 +9,8 @@ import prepare_tests
class TestApiClient(unittest.TestCase):
def setUp(self):
test_path = prepare_tests.real_config
test_path = prepare_tests.api_client_path
print("Running from api_config: %s" % test_path)
if not os.path.exists(test_path):
print("path for config does not exist: '%s' % test_path")
# TODO : is there a cleaner way to exit the unit testing?

View File

@ -3,48 +3,37 @@ from media.monitor.eventcontractor import EventContractor
#from media.monitor.exceptions import BadSongFile
from media.monitor.events import FakePyinotify, NewFile, MoveFile, \
DeleteFile
from mock import patch
class TestMMP(unittest.TestCase):
def test_event_registered(self):
ev = EventContractor()
e1 = NewFile( FakePyinotify('bullshit.mp3') )
e2 = MoveFile( FakePyinotify('bullshit.mp3') )
e1 = NewFile( FakePyinotify('bullshit.mp3') ).proxify()
e2 = MoveFile( FakePyinotify('bullshit.mp3') ).proxify()
ev.register(e1)
self.assertTrue( ev.event_registered(e2) )
def test_get_old_event(self):
ev = EventContractor()
e1 = NewFile( FakePyinotify('bullshit.mp3') )
e2 = MoveFile( FakePyinotify('bullshit.mp3') )
e1 = NewFile( FakePyinotify('bullshit.mp3') ).proxify()
e2 = MoveFile( FakePyinotify('bullshit.mp3') ).proxify()
ev.register(e1)
self.assertEqual( ev.get_old_event(e2), e1 )
def test_register(self):
ev = EventContractor()
e1 = NewFile( FakePyinotify('bullshit.mp3') )
e2 = DeleteFile( FakePyinotify('bullshit.mp3') )
e1 = NewFile( FakePyinotify('bullshit.mp3') ).proxify()
e2 = DeleteFile( FakePyinotify('bullshit.mp3') ).proxify()
self.assertTrue( ev.register(e1) )
# Check that morph_into is called when it should be
with patch.object(NewFile, 'morph_into', return_value='kimchi') \
as mock_method:
ret = ev.register(e2)
self.assertFalse(ret)
mock_method.assert_called_once_with(e2)
# This time we are not patching morph
self.assertFalse( ev.register(e2) )
# We did not an element
self.assertTrue( len(ev.store.keys()) == 1 )
morphed = ev.get_old_event(e2)
self.assertTrue( isinstance(morphed, DeleteFile) )
self.assertEqual( len(ev.store.keys()), 1 )
delete_ev = e1.safe_pack()[0]
self.assertEqual( delete_ev['mode'], u'delete')
self.assertTrue( len(ev.store.keys()) == 0 )
self.assertEqual( len(ev.store.keys()), 0 )
e3 = DeleteFile( FakePyinotify('horseshit.mp3') )
e3 = DeleteFile( FakePyinotify('horseshit.mp3') ).proxify()
self.assertTrue( ev.register(e3) )
self.assertTrue( ev.register(e2) )
@ -58,11 +47,12 @@ class TestMMP(unittest.TestCase):
DeleteFile( FakePyinotify(p) ),
NewFile( FakePyinotify(p) ),
NewFile( FakePyinotify(p) ), ]
events = map(lambda x: x.proxify(), events)
actual_events = []
for e in events:
if ev.register(e):
actual_events.append(e)
self.assertEqual( len(ev.store.keys()), 1 )
packed = [ x.safe_pack() for x in actual_events ]
#packed = [ x.safe_pack() for x in actual_events ]
if __name__ == '__main__': unittest.main()