Learning Python #3

A little while ago I discovered the concept of List Comprehension. Once I understood it, I was quite eager to implement it in my first script for this series of posts about learning python.

Motivation

A nagging feeling tells me that the scripts is a bit – or maybe a lot – to long for its purpose.

So, how hard can it be to use “List Comprehension” to mitigate the issue. Spoiler alert. It is not that hard.

First code snippet

First snippet to “fix”:

year_list = list(range(1900, 2100))

years_list = list()
for y in year_list:
    years_list.append(str(y))

Transformed into a List Comprehension, this becomes:

years_list = [str(y) for y in list(range(1900, 2100))]

Refactoring 4 lines into 1 for a newbie is not bad at all, right?

Second code snippet

I found another wonky implementation of a list in my code, that I wanted to change.

work_dir_content = os.listdir(work_dir)
files_list = list()
for fl in work_dir_content:
    sfl = fl.split('-')
    if os.path.isfile(os.path.join(work_dir, fl)):
    if sfl[0] in string_year_list and sfl[1] in months:
        files_list.append(fl)

Unfortunately, with all the linting and Flake8 rules, the snippet did not get shorter but longer, which I decided to keep, because I could practise functions and List Comprehension at the same time.

The result…

def fList(fl):
    sfl = fl.split('-')
    if os.path.isfile(os.path.join(work_dir, fl)):
        if sfl[0] in years_list and sfl[1] in months:
            return fl


files_list = [fList(f) for f in os.listdir(work_dir) if fList(f) is not None]

… is not bad either, am I right? Don’t answer that.

The new complete code

The entire code now looks like:

#!/usr/bin/python3

import os

# 1. Variables for needed paths.
home_dir = os.environ['HOME']
work_dir = os.path.join(home_dir, 'projects/python/file-sorting/inbox')
dest_dir = os.path.join(home_dir, 'projects/python/file-sorting/sorted')


# 2. Check if files have date in name. Put those with date in a list.
years_list = [str(x) for x in list(range(1900, 2100))]

months = ['01', '02', '03', '04', '05',
          '06', '07', '08', '09', '10', '11', '12']


def fList(fl):
    sfl = fl.split('-')
    if os.path.isfile(os.path.join(work_dir, fl)):
        if sfl[0] in years_list and sfl[1] in months:
            return fl


files_list = [fList(f) for f in os.listdir(work_dir) if fList(f) is not None]
print(files_list)


# 3. Functions for creating class attributes.
def fInfo(file):
    fyear = file.split('-')[0]
    fmonth = file.split('-')[1]
    fsrc = os.path.join(work_dir, file)
    fdest = os.path.join(os.path.join(dest_dir, fyear, fmonth), file)
    return [fyear, fmonth, fsrc, fdest]


# 4. Class for the files.
class FilesToMove:
    def __init__(self, file_year, file_month, file_src, file_dest):
        self.file_year = file_year
        self.file_month = file_month
        self.file_src = file_src
        self.file_dest = file_dest

    def moveFile(self):
        os.rename(self.file_src, self.file_dest)


# 3. Check for existance for Destination directory.
# If it exists, move the file. If it does not, create it and move file.
if __name__ == '__main__':
    for f in files_list:
        f = FilesToMove(fInfo(f)[0], fInfo(f)[1], fInfo(f)[2], fInfo(f)[3])
        if os.path.isdir(os.path.dirname(f.file_dest)):
            f.moveFile()
        else:
            os.makedirs(os.path.dirname(f.file_dest))
            f.moveFile()

Hmmm, I did not manage to get the script to be shorter, but I learned one or two things along the was, which is the whole purpose of this.

Next steps for this script

  • Add conditions to avoid duplicates
  • Use “sys.argv” to get the source path from the cli instead of hard coding it.
  • Add an option to change the creation date of a file.
  • Add an option to add the date to the file name.
  • What ever else I will come up with