bgneal@668: """avatar_cleanup - a management command to remove orphaned avatar files."""
bgneal@668: 
bgneal@668: from optparse import make_option
bgneal@668: import os
bgneal@668: 
bgneal@668: from django.core.management.base import NoArgsCommand
bgneal@668: from django.conf import settings
bgneal@668: 
bgneal@668: from bio.models import UserProfile
bgneal@668: 
bgneal@668: 
bgneal@668: class Command(NoArgsCommand):
bgneal@668:     help = "Finds and optionally deletes orphan avatar files"
bgneal@668:     option_list = NoArgsCommand.option_list + (
bgneal@668:             make_option('--delete',
bgneal@668:                 action='store_true',
bgneal@668:                 dest='delete',
bgneal@668:                 default=False,
bgneal@668:                 help='Delete orphan files'),
bgneal@668:     )
bgneal@668: 
bgneal@668:     def handle_noargs(self, **options):
bgneal@668:         """Find and optionally delete orphan avatar files."""
bgneal@668: 
bgneal@668:         delete = options.get('delete', False)
bgneal@668: 
bgneal@668:         orphans = set()
bgneal@668:         missing = []
bgneal@668: 
bgneal@668:         avatar_dir = os.path.join(settings.MEDIA_ROOT, settings.AVATAR_DIR)
bgneal@668:         default_avatar = os.path.join(avatar_dir,
bgneal@668:                 settings.AVATAR_DEFAULT_URL.split('/')[-1])
bgneal@668: 
bgneal@668:         # find the set of all files in the downloads area
bgneal@668:         for root, dirs, files in os.walk(avatar_dir, followlinks=True):
bgneal@668:             for name in files:
bgneal@668:                 orphans.add(unicode(os.path.join(root, name), 'utf-8'))
bgneal@668: 
bgneal@668:         try:
bgneal@668:             orphans.remove(default_avatar)
bgneal@668:         except KeyError:
bgneal@668:             self.stderr.write("Default avatar file missing!  - {}\n".format(
bgneal@668:                 default_avatar))
bgneal@668: 
bgneal@668:         # examine the UserProfile objects
bgneal@668:         for profile in UserProfile.objects.iterator():
bgneal@668:             if profile.avatar:
bgneal@668:                 try:
bgneal@668:                     orphans.remove(profile.avatar.path)
bgneal@668:                 except KeyError:
bgneal@668:                     missing.append(profile)
bgneal@668: 
bgneal@668:         if orphans and delete:
bgneal@668:             for path in orphans:
bgneal@668:                 self.stdout.write("Deleting: {}\n".format(path))
bgneal@668:                 os.remove(path)
bgneal@668:         elif orphans:
bgneal@668:             self.stdout.write("Orphan files:\n")
bgneal@668:             for orphan in orphans:
bgneal@668:                 self.stdout.write("{}\n".format(orphan))
bgneal@668: 
bgneal@668:         if missing:
bgneal@668:             self.stdout.write("Users with missing avatar files:\n")
bgneal@668:             for profile in missing:
bgneal@668:                 self.stdout.write("{}\n".format(profile.user.username))
bgneal@668: 
bgneal@668:         empty_dirs = []
bgneal@668:         # check for empty directories after deletions
bgneal@668:         for root, dirs, files in os.walk(avatar_dir, followlinks=True):
bgneal@668:             if not len(dirs) and not len(files):
bgneal@668:                 empty_dirs.append(root)
bgneal@668: 
bgneal@668:         if empty_dirs and delete:
bgneal@668:             for path in empty_dirs:
bgneal@668:                 self.stdout.write("Deleting empty dir: {}\n".format(path))
bgneal@668:                 os.removedirs(path)
bgneal@668:         elif empty_dirs:
bgneal@668:             self.stdout.write("Empty directories:\n")
bgneal@668:             for d in empty_dirs:
bgneal@668:                 self.stdout.write("{}\n".format(d))