Skip to content

Commit

Permalink
Added pytest-tests for imagerenaming.
Browse files Browse the repository at this point in the history
  • Loading branch information
EddyXorb committed Nov 6, 2022
1 parent 15966ab commit 5bbcb3f
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 27 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
__pycache*
.vscode*
.vscode*
test/test_renamed
Empty file added __init__.py
Empty file.
10 changes: 8 additions & 2 deletions image/imagehelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@
def getExifDateFrom(file: str) -> dt.datetime:
img = Image.open(file)
img_exif = img.getexif()

exifvalueOriginalCreation = 36867
exifvalueChangedDate = 306
if img_exif is None:
raise Exception("Sorry, image has no exif data: ", file)
elif exifvalueOriginalCreation not in img_exif and exifvalueChangedDate not in img_exif:
raise Exception(f"Sorry, image has exif data but it's creation date value is not present")
else:
date = img_exif[306]
if exifvalueOriginalCreation in img_exif:
date = img_exif[exifvalueOriginalCreation]
else:
date = img_exif[exifvalueChangedDate]
return dt.datetime.strptime(date, "%Y:%m:%d %H:%M:%S")
69 changes: 49 additions & 20 deletions image/imagerenamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import os
from os.path import join, splitext
import datetime as dt
from typing import List
from typing import List, Dict
from pathlib import Path

from image.imagefile import ImageFile
from image.imagehelper import getExifDateFrom
from image.imagefile import ImageFile
from general.verboseprinterclass import VerbosePrinterClass
from .imagefile import ImageFile
from .imagehelper import getExifDateFrom
from .imagefile import ImageFile
from ..general.verboseprinterclass import VerbosePrinterClass

