-1

I am trying to add course data to a course, I enter id, name, start and end date, credits. Then I load a file with educations to connect the course, I choose one with the index nr, then I add the education name data to course and finally save the data back to the courses.json file.

But when I look at the course.json file, it lists all the education, not just the course I added with the education name as one of the attributes.

This is my code:

# import packages
import json
from datetime import datetime

def courses():
    # Json File Name
    filename = 'courses.json'
    
    # Read Json file and load content in data variable
    try:
        with open("courses.json", "r") as file:
            # load content
            content = file.read()
            # if json file has content save in data variable
            if content:
                data = json.loads(content)
            # else load empty json object in data variable
            else:
                data = json.loads('{"Courses": [] }')
    # exception handling
    except FileNotFoundError:
        data = json.loads('{"Courses": [] }')
    except json.decoder.JSONDecodeError:
        data = json.loads('{"Courses": [] }')

    # While User wants to add more records
    while True:
        # input course id
        courseId = input("Enter course id: ")
        # input course name
        course_name = input("Enter course name: ")
        
        # input start date
        start_input_date = input("Enter the start date in the format (yyyy-mm-dd): ")
        start_date = datetime.strptime(start_input_date, "%Y-%m-%d")
        start_date_str = start_date.strftime("%Y-%m-%d")

        # input end date
        end_input_date = input("Enter the end date in the format (yyyy-mm-dd): ")
        end_date = datetime.strptime(end_input_date, "%Y-%m-%d")
        end_date_str = end_date.strftime("%Y-%m-%d")
        
        # input course credits
        credits = int(input("Enter amount of course credits: "))
        
        # Load the JSON file
        with open("educations.json", "r") as file:
            # Load the JSON data
            data = json.load(file)
            
            # Try to convert the loaded JSON data into a list
            try:
                educations = list(data["Educations"])
            # If the "Educations" key does not exist in the "data" dictionary, assign an empty list to the "educations" variable
            except KeyError:
                educations = []
        # Get the number of educations
        num_educations = len(educations)
        print("Number of Educations:", num_educations)

        # Enumerate through the list of educations and print the index and education name
        for index, education in enumerate(educations, start=1):
            print(index, education["education_name"])

        # Get the index of the education to be chosen
        chosen_index = int(input("Enter the index of the education you want to choose: "))

        # Check if the chosen index is within the range
        if chosen_index > 0 and chosen_index <= num_educations:
            # Print the chosen education
            print("Chosen Education: ", educations[chosen_index - 1])
        else:
            print("Invalid index. Please try again.")
            
        education_name = educations[chosen_index - 1]["education_name"]
        entry = {'courseId': courseId, 'course_name': course_name, 'start_date': start_date_str, 'end_date': end_date_str, 'credits': credits, 'education_name': education_name}
        
        # Try to append new row in the "Courses" key of the "data" dictionary
        try:
            data["Courses"].append(entry)
        # If the "Courses" key does not exist in the "data" dictionary, create the "Courses" key with an empty list as its value
        except KeyError:
            data["Courses"]=[]
            data["Courses"].append(entry)
        # Store all courses in a list
        courses = data["Courses"]
        
        print("All courses: ")
        for index, course in enumerate(courses):
            print(f"{index + 1}. Course:")
            for key, value in course.items():
                print(f"\t{key}: {value}")
        
        # append data in json file
        with open(filename, 'a') as outfile:
            json.dump(courses, outfile, indent=4) # using indent to make json more readable
            outfile.write('\n')
        print("Course added to file successfully!\n")
        # ask users for inserting more records or exit program
        add_more = input("Do you want to add more courses? (y/n)")
        if add_more.lower() == 'n':
            break
    
courses()

The educations.json file has the following content:

{
    "Educations": [
        {
            "education_name": "itsak",
            "start_date": "2022-08-22",
            "end_date": "2024-05-20",
            "education_id": "itsak2023"
        },
        {
            "education_name": "Jamstack",
            "start_date": "2023-08-22",
            "end_date": "2025-05-20",
            "education_id": "Jamst2023"
        },
        {
            "education_name": "Backend developer",
            "start_date": "2023-08-22",
            "end_date": "2025-05-20",
            "education_id": "Backe2023"
        }
    ]
}

The outcome of the file is:

[
    {
        "courseId": "itstrapi",
        "course_name": "Strapi",
        "start_date": "2022-08-22",
        "end_date": "2022-09-21",
        "credits": 45,
        "education_name": "Backend developer"
    }
]
[
    {
        "courseId": "itgatsby",
        "course_name": "Gatsby",
        "start_date": "2022-09-22",
        "end_date": "2022-10-21",
        "credits": 30,
        "education_name": "Jamstack"
    }
]

But the desired outcome should be the following:

{
    "Courses": [
    {
        "courseId": "itstrapi",
        "course_name": "Strapi",
        "start_date": "2022-08-22",
        "end_date": "2022-09-21",
        "credits": 45,
        "education_name": "Backend developer"
    },
    {
        "courseId": "itgatsby",
        "course_name": "Gatsby",
        "start_date": "2022-09-22",
        "end_date": "2022-10-21",
        "credits": 30,
        "education_name": "Jamstack"
    }
]

}

I am getting output now, but not in a proper format though. What have I done wrong?

halfer
  • 19,824
  • 17
  • 99
  • 186
