diff --git a/.project b/.project
new file mode 100644
index 0000000..88fcb7e
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ magazine_renamer
+
+
+
+
+
+ org.python.pydev.PyDevBuilder
+
+
+
+
+
+ org.python.pydev.pythonNature
+
+
diff --git a/.pydevproject b/.pydevproject
new file mode 100644
index 0000000..b201cec
--- /dev/null
+++ b/.pydevproject
@@ -0,0 +1,8 @@
+
+
+
+ /${PROJECT_DIR_NAME}/src
+
+ python interpreter
+ Default
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..4de25ba
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding//src/magazine_renamer.py=utf-8
diff --git a/README.md b/README.md
index 6507675..a443296 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,15 @@
-# magazine-renamer
+# Magazine Renamer
+
+## What?
A small tool to rename IEEE magazines and more.
+
+## Why?
+As I plan to slim down what I store, and organize the remaining files, I wanted to organize the IEEE magazines I received over the years. As a part of this effort, I wanted rename them in bulk. Since some of the effort needed some computation and I failed to find a ready made tool, I've written my own.
+
+## How?
+The script gets a list of files, and runs a series of regular expression matches on them. Since the files are named with a pattern, it can detect the magazine and naming convention. Then it creates the new name and renames the file.
+
+The tool should ignore the files it doesn't know, and shouldn't re-match already renamed files, so it's relatively safe to run it on a very crowded folder, but don't take my word on it, and be cautious. This is why there's a `--simulate` option implemented. Use it. The responsibility is yours. There's no warranty attached.
+
+## Notes
+The tool is coded quickly, so it's neither innovative, nor clever, nor optimized. I may clean the code and polish things as I use it and new features though.
\ No newline at end of file
diff --git a/src/magazine_renamer.py b/src/magazine_renamer.py
new file mode 100755
index 0000000..5f5240b
--- /dev/null
+++ b/src/magazine_renamer.py
@@ -0,0 +1,336 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+'''
+Magazine Renamer - A RegEx based file renamer for IEEE Magazines and more.
+Copyright (C) 2022 Hakan Bayindir
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+Created on 17 April 2022
+
+@author: Hakan Bayindir
+'''
+
+# Core imports goes here.
+import sys
+import os
+import os.path
+import re
+
+# Utility imports are next.
+import argparse
+import logging
+
+# Program wide defaults. Change the way you like.
+LOGGING_LEVEL = logging.INFO
+LOGFILE_PATH = None
+
+'''
+@summary: A dead-simple, naive short month name to month number conversion function.
+@warning: This function is no way optimized.
+'''
+def monthStringToNumber(monthString):
+ if monthString.casefold() == 'jan'.casefold() or monthString.casefold() == 'january'.casefold():
+ return '01'
+
+ elif monthString.casefold() == 'feb'.casefold() or monthString.casefold() == 'february'.casefold():
+ return '02'
+
+ elif monthString.casefold() == 'mar'.casefold() or monthString.casefold() == 'march'.casefold():
+ return '03'
+
+ elif monthString.casefold() == 'apr'.casefold() or monthString.casefold() == 'april'.casefold():
+ return '04'
+
+ elif monthString.casefold() == 'may'.casefold():
+ return '05'
+
+ elif monthString.casefold() == 'jun'.casefold() or monthString.casefold() == 'june'.casefold():
+ return '06'
+
+ elif monthString.casefold() == 'jul'.casefold() or monthString.casefold() == 'july'.casefold():
+ return '07'
+
+ elif monthString.casefold() == 'aug'.casefold() or monthString.casefold() == 'august'.casefold():
+ return '08'
+
+ elif monthString.casefold() == 'sep'.casefold() or monthString.casefold() == 'september'.casefold():
+ return '09'
+
+ elif monthString.casefold() == 'oct'.casefold() or monthString.casefold() == 'october'.casefold():
+ return '10'
+
+ elif monthString.casefold() == 'nov'.casefold() or monthString.casefold() == 'november'.casefold():
+ return '11'
+
+ elif monthString.casefold() == 'dec'.casefold() or monthString.casefold() == 'december'.casefold():
+ return '02'
+
+ else:
+ return '00'
+
+'''
+@summary: A dead-simple, naive short edition to human readable edition string conversion function.
+@warning: This function is no way optimized.
+'''
+def convertEditionString(editionString):
+ if editionString.casefold() == 'INT'.casefold():
+ return 'International'
+
+ elif editionString.casefold() == 'NA'.casefold():
+ return 'North America'
+
+if __name__ == '__main__':
+
+ # Let's parse some arguments.
+ argumentParser = argparse.ArgumentParser()
+
+ argumentParser.add_argument ('file', metavar='FILE', help='File(s) to be renamed.', type=str, nargs='+')
+
+ argumentParser.add_argument ('-s', '--simulate', help='Do not actually rename the files, but show actions and quit.', action='store_true', default=False)
+
+ # Verbosity settings are implemented as an mutually exclusive group.
+ verbosityGroup = argumentParser.add_mutually_exclusive_group()
+ verbosityGroup.add_argument ('-v', '--verbose', help='Print more detail about the process. Using more than one -v increases verbosity.', action='count', default=0)
+ verbosityGroup.add_argument ('-q', '--quiet', help='Do not print anything to console (overrides verbose).', action='store_true', default=False)
+
+ # Version always comes last.
+ argumentParser.add_argument ('-V', '--version', help='Print ' + argumentParser.prog + ' version and exit.', action='version', version=argumentParser.prog + ' version 0.0.1')
+
+ arguments = argumentParser.parse_args()
+
+ # At this point we have the required arguments, let's start with logging duties.
+ if arguments.verbose != None:
+ if arguments.verbose == 1:
+ LOGGING_LEVEL = logging.INFO
+ elif arguments.verbose >= 2:
+ LOGGING_LEVEL = logging.DEBUG
+
+ # Let's set the logger up.
+ try:
+ logging.basicConfig(filename=LOGFILE_PATH, level=LOGGING_LEVEL, format='%(levelname)s: %(message)s')
+
+ # Get the local logger and start.
+ localLogger = logging.getLogger('main')
+
+ # Handle the quiet switch here, since it directly affects the logger.
+ if arguments.quiet == True:
+ logging.disable(logging.CRITICAL) # Critical is the highest built-in level. This line disables CRITICAL and below.
+
+ localLogger.debug('Logger setup completed.')
+ localLogger.debug('%s is starting.', sys.argv[0])
+ except IOError as exception:
+ print ('Something about disk I/O went bad: ' + str(exception), file=sys.stderr)
+ sys.exit(1)
+
+ # Let's print some information about what we're going to do.
+ localLogger.debug('Files to rename are: %s', str(arguments.file))
+ localLogger.debug('Total file count: %s', str(len(arguments.file)))
+ localLogger.debug('Simulation state is %s', str(arguments.simulate))
+
+ # We're going to match some regular expressions. We'll compile them here for conciseness.
+ regexIEEESpectrumV1 = re.compile('Spectrum_[\d]{5}_[A-Z][a-z]{2}_[\d]{4}\.PDF')
+ regexIEEESpectrumV2 = re.compile('[\d]{2}_Spectrum_[\d]{4}\.pdf')
+ regexIEEESpectrumV2A = re.compile('[\d]{2}_Spectrum_[\d]{4}[_][A-Z]{2,3}\.pdf')
+ regexIEEESpectrumV2B = re.compile('[\d]{2}_Spectrum_[\d]{4}[\.][A-Z]{2,3}\.pdf')
+ regexIEEESpectrumV2C = re.compile('[\d]{2}_Spectrum_[\d]{2}\.pdf')
+ regexIEEETheInstituteV1 = re.compile('ieee_institute_[a-z]*[\d]{4}\.pdf')
+ regexIEEETheInstituteV1A = re.compile('ieee_institute_[a-z]*_[\d]{4}\.pdf')
+ regexIEEETheInstituteV2 = re.compile('TheInstitute_[A-Z][a-z]{2}_[\d]{4}\.pdf')
+ regexIEEEComputationalIntelligenceV1 = re.compile('CIG_[\d]{8}_[A-Z][a-z]{2}_[\d]{4}\.PDF')
+ regexIEEEComputationalIntelligenceV2 = re.compile('ieee_computationalintelligence_[0-9]{6}\.pdf')
+ regexIEEEPotentialsV1 = re.compile('POT_[\d]{8}_[A-Z][a-z]{2}_[\d]{4}\.PDF')
+
+ # Just allocate a variable for a new file name.
+ newFileName = None
+
+ # Let's start on working files. Get them one by one, and test against RegEx rules.
+ for file in arguments.file:
+ localLogger.debug('Working on file ' + str(file) + '.')
+
+ # Need to divide the path and file itself before diving deeper, and need to preserve that path for merging later.
+ if os.path.isfile(file) == False:
+ localLogger.debug('Path ' + file + ' is not a file, skipping.')
+ continue
+
+ (filePath, fileToRename) = os.path.split(file)
+ (fileName, fileExtension) = os.path.splitext(fileToRename)
+ localLogger.debug('File \'%s\' is located at \'%s\'.', str(fileToRename), str(filePath))
+ localLogger.debug('File name is \'%s\', with extension \'%s\'', fileName, fileExtension)
+
+ #IEEE Spectrum, V1.
+ if regexIEEESpectrumV1.match(fileToRename):
+ localLogger.info('%s is an IEEE Spectrum Magazine file, with V1 format.', fileToRename)
+
+ # First divide file name into its parts via split.
+ fileNameParts = fileName.split('_')
+
+ localLogger.debug('Month to convert is %s.', fileNameParts[2])
+ localLogger.debug('Year is %s.', fileNameParts[3])
+
+ # Let's convert month name to number.
+ monthNumber = monthStringToNumber(fileNameParts[2])
+ localLogger.debug('New month identifier is \'%s\'.', monthNumber)
+
+ # We have everything we need, so assemble the new filename here.
+ newFileName = 'IEEE Spectrum ' + fileNameParts[3] + '-' + monthNumber + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEESpectrumV2.match(fileToRename):
+ localLogger.info('%s is an IEEE Spectrum Magazine file, with V2 format.', fileToRename)
+
+ # This one is actually easy, since we're just going to rearrange some fields together.
+ fileNameParts = fileName.split('_')
+
+ localLogger.debug('Month is %s.', fileNameParts[0])
+ localLogger.debug('Year is %s.', fileNameParts[2])
+
+ # We have everything we need, so assemble the new filename here.
+ newFileName = 'IEEE Spectrum ' + fileNameParts[2] + '-' + fileNameParts[0] + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEESpectrumV2A.match(fileToRename):
+ localLogger.info('%s is an IEEE Spectrum Magazine file, with V2A format.', fileToRename)
+
+ # This is a variation on V2, but contains region information (NA for North America, INT for International).
+ fileNameParts = fileName.split('_')
+
+ localLogger.debug('Month is %s.', fileNameParts[0])
+ localLogger.debug('Year is %s.', fileNameParts[2])
+ localLogger.debug('Edition to convert is %s.', fileNameParts[3])
+
+ longEditionString = convertEditionString(fileNameParts[3])
+ localLogger.debug('New edition string is \'%s\'.', longEditionString)
+
+ # At this point we have everything we need, let's assemble the new filename.
+ newFileName = 'IEEE Spectrum ' + fileNameParts[2] + '-' + fileNameParts[0] + ' ' + longEditionString + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEESpectrumV2B.match(fileToRename):
+ localLogger.info('%s is an IEEE Spectrum Magazine file, with V2B format.', fileToRename)
+
+ # This is a further variation on V2A, but contains region information after a dot (NA for North America, INT for International).
+ fileNameParts = fileName.split('_')
+ magazine_year = fileNameParts[2].split('.')[0]
+ magazine_edition = fileNameParts[2].split('.')[1]
+
+ localLogger.debug('Month is %s.', fileNameParts[0])
+ localLogger.debug('Year is %s.', magazine_year)
+ localLogger.debug('Edition to convert is %s.', magazine_edition)
+
+ longEditionString = convertEditionString(magazine_edition)
+ localLogger.debug('New edition string is \'%s\'.', longEditionString)
+
+ # At this point we have everything we need, let's assemble the new filename.
+ newFileName = 'IEEE Spectrum ' + magazine_year + '-' + fileNameParts[0] + ' ' + longEditionString + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEESpectrumV2C.match(fileToRename):
+ localLogger.info('%s is an IEEE Spectrum Magazine file, with V2C format.', fileToRename)
+
+ # This is a direct variation of V2, but with two digit years.
+ fileNameParts = fileName.split('_')
+
+ localLogger.debug('Month is %s.', fileNameParts[0])
+ localLogger.debug('Year is %s.', fileNameParts[2])
+
+ # We have everything we need, so assemble the new filename here.
+ newFileName = 'IEEE Spectrum ' + '20' + fileNameParts[2] + '-' + fileNameParts[0] + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEETheInstituteV1.match(fileToRename):
+ localLogger.info('%s is an IEEE The Institute Magazine file, with V1 format.', fileToRename)
+
+ # This format has month and year concatenated, so we need to make some harder-coded extractions.
+ # Get the year and month only, we know the rest.
+ fileNameParts = fileName.split('_')[2]
+ fileYear = fileNameParts[-4:] # Get last 4 characters.
+ fileMonth = fileNameParts[:-4] # Get from beginning to last 4 characters.
+
+ localLogger.debug('Month to convert is %s.', fileMonth)
+ monthNumber = monthStringToNumber(fileMonth)
+ localLogger.debug('New month identifier is %s.', monthNumber)
+
+ newFileName = 'IEEE The Institute ' + fileYear + '-' + monthNumber + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEETheInstituteV1A.match(fileToRename):
+ localLogger.info('%s is an IEEE The Institute Magazine file, with V1A format.', fileToRename)
+
+ # This is a better variation on V1, with better separation between month and year.
+ # Get the year and month only, we know the rest.
+
+ localLogger.debug('Month to convert is %s.', fileName.split('_')[2])
+ monthNumber = monthStringToNumber(fileName.split('_')[2])
+ localLogger.debug('New month identifier is %s.', monthNumber)
+
+ newFileName = 'IEEE The Institute ' + fileName.split('_')[3] + '-' + monthNumber + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEETheInstituteV2.match(fileToRename):
+ localLogger.info('%s is an IEEE The Institute Magazine file, with V2 format.', fileToRename)
+
+ # This is a variation on V1A, with some changing in name casing and date format, but it can be parsed the same way.
+ # Get the year and month only, we know the rest.
+
+ localLogger.debug('Month to convert is %s.', fileName.split('_')[1])
+ monthNumber = monthStringToNumber(fileName.split('_')[1])
+ localLogger.debug('New month identifier is %s.', monthNumber)
+
+ newFileName = 'IEEE The Institute ' + fileName.split('_')[2] + '-' + monthNumber + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEEComputationalIntelligenceV1.match(fileToRename):
+ localLogger.info('%s is an IEEE Computational Intelligence Magazine file, with V1 format.', fileToRename)
+
+ # This is again some very concatenated format, but it can be easily disassembled into its parts with harder-coded ways.
+ monthAndYear = fileName.split('_')[1]
+ fileMonth = monthAndYear[-4:-2]
+ fileYear = monthAndYear[:-4]
+
+ newFileName = 'IEEE Computational Intelligence Magazine ' + fileYear + '-' + fileMonth + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+
+ elif regexIEEEComputationalIntelligenceV2.match(fileToRename):
+ localLogger.info('%s is an IEEE Computational Intelligence Magazine file, with V2 format.', fileToRename)
+
+ # This is again some very concatenated format, but it can be easily disassembled into its parts with harder-coded ways.
+ monthAndYear = fileName.split('_')[2]
+ fileMonth = monthAndYear[-2:]
+ fileYear = monthAndYear[:-2]
+
+ newFileName = 'IEEE Computational Intelligence Magazine ' + fileYear + '-' + fileMonth + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+
+ elif regexIEEEPotentialsV1.match(fileToRename):
+ localLogger.info('%s is an IEEE Potentials Magazine file, with V1 format.', fileToRename)
+
+ # This is again some very concatenated format, but it can be easily disassembled into its parts with harder-coded ways.
+ monthAndYear = fileName.split('_')[1]
+ fileMonth = monthAndYear[-4:-2]
+ fileYear = monthAndYear[:-4]
+
+ newFileName = 'IEEE Potentials ' + fileYear + '-' + fileMonth + fileExtension.lower()
+ localLogger.info('File will be renamed to \'%s\'.', newFileName)
+ else:
+ localLogger.info('%s in an unknown file, skipping.', fileToRename)
+ continue
+
+ if arguments.simulate == False:
+ # Let it rip!
+ os.rename(file, os.path.join(filePath, newFileName))
+
+ sys.exit(0)
+ pass
\ No newline at end of file