#%%
class ImageRenamer(VerbosePrinterClass):
Expand All @@ -17,11 +18,13 @@ class ImageRenamer(VerbosePrinterClass):
recursive : if true, dives into every subdir to look for image files
move : if true, moves files, else copies them
restoreOldNames : inverts the renaming logic to simply remove the timestamp prefix.
maintainFolderStructure: if recursive is true will rename subfolders into subfolders, otherwise all files are put into root repo of dest
dry: don't actually rename files
"""

def getNewImageFileNameFor(file: str) -> str:
date = getExifDateFrom(file)
prefixDate = f"{date:%Y-%m-%d_%H.%M.%S}"
prefixDate = f"{date:%Y-%m-%d@%H%M%S}"
return os.path.join(
os.path.dirname(file), prefixDate + "_" + os.path.basename(file)
)
Expand All @@ -34,23 +37,31 @@ def __init__(
move: bool = False,
restoreOldNames=False,
verbose=False,
maintainFolderStructure=True,
dry=False,
):
super().__init__(verbose)
self.src = os.path.abspath(src)
self.dst = os.path.abspath(dst)
self.recursive = recursive
self.move = move
self.maintainFolderStructure = maintainFolderStructure
self.dry = dry

self.restoreOldNames = restoreOldNames
self.skippedfiles: List[str] = []
self.toTreat: List[ImageFile] = []
self.oldToNewMapping: Dict[str, str] = {} # always jpg-names
self.treatedfiles = 0

def __call__(self):
self.printv("Start renaming from source ", self.src, " into ", self.dst)

self.createDestinationDir()
self.collectImagesToTreat()
self.treatImages()
self.createNewNames()

self.executeRenaming()

self.printStatistic()

Expand All @@ -66,17 +77,29 @@ def collectImagesToTreat(self):
continue

for file in files:
ifile = ImageFile(join(root, file))
path = Path(join(root, file))
ifile = ImageFile(str(path))
if not ifile.isValid():
continue
self.toTreat.append(ifile)

def treatImages(self):
def createNewNames(self):
for im in self.toTreat:
oldJpgName = im.getJpg()
newJpgName = self.getRenamedFileFrom(im.getJpg())
if newJpgName is None:
self.skippedfiles.append(oldJpgName)
continue

self.oldToNewMapping[oldJpgName] = newJpgName

def executeRenaming(self):
for im in self.toTreat:
if im.getJpg() in self.skippedfiles:
continue

newName = self.getRenamedFileFrom(im.getJpg())
if newName is None:
self.skippedfiles.append(im.getJpg())
newName = self.oldToNewMapping[im.getJpg()]
if self.dry:
continue

if self.move:
Expand All @@ -88,26 +111,32 @@ def treatImages(self):

def getRenamedFileFrom(self, file: str) -> str:
newName = ""

if self.restoreOldNames:
splitted = os.path.basename(file).split("_")
if len(splitted) != 2:
return None
newName = join(self.dst, splitted[1])
else:
if self.filewasalreadyrenamed(file):
if self.fileWasAlreadyRenamed(file):
return None
newName = join(
self.dst, os.path.basename(ImageRenamer.getNewImageFileNameFor(file))
)

newFileName = os.path.basename(ImageRenamer.getNewImageFileNameFor(file))
if self.maintainFolderStructure:
newName = join(
self.dst,
join(str(Path(file).relative_to(self.src).parent), newFileName),
)
else:
newName = join(self.dst, newFileName)

if os.path.exists(newName):
self.printv("File", newName, "already exists. Skip this one.")
self.skippedfiles.append(file)
self.printv("File", newName, "already exists.")
return None

return newName

def filewasalreadyrenamed(self, file: str):
def fileWasAlreadyRenamed(self, file: str):
if "_" in file and ".T." in file:
self.printv(
"Skip file ",
Expand Down
6 changes: 3 additions & 3 deletions image/imagesearcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from typing import DefaultDict, Dict, List
from collections import defaultdict

from general.verboseprinterclass import VerbosePrinterClass
from image.imagefile import ImageFile
from image.imagerenamer import ImageRenamer
from ..general.verboseprinterclass import VerbosePrinterClass
from .imagefile import ImageFile
from .imagerenamer import ImageRenamer


class ImageSearcher(VerbosePrinterClass):
Expand Down
Empty file added test/__init__.py
Empty file.
137 changes: 137 additions & 0 deletions test/test_imagerenamer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from datetime import datetime
from ..image.imagerenamer import *
import shutil
from os.path import join

tempsrcfolder = "filestorename"
src = os.path.abspath(join("test", tempsrcfolder))
dst = os.path.abspath("./test/test_renamed")
targetDir = join(dst, "subsubfolder")
srcfile = join(src, "subsubfolder", "test3.JPG")


def prepareTest():
shutil.rmtree(src, ignore_errors=True)
shutil.rmtree(dst, ignore_errors=True)
os.makedirs(os.path.dirname(srcfile))
shutil.copy(
join("test", "test3.JPG"),
srcfile,
)


def test_subfolderaremaintained():
prepareTest()

renamer = ImageRenamer(
src,
dst,
recursive=True,
move=False,
restoreOldNames=False,
verbose=True,
maintainFolderStructure=True,
dry=True,
)
renamer()
print(renamer.oldToNewMapping)

assert len(renamer.toTreat) == 1
for old, new in renamer.oldToNewMapping.items():
print(old, new)
assert os.path.dirname(srcfile) in old
assert join("test", "test_renamed", "subsubfolder") in new


def test_copyworks():
prepareTest()

assert (
len(os.listdir(join(src, "subsubfolder"))) == 1
) # source should contain only one file

renamer = ImageRenamer(
src,
dst,
recursive=True,
move=False,
restoreOldNames=False,
verbose=True,
maintainFolderStructure=True,
dry=False,
)

renamer()

assert (
len(os.listdir(join(src, "subsubfolder"))) == 1
) # source should still contain only one file
assert len(os.listdir(targetDir)) == 1 # should contain a new file after copying


def test_moveworks():
prepareTest()

assert (
len(os.listdir(join(src, "subsubfolder"))) == 1
) # source should contain only one file

renamer = ImageRenamer(
src,
dst,
recursive=True,
move=True,
restoreOldNames=False,
verbose=True,
maintainFolderStructure=True,
dry=False,
)

renamer()

assert (
len(os.listdir(os.path.dirname(srcfile))) == 0
) # source should still contain only one file
assert len(os.listdir(targetDir)) == 1 # should contain a new file after copying


def test_nonemptyDestinationIsNoProblem():
prepareTest()
os.makedirs(dst, exist_ok=True)
renamer = ImageRenamer(
src,
dst,
recursive=True,
move=True,
restoreOldNames=False,
verbose=True,
maintainFolderStructure=True,
dry=False,
)
renamer()

assert len(os.listdir(targetDir)) == 1 # should contain a new file after copying

def test_timeStampIsCorrect():
prepareTest()

os.makedirs(dst, exist_ok=True)
renamer = ImageRenamer(
src,
dst,
recursive=True,
move=True,
restoreOldNames=False,
verbose=True,
maintainFolderStructure=True,
dry=False,
)
renamer()

for old,new in renamer.oldToNewMapping.items():
timestamp = os.path.basename(new).split("_")[0] #we assume YYYY-MM-DD@HHMMSS_OLDNAME e.g. "2022-07-27@215555_test3"
assert(len(timestamp) == 17)
dt = datetime.strptime(timestamp,"%Y-%m-%d@%H%M%S")



2 changes: 1 addition & 1 deletion x_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def executeRenaming(args):

ImageRenamer(
args.src, args.dst, args.recursive, args.move, args.invert, args.verbose
)
)()


def setargsforoption_all(args):
Expand Down

0 comments on commit 5bbcb3f

Please sign in to comment.