def test_scan_missing(self): (srcdir, mediadir, tmpdir) = self._setup_test_data() try: rows = 0 photoman._find_and_archive_photos(srcdir, mediadir, True, 'foo') rep = media_common.Repository() rep.open(os.path.join(mediadir)) rows = self._get_row_count(rep) rep.close() os.remove( os.path.join(mediadir, 'photos/2012/07_July/gnexus 160.jpg')) photoman._scan_missing_photos(mediadir) rep = media_common.Repository() rep.open(os.path.join(mediadir)) self.assertEquals(rows - 1, self._get_row_count(rep)) cur = rep.con.cursor() filepath = os.path.join(mediadir, 'photos/2012/07_July/gnexus 160.jpg') rows = cur.execute( ''' select id FROM photos WHERE archive_path = ?''', [filepath]) self.assertEquals(None, rows.fetchone()) rep.close() finally: shutil.rmtree(tmpdir)
def test_query_all(self): (srcdir, mediadir, tmpdir) = self._setup_test_data() try: photoman._find_and_archive_photos(srcdir, mediadir, True, 'foo') rep = media_common.Repository() rep.open(mediadir) cur = rep.con.cursor() cur.execute(''' select id, archive_path FROM photos''') rows = 0 for row in cur: rows += 1 self.assertEquals(5, rows) cur = rep.con.cursor() ids = [1, 5] cur.execute( ' DELETE from photos where id in (' + ','.join('?' * len(ids)) + ')', ids) cur.execute(''' select id, archive_path FROM photos''') rows = 0 for row in cur: rows += 1 self.assertEquals(3, rows) rep.close() finally: shutil.rmtree(tmpdir)
def test_close(self): rep = media_common.Repository() connection = MagicMock() rep.con = connection rep.close() self.assertEquals(None, rep.con, 'expect connection removed') self.assertEquals(call.commit(), connection.mock_calls[0]) self.assertEquals(call.close(), connection.mock_calls[1])
def test_remove(self): rep = media_common.Repository() rep.con = MagicMock() execute = rep.con.cursor.return_value.execute photo = MagicMock() photo.md5 = 42 rep.remove(photo) execute.assert_called_with(ANY, [42])
def test_add_or_update(self): rep = media_common.Repository() photo = Mock() rep.con = Mock() rep.con.cursor.return_value.lastrowid = 42 self.assertEquals(42, rep.add_or_update(photo)) self.assertTrue(rep.con.cursor.called) self.assertTrue(rep.con.cursor.return_value.execute.called) self.assertTrue(rep.con.commit.called)
def _scan_missing_photos(lib_base_dir): """Removes photos from the repository that don't exist in the archive""" rep = media_common.Repository() try: rep.open(lib_base_dir) missing_files = [] for (db_id, filepath) in rep.iter_all_photos(): if not os.path.isfile(filepath): logging.warning('The photo %s was deleted from the ' + 'archive unexpectedly. It will be removed from ' + 'the database.', filepath) missing_files.append(db_id) rep.remove_photos(missing_files) finally: rep.close()
def test_remove_photos(self): rep = media_common.Repository() rep.con = MagicMock() execute = rep.con.cursor.return_value.execute rep.remove_photos([1, 4]) execute.assert_called_with(ANY, [1, 4])
def test_lookup_hash(self): rep = media_common.Repository() rep.con = MagicMock() fetch_mock = rep.con.cursor.return_value.execute.return_value.fetchone fetch_mock.return_value = (42, '/tmp/foo') self.assertEquals('/tmp/foo', rep.lookup_hash('bar')[1])
def _execute_tester(self, method): rep = media_common.Repository() rep.con = MagicMock() rep.con.cursor.return_value.execute.return_value = [1, 4] self.assertEquals(rep.con.cursor.return_value, method(rep)) self.assertTrue(rep.con.cursor.return_value.execute.called)
def test_tree_setup(self, mkdir): media_common.Repository()._tree_setup('/tmp/foo') self.assertEquals(2, mkdir.call_count)
def setUp(self): self.rep = media_common.Repository() pass
def test_init_db(self): rep = media_common.Repository() cur = Mock() rep._init_db(cur) self.assertTrue(cur.execute.called)
def test_get_db_name(self): rep = media_common.Repository() self.assertEquals('media.db', rep._get_db_name())
def test_lookup_hash_not_found(self): rep = media_common.Repository() rep.con = MagicMock() fetch_mock = rep.con.cursor.return_value.execute.return_value.fetchone fetch_mock.return_value = None self.assertEquals(None, rep.lookup_hash('bar'))
def _find_and_archive_photos(search_dir, lib_base_dir, delete_source_on_success, group_name): """Sets up or opens a media library and adds new photos to the library and its database. The source image files will be deleted if --del_src is specified. """ rep = media_common.Repository() rep.open(lib_base_dir) paths = os.walk(search_dir) results = [] group_id = media_common.get_group_id(group_name) files_to_delete = [] archive_count = 0 for (dirpath, dirnames, filenames) in paths: for filename in filenames: path = os.path.join(dirpath, filename) if os.path.isfile(path): photo = media_common.Photo(path) photo.load_metadata() db_result = rep.lookup_hash(photo.md5) if (db_result is not None and os.path.abspath(db_result[1]) == os.path.abspath(path)): logging.info('Found existing archived photo %s, ignoring', db_result[1]) elif (db_result is not None and os.path.isfile(db_result[1]) and delete_source_on_success): # file is a duplicate and the original is still around logging.info('Deleting the source file %s, which is a ' + 'duplicate of existing file %s', photo.source_path, db_result[1]) os.remove(photo.source_path) elif (db_result is not None and os.path.isfile(db_result[1])): # same as above, but client didn't request deletion logging.info('Ignoring the source file %s, which is a ' + 'duplicate of existing file %s', photo.source_path, db_result[1]) elif db_result is not None: # file was deleted from archive, remove it from repository logging.info('Photo %s was deleted from the archive, replacing' + ' it with the new one.', db_result[1]) if (_archive_photo(photo, lib_base_dir, rep, group_id) and delete_source_on_success): files_to_delete.append(photo.source_path) else: archive_count += 1 if (_archive_photo(photo, lib_base_dir, rep, group_id) and delete_source_on_success): files_to_delete.append(photo.source_path) else: logging.warning('Found a non-file when looking for photos: %s, ' + 'it will not be modified', path) rep.close() for filepath in files_to_delete: os.remove(filepath) logging.info('Successfully completed archiving %d files', archive_count)
gflags.DEFINE_string( 'group_name', '', 'The name of the group to use' + ' for the daemon process') gflags.DEFINE_string('user_name', '', 'The name of the user to use' + ' for the daemon process') gflags.DEFINE_string('media_dir', None, 'Directory of media library') gflags.DEFINE_boolean('daemon', True, 'Run the server as a daemon') gflags.MarkFlagAsRequired('media_dir') gflags.MarkFlagAsRequired('group_name') gflags.MarkFlagAsRequired('user_name') FLAGS = gflags.FLAGS repository = media_common.Repository() def run_server(lib_base_dir): repository.open(lib_base_dir) logging.info('opened repository at %s', lib_base_dir) server = SimpleXMLRPCServer(('192.168.1.27', 9333), logRequests=True) server.register_function(check_file_hash) try: print 'Use Control-C to exit' server.serve_forever() except KeyboardInterrupt: print 'Exiting' def check_file_hash(hash):
def test_iter_all_photos(self): rep = media_common.Repository() rep.con = MagicMock() rep.con.cursor.return_value.execute.return_value = [1, 4] self.assertEquals(rep.con.cursor.return_value, rep.iter_all_photos()) self.assertTrue(rep.con.cursor.return_value.execute.called)