Batch renaming a file name with specific conditions - python

I want to batch rename a set of files in a folder with specific conditions, which is (add prefix, start from a specific number and increase in +1 series, skip a specific number in the series, add files with same number using an underscore)
I have a set of scanned document files, starting from a specif number let's say 10 and increments with 1. Ex 10,11,12,...
.But some files will be missing for ex maybe 13 will be missing, so need an option to skip that 13 and start with 14. Also some documents will be 2 pages, so need to put underscore to distinguish. For ex 15th document is 2 pages, so need to put 15_2 for the second file.
UPDATE1: I have fixed almost everything, now its working perfectly, but now i need to do two things
1. Need a while loop to check whether the entered number for skip is less than or equal to x.
I have no idea on how to do the below condtion:
Also some documents will be 2 pages, so need to put underscore to distinguish. For ex 15th document is 2 pages, so need to put 15_2 for the second file.
UPDATE 2 : I have added the condition for when there in repeated files but syntax error as below
UPDATE 3 : My friend helped me to get rid of the syntax error.
UPDATE 4 : My friend helped with some logic and i have solved the puzzle, updated here for reference. Thank you Jishnu
line 20
i -= 1 and dst = pre.upper() + str(i) + "_2.pdf"
import os
pre = str(input("Enter prefix : "))
while pre not in ("cin", "CIN", "CRT", "crt", "inv", "INV", "DO", "do"):
pre = str(input("Please check the entered prefix and try again : "))
x = int(input("Enter first no : "))
skip = int(input("Skip : "))
rt = int(input("Enter repeating number"))
rt = rt + 1
def main():
i = x
for filename in os.listdir("C:/Users/Ajeshhome/Desktop/scan/"):
if skip == i:
i += 1
dst = pre.upper() + str(i) + ".pdf"
src = 'C:/Users/Ajeshhome/Desktop/scan/' + filename
dst = 'C:/Users/Ajeshhome/Desktop/scan/' + dst
os.rename(src, dst)
i += 1
elif rt == i:
dst = pre.upper() + str(i-1) + "_2.pdf"
src = 'C:/Users/Ajeshhome/Desktop/scan/' + filename
dst = 'C:/Users/Ajeshhome/Desktop/scan/' + dst
os.rename(src, dst)
i += 1
else:
dst = pre.upper() + str(i) + ".pdf"
src = 'C:/Users/Ajeshhome/Desktop/scan/' + filename
dst = 'C:/Users/Ajeshhome/Desktop/scan/' + dst
os.rename(src, dst)
i += 1
def lis():
path = 'C:/Users/Ajeshhome/Desktop/scan/'
files = []
# r=root, d=directories, f = files
for r, d, f in os.walk(path):
for file in f:
if '.pdf' in file:
files.append(os.path.join(r, file))
for f in files:
print(f)
# Driver Code
if __name__ == '__main__':
# Calling main() function
main()
lis()
os.system('pause')

I'm not sure how to solve everything you're asking about. But here's a start: reliable prefix selection. Here's the whole code:
PREFIXES = {
"CIN",
"CRT",
"DO",
"INV",
}
def ask_prefix():
prefixes = sorted(PREFIXES)
for i, prefix in enumerate(prefixes, start=1):
print(f'{i}) {prefix}')
while True:
reply = input('Select prefix: ').strip().upper()
try:
index = int(reply) - 1
except ValueError:
if reply in PREFIXES:
return reply
else:
if 0 <= index < len(prefixes):
return prefixes[index]
print(f'{reply!r} is not a valid prefix.')
pre = ask_prefix()
A global set for all valid prefixes:
PREFIXES = {
"CIN",
"CRT",
"DO",
"INV",
}
Now let's look at ask_prefix(). The first line creates a sorted list from the PREFIXES so they print out in a nice order. Then:
for i, prefix in enumerate(prefixes, start=1):
print(f'{i}) {prefix}')
Use enumerate to number the prefixes then print out the number and prefix to create a menu. Next, enter an infinite loop (while True:), which only breaks when the user enters or selects a valid prefix.
Ask the user for a prefix or a number from the menu and clean up the input:
reply = input('Select prefix: ').strip().upper()
Try the reply as an index:
try:
index = int(reply) - 1
except ValueError:
...
else:
if 0 <= index < len(prefixes):
return prefixes[index]
If it doesn't work as an index, try it as a prefix:
if reply in PREFIXES:
return reply

