patternpythonMinor
Creating a table within a .csv file
Viewed 0 times
filecreatingcsvwithintable
Problem
I recently made a post on Code Review entitled Writing lists to .csv file. I found the feedback extremely helpful, and wished to provide a continuation of that particular topic; of working with Python and .csv files.
I therefore set the somewhat more complex challenge of creating and populating a table within a .csv file. In particular, taking on-board previous feedback, I have begun using user-defined functions to separate concerns.
This creates a .csv file from 'scratch', prompting the user for the required input along the way:
```
#Import modules:
import datetime
#Declarations:
fileName = ""
dateList = []
date = ""
formattedDate = ""
revisedDateList = []
timeList = []
studyTime = ""
revisedTimeList = []
formattedTime = ""
timeRangeList = []
listOfTimes = []
eachRow = []
subject = ""
rowDate = ""
timeRangesAsString = ""
rowTextList = []
def createCsvFile() :
#Creates and populates a .csv file, with time periods as columns and dates as rows.
fileName = input("Please enter a name for your file: ")
with open(fileName+".csv","w+") as usersFile :
revisedDateList = listOfDates()
timeRangeList = inputTimes()
timeRangesAsString = ",".join(timeRangeList)
usersFile.write(","+timeRangesAsString+"\n")
for date in revisedDateList :
fileText = rowContent(revisedDateList,timeRangeList,date)
usersFile.write(fileText)
return
def listOfDates() :
#Asks user to input dates in specified format and returns a list of strings representing dates.
datesAsString = input("Please enter your dates as required (dd/mmm/yy dd/mmm/yy etc.) ") # Example: 17/jan/16 18/jan/16
revisedDateList = formatDate(datesAsString)
return revisedDateList
def formatDate(datesAsString) :
#Converts a list of strings, representing dates, from dd/mmm/yy to dd/mm/yyyy.
dateList = datesAsString.split()
for date in dateList :
structTime = datetime.datetime.strptime(date,"%d/%b/%y") #Example: 1
I therefore set the somewhat more complex challenge of creating and populating a table within a .csv file. In particular, taking on-board previous feedback, I have begun using user-defined functions to separate concerns.
This creates a .csv file from 'scratch', prompting the user for the required input along the way:
```
#Import modules:
import datetime
#Declarations:
fileName = ""
dateList = []
date = ""
formattedDate = ""
revisedDateList = []
timeList = []
studyTime = ""
revisedTimeList = []
formattedTime = ""
timeRangeList = []
listOfTimes = []
eachRow = []
subject = ""
rowDate = ""
timeRangesAsString = ""
rowTextList = []
def createCsvFile() :
#Creates and populates a .csv file, with time periods as columns and dates as rows.
fileName = input("Please enter a name for your file: ")
with open(fileName+".csv","w+") as usersFile :
revisedDateList = listOfDates()
timeRangeList = inputTimes()
timeRangesAsString = ",".join(timeRangeList)
usersFile.write(","+timeRangesAsString+"\n")
for date in revisedDateList :
fileText = rowContent(revisedDateList,timeRangeList,date)
usersFile.write(fileText)
return
def listOfDates() :
#Asks user to input dates in specified format and returns a list of strings representing dates.
datesAsString = input("Please enter your dates as required (dd/mmm/yy dd/mmm/yy etc.) ") # Example: 17/jan/16 18/jan/16
revisedDateList = formatDate(datesAsString)
return revisedDateList
def formatDate(datesAsString) :
#Converts a list of strings, representing dates, from dd/mmm/yy to dd/mm/yyyy.
dateList = datesAsString.split()
for date in dateList :
structTime = datetime.datetime.strptime(date,"%d/%b/%y") #Example: 1
Solution
Keep variables local
This part of code is very worrysome:
I suggest declaring the variables just before using them inside the function that uses them, not all at once. Like this the reader will have to keep all of them in mind.
Give inputs to functions
Your functions for the most part rely on global variables. It means that any global variable may be used and changed from anywhere, a very confusing fact.
Smaller and simpler?
I wrote a smaller version of this code by myself to get an idea of how much it would take, I did not handle formatting nor of time nor of dates, just taking input and writing to the
Re-use + generators + avoiding reinventing the wheel
Much of your code is overly long, look at this example:
Becomes:
Where
Your function was also virtually impossible to test because it relied on modifying the external variable
Mine can easily be run in the interactive shell or in a doctest:
Another example is
To sum up, here is how I write functions, I think this method will help you too.
This part of code is very worrysome:
#Declarations:
fileName = ""
dateList = []
date = ""
formattedDate = ""
revisedDateList = []
timeList = []
studyTime = ""
revisedTimeList = []
formattedTime = ""
timeRangeList = []
listOfTimes = []
eachRow = []
subject = ""
rowDate = ""
timeRangesAsString = ""
rowTextList = []I suggest declaring the variables just before using them inside the function that uses them, not all at once. Like this the reader will have to keep all of them in mind.
Give inputs to functions
Your functions for the most part rely on global variables. It means that any global variable may be used and changed from anywhere, a very confusing fact.
Smaller and simpler?
I wrote a smaller version of this code by myself to get an idea of how much it would take, I did not handle formatting nor of time nor of dates, just taking input and writing to the
.csv and it came out 7 lines long. I think handling your formatting may take 10 more lines so less than 20 lines.Re-use + generators + avoiding reinventing the wheel
Much of your code is overly long, look at this example:
def formatTimes(revisedTimeList) :
#Grouping individual times to create time ranges:
listOfTimes = revisedTimeList
iteratorList = iter(listOfTimes)
for time in iteratorList :
timeRange = time + " - " + next(iteratorList)
timeRangeList.append(timeRange)
return timeRangeListBecomes:
def create_ranges(revisedTimeList) :
for start, end in pairwise(revisedTimeList):
yield "{start} - {end}".format(**locals())Where
pairwise may be taken from StackOverflow and list may be needed to be called on the result if you want to index on it. Creating a generator using yield is simpler than returning a whole list.Your function was also virtually impossible to test because it relied on modifying the external variable
timeRangeList.Mine can easily be run in the interactive shell or in a doctest:
>>> list(create_ranges(["10:23", "10:45", "11:22", "13:10"]))
['10:23 - 10:45', '11:22 - 13:10']Another example is
formatDate(datesAsString) that applies a (series of) function(s) to all the items of a list, just like map does.To sum up, here is how I write functions, I think this method will help you too.
- Stop and think about a function before writing it, spend extra time on its name, it is more important than it seems at first glance.
- Decide the inputs and the outputs, make it interact with the user and the file-system only if strictly necessary.
- Decompose it into smaller chunks, describe it in English, write an example.
- Use the chunks from the std-lib or search them on StackOverflow.
Code Snippets
#Declarations:
fileName = ""
dateList = []
date = ""
formattedDate = ""
revisedDateList = []
timeList = []
studyTime = ""
revisedTimeList = []
formattedTime = ""
timeRangeList = []
listOfTimes = []
eachRow = []
subject = ""
rowDate = ""
timeRangesAsString = ""
rowTextList = []def formatTimes(revisedTimeList) :
#Grouping individual times to create time ranges:
listOfTimes = revisedTimeList
iteratorList = iter(listOfTimes)
for time in iteratorList :
timeRange = time + " - " + next(iteratorList)
timeRangeList.append(timeRange)
return timeRangeListdef create_ranges(revisedTimeList) :
for start, end in pairwise(revisedTimeList):
yield "{start} - {end}".format(**locals())>>> list(create_ranges(["10:23", "10:45", "11:22", "13:10"]))
['10:23 - 10:45', '11:22 - 13:10']Context
StackExchange Code Review Q#117700, answer score: 3
Revisions (0)
No revisions yet.