-- | The coreModule is the core of the Programm, it handles core functionality. -- this functionallity is in reading file indexes of directories, comapring the files read, and deleting or moving files. -- to be more concrete, the core reads (and filters) a directory containing so called jpeg files and one containing so called raw files -- then it comapares the two lists (jpeg list and raw list). in order to find all raw files, that have no matching jpeg file. (called differences) -- in the last step, the core, depending on the settings, deletes or moves all differences. -- Info: For simplicitie's sake the files matching the rawEnding of the settings are -- calleds raw files and the files matching the jpegEnding are called jpeg files even they could have any ending and could be files of any type. module Core(PhotoSetting (..), PhotoFile(..), loadAndGetDifference, deleteDifferenceFiles, moveDifferenceFilesToBin, toPhotoFile, concatFilePath, getFilepathsFromPhotoFiles, addPathToPhotoFiles) where import System.Directory import System.FilePath import Control.Monad import Data.List import Data.Char -- | data PhotoFile -- This data represents a Photo File, or generally spoken a File. data PhotoFile = PhotoFile { path :: String -- ^ the Path where te File is located ex.: \/home\/photos , fileName :: String -- ^ the name of the File ex.: picture , fileExtension :: String -- ^ the fileextension ex.: .jpg } deriving (Show) -- | in this instance of eq, two PhotoFiles are equal, iff their names are equal. instance Eq PhotoFile where PhotoFile a1 b1 c1 == PhotoFile a2 b2 c2 = b1 == b2 instance Ord PhotoFile where PhotoFile a1 b1 c1 <= PhotoFile a2 b2 c2 = b1 <= b2 -- | data PhotoSetting -- PhotoSetting is a Data structure, that reprensents parameters -- which have to be set, bevore the Programm can do its job. data PhotoSetting = PhotoSetting { jpegPath :: String -- ^ the Path where the Jpegs are located. (the path with the less ammount of files.) , rawPath :: String -- ^ the Path where the Raw Files are located. (the path, containing some files to move or delete.) , binPath :: String -- ^ the Path where Raw files, should be moved to. , deleteFiles :: Bool -- ^ a flag. if True, the files will be deleted. If False, the files will be moved to the specified binPath. , rawEnding :: String -- ^ the file extension of the files, which should be classified as \"Raw files\" for example \".CR2\" , jpegEnding :: String -- ^ the filex extension of the files, which should be classified as \"Jpeg file\" for example \".jpg\" } deriving (Eq) -- | this instance of show, formats the Photosettings String to make it appear more beautiful. instance Show PhotoSetting where show (PhotoSetting a b c d e f) = concat [ "PhotoSettings: \r\n" , "jpegPath: "++ show a ++ "\r\n" , "rawPath: "++ show b ++ "\r\n" , "binPath: "++ show c ++ "\r\n" , "jpegEnding: "++ show f ++ "\r\n" , "rawEnding: "++ show e ++ "\r\n" , "deleteFiles: "++ show d ++ "\r\n"] -- | toPhotoFiles gets a FilePath aka String, which contains the Path, the Filename and the fileextension. -- then it cuts the Path in three Parts and returns a PhotoFile. toPhotoFile :: FilePath -- ^ a FilePath containing the Path, a Filename and a File extension. -> PhotoFile -- ^ a PhotoFile Representation generated from the Path. toPhotoFile file = PhotoFile{path = (dropFileName file), fileName = (takeBaseName file), fileExtension = (takeExtension file)} -- | concatFilePath is the reverse function of toPhotoFile. -- It needs a PhotoFile, and concats its part to a fully qualified FilePath, with path, filename and file extension. concatFilePath :: PhotoFile -- ^ a Photofile Representation -> FilePath -- ^ The FilePath, representing the Photofile. concatFilePath photo = path photo ++ fileName photo ++ fileExtension photo -- | loadAndGetDifference receives PhotoSettings. -- at first it load both, in the settings specified paths (rawPath and jpegPath) -- then it creates PhotoFiles out of the FileLists and filters them, using the -- raw- and jpegending, also specified in the settings. -- then it compares both lists, and returns a list of all \"Raw Files\", that have no -- counterpart in the \"Jpeg Files\" list. loadAndGetDifference :: PhotoSetting -- ^ the PhotoSettings, containing all relevant paths. -> IO [PhotoFile] -- ^ a List of \"Raw\" PhotoFiles, which have not counterpart in the "Jpeg" File list. loadAndGetDifference settings = do let jPath = jpegPath settings let rPath = rawPath settings jpegs <- loadPhotoFiles jPath (jpegEnding settings) raws <- loadPhotoFiles rPath (rawEnding settings) let jpegFotoFile = addPathToPhotoFiles jpegs jPath let rawFotoFile = addPathToPhotoFiles raws rPath return $ rawFotoFile \\ jpegFotoFile -- \\ returns all files that are in rawFotoFile but not in jpegFotoFile -- | loadPhotoFiles receives a FilePath and a Fileextension -- and retunrs a list of all files, in the specified path with the specified extension. loadPhotoFiles :: FilePath -- ^ the FilePath to read / load from. -> String -- ^ the File extension of the considered files. -> IO [PhotoFile] -- ^ a List of PhotoFiles (Files), which are located in the specified path. loadPhotoFiles path extension = do files <- listDirectory path return $ filter (\x -> map toLower (fileExtension x) == extension)(map toPhotoFile files) -- | deleteDifferenceFiles -- gets some PhotoSettings. At first it loads from the specific paths, -- and calculates the Raw Files, wich have no counterpart in the Jpeg List -- then it deletes the calculated Raw Files. deleteDifferenceFiles :: PhotoSetting -- ^ PhotoSettings, containing the relevant Paths and extensions. -> IO [PhotoFile] -- ^ A List of PhotoFiles, that have been deleted. deleteDifferenceFiles settings = do pfiles <- loadAndGetDifference settings let paths = getFilepathsFromPhotoFiles pfiles deleteListOfFiles paths return pfiles -- | moveDifferenceFilesToBin -- gets some PhotoSettings. At first it loads from the specific paths, -- and calculates the Raw Files, wich have no counterpart in the Jpeg List -- then it moves the calculated Raw Files into the specified binPath moveDifferenceFilesToBin :: PhotoSetting -- ^ PhotoSettings, containing the relevant Paths and extensions. -> IO [PhotoFile] -- ^ a List of PhotoFiles, that have been moved to the binPath. moveDifferenceFilesToBin settings = do pfiles <- loadAndGetDifference settings let npfiles = addPathToPhotoFiles pfiles (binPath settings) let oldPaths = sort $ getFilepathsFromPhotoFiles pfiles let newPaths = sort $ getFilepathsFromPhotoFiles npfiles createDirectoryIfMissing True (binPath settings) moved <- moveListOfFiles (oldPaths,newPaths) if (length (fst moved) == 0) then return [] else return pfiles -- | moveListOfFiles -- gets a list of Filepaths (old), contining the current location of the files to move -- and a second list of FilePaths (new), containing the destination path of the files. -- then it moves all files from the old to the new paths during a recursion. moveListOfFiles :: ([FilePath],[FilePath]) -- ^ a tuple of old an new file paths. IMPORTANT: -- a file, that has its path in the \"old\" list at index 2, -- needs to have its new Ppath in the \"new\" list also at index 2. -> IO ([FilePath],[FilePath]) -- ^ the tuple of old an new file paths, reduced by the head. moveListOfFiles (oldpaths,newpaths) = do if length oldpaths == length newpaths then do if length oldpaths > 0 then do let oldPath = head oldpaths let newPath = head newpaths moved <- moveListOfFiles (drop 1 oldpaths,drop 1 newpaths) renamePath oldPath newPath return (oldPath : fst moved, newPath : snd moved) else return ([],[]) else return ([],[]) -- | deleteListOfFiles -- gets a list of FilePaths and deletes the addressed files during a recursion deleteListOfFiles :: [FilePath] -- ^ a list, containing filePaths / files to delete. -> IO [FilePath] -- ^ the input list, reduced by its head. deleteListOfFiles paths = do if length paths > 0 then do let toDelete = head paths deleted <- deleteListOfFiles (drop 1 paths) removePathForcibly toDelete return (toDelete : deleted) else return [] -- | getFilepathsFromPhotofile -- gets a list of Photofiles, and produces a list of FilePaths out of it. getFilepathsFromPhotoFiles :: [PhotoFile] -- ^ a list of Photofiles, which are needed the FilePaths from -> [FilePath] -- ^ the list of FilePaths, made out of the list of PhotoFiles. getFilepathsFromPhotoFiles files = map concatFilePath files -- | addPathToPhotoFiles -- gets a list of Photofiles and a FilePath, and sets -- the specified path as path of any photoFile in the list. -- its like an giant "setter" setting the same attribute in all -- objects in the list aka produces a new list with the path as path of each listitem. addPathToPhotoFiles :: [PhotoFile] -- ^ a list of PhotoFiles, where the path sould be set. -> FilePath -- ^ a path to set as path of all Photofiles in the list. -> [PhotoFile] -- ^ a list of Photofiles with the specified path set. addPathToPhotoFiles files path = map (\x -> x {path = path}) files