patternpythonMinor
Record Cataloging Program
Viewed 0 times
recordprogramcataloging
Problem
I made a simple program to catalog some old records I have. It seems a tad redundant in the searching function. Does anyone know what I can do about that?
```
import easygui as eg
import sys
namedoc = open(r"C:\Users\User\Desktop\RcrdCat\names.txt", 'a')
nd2 = open(r"C:\Users\User\Desktop\RcrdCat\names(2).txt", 'a')
authdoc = open(r"C:\Users\User\Desktop\RcrdCat\authors.txt", 'a')
yeardoc = open(r"C:\Users\User\Desktop\RcrdCat\dates.txt", 'a')
pubdoc = open(r"C:\Users\User\Desktop\RcrdCat\pubs.txt", 'a')
rpmdoc = open(r"C:\Users\User\Desktop\RcrdCat\rpms.txt", 'a')
conddoc = open(r"C:\Users\User\Desktop\RcrdCat\conditions.txt", 'a')
sleevedoc = open(r"C:\Users\User\Desktop\RcrdCat\sleeves.txt", 'a')
doclist = [namedoc, yeardoc, pubdoc, rpmdoc, conddoc, sleevedoc]
def getlen(doc):
templist = doc.readlines()
listlen = len(templist)
templist = []
return listlen
def mainmenus():
mm = eg.buttonbox("What would you like to do?", "Categorizer", ["Add Records", "Search Records", "Exit"])
if mm == "Exit":
exitprgm()
elif mm == "Add Records":
addrecs()
elif mm == "Search Records":
searchrecs(getsearchterms())
def addrecs():
info = eg.multenterbox("Please enter all record info", "Add Records", ["Name", "Year", "Publisher", "RPM", "Condition", "Sleeve", "Name (2)", "Artist"])
if info == None:
mainmenus()
else:
namedoc.write(info[0] + " \r\n")
yeardoc.write(info[1] + " \r\n")
pubdoc.write(info[2] + " \r\n")
rpmdoc.write(info[3] + " \r\n")
conddoc.write(info[4] + " \r\n")
nd2.write(info[6] + " \r\n")
authdoc.write(info[7] + " \r\n")
if info[5] == "Yes" or info[5] == "No":
sleevedoc.write(info[5] + " \r\n")
else:
eg.msgbox("Please enter \"Yes\" or \"No\"")
addrecs()
addrecs()
def getsearchterms():
term = eg.enterbox("Please enter your term in the following way: the word \"name\", \
```
import easygui as eg
import sys
namedoc = open(r"C:\Users\User\Desktop\RcrdCat\names.txt", 'a')
nd2 = open(r"C:\Users\User\Desktop\RcrdCat\names(2).txt", 'a')
authdoc = open(r"C:\Users\User\Desktop\RcrdCat\authors.txt", 'a')
yeardoc = open(r"C:\Users\User\Desktop\RcrdCat\dates.txt", 'a')
pubdoc = open(r"C:\Users\User\Desktop\RcrdCat\pubs.txt", 'a')
rpmdoc = open(r"C:\Users\User\Desktop\RcrdCat\rpms.txt", 'a')
conddoc = open(r"C:\Users\User\Desktop\RcrdCat\conditions.txt", 'a')
sleevedoc = open(r"C:\Users\User\Desktop\RcrdCat\sleeves.txt", 'a')
doclist = [namedoc, yeardoc, pubdoc, rpmdoc, conddoc, sleevedoc]
def getlen(doc):
templist = doc.readlines()
listlen = len(templist)
templist = []
return listlen
def mainmenus():
mm = eg.buttonbox("What would you like to do?", "Categorizer", ["Add Records", "Search Records", "Exit"])
if mm == "Exit":
exitprgm()
elif mm == "Add Records":
addrecs()
elif mm == "Search Records":
searchrecs(getsearchterms())
def addrecs():
info = eg.multenterbox("Please enter all record info", "Add Records", ["Name", "Year", "Publisher", "RPM", "Condition", "Sleeve", "Name (2)", "Artist"])
if info == None:
mainmenus()
else:
namedoc.write(info[0] + " \r\n")
yeardoc.write(info[1] + " \r\n")
pubdoc.write(info[2] + " \r\n")
rpmdoc.write(info[3] + " \r\n")
conddoc.write(info[4] + " \r\n")
nd2.write(info[6] + " \r\n")
authdoc.write(info[7] + " \r\n")
if info[5] == "Yes" or info[5] == "No":
sleevedoc.write(info[5] + " \r\n")
else:
eg.msgbox("Please enter \"Yes\" or \"No\"")
addrecs()
addrecs()
def getsearchterms():
term = eg.enterbox("Please enter your term in the following way: the word \"name\", \
Solution
Another piece of python wisdom to consider is 'if you can do it with the standard library, do!'
Field and record based storage is really easy to do with the csv module, which reads and writes comma-separated spreadsheet style files. A file formatted like this:
is easy to read like so:
DictReader is especially nice since it automatically parses the first line as the headers and returns a dictionary so you don't need to create a custom class to organize your data (as an aside: you could also do this using the sqllite module, which would let you read and write the entire database directly and query it with SQL. I'm not going to go into that since SQL is it's own thing - but for bigger applications it would be the right thing to research). Writing out the data is the inverse of the above:
So, using CSV files and DictReader/DictWriter to handle the reading/writing, our 'database' is a list of dictionaries':
Searching this can be done very efficiently using the built in filter function. Filter takes a list and a function as arguments; it returns a list of all the items where the function returns true. For example:
and so on.
The 'ui' for the program then really amounts to creating custom filter functions and returning them. You can manufacture them easily:
and so on. You can even combine them:
All of these filters automatically return lists, so you don't have to loop or create temporary variables to collect matches.
I think it's easy to extrapolate from this how to write the program very compactly; using standard library functions means you can concentrate on logic and user-facting stuff (do you want to include partial name matches, or regular expressions, for example? Do you silently ignore bad input or scold the user? etc).
Field and record based storage is really easy to do with the csv module, which reads and writes comma-separated spreadsheet style files. A file formatted like this:
name,artist,publisher,date,rpm, condition, sleeve
Wee small hours,Frank Sinatra,Columbia,1953,33,good,originalis easy to read like so:
import csv
records = []
with open ('/path/to/file.csv', 'rb') as filehandle:
reader = csv.DictReader(filehandle)
for r in reader:
# make sure the dates are numeric so you can compare them
try:
r['date'] = int(r['date'])
except:
r['date'] = 1900 # you might prefer some other default for bad data
records.append(r)DictReader is especially nice since it automatically parses the first line as the headers and returns a dictionary so you don't need to create a custom class to organize your data (as an aside: you could also do this using the sqllite module, which would let you read and write the entire database directly and query it with SQL. I'm not going to go into that since SQL is it's own thing - but for bigger applications it would be the right thing to research). Writing out the data is the inverse of the above:
# assume our records are dictionaries in the records variable...
with open ('/path/to/file.csv', 'wb') as filehandle:
writer = csv.DictWriter(filehandle, fieldnames = ('name','artist','publisher','date','rpm','condition','sleeve'))
writer.writerows(records)So, using CSV files and DictReader/DictWriter to handle the reading/writing, our 'database' is a list of dictionaries':
[
{'publisher': 'Columbia', 'name': 'Wee small hours', 'artist': 'Frank Sinatra', ' sleeve': 'original', 'date': '1953', 'rpm': '33', ' condition': 'good'}
{'publisher': ' Electra', 'name': 'Jump', 'artist': ' Van Halen', ' sleeve': ' repaired', 'date': ' 1983', 'rpm': ' 33', ' condition': ' poor'}
# etc
]Searching this can be done very efficiently using the built in filter function. Filter takes a list and a function as arguments; it returns a list of all the items where the function returns true. For example:
sinatra = lambda p: 'sinatra' in p['artist'].lower()
sinatra_albums = filter(records, sinatra)
before_1970 = lambda p: p['date'] < 1970
old_stuff = filter(records, before_1970)and so on.
The 'ui' for the program then really amounts to creating custom filter functions and returning them. You can manufacture them easily:
def make_artist_filter(artist):
return lambda x: artist.lower() in x['artist']
def make_year_filter(year):
return lambda x: x['date'] == yearand so on. You can even combine them:
def and_filter (filter1, filter22):
return lambda x: filter1(x) and filter2(x)
def or_filter (filter1, filter2):
return lambda x: filter1(x) or filter2(x)All of these filters automatically return lists, so you don't have to loop or create temporary variables to collect matches.
I think it's easy to extrapolate from this how to write the program very compactly; using standard library functions means you can concentrate on logic and user-facting stuff (do you want to include partial name matches, or regular expressions, for example? Do you silently ignore bad input or scold the user? etc).
Code Snippets
name,artist,publisher,date,rpm, condition, sleeve
Wee small hours,Frank Sinatra,Columbia,1953,33,good,originalimport csv
records = []
with open ('/path/to/file.csv', 'rb') as filehandle:
reader = csv.DictReader(filehandle)
for r in reader:
# make sure the dates are numeric so you can compare them
try:
r['date'] = int(r['date'])
except:
r['date'] = 1900 # you might prefer some other default for bad data
records.append(r)# assume our records are dictionaries in the records variable...
with open ('/path/to/file.csv', 'wb') as filehandle:
writer = csv.DictWriter(filehandle, fieldnames = ('name','artist','publisher','date','rpm','condition','sleeve'))
writer.writerows(records)[
{'publisher': 'Columbia', 'name': 'Wee small hours', 'artist': 'Frank Sinatra', ' sleeve': 'original', 'date': '1953', 'rpm': '33', ' condition': 'good'}
{'publisher': ' Electra', 'name': 'Jump', 'artist': ' Van Halen', ' sleeve': ' repaired', 'date': ' 1983', 'rpm': ' 33', ' condition': ' poor'}
# etc
]sinatra = lambda p: 'sinatra' in p['artist'].lower()
sinatra_albums = filter(records, sinatra)
before_1970 = lambda p: p['date'] < 1970
old_stuff = filter(records, before_1970)Context
StackExchange Code Review Q#39702, answer score: 4
Revisions (0)
No revisions yet.