Related

Python Duplicate naming

The goal is to create a naming system for duplicate strings.
If the name is hotdog.jpg and I want to make a duplicate and that the next string is hotdog_1.jpg. And so on. But the problem I'm facing is that if you make a duplicate of hotdog_1.jpg we get hotdog_1_1.jpg. I tried to just check if the string ends with "(underscore) + number". But then the problem is that the string could also have "(underscore) + number + number". like hotdog_13.jpg.
is there any good way to implement this?
for current_image_name in self.images_names:
extension = os.path.splitext(current_image_name)[1]
current_image_name = os.path.splitext(current_image_name)[0]
if current_image_name[-2] == '_' and current_image_name[-1].isdigit():
self.images_names.insert(self.current_index + 1, current_image_name[:-1] + str(int(self.images_names[self.current_index][-1]) + 1) + extension)
name_change = True
if not name_change:
self.images_names.insert(self.current_index + 1, self.images_names[self.current_index] + '_1')
You can do this with a simple method that does some string magic.
EDITED after reading the comments on the question. Added a method to handle lists
code
def inc_filename(filename: str) -> str:
if "." in filename:
# set extension
extension = f""".{filename.split(".")[-1]}"""
# remove extension from filename
filename = ".".join(filename.split(".")[:-1])
else:
# set extension to empty if not included
extension = ""
try:
# try to set the number
# it will throw a ValueError if it doesn't have _1
number = int(filename.split("_")[-1]) +1
newfilename = "_".join(filename.split("_")[:-1])
except ValueError:
# catch the ValueError and set the number to 1
number = 1
newfilename = "_".join(filename.split("_"))
return f"{newfilename}_{number}{extension}"
def inc_filelist(filelist: list) -> list:
result = []
for filename in filelist:
filename = inc_filename(filename)
while filename in filelist or filename in result:
filename = inc_filename(filename)
result.append(filename)
return result
print(inc_filename("hotdog_1"))
print(inc_filename("hotdog"))
print(inc_filename("hotdog_1.jpg"))
print(inc_filename("hotdog.jpg"))
print(inc_filename("ho_t_dog_15.jpg"))
print(inc_filename("hotdog_1_91.jpg"))
filelist = [
"hotdog_1.jpg",
"hotdog_2.jpg",
"hotdog_3.jpg",
"hotdog_4.jpg"
]
print(inc_filelist(filelist))
output
hotdog_2
hotdog_1
hotdog_2.jpg
hotdog_1.jpg
ho_t_dog_16.jpg
hotdog_1_92.jpg
['hotdog_5.jpg', 'hotdog_6.jpg', 'hotdog_7.jpg', 'hotdog_8.jpg']

Launching a python script after another program (Unity3D) is closed

