From dc95ab1d00975ee93e0e0813dbf6aedb44e24c1e Mon Sep 17 00:00:00 2001 From: denise <denise@denise-DX4860sourcefabric.org> Date: Wed, 19 Sep 2012 11:05:17 -0400 Subject: [PATCH 01/10] CC-4155: Playlist Builder: Style static/dynamic expansion blocks in playlist contents -changed null values to empty strings --- airtime_mvc/public/js/airtime/library/spl.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/airtime_mvc/public/js/airtime/library/spl.js b/airtime_mvc/public/js/airtime/library/spl.js index ca9d88cf3..2513f97d9 100644 --- a/airtime_mvc/public/js/airtime/library/spl.js +++ b/airtime_mvc/public/js/airtime/library/spl.js @@ -428,6 +428,12 @@ var AIRTIME = (function(AIRTIME){ if (isStatic) { $.each(data, function(index, ele){ if (ele.track_title !== undefined) { + if (ele.creator === null) { + ele.creator = ""; + } + if (ele.track_title === null) { + ele.track_title = ""; + } $html += "<li>" + "<span class='block-item-title'>"+ele.track_title+" - </span>" + "<span class='block-item-author'>"+ele.creator+"</span>" + From 99b28cca82eb3753f2a95f1c41f6c58c10a51f7f Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Wed, 19 Sep 2012 11:11:50 -0400 Subject: [PATCH 02/10] Removed unused methods --- airtime_mvc/application/models/StoredFile.php | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index deada4d38..c005243db 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -240,33 +240,6 @@ class Application_Model_StoredFile } } - /** - * Get one metadata value. - * - * @param string $p_category (MDATA_KEY_URL) - * @return string - */ - public function getMetadataValue($p_category) - { - // constant() was used because it gets quoted constant name value from - // api_client.py. This is the wrapper funtion - return $this->getDbColMetadataValue(constant($p_category)); - } - - /** - * Get one metadata value. - * - * @param string $p_category (url) - * @return string - */ - public function getDbColMetadataValue($p_category) - { - $propelColumn = $this->_dbMD[$p_category]; - $method = "get$propelColumn"; - - return $this->_file->$method(); - } - /** * Get metadata as array, indexed by the column names in the database. * From 7dac1c6561df15a6909a9202be5db058496fdb38 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Wed, 19 Sep 2012 11:14:26 -0400 Subject: [PATCH 03/10] Added them back because not sure --- airtime_mvc/application/models/StoredFile.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index c005243db..deada4d38 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -240,6 +240,33 @@ class Application_Model_StoredFile } } + /** + * Get one metadata value. + * + * @param string $p_category (MDATA_KEY_URL) + * @return string + */ + public function getMetadataValue($p_category) + { + // constant() was used because it gets quoted constant name value from + // api_client.py. This is the wrapper funtion + return $this->getDbColMetadataValue(constant($p_category)); + } + + /** + * Get one metadata value. + * + * @param string $p_category (url) + * @return string + */ + public function getDbColMetadataValue($p_category) + { + $propelColumn = $this->_dbMD[$p_category]; + $method = "get$propelColumn"; + + return $this->_file->$method(); + } + /** * Get metadata as array, indexed by the column names in the database. * From ecb5c93ddfc21c8426f3292f1d941cee66670024 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Wed, 19 Sep 2012 11:14:59 -0400 Subject: [PATCH 04/10] Removed methods again... --- airtime_mvc/application/models/StoredFile.php | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index deada4d38..c005243db 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -240,33 +240,6 @@ class Application_Model_StoredFile } } - /** - * Get one metadata value. - * - * @param string $p_category (MDATA_KEY_URL) - * @return string - */ - public function getMetadataValue($p_category) - { - // constant() was used because it gets quoted constant name value from - // api_client.py. This is the wrapper funtion - return $this->getDbColMetadataValue(constant($p_category)); - } - - /** - * Get one metadata value. - * - * @param string $p_category (url) - * @return string - */ - public function getDbColMetadataValue($p_category) - { - $propelColumn = $this->_dbMD[$p_category]; - $method = "get$propelColumn"; - - return $this->_file->$method(); - } - /** * Get metadata as array, indexed by the column names in the database. * From 014da73eedc2a68d671e43e66a9a79d403f76e2c Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Wed, 19 Sep 2012 11:17:32 -0400 Subject: [PATCH 05/10] heredoc sql --- airtime_mvc/application/models/StoredFile.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index c005243db..7254583dd 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -993,15 +993,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'"; From f801fcb81e7955fa5be3361745d1ea70273bfe5c Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Wed, 19 Sep 2012 11:20:26 -0400 Subject: [PATCH 06/10] empty space --- airtime_mvc/application/models/StoredFile.php | 1 - 1 file changed, 1 deletion(-) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 7254583dd..efe81c7b9 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -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 From 81620d4f94575a36f49e95975ba2fd064359ae12 Mon Sep 17 00:00:00 2001 From: denise <denise@denise-DX4860sourcefabric.org> Date: Wed, 19 Sep 2012 12:41:54 -0400 Subject: [PATCH 07/10] CC-4481: Library -> Advanced Search -> Validation icon does not show up sometimes -fixed --- airtime_mvc/public/js/airtime/library/library.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index e5cae78e0..795bcb010 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -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) { From a6f1d4b7ecd2365e8bc3b19fa94791e5d17756e8 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Wed, 19 Sep 2012 12:47:11 -0400 Subject: [PATCH 08/10] Completely reworked event contractor --- .../media/monitor/eventcontractor.py | 39 ++++++++++++++++++ .../media-monitor2/media/monitor/events.py | 40 +++++++++++++++++++ .../media-monitor2/media/monitor/pure.py | 1 - .../media/monitor/watchersyncer.py | 3 +- .../media-monitor2/tests/prepare_tests.py | 14 +++---- .../media-monitor2/tests/test_api_client.py | 3 +- .../tests/test_eventcontractor.py | 34 ++++++---------- 7 files changed, 100 insertions(+), 34 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/eventcontractor.py b/python_apps/media-monitor2/media/monitor/eventcontractor.py index ececfb846..b80f3ba4e 100644 --- a/python_apps/media-monitor2/media/monitor/eventcontractor.py +++ b/python_apps/media-monitor2/media/monitor/eventcontractor.py @@ -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 diff --git a/python_apps/media-monitor2/media/monitor/events.py b/python_apps/media-monitor2/media/monitor/events.py index 88c7c1b30..69f751d69 100644 --- a/python_apps/media-monitor2/media/monitor/events.py +++ b/python_apps/media-monitor2/media/monitor/events.py @@ -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): """ diff --git a/python_apps/media-monitor2/media/monitor/pure.py b/python_apps/media-monitor2/media/monitor/pure.py index b6f3897ff..adeebfe69 100644 --- a/python_apps/media-monitor2/media/monitor/pure.py +++ b/python_apps/media-monitor2/media/monitor/pure.py @@ -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) diff --git a/python_apps/media-monitor2/media/monitor/watchersyncer.py b/python_apps/media-monitor2/media/monitor/watchersyncer.py index 214335fb6..61146326a 100644 --- a/python_apps/media-monitor2/media/monitor/watchersyncer.py +++ b/python_apps/media-monitor2/media/monitor/watchersyncer.py @@ -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) diff --git a/python_apps/media-monitor2/tests/prepare_tests.py b/python_apps/media-monitor2/tests/prepare_tests.py index 2522468ef..4e9a2247c 100644 --- a/python_apps/media-monitor2/tests/prepare_tests.py +++ b/python_apps/media-monitor2/tests/prepare_tests.py @@ -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 diff --git a/python_apps/media-monitor2/tests/test_api_client.py b/python_apps/media-monitor2/tests/test_api_client.py index f89b5efb3..26f1a25ef 100644 --- a/python_apps/media-monitor2/tests/test_api_client.py +++ b/python_apps/media-monitor2/tests/test_api_client.py @@ -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? diff --git a/python_apps/media-monitor2/tests/test_eventcontractor.py b/python_apps/media-monitor2/tests/test_eventcontractor.py index da35a71d1..f5ad827c2 100644 --- a/python_apps/media-monitor2/tests/test_eventcontractor.py +++ b/python_apps/media-monitor2/tests/test_eventcontractor.py @@ -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() From 8652681edadb98ac630f6941e21bae7c369fe6a4 Mon Sep 17 00:00:00 2001 From: james <james@james-P35-DS3L.(none)> Date: Wed, 19 Sep 2012 12:51:03 -0400 Subject: [PATCH 09/10] CC-4306: Watch folder: rescan watch directory and remove watch directory tooltips should be shifted depending on the path length - fixed --- .../views/scripts/form/preferences_watched_dirs.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/form/preferences_watched_dirs.phtml b/airtime_mvc/application/views/scripts/form/preferences_watched_dirs.phtml index 21cb6b0a4..93210b570 100644 --- a/airtime_mvc/application/views/scripts/form/preferences_watched_dirs.phtml +++ b/airtime_mvc/application/views/scripts/form/preferences_watched_dirs.phtml @@ -39,7 +39,7 @@ <?php if (count($watched_dirs) > 0): ?> <?php foreach($watched_dirs as $watched_dir): ?> <dd class="block-display selected-item"> - <?php echo ($watched_dir->getExistsFlag())?"":"<span class='ui-icon-alert'><img src='/css/images/warning-icon.png'></span>"?><span id="folderPath"><?php echo $watched_dir->getDirectory();?></span></span> + <?php echo ($watched_dir->getExistsFlag())?"":"<span class='ui-icon-alert'><img src='/css/images/warning-icon.png'></span>"?><span id="folderPath" style="display:block; width:350px"><?php echo $watched_dir->getDirectory();?></span></span> <span title="Rescan watched directory (This is useful if it is network mount and may be out of sync with Airtime)" class="ui-icon ui-icon-refresh"></span> <span title="Remove watched directory" class="ui-icon ui-icon-close"></span> From 51bcf048038896f355e81fa357ebac78279e26ea Mon Sep 17 00:00:00 2001 From: james <james@james-P35-DS3L.(none)> Date: Wed, 19 Sep 2012 13:11:08 -0400 Subject: [PATCH 10/10] CC-4491: Calendar -> Add /Remove contents: Cannot expand/collapse Advanced Search Options - fixed --- .../views/scripts/showbuilder/builderDialog.phtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml index 42daa30da..92331d6cc 100644 --- a/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml +++ b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml @@ -1,7 +1,10 @@ <div class="wrapper"> <div id="library_content" class="lib-content tabs ui-widget ui-widget-content block-shadow alpha-block padded"> <div id="import_status" style="display:none">File import in progress...</div> - <div id="advanced_search" class="advanced_search form-horizontal"></div> + <fieldset class="toggle" id="filter_options"> + <legend style="cursor: pointer;"><span class="ui-icon ui-icon-triangle-2-n-s"></span>Advanced Search Options</legend> + <div id="advanced_search" class="advanced_search form-horizontal"></div> + </fieldset> <table id="library_display" cellpadding="0" cellspacing="0" class="datatable"></table> </div> <div id="show_builder" class="sb-content ui-widget ui-widget-content block-shadow omega-block padded">