user2371684
  • 1,475
  • 5
  • 20
  • 45
  • 7
    You're reusing your variable `data` in `with open("educations.json", "r") as file` as `data = json.load(file)`... Maybe you should break your function down into smaller functions. You seem to be doing so much that you aren't keeping track of your variables. – Abdul Aziz Barkat Feb 09 '23 at 14:29
  • 1
    `json.dump(data, outfile, indent=4)` Here you're dumping `data`. Do you want to dump `courses` instead? – slothrop Feb 09 '23 at 14:36
  • 1
    BTW, there's an easy technique for breaking your code into smaller functions as suggested by @AbdulAzizBarkat. Look for your comments and turn them into code: `# Read Json file and ...` becomes `data = read_json(filename)` and so on. – Friedrich Feb 09 '23 at 14:43
  • 1
    Note that we prefer a technical style of writing here. We gently discourage greetings, hope-you-can-helps, thanks, advance thanks, notes of appreciation, regards, kind regards, signatures, please-can-you-helps, chatty material and abbreviated txtspk, pleading, how long you've been stuck, voting advice, meta commentary, etc. Just explain your problem, and show what you've tried, what you expected, and what actually happened. – halfer Feb 17 '23 at 13:55
  • 1
    This question was left in a confusing state. An "update" message was tacked onto the end, but not marked as such - but it amended a good deal of the original question. It's narrowly OK to make that sort of edit (as long as you have not received answers already) but do please try to ensure that the question makes sense for future readers who were not following the original ebb-and-flow of the question. – halfer Feb 17 '23 at 14:02

1 Answers1

3

I've rewrote your script into separate functions such that you can keep track of your logic and data a bit better.

Note that I only write out to the courses.json once the user indicates that they don't want to add more courses. If you want to save intermediate results, I think the easiest would be to use the .jsonl format. That format allows you to append a json object on a new line, without it being part of a top-level key.

So instead of this data structure in courses.json:

{
    "courses": [
        {"course_id": 1, ...},
        {"course_id": 2, ...},
    ]
}

you would have:

{"course_id": 1, ...}
{"course_id": 2, ...}

This answer shows how you can read .jsonl back in using Python.

# import packages
import json
from datetime import datetime


def read_courses(fname="courses.json", default_dict=dict(Courses=[])):
    # Read Json file and load content in data variable
    try:
        with open(fname, "r") as file:
            # load content
            coursedata = json.load(file)
    # exception handling
    except (FileNotFoundError, json.decoder.JSONDecodeError):
        # If no file found or if file is broken JSON load the empty default
        coursedata = default_dict

    return coursedata


def read_education(fname="educations.json"):
    # Re-use the course reader, but with a different file and default dict
    edudata = read_courses(fname=fname, default_dict=dict(Educations=[]))
    educations = list(edudata["Educations"])
    # Print the number of educations
    print("Number of Educations:", len(educations))
    return educations


def get_user_input():
    # input course id
    course_id = input("Enter course id: ")
    # input course name
    course_name = input("Enter course name: ")

    # input start date
    start_input_date = input("Enter the start date in the format (yyyy-mm-dd): ")
    start_date = datetime.strptime(start_input_date, "%Y-%m-%d")
    start_date_str = start_date.strftime("%Y-%m-%d")

    # input end date
    end_input_date = input("Enter the end date in the format (yyyy-mm-dd): ")
    end_date = datetime.strptime(end_input_date, "%Y-%m-%d")
    end_date_str = end_date.strftime("%Y-%m-%d")

    # input course credits
    credits = int(input("Enter amount of course credits: "))

    user_input = dict(
        course_id=course_id,
        course_name=course_name,
        start_date=[start_date, start_date_str],
        end_date=[end_date, end_date_str],
        credits=credits,
    )
    return user_input


def get_selected_education(educations):
    edu_selected = False
    while edu_selected is False:
        # Get the index of the education to be chosen
        chosen_index = int(input("Enter the index of the education you want to choose: "))
        # Check if the chosen index is within the range
        if chosen_index >= 0 and chosen_index <= len(educations):
            # Print the chosen education
            print("Chosen Education: ", educations[chosen_index])
            edu_selected = True
        else:
            print(
                f"{chosen_index} is an an invalid index. Please select a number in the range of 0 and {len(educations)}"
            )

    education_name = educations[chosen_index]["education_name"]
    return education_name


def print_courses(coursedata):
    # Store all courses in a list
    courses = coursedata["Courses"]

    print("All courses: ")
    for index, course in enumerate(courses):
        print(f"{index + 1}. Course:")
        for key, value in course.items():
            print(f"\t{key}: {value}")


def courses():
    coursedata = read_courses()
    educations = read_education()
    # While User wants to add more records
    while True:
        user_input = get_user_input()
        for index, education in enumerate(educations):
            print(index, education["education_name"])

        education_name = get_selected_education(educations=educations)

        entry = {
            "courseId": user_input["course_id"],
            "course_name": user_input["course_name"],
            "start_date": user_input["start_date"][1],
            "end_date": user_input["end_date"][1],
            "credits": user_input["credits"],
            "education_name": education_name,
        }
        # Append new row in the "Courses" key of the "coursedata" dictionary
        coursedata["Courses"].append(entry)

        print_courses(coursedata=coursedata)
        # ask users for inserting more records or exit program
        add_more = input("Do you want to add more courses? (y/n)")
        if add_more.lower() == "n":
            # write data to json file
            with open("courses.json", "w") as outfile:
                json.dump(coursedata, outfile, indent=4)  # using indent to make json more readable
            print("Courses added to file successfully!\n")
            break


if __name__ == "__main__":
    courses()

Output format of courses.json with above code:

{
    "Courses": [
        {
            "courseId": "1",
            "course_name": "2",
            "start_date": "1234-06-05",
            "end_date": "2345-04-03",
            "credits": 12,
            "education_name": "Backend developer"
        },
        {
            "courseId": "4",
            "course_name": "231",
            "start_date": "3456-03-02",
            "end_date": "2345-03-02",
            "credits": 44,
            "education_name": "Backend developer"
        }
    ]
}
Saaru Lindestøkke
  • 2,067
  • 1
  • 25
  • 51