I have written a script on python for unity applications (on Windows) to parse through logs. It just opens .log files, goes through it and creates new ones. I need to launch it after unity application is closed. There is the easiest way to make it work just by launching it manually after you've closed the Unity, but is there any way to automate that process and make the script start after Unity process is terminated? And if so - how? It's a simple console application that doesn't require any input, all that is needed is just to run it.
import glob
import os
import time
import re
import getpass
user = getpass.getuser()
def exception_search(full_line): #Parsing function, that will filter original log file
nonfiltered = full_line.split('\n') #Spliting file contents to have it as list separated by each line from file
result = ["List of exceptions:"] #first line
i = 0 #index which will be used in for loop to iterate through string list
previous_exc = [] #Will contain previous found exception to avoid printing the same one
current_exc = [] #Will contain current exception to compare it
repeat = 0 #repeating count that will be used to type how many times exception was repeated in a row
for i in range(len(nonfiltered) - 1): #going through a range of array elements
if "Exception:" in nonfiltered[i]: #Checking if we found an exception line
#checking if element on top is not another exception, so, we would need to print it for context
if (not "(Filename: <" in nonfiltered[i - 2]) and (not "(Filename: currently not" in nonfiltered[i - 2]):
result.append(nonfiltered[i - 3])
result.append(nonfiltered[i - 2])
result.append(nonfiltered[i - 1])
previous_exc = [] #Zeroing previous exception remembered because after printing nonexception we need to reset it
while (not "(Filename: <" in nonfiltered[i]) and (not "(Filename: currently not" in nonfiltered[i]):
current_exc.append(nonfiltered[i]) #Filling current exception list with exception lines until we reach the last line of it
i += 1 #incrementing index because we are using non-for incrementation to go through elements
current_exc.append(nonfiltered[i]) #adding the last line and separator for visibility
current_exc.append("")
if previous_exc != current_exc: #checking if exception was the same as the last one, if it is - we don't need to print it but increment the count
result.extend(current_exc) #Not the same so we add the exception to the resulting list
previous_exc = current_exc[:] #Putting it as the previous exception in case it will be the same later
if repeat != 0: #If count wasn't 0, so, we got exception repeated and we inform about it the reader
result.append("It was repeated " + str(repeat) + " times")
result.append("")
repeat = 0 #zeroing the counter after using it
current_exc = [] #zeroing the current exception for the next use
else: #exception is repeatig, so, we just summ up the count
repeat += 1
current_exc = []
result = '\n'.join(result) #Turning the list back into string with \n as separator
return result
logpath = "C:\\Users\\" + user +"\\AppData\\LocalLow" #Path where all logs are contained
text_files = glob.glob(logpath + "/**/Playe*.log", recursive = True) #Making a list of Player.log/Player-prev.log named files
for path in text_files:
projname = re.match(r"(.+\\)(.+)(\\Player\-prev.log)", path) #Extracting the name of the project from the folder inside which log files are content
if projname is None:
projname = re.match(r"(.+\\)(.+)(\\Player.log)", path).group(2)
else:
projname = projname.group(2)
#getting the last modified time of the log file
lastmod = time.asctime(time.localtime(os.path.getmtime(path))).replace(":", "h ", 1).replace(":", "m ")
filecontent = ""
with open(path,'r', encoding="utf-8") as file: #copying the contents of the original log file
filecontent = file.read()
newpath = 'C:\\Users\\' + user + '\\Desktop\\logs'
if not os.path.isdir(newpath): #creating a directory for logs if it's not around
os.mkdir(newpath)
newpath = newpath + '\\'
if not os.path.isdir(newpath + projname): #creating a directory for project if it's not around
os.mkdir(newpath + projname)
#getting a filepath for a log to check if it's already here
filepath = os.path.join(newpath + projname, "full " + projname + " " + lastmod + ".log")
if not os.path.isfile(filepath): #checking if log is not copied, if that's so, we are copying it by creating a new file
with open(filepath,'x', encoding="utf-8") as file:
temp = file.write(filecontent)
filepath = filepath.replace("full ", "Exception ")
filecontent = exception_search(filecontent) #parsing the contents of log file to get only neccesary exception lines
if (not os.path.isfile(filepath)) and (len(filecontent) > 19): #checking if file is not created and there are exception which needed to be mentioned
with open(filepath,'x', encoding="utf-8") as file:
temp = file.write(filecontent)
As usual also Unity when run via Command Line only returns once it has exited.
So you can simply have a wrapper script like e.g. in batch (or whatever language you use)
C:\Path\To\Your\UnityInstall\Unity.exe -batchmode -username "name#example.edu.uk" -password "XXXXXXXXXXXXX" -serial "E3-XXXX-XXXX-XXXX-XXXX-XXXX" –quit -projectPath "C:\Path\To\Your\UnityProjectFolder" -logFile "C:\Path\ToWrite\The\Log\To"
C:\Path\To\Your\PythonInstall\Pyhton.exe C:\Path\To\Your\Script.py
You could also directly run your stuff from within Unity itself.
But then of course you would need to include that script into every project you want to use this way ;)
But yes, you can use [InitializeOnLoadMethod] and Application.quitting
public class test
{
[InitializeOnLoadMethod]
private static void Init()
{
Application.quitting += OnQuit;
}
private static void OnQuit()
{
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\PATH\TO\YOUR\PYTHONINSTALL\PYTHYON.exe",
WorkingDirectory = #"C:\PATH\TO\YOUR\PYTHONINSTALL",
Arguments = "C:\PATH\TO\YOUR\SCRIPT.py",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = false,
WindowStyle = ProcessWindowStyle.Normal
}
};
p.Start();
}
}

