Delete a folder when a file is moved (use carefully, not fully tested yet) and adds some unittests to the project.

This commit is contained in:
Fred Pauchet 2011-09-27 16:41:18 +02:00
parent 7ac88bb128
commit c7e1f822d4
10 changed files with 248 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
#python specific
*.pyc
## generic files to ignore
*~
*.lock
*.DS_Store
*.swp
*.out

0
CHANGES.txt Normal file
View File

0
LICENCE.txt Normal file
View File

2
MANIFEST.in Normal file
View File

@ -0,0 +1,2 @@
include *.txt
recursive-include docs *.txt

0
pigeonhole/__init__.py Normal file
View File

14
pigeonhole/config.py Normal file
View File

@ -0,0 +1,14 @@
# -*- coding: UTF8 -*-
# Configuration file
### If a folder only contains these types of files, we can delete it.
useless_files_extensions = ('srr', 'nfo', 'sfv')
### Consider only files with these extensions
shows_extensions = ('avi', 'mkv')
### Dictionary for special filenames
shows_dict = {
'white collar' : ['wc'],
'the big bang theory' : ['tbbt']
}

132
pigeonhole/pigeonhole.py Normal file
View File

@ -0,0 +1,132 @@
#encoding: utf-8
import os
import re
import shutil
import filecmp
import config
class Folder(object):
""" Directory show instanciation, relative to a path on the disk
ie. Show name
- Season 1
- Season 2
- ...
"""
directory = None
name = None
def __init__(self, path):
self.directory = path;
self.name = os.path.basename(self.directory)
def __str__(self):
return self.name + ' [' + self.directory + ']'
class Show(object):
""" Represents a show file; ie. a file associated to its fullname """
path = None
name = None
def __init__(self, fullname, filename):
self.path = fullname
self.name = filename
def __str__(self):
return self.name
class PigeonHole(object):
""" Takes all the media files in a (download) folder and sort
them into the corresponding folder, based on the found file name
"""
directories = None
series = None
matches = None
downloadDir = ""
rootShows = ""
def __init__(self, root, downloaddir):
self.downloadDir = downloaddir
self.rootShows = root
self.directories = os.listdir(self.rootShows)
self.series = list()
def walk(self):
""" Walks through the downloaded folders and yields .avi and .mkv files """
for root, dirs, files in os.walk(self.downloadDir):
for filename in files:
if filename.endswith(config.shows_extensions):
yield Show(os.path.join(root, filename), filename)
def walk(self, foldername, extensions):
for root, dirs, files in os.walk(foldername):
for filename in files:
if not filename.endswith(extensions):
yield os.path.join(root, filename)
def process(self):
""" Parses the directories within the 'rootShows' folder and stores them as shows in a list. """
self.series = [ Folder(os.path.join(self.rootShows, x)) for x in self.directories]
for path in self.walk(config.shows_extensions):
self.moveToFolder(path)
def moveToFolder(self, show):
""" Moves a specific show to its right folder. """
destinationfile = self.findFolder(show)
if destinationfile is not None:
self.move(show.path, destinationfile)
if self.isDeletable(show.path):
shutil.rmtree(show.path)
def findFolder(self, show):
"""Finds and returns the complete destinationpath for a specific show."""
rx = re.compile('\W+')
result = rx.sub(' ', show.name.lower()).strip()
for s in self.series:
if s.name.lower() in result:
return os.path.join(s.directory, show.name)
def move(self, originalfile, destinationfile):
""" Moves the downloaded file to the found folder. """
print "Moving " + show.name + " to " + destinationfile
shutil.move(originalfile, destinationfile)
def isDeletable(self, foldername):
""" Walks through the current directory and deletes it if nothing's really important in it
ie. .nfo, .srr or .sfv files.
"""
if foldername is None:
return False
if foldername == self.downloadDir or foldername == self.rootShows:
return False
if self.downloadDir in foldername or self.rootShows in foldername:
return False
if sum(1 for x in self.walk(foldername, config.useless_files_extensions)) is 0:
return True
return False
def __str__(self):
return 'PigeonHole module'
def __name__(self):
return 'PigeonHole'
if __name__ == "__main__":
pHole = PigeonHole(r'C:\test', r'C:\temp')
pHole.process()

View File

View File

@ -0,0 +1,76 @@
import random
import unittest
import tempfile
import shutil
import os
from pigeonhole import PigeonHole
import config
class TestPigeonHoleFunctions(unittest.TestCase):
"""Test the methods defined inside the PigeonHole class"""
def setUp(self):
"""Set up the test environment"""
self.rootdir = tempfile.mkdtemp(prefix='pigeonHole_root_')
self.downloaddir = tempfile.mkdtemp(prefix='pigeonHole_dl_dir_')
# Create an environment with three folders
os.mkdir(os.path.join(self.rootdir, 'White Collar'))
os.mkdir(os.path.join(self.rootdir, 'The Big Bang Theory'))
os.mkdir(os.path.join(self.rootdir, 'Being Erica'))
self.pigeonHole = PigeonHole(self.rootdir, self.downloaddir)
self.notDeletableTmpDir = tempfile.mkdtemp(prefix='pigeonHole_')
self.deletableTmpDir = tempfile.mkdtemp(prefix='pigeonHole_')
def tearDown(self):
"""Tear down the test environment"""
self.pigeonHole = None
shutil.rmtree(self.notDeletableTmpDir)
shutil.rmtree(self.deletableTmpDir)
shutil.rmtree(self.rootdir)
shutil.rmtree(self.downloaddir)
def test_init(self):
""" Testing the constructor """
self.assertEqual(self.pigeonHole.rootShows, self.rootdir)
self.assertEqual(self.pigeonHole.downloadDir, self.downloaddir)
self.assertTrue(str(self.pigeonHole) == 'PigeonHole module', 'The module string is not correct.')
self.assertTrue(str(self.pigeonHole.__name__ == 'PigeonHole'), 'The module name is not correct.')
def test_clean(self):
"""Testing the cleaning method"""
self.generatedfiles_bad = list()
self.generatedfiles_good = list()
for x in config.useless_files_extensions + config.shows_extensions:
fd, temppath = tempfile.mkstemp(x, 'tmp', self.notDeletableTmpDir)
self.generatedfiles_bad.append(temppath)
os.close(fd)
for y in config.useless_files_extensions:
fd, temppath = tempfile.mkstemp(y, 'tmp', self.deletableTmpDir)
self.generatedfiles_good.append(temppath)
os.close(fd)
self.assertFalse(self.pigeonHole.isDeletable(self.notDeletableTmpDir))
self.assertTrue(self.pigeonHole.isDeletable(self.deletableTmpDir))
self.assertFalse(self.pigeonHole.isDeletable(self.rootdir))
self.assertFalse(self.pigeonHole.isDeletable(self.downloaddir))
def test_findFolder(self):
"""Try to move a file to a specific location"""
if __name__ == '__main__':
unittest.main()

15
setup.py Normal file
View File

@ -0,0 +1,15 @@
from distutils.core import setup
setup {
name='PigeonHole',
version='0.1.0',
author='Fred Pauchet'
author_email='fpauchet@gmail.com',
packages=['pigeonhole','pigeonhole.test'],
scripts=[],
url='',
licence='LICENCE.txt',
description='',
long_description=long_description=open('README.txt').read(),
install_require=[],
}