rewrite this script so that the 'subcategory' and 'timestamp' are optional properties for each entry. related_persons should be optional too

---

import json
import os
from datetime import datetime
from tabulate import tabulate
from colorama import Fore, Back, Style, init
import re

init(autoreset=True)

class Knowledge:
    def __init__(self):
        self.data = []
        self.persons = []
        self.knowledge_file = "./.meta/knowledge.json"
        self.load()
        self.print_summary()  # Print summary on initialization

    def load(self):
        if os.path.exists(self.knowledge_file):
            with open(self.knowledge_file, "r") as f:
                try:
                    knowledge = json.load(f)
                    self.data = knowledge["data"]
                    self.persons = knowledge["persons"]
                except json.JSONDecodeError:
                    print(f"{Fore.RED}Error: knowledge.json is corrupted.")

    def save(self):
        knowledge = {"data": self.data, "persons": self.persons}
        os.makedirs(os.path.dirname(self.knowledge_file), exist_ok=True)
        with open(self.knowledge_file, "w") as f:
            json.dump(knowledge, f, indent=4)

    def add_data(self, key, category, subcategory, content, related_persons, timestamp):
        if not all([key, category, subcategory, content, related_persons, timestamp]):
            raise ValueError("All keys are mandatory for data entries.")
        
        key = self.make_key_unique(key)
        
        entry = {
            "key": key,
            "category": category,
            "subcategory": subcategory,
            "content": content,
            "related_persons": related_persons,
            "timestamp": timestamp
        }

        for person in related_persons:
            if person not in [p["id"] for p in self.persons]:
                self.add_person(person, f"<no description yet, please provide one with the available information>")

        self.data.append(entry)
        self.save()
        self.print_entry_added(entry)

    def make_key_unique(self, key):
        base_key = re.match(r"([a-zA-Z]+)(\d+)", key)
        if not base_key:
            raise ValueError("Invalid key format. Key must be in the format 'nameN' where N is a number.")
        
        base_name, number = base_key.groups()
        number = int(number)
        
        existing_keys = {entry["key"] for entry in self.data}
        
        while key in existing_keys:
            number += 1
            key = f"{base_name}{number}"
        
        return key

    def add_person(self, id, description):
        if not all([id, description]):
            raise ValueError("All keys are mandatory for person entries.")
        entry = {
            "id": id,
            "description": description
        }
        self.persons.append(entry)
        self.save()
        self.print_person_added(entry)

    def search_by_category(self, category):
        results = [entry for entry in self.data if entry["category"] == category]
        self.print_search_results("Category", category, results)
        return results

    def search_by_subcategory(self, subcategory):
        results = [entry for entry in self.data if entry["subcategory"] == subcategory]
        self.print_search_results("Subcategory", subcategory, results)
        return results

    def search_by_person_id(self, person_id):
        results = [entry for entry in self.data if person_id in entry["related_persons"]]
        self.print_search_results("Person ID", person_id, results)
        return results

    def get_person_description(self, person_id):
        for person in self.persons:
            if person["id"] == person_id:
                self.print_person_description(person)
                return person["description"]
        print(f"{Fore.YELLOW}Warning: No person found with ID {person_id}")
        return None

    def search_by_timestamp(self, timestamp, relation):
        if relation not in ["before", "at_day", "after"]:
            raise ValueError("Invalid relation. Must be 'before', 'at_day', or 'after'.")
        results = []
        for entry in self.data:
            entry_timestamp = datetime.strptime(entry["timestamp"], "%Y-%m-%d %H:%M:%S")
            if relation == "before" and entry_timestamp < timestamp:
                results.append(entry)
            elif relation == "at_day" and entry_timestamp.date() == timestamp.date():
                results.append(entry)
            elif relation == "after" and entry_timestamp > timestamp:
                results.append(entry)
        self.print_search_results("Timestamp", f"{relation} {timestamp}", results)
        return results

    # Debug print functions

    def print_summary(self):
        print(f"\n{Back.BLUE}{Fore.WHITE} Knowledge Base Summary ")
        
        categories = {}
        subcategories = {}
        for entry in self.data:
            categories[entry["category"]] = categories.get(entry["category"], 0) + 1
            subcategories[entry["subcategory"]] = subcategories.get(entry["subcategory"], 0) + 1

        summary = [
            ["Total entries", len(self.data)],
            ["Total persons", len(self.persons)],
            ["Unique categories", len(categories)],
            ["Unique subcategories", len(subcategories)]
        ]

        print(tabulate(summary, headers=["Metric", "Count"], tablefmt="fancy_grid"))

    def print_entry_added(self, entry):
        print(f"{Fore.GREEN}New Entry: [{entry['key']}] {entry['category']} > {entry['subcategory']} - {entry['content']}")

    def print_person_added(self, person):
        print(f"{Fore.GREEN}New Person: {person['id']} - {person['description']}")

    def print_search_results(self, search_type, search_value, results):
        print(f"\n{Back.YELLOW}{Fore.BLACK} Search Results ")
        print(f"{Fore.BLUE}Search Type: {search_type}")
        print(f"{Fore.BLUE}Search Value: {search_value}")
        print(f"{Fore.BLUE}Results Found: {len(results)}")
        
        if results:
            table = [[entry["key"], entry["category"], entry["subcategory"], entry["content"]] for entry in results]
            print(tabulate(table, headers=["Key", "Category", "Subcategory", "Content Preview"], tablefmt="fancy_grid"))

    def print_person_description(self, person):
        print(f"\n{Back.MAGENTA}{Fore.WHITE} Person Description ")
        table = [[k, v] for k, v in person.items()]
        print(tabulate(table, headers=["Field", "Value"], tablefmt="fancy_grid"))