create documentation in detail and explain code line by line and create a flow chart also 


import time
import schedule
from settings import *
from services.waferservice import WaferService
from utils.defecthelper import *
from datetime import datetime
from modules.quadtree import Point, Rect, QuadTree

logger = logging.getLogger('service-logger')
wafer_service = WaferService()


def main():
    try:
        # get tolerance value for calculation
        tolerance = wafer_service.get_tolerance()
        tolerance = micro_to_mm(tolerance)
        wafer_status = wafer_service.get_wafer_status()
        check_process = ""
        for wafer in wafer_status:
            lot_id = wafer.get("lotId")
            wafer_number = wafer.get("waferNumber")
            # if lot_id + wafer_number == check_process:
            #     # if the process is already checked continue to next iteration.
            #     pass #continue  removed process check to diffrenetiate testNO
            wafer_id = wafer.get("waferId")
            process = wafer_service.get_process(lot_id, wafer_number,wafer)
            start_time = time.time()
            if len(process) > 1:
                check_process = lot_id + wafer_number
                for count, proc in enumerate(process):
                    wafer_inspection = wafer_service.get_wafer_inspection_data(
                        proc.get("waferId"))
                    if wafer_inspection:
                        if not wafer_inspection.get("inspectionStatus", 0):
                            # lock the wafer inspection by changing the status to 2.
                            wafer_service.update_inspection_status(
                                proc.get("waferId"),
                                INSPECTION_STATUS.get("ADDER_CALCULATION_STARTED"))
                            process_quadtree(proc, process, count,
                                             tolerance, False)

                            recalculation_needed = check_for_recalculation(
                                process)
                            if recalculation_needed and count:
                                # recalculate the adders for previous processes
                                recalculate_adder(process, count, tolerance)
                    else:
                        logger.error("Inspection Record not found for  process %s (lotId : %s, waferNumber : %s) ",
                                     proc.get("stepId"), lot_id, wafer_number)
            else:
                wafer_inspection = wafer_service.get_wafer_inspection_data(
                    wafer.get("waferId"))
                if wafer_inspection:
                    # if only one process, set all the defects for that wafer as adder
                    if not wafer_inspection.get("inspectionStatus", 0):
                        # change the inspection status to 2
                        wafer_service.update_inspection_status(wafer.get("waferId"),
                                                               INSPECTION_STATUS.get("ADDER_CALCULATION_STARTED"))
                        logger.info("Service Started at : %s for process %s "
                                    "(lotId: %s, waferNumber: %s)", datetime.now().strftime(
                                        "%d/%m/%Y %H:%M:%S"),
                                    wafer.get("stepId"), lot_id, wafer_number)
                        wafer_id = wafer.get("waferId")
                        wafer_service.set_adder(wafer_id)
                        defects = wafer_service.get_wafer_defects(wafer_id)
                        # update wafer summary
                        wafer_service.update_wafer_summary(
                            wafer_id, len(defects))
                        # change the inspection status to 1
                        wafer_service.update_inspection_status(wafer.get("waferId"),
                                                               INSPECTION_STATUS.get("ACTIVE"))
                        logger.info("%s Adders found for process %s "
                                    "(lotId : %s, waferNumber : %s) in %s seconds with tolerance %sμm",
                                    len(defects), wafer.get(
                                        "stepId"), lot_id, wafer_number,
                                    (time.time() - start_time), mm_to_micro(tolerance))
                else:
                    logger.error("Inspection Record not found for  process %s (lotId : %s, waferNumber : %s) ",
                                 wafer.get("stepId"), lot_id, wafer_number)
    except Exception as e:
        logger.error(e, exc_info=False)