read multiple files automatically no manual file naming

I have a directory contains 50 files I want to read them one by one and compare wit the other files - that is fixed. I am using glob.blob. But it didn't work.
Here how I am reading all files. Instead, path = '*.rbd' if I give the file name like path = run-01.rbd it works.
path = '*.rbd'
path = folder + path
files=sorted(glob.glob(path))
complete code
import glob
from itertools import islice
import linecache
num_lines_nonbram = 1891427
bits_perline = 32
total_bit_flips = 0
num_bit_diff_flip_zero = 0
num_bit_diff_flip_ones = 0
folder = "files/"
path = '*.rbd'
path = folder + path
files=sorted(glob.glob(path))
original=open('files/mull-original-readback.rbd','r')
#source1 = open(file1, "r")
for filename in files:
del_lines = 101
with open(filename,'r') as f:
i=1
while i <= del_lines:
line1 = f.readline()
lineoriginal=original.readline()
i+=1
i=0
num_bit_diff_flip_zero = 0
num_bit_diff_flip_ones = 0
num_lines_diff =0
i=0
j=0
k=0
a_write2 = ""
while i < (num_lines_nonbram-del_lines):
line1 = f.readline()
lineoriginal = original.readline()
while k < bits_perline:
if ((lineoriginal[k] == line1[k])):
a_write2 += " "
else:
if (lineoriginal[k]=="0"):
#if ((line1[k]=="0" and line1[k]=="1")):
num_bit_diff_flip_zero += 1
if (lineoriginal[k]=="1"):
#if ((line1[k]=="0" and line1[k]=="1")):
num_bit_diff_flip_ones += 1
#if ((line1[k]==1 and line1[k]==0)):
#a_write_file2 = str(i+1) + " " + str(31-k) + "\n" + a_write_file2
#a_write2 += "^"
#num_bit_diff_flip_one += 1
# else:
# a_write2 += " "
k+=1
total_bit_flips=num_bit_diff_flip_zero+num_bit_diff_flip_ones
i+=1
k=0
i = 0
print files
print "Number of bits flip zero= %d" %num_bit_diff_flip_zero +"\n" +"Number of bits flip one= %d" %num_bit_diff_flip_ones +"\n" "Total bit flips = %d " %total_bit_flips
f.close()
original.close()
You could use the os module to first list everything in a directory (both files and modules) then use a python generator to filter out only the files. You could then use a second python generator to filter out files with a specific extension. There is probably a more efficient way of doing it but this works:
import os
def main():
path = './' # The path to current directory
# Go through all items in the directory and filter out files
files = [file for file in os.listdir(path) if
os.path.isfile(os.path.join(path, file))]
# Go through all files and filter out files with .txt (for example)
specificExtensionFiles = [file for file in files if ".txt" in file]
# Now specificExtensionFiles is a generator for .txt files in current
# directory which you can use in a for loop
print (specificExtensionFiles)
if __name__ == '__main__':
main()
For further reference:
How do I list all files of a directory?
The problem is that you're not going back to the beginning of originalfile whenever you start comparing with the next file in the for filename in files: loop. The simplest solution is to put:
original.seek(0)
at the beginning of that loop.
You could also read the whole file into a list just once before the loop, and use that instead of reading the file repeatedly.
And if you only want to process part of the files, you can read the file into a list, and then use a list slice to get the lines you want.
You also shouldn't be setting num_bit_diff_flip_zero and num_bit_diff_flip_one to 0 each time through the loop, since these are supposed to be the total across all files.
with open('files/mull-original-readback.rbd','r') as original:
original_lines = list(original)[del_lines:num_lines_nonbram]
for filename in files:
with open(file, 'r') as f:
lines = list(f)[del_lines:num_lines_nonbram]
for lineoriginal, line1 in zip(original_lines, lines):
for k in range(bits_perline):
if lineoriginal[k] == line1[k]:
a_write2 += " "
elif lineoriginal[k] == "0"
num_bit_diff_flip_zero += 1
else:
num_bit_diff_flip_ones += 1
total_bit_flips = num_bit_diff_flip_zero + num_bit_diff_flip_ones

Multiple Paths Traversed and Displayed Filed type in Maya Menu with Python

