Completely Lost on Event Coordinator Project

I’m on the Event Coordinator project in Generators section of the Intermediate Python course, and I’m completely at a loss as to what to do.

First step is “Modify this function to be a generator function that will yield each read line so that each guest name is yielded each time for the generator.” I have no clue how to do this, and there are no hints in Codecademy, and there isn’t a solution in the project folder.

Does anyone have any pointers?

Honestly, the yield syntax for generators is by far the worst and least intuitive thing I’ve seen in Python yet.

7 Likes

For a basic method of yielding each line in turn from a text file something like the following might suit-

def yield_file_lines(filepath):
    with open(filepath, "r") as textfile:
        for line in textfile:
            # any cleaning/splitting etc. to separate out the 'name'
            yield line  # or name...

In that project I believe you have to parse each line carefully to remove excess whitespace and to separate a guests name and age. Those steps are things you’ll likely have covered before.

Maybe spend a bit of time just playing around with basic generators and revisiting some of the earlier generator lessons if you’re completely lost. The lessons jump very quickly into the co-routine options and .send but generators themselves are exceedingly useful and much simpler than when you’re worrying about state whilst also sending values in. If you’ve come to the project and it’s not clear then it seems like a better choice to review the topic than to continue plowing ahead.

If that’s no good then it might be worth clarifying exactly what’s tripping you up with generators. If you can work out at what point you can no longer track the flow then it might be easier to get useful guidance.

1 Like

Hi there tgrtim, thanks for replying.

After digging through the forum, I’m happy to say a couple of different posts helped me get this sorted. I’m less happy to report that I had trouble on nearly every step of this project.

Firstly, in step 1, I was confused about this instruction:

Modify this function to be a generator function that will yield each read line so that each guest name is yielded each time for the generator.

I still don’t know whether this is asking me to yield the line read from the text file (yield line_data) or yield the name extracted from that line (yield name). But I opted for the later, and appended yield name to the end of the while loop to make it a generator.

Next, I was bamboozled by the instruction to use a for loop to iterate through the first 10 guests. I kept wondering, shouldn’t this be a while loop with a counter instead? Is this a typo? I now appreciate that the obvious solution was to use range() to iterate over, e.g. for r in range(1, 11). Fair enough.

It wound up getting me wildly off track, though, because instead I tried to combine a for loop with a counter, like this:

generator_object = read_guestlist("guest_list.txt")
counter = 0
for guest in generator_object:
  counter += 1
  if counter <=10:
    print(next(generator_object))

which outputs:

Tonya
Ann
Sam
Kenton
Dixie
Julian
Rose

Only seven names were printed. I was mystified. I added print("Continuing...") right before yield name to try and see if the loop was executing enough, and it was:

Continuing...
Continuing...
Tonya
Continuing...
Continuing...
Ann
Continuing...
Continuing...
Sam
Continuing...
Continuing...
Kenton
Continuing...
Continuing...
Dixie
Continuing...
Continuing...
Julian
Continuing...
Continuing...
Rose

After realizing that my earlier while loops had worked as expected, I realized that I was using next() to advance the iteration while for was iterating over it, resulting in…this weirdness.

The next problem I ran into was the issue of modifying the function to become a generator yielding the name, yet also capable of accepting Jane,35 via .send(), as this later value is more than a name. It’s a comma-separated value such as those that the function has been parsing from guest_list.txt (curious why this isn’t guest_list.csv?). This clearly wouldn’t be as simple as dropping in yield name at the end of the function. Ultimately, the solution would be to add a variable outside the while loop, then test to see if that variable was empty, and if not, format it and set it as line_data, otherwise to take line_data from the text file. Then, at the end of the loop, set the variable equal to yield name.

The lesson on using yield to supply a value from .send() though was confusing to me. As I write this, I’m just now wrapping my head around the fact that when the generator starts, nothing after yield matters, only what is before it (e.g., with var = yield name, the generator only reads val = { value from .send() }). Then, at the end of the loop, nothing before yield matters, only what comes after it (with var = yield name, the loop just “sees” yield name and exits until the next iteration).

So, I admit I had trouble getting this bit, but I wasn’t helped by also trying to diagnose other issues (Should I be yielding just the name or the whole line? shouldn’t this be a while loop? how do I format this string again? where in scope should I set the variable I set yield to? what’s up with this wonky for loop? etc.) at the same time that I was trying to wrap my head around this issue.

These things were largely interrelated as I tried to diagnose and figure out steps 1-4. I finally came across this post which had a solution for everything I had struggled with thus far.

When I got to step 5, I didn’t even understand what it meant. Were these three functions taking inputs? Was each table assigned a single dish, or does each guest select their meal? Was I supposed to use itertools.combinations() from the last section to find out how many combinations of food, table and seat were possible for each guest? Was I making a combined generator? Apparently not, since it plainly stated “Create 3 separate generator functions,” and the hint for this step didn’t allude to a combined function.

