Python file read write issue

Question:

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?

Asked By: user2371684

||

Answers:

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"
        }
    ]
}
Answered By: Saaru Lindestøkke
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.