I'm new here so bare in mind that and I hope my questions are clearly asked for you lot to help me out. I am trying to alter Brent Tylers Dropbox script so that I will be able to list Python under Python, Mel under Mel and so on(eventually plugins and other files too but not for now)
Ok so my directory is like so:
1.
sf=C:/users/scripts/ a.py + b.mel
pf=C:/users/scripts/Python/c.py
mf=C:/users/scripts/Mel/d.mel
(These are the folders my scripts will be placed in)
Code :
absoluteFiles = []
relativeFiles = []
folders = []
allFiles = []
currentFile = ''
for root, dirs, files in os.walk(sf):
for x in files:
correct = root.replace('\\', '/')
currentFile = (correct + '/' + x)
allFiles.append(currentFile)
if currentFile.endswith('.mel'):
relativeFiles.append(currentFile.replace((mf + '/'), ""))
if currentFile.endswith('.py'):
relativeFiles.append(currentFile.replace((pf + '/'), ""))
relativeFiles.sort()
for relativeFile in relativeFiles:
split = relativeFile.split('/')
fileName = split[-1].split('.')
i=0
while i<(len(split)):
### Create Folders ###
if i==0 and len(split) != 1:
if cmds.menu(split[i] ,ex=1) == 0:
cmds.menuItem(split[i], p=PadraigsTools, bld=1, sm=1, to=1, l=split[i])
if i > 0 and i < (len(split)-1):
if cmds.menu(split[i] ,ex=1) == 0:
cmds.menuItem(split[i], p=split[i-1], bld=1, sm=1, to=1, l=split[i])
### Create .mel Files ###
if fileName[-1] == 'mel':
if i==len(split)-1 and len(split) > 1:
scriptName = split[-1].split('.')
temp1 = 'source ' + '"' + sf + '/' + relativeFile + '"; ' + scriptName[0]
command = '''mel.eval(''' + "'" + temp1 + '''')'''
cmds.menuItem(split[i], p=split[i-1], c=command, l=split[i])
if i==len(split)-1 and len(split) == 1:
scriptName = split[-1].split('.')
temp1 = 'source ' + '"' + sf + '/' + relativeFile + '"; ' + scriptName[0]
command = '''mel.eval(''' + "'" + temp1 + '''')'''
cmds.menuItem(split[i], p=Mel, c=command, l=split[i])
### Create .py Files ###
if fileName[-1] == 'py':
if i==len(split)-1 and len(split) > 1:
command = 'import ' + fileName[0] + '\n' + fileName[0] + '.' + fileName[0]+ '()'
cmds.menuItem(split[i], p=split[i-1], c=command, l=split[i])
if i==len(split)-1 and len(split) == 1:
command = 'import ' + fileName[0] + '\n' + fileName[0] + '.' + fileName[0]+ '()'
cmds.menuItem(split[i], p=Python, c=command, l=split[i])
i+=1
So far I can print out individually (sf, pf, mf) to the corresponding Directory but I cant list out everything at once and the files under sf will not show at all. regarding the folders created it ends up very odd. sometimes i would get a duplicate folder as a submenu and if i use sf it give me C:/.
After days and hours of research trying to mend this script I have found no answer including
from itertools import chain
paths = (mf, sf, pf)
for path, dirs, files in chain.from_iterable(os.walk(path) for path in paths):
::QUESTION::
Is there a way i can put this together sanely so that new folders will show up with their contents on refresh as a submenu and the files will show up and allow me to execute them from their corresponding submenu.
I would appreciate any help possible including down votes haha. And bare in mind I don't want you to hand me the answer on a golden spoon because I wont know what is corrected or needs to be :)
Thanks Greatly
-- Padraig
There's a couple of things you can do to simplify things a bit.
First, it's a good idea to make this as data-driven as possible so you don't have to re-write it if your needs change. This does more or less what you do, but collects the results into a dictionary where the key are the root paths you supplied and the values are lists of relative paths:
def find_files(root, extensions = ('mel', 'py')):
def clean_path(*p):
return "/".join(p).replace('\\', '/')
for root, _, files in os.walk(root):
used = [f for f in files if f.split(".")[-1] in extensions]
for u in used:
yield clean_path(root, u)
def relativize(abs, roots):
low_roots = map (str.lower, roots) # all lower for comparison
for root, low_root in zip(roots,low_roots):
if abs.lower().startswith(low_root):
return root, abs[len(root):]
return ("", abs)
relative_paths = find_files('c:/users/scripts')
root_dict = {}
for item in relative_paths :
folder, file = relativize(item, ('C:/users/scripts/Python/', 'C:/users/scripts/Mel/', 'C:/users/scripts/'))
if not folder in root_dict:
root_dict[folder] = []
root_dict[folder].append(file)
So now you have a dictionary with a bunch of root folders and lists of relative paths (files that were not in any relative path you supplied are keyed to empty string and show up as absolute paths). You can make the menus in a very generic way because they are all in the same format. If you need the entire list, you can get it like this:
results = []
for each_root in root_dict:
for relpath in root_dict[each_root]:
results.append(each_root + relpath)
For creating the actual menus, you want to use a single function and bind it to the filename for each menu item as you make it. This is a slightly tricky topic (more detail here). The easy way to do this is to use a functools.partial object, which bundles a command and a bunch of arguments into an object which looks like a function: you can create a partial and attach it to the command of your menu items so they all call the same function with their individual arguments. Here's a simple example using the variables from above and a menubar; you can see how to adapt it to other kinds of menus pretty easily:
from functools import partial
# call this on every button selection
def test(filepath, ignore):
# maya will send "test(name, False)"; we just ignore the 'False'
print "Here's where I would reload", filepath
example = cmds.window(title = 'example')
menubar = cmds.menuBarLayout()
for name in folder_names:
menuname = name
if menuname:
menuname = menuname.split("/")[-2] # we used trailing slashes
else:
menuname = "root"
cmds.menu(label = menuname)
file_names = root_dict[name]
file_names.sort()
for fn in file_names:
mi = cmds.menuItem(label = fn, command = partial(test, fn))
cmds.setParent(menubar)