def build_quad_tree(processes, start_time, tolerance):
    """
    function which builds a quadTree and returns the tree and points
    """
    if not len(processes) == 1:
        root_process = processes[0]
        root_process_defects = wafer_service.get_wafer_defects(
            root_process.get("waferId"))
        # get the waferInspection data for that process to calculate the dies
        wafer_inspection = wafer_service.get_wafer_inspection_data(
            root_process.get("waferId"))

        lot_record = wafer_inspection.get("lotRecord")
        wafer_diameter = nm_to_mm(lot_record.get("sampleSize").get("x"))
        wafer_record = lot_record.get("waferRecord")
        die_pitch = dict(height=nm_to_mm(wafer_record.get("diePitch").get("y")),
                         width=nm_to_mm(wafer_record.get("diePitch").get("x")))

        die_origin = dict(x=nm_to_mm(wafer_record.get("dieOrigin").get("x")),
                          y=nm_to_mm(wafer_record.get("dieOrigin").get("y")))

        sample_center = wafer_record.get("sampleCenterLocation")
        sample_center_location = dict(x=nm_to_mm(
            sample_center.get("x")), y=nm_to_mm(sample_center.get("y")))
        # sampleCenterLocation and dieOrigin are distance from sample coordinate origin
        wafer_center = dict(x=SAMPLE_COORDINATE_ORIGIN.get("X") + sample_center_location.get("x"),
                            y=SAMPLE_COORDINATE_ORIGIN.get("Y") - sample_center_location.get("y"))
        d_origin = dict(x=SAMPLE_COORDINATE_ORIGIN.get("X") + die_origin.get("x"),
                        y=SAMPLE_COORDINATE_ORIGIN.get("Y") - die_origin.get("y"))
        # generate the dies
        dies_obj = generate_dies(
            wafer_center, die_pitch, d_origin, wafer_diameter)
        width, height = wafer_diameter * 2, wafer_diameter * 2
        domain = Rect(width / 2, height / 2, width, height)

        q_tree = QuadTree(domain, QUAD_DEPTH)
        # get the defects of most recent process which needs to be compared with other defects
        root_points = list()
        for defect in root_process_defects:
            x1, y1, update_quad_tree = calculate_defect_position(
                defect, dies_obj, die_pitch)
            if update_quad_tree:
                point = Point(x1, y1, defect.get("_id"))
                root_points.append(point)
        points = list()
        # add the defects from previous processes to the quad tree
        for count, process in enumerate(processes):
            # get the defects from waferDefects collection for the most recent process
            if count:
                process_defects = wafer_service.get_wafer_defects(
                    process.get("waferId"))
                for defect in process_defects:
                    x1, y1, update_quad_tree = calculate_defect_position(
                        defect, dies_obj, die_pitch)
                    if update_quad_tree:
                        point = Point(x1, y1)
                        points.append(point)
                        q_tree.insert(point)
        return q_tree, root_points
    else:
        proc = processes[0]
        # make all the defects in the last process as adder
        wafer_service.set_adder(proc.get("waferId"))
        defects = wafer_service.get_wafer_defects(proc.get("waferId"))
        # update wafer summary
        wafer_service.update_wafer_summary(proc.get("waferId"), len(defects))
        # change inspection status to 1
        wafer_service.update_inspection_status(proc.get("waferId"),
                                               INSPECTION_STATUS.get("ACTIVE"))
        logger.info("%s Adders found for process %s "
                    "(lotId : %s, waferNumber : %s) in %s seconds with tolerance %sμm",
                    len(defects), proc.get("stepId"), proc.get(
                        "lotId"),  proc.get("waferNumber"),
                    (time.time() - start_time), mm_to_micro(tolerance))
        return [], []


def calculate_adder(q_tree, points, tolerance, q):
    """
    function which checks for the point inside the tree
    """
    if len(points):
        for p in points:
            found_points = list()
            q_tree.query_radius((p.x, p.y), tolerance, found_points)
            if not len(found_points):
                q.append(p.payload)


def check_for_recalculation(processes):
    """
    function which checks whether recalculation is needed for a process( if KRF is not parsed in order).
    """
    count = 0
    for proc in processes:
        wafer_inspection = wafer_service.get_wafer_inspection_data(
            proc.get("waferId"))
        if wafer_inspection.get("inspectionStatus") == INSPECTION_STATUS.get("ACTIVE"):
            count += 1
    if len(processes) == count:
        return True
    else:
        return False


def recalculate_adder(process, stop_count, tolerance):
    for count, proc in enumerate(process):
        if count == stop_count:
            break
        process_quadtree(proc, process, count, tolerance, True)


def process_quadtree(proc, process, count, tolerance, is_recalculate):

    lot_id = proc.get("lotId")
    wafer_number = proc.get("waferNumber")
    wafer_id = proc.get("waferId")
    start_time = time.time()
    logger.info("Service Started at : %s for process %s "
                "(lotId: %s, waferNumber: %s)", datetime.now().strftime(
                    "%d/%m/%Y %H:%M:%S"),
                proc.get("stepId"), lot_id, wafer_number)
    if is_recalculate:
        # remove the adders previously set
        wafer_service.remove_adder(wafer_id)
    # loop through each process to create its quadTree with remaining processes and calculate its adder/carryover
    q_tree, points = build_quad_tree(
        process[count:], start_time, tolerance)
    adder = list()
    if len(points):

        calculate_adder(q_tree, points, tolerance, adder)
        if len(adder):
            wafer_service.set_wafer_adder(adder)
        # update wafer summary count
        wafer_service.update_wafer_summary(
            proc.get("waferId"), len(adder))
        if not is_recalculate:
            # change the wafer inspection status for the process
            wafer_service.update_inspection_status(proc.get("waferId"),
                                                   INSPECTION_STATUS.get("ACTIVE"))
        logger.info("%s Adders found for process %s "
                    "(lotId : %s, waferNumber : %s) in %s seconds with tolerance %sμm",
                    len(adder), proc.get(
                        "stepId"), lot_id, wafer_number,
                    (time.time() - start_time), mm_to_micro(tolerance))


schedule.every(SERVICE_TRIGGER_INTERVAL).seconds.do(main)

if __name__ == '__main__':
    while True:
        schedule.run_pending()
        time.sleep(1)