I was really at a total stand-still, until I came across this post, whose solution was a little ersatz in places, but shed light on this and further problems.

Step six alludes to the “connected generator seating/food selection we created in the previous step.” Hmm. As I noted previously, I didn’t get any bit of that from the wording or the hint for that step. It was the post above that clued me in that yes, that would indeed be required for the solution.

There were a number of issues here. I was certainly challenged by the material (and this is, after all, an intermediate course). But I think that the wording of this project’s steps was unclear at points. The hints were certainly of little help–the hint on step 2, which entailed figuring out how to refactor the generator so that it would yield the name but accept as input a comma-separated value like Jane,35, simply reminded me to use the send() method to supply a value to yield. Unlike other projects I recall having a solution.py, there was no such help here, by which I might be able at least to deduce what was less than clear to me in the way the steps and hints were worded. And this is my fault for not understanding Codecademy’s interface, but it took me a while before I realized people had posted in the forum about this: I’m accustomed to the link at the end of the steps/checkpoints that links directly the FAQ post, though these are only for lessons, I see.

I’ve filed a content bug on the project linking to this feedback, so perhaps Codecademy will review it. I hope this feedback is helpful.

Finally, if it would do any good to future readers (including myself), here was my final solution:

guests = {} def read_guestlist(file_name): text_file = open(file_name,'r') val = None while True: if val is not None: line_data = val.strip().split(",") else: line_data = text_file.readline().strip().split(",") if len(line_data) < 2: # If no more lines, close file text_file.close() break name = line_data[0] age = int(line_data[1]) guests[name] = age val = yield name guestlist = read_guestlist("guest_list.txt") for i in range(1,11): print(next(guestlist)) print(guestlist.send("Jane,35")) for g in guestlist: print(g) print("\n") whoIsOver21 = (guest for guest in guests if guests[guest] >= 21) for g in whoIsOver21: print(g) def table_one(): for i in range(1,6): yield ("Chicken", "Table 1", "Seat {}".format(i)) def table_two(): for i in range(1,6): yield ("Beef", "Table 2", "Seat {}".format(i)) def table_three(): for i in range(1,6): yield ("Fish", "Table 3", "Seat {}".format(i)) def combined_tables(): yield from table_one() yield from table_two() yield from table_three() table_assignment = combined_tables() def assign_tables(list_of_guests): for name in list_of_guests: yield (name, next(table_assignment)) guest_assignment = assign_tables(guests) for person in guest_assignment: print(person)
31 Likes

Thank you so, so much. :expressionless: :pray: This project is a real burden! Oh and, maybe it’s just me, but seating guests depending on whether they prefer beef over chicken seems somewhat… headstrong? :worried:

1 Like

I’ve noticed a lot of us getting stuck on this project, with minimal help provided. Myself included… Given my stubborn and competitive nature I had to see it to completion. Not saying my code is perfect or even fully encompasses whats asked of us in the project but I thought I’d share my code if it helps any future lost coders undertaking the Event Coordinator Project. Hope it helps!

guests = {} #Generator Functions #steps 1-4 def read_guestlist(file_name): text_file = open(file_name,'r') while True: line_data = text_file.readline().strip().split(",") if len(line_data) < 2: # If no more lines, close file text_file.close() break name = line_data[0] age = int(line_data[1]) guests[name] = age n = yield name if n != None: with open(file_name, 'a') as f: f.write('\n') f.write(n) #steps 5&6 def table1(Name, Table): yield (Name, "Chicken", "Table 1", "Seat {}".format(Table)) def table2(Name, Table): yield (Name, "Beef", "Table 2", "Seat {}".format(Table)) def table3(Name, Table): yield (Name, "Fish", "Table 2", "Seat {}".format(Table)) def assign_seating(guests): counter = 1 for GSTs in guests: if counter < 6: yield from table1(GSTs, counter) counter += 1 elif counter < 11: N = counter - 5 yield from table2(GSTs, N) counter += 1 elif counter < 16: N = counter - 10 yield from table3(GSTs, N) counter += 1 else: return "No More Seats Available" #Calling the Functions #step 1 guest_list = read_guestlist('guest_list.txt') for i in range(10): print(next(guest_list)) #step 2 guest_list.send('Jane,35') #step 3 for guest in guest_list: print(guest) #step 4 over_21 = (i for i in guests if guests[i] >= 21) for guest in over_21: print(guest + ' is over 21') #step 5 & 6 assignment = assign_seating(guests) for seating in assignment: print(seating)
10 Likes

thank you very much for your sharing

thanks so much for your explanation! describing how you solved the task was so helpful! i was a little lazy trying to solve for myself. but thanks to your post i still learned a lot… i believe :smiley:

I agree with all of your points. What I was also oblivious to was that the value being sent into the generator function by the send() should first be declared outside of the loop, as it was explained NOWHERE.

I modified the code to accept a list (as all name, age data in the original guest_list.txt are being split into name and age and being stored as a list in the line_data variable. But other than that yeah, it was very frustrating that there was no solutions.py or a project coding video for me to understand this very non-intuitive topic of generators.

Here is my code that I used for the first portion of this project by adding jane to the dictionary.

guests = {}
def read_guestlist(file_name):
  text_file = open(file_name,'r')
  while True:
    line_data = text_file.readline().strip().split(",")
    if len(line_data) < 2:
    # If no more lines, close file
      text_file.close()
      break
    name = line_data[0]
    age = int(line_data[1])
    guests[name] = age
    n = yield name
    if n is not None:
      n = n.strip().split(",")
      name = n[0]
      age = int(n[1])
      guests[name] = age
  

read = read_guestlist("guest_list.txt")
count = 0 
for i in read:
  if count == 10:
    read.send("Jane,35")
    count += 1
  else:
    count += 1

#checking to see if all was added 
print(guests)

Completely agree with everything you said. Thanks so much for posting this. I really hope Codecademy reviews this post. This is one of the worst projects I’ve seen on here to date. If anything they should at least have a walkthrough video like they do for others.

1 Like

Thank you so much. :clap: :tada:

Without this I would never have completed this project and to be truthful I never actually understood much of what was going on. A proper walkthrough on this would have been worth 1000x the walkthroughs on the simple beginner courses.
I hope that one day cc will add such a walkthrough so I can revisit this dreadery!

Anyway, I update your code slightly with these changes that I think are more in the spirit and expected result of the assignment:

  • Changed your file adds to be dictionary adds on line 18&19
  • Fixed a bug where you code skips printing Mallory because using send() will skip an output. TO combat this I moved send up before the print 10 call although you can’t send to a generator that has not started so i added a next() call to prime it on line 52
  • Reduced counters in assign_seating to 1 at the bottom. Functionally the same but just a little less cluttered. line 43

The rest is 100% stolen, if i hadn’t I’d have had to skip this projecto r be stuck on it (and the course) forever.

guests = {} #Generator Functions #steps 1-4 def read_guestlist(file_name): text_file = open(file_name,'r') while True: line_data = text_file.readline().strip().split(",") if len(line_data) < 2: # If no more lines, close file text_file.close() break name = line_data[0] age = int(line_data[1]) guests[name] = age n = yield name if n != None: name, age = n.split(',') guests[name] = int(age) #steps 5&6 def table1(Name, Table): yield (Name, "Chicken", "Table 1", "Seat {}".format(Table)) def table2(Name, Table): yield (Name, "Beef", "Table 2", "Seat {}".format(Table)) def table3(Name, Table): yield (Name, "Fish", "Table 2", "Seat {}".format(Table)) def assign_seating(guests): counter = 1 for GSTs in guests: if counter < 6: yield from table1(GSTs, counter) counter += 1 elif counter < 11: N = counter - 5 yield from table2(GSTs, N) counter += 1 elif counter < 16: N = counter - 10 yield from table3(GSTs, N) counter += 1 else: return "No More Seats Available" #Calling the Functions #step 1 guest_list = read_guestlist('guest_list.txt') #step 2 next(guest_list) # prime the generator guest_list.send('Jane,35') for i in range(10): print(next(guest_list)) #step 3 for guest in guest_list: print(guest) #step 4 over_21 = (i for i in guests if guests[i] >= 21) for guest in over_21: print(guest + ' is over 21') #step 5 & 6 assignment = assign_seating(guests) for seating in assignment: print(seating)

Yo I know that project got me too its literally the only time ive ever had literally 0 idea what to do x.x Then there aren’t even tips or a solution file either, I don’t even really understand what they want me to do. The first project I have genuinely thought was kinda badly made :S

@dev0241915634 Would you guys mind explaining to me what you believe the question is even asking? And why/how you got and implemented your solutions? I legitimately just can’t even grasp what they are even asking, I’ve re-written my code like 30 times to do different things just out of pure confusion. Would appreciate it if you could! I can see what the code is doing but it is still a bit ambiguous and I would like to understand what they are even actually asking us to do. x.x

I’m not the right person to answer this as I ended up copying 99% of the code, but I’ll try and hope I don’t lead you too much astay.

Task 1:

Modify this function to be a generator function that will yield each read line so that each guest name is yielded each time for the generator.

Using a for loop, iterate through the generator object that is retrieved by calling the generator function read_guestlist() and print out the first 10 guests on the guestlist.

The chapter taught us about iterators and how the difference between writing a loop function and an iterator is that an iterators can “pick back up where it left off” with the yield functionality
Here we are being asked a couple of things.
My first go to for these tasks is to initiate an object of the iterator:

guest_list = read_guestlist('guest_list.txt')

This doesnt do anything except creating a guest_list object so next I want to output the 10 guests using our initiated iterator. I’m doing this to just get an idea on what the iterator CC provided will do.

for i in range(10):
  print(next(guest_list))

RUnning this then creates an error:

TypeError: 'NoneType' object is not an iterator

OH NO! Why? No clue! But you know what is not inside the read_lines iterator? Good 'ol yield so i add that yield name to the end of the while loop

So this is what i have at the end of Task 1:

guests = {} def read_guestlist(file_name): text_file = open(file_name,'r') while True: line_data = text_file.readline().strip().split(",") if len(line_data) < 2: # If no more lines, close file text_file.close() break name = line_data[0] age = int(line_data[1]) guests[name] = age yield name guest_list = read_guestlist('guest_list.txt') for i in range(10): print(next(guest_list))

Which prints out:
Tim
Tonya
Mary
Ann
Beth
Sam
Manny
Kenton
Kenny
Dixie

And that matches the 10 first lines in the projects associated guest_list.txt file

Task 1 was the one I just go myself out of the box, I can try and explain my understanding of task 2 if there is a desire for it.

It’s really just that first question that’s got me as i have no idea what it is even asking me to do x.x (only reading it now)

It’s what they are even asking for question one that confuses me. The question is written so badly lol. My assumption was that they wanted the generator itself to print out the 10 names originally, and then when we turn it into a generator the dictionary ends up empty (least when I did it) which I have no idea if it is supposed to or if I am just missing something >.> God I have already done the entire ai/ml foundations course, over 40% to 75% of 3 career paths and this is the first thing I’ve gotten stuck on >.>

So I guess to make it easier, should the dictionary (guests) be empty?

It makes the entire first name - age - guests, part of the code block useless, so I feel like I am (and potentially we are) not doing something right here. ■■■■ this question honestly lol

Also, just to mention I see in your code you used next(guest_list) before your send call, and before the loop, you shouldn’t add the next call it is unnecessary and would likely lead to errors if you build that habit long-term. Also, the send call should be after the loop, it doesn’t really affect anything in this situation but again sequentially that would be the proper way to do it and in other code it could leads to errors. Thanks for replying btw, I have a feeling that we are all still going to have done this either wrong or in such a Frankenstein way lol I’m going to feed the entirety of the questions to the gpt-4 api later when I am working with it and I’ll see what it says and try to post it back here. It is surprisingly great an coding and explananing what it is doing and why.

PS. If you don’t use it might want to try out/ consider using the BingChat AI on Microsoft Edge. It is actually ridiculously handy and has boosted my efficiency and quality studying by least 50% legitimately. Is also good practice to learn to use it as in the near future likely barely a couple years away using tools like it are going to be mandatory to compete. BingChat is free, connects to the internet, is based on gpt-4, and you can use it in the sidebar while still working on your browser/webpages. It rly is quite the tool, I got early testing for it like 3-4 months ago or something and since then its loss rate (failed questions) is only 3% 0.0. With a majority of those fails being questions I designed specifically to trip it up, or time related questions it basically just can’t do also. It can but has a bit of difficulty with calculus as well, it can do it up to like intermediate problems, but it cant explain itself well for anything above beginner level problems. Anyways yeah lol It really is a huge boost to productivity and the quality of your studies. Almost like a private tutor and rather then searching up 100 website pages to find the solution to something you need, can ask it and get almost always a proper answer while also being able to ask it specific questions.

Def worth trying out at least, I pretty much guarantee you face palm for not learning about/using it earlier lol

(post deleted by author)

@markjensen9312265763 @andreworwiler3234834 I came up with a much better and easier answer for question 5 if you’d like it, I am pretty sure this is at least more along the lines of what we were supposed to do. I find its self explanatory but if you need me to explain it just ask.

import random def table_1(table='Table 1'): food = {1:'Chicken', 2: 'Beef', 3: 'Fish'} for i in range(1,6): rand = random.randint(1,3) yield (food[rand], table, f'Seat {i}') def table_2(table='Table 2'): food = {1:'Chicken', 2: 'Beef', 3: 'Fish'} for i in range(1,6): rand = random.randint(1,3) yield (food[rand], table, f'Seat {i}') def table_3(table='Table 3'): food = {1:'Chicken', 2: 'Beef', 3: 'Fish'} for i in range(1,6): rand = random.randint(1,3) yield (food[rand], table, f'Seat {i}') table1 = table_1() print(list(table1), '\n') table2 = table_2() print(list(table2), '\n') table3 = table_3() print(list(table3), '\n')

loop 10
send 1
loop rest

in this order it skipped 1 user for me due to the send, which is why i move it before my 10 loop, and to make that work i had to initiate the iterator