Python: Conditional logic if entry is file or directory not working

I have the following directory structure on my file system:
/home/myUser/
stuff_home/
fizz/
a.txt
b.txt
buzz/
1.pdf
widgets/
c.txt
2.pdf
3.pdf
4.pdf
I want to traverse stuff_home/ recursively and count the number of subdirectories, .txt files and .pdf documents it contains. I have written a small Python script:
import os
dirCnt = 0
txtCnt = 0
pdfCnt = 0
def main():
get_counts("/home/myUser/stuff_home")
t = str(txtCnt)
p = str(pdfCnt)
d = str(dirCnt)
print "\nRESULTS\Text Files:\t" + t + "\nPDF Files:\t" + p + "\nDirectories:\t" + d + "\n\n"
def get_counts(root):
contents = os.listdir(root)
for file in contents:
if os.path.isdir(file):
dirCnt = dirCnt + 1
elif os.path.splitext(file)[1] == "txt":
txtCnt = txtCnt + 1
elif os.path.splitext(file)[1] == "pdf":
pdfCnt = pdfCnt + 1
else:
print "Encountered unknown file: " + file
When I run this, I get no errors, but the script is clearly coded wrong. Here is the output I get:
Encountered unkown file: fizz
Encountered unkown file: buzz
Encountered unkown file: widgets
RESULTS
Text Files: 0
PDF Files: 0
Directories: 0
Anything jump out to you Pythonians out there? It looks like none of my logic (for detecting file vs. directory, as well as using splitext to grabs the file extension) is working here...thanks in advance!
This seems like a job for os.walk (if I understand correctly):
def count_pdf_txt(top):
npdf = 0
ntxt = 0
ndir = 0
for root,dirs,files in os.walk(top):
ndir += len(dirs)
for f in files:
if f.endswith('txt'): #use `splitext` if you like.
ntxt += 1
elif f.endswith('pdf'):
npdf += 1
else:
print "unknown"
return npdf,ntxt,ndirs
Note that your version gives a wrong result because of the lines like:
pdfCount = pdfCount + 1
inside your get_counts function. This creates a new local variable which doesn't influence the global variable in any way. In order to have your local variables change the global variables, you need to declare them as global. e.g. global pdfCount. However, the presence of a global keyword in your function should always make you think "there's got to be a better way to do this".

Categories

Resources