def get_cameras_to_export(export_info: ExportDTO, areas: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
    """
    Returns the list of cameras (camera_id, camera_list) requested in the <export_info> and the cameras
    included in the <areas>.
    """
    all_cameras = extract_config("cameras").values()
    selected_cameras = []
    if export_info.all_cameras:
        selected_cameras = all_cameras
    else:
        selected_cameras = [c for c in all_cameras if c["Id"] in export_info.cameras]
        if len(selected_cameras) != len(export_info.cameras):
            # Some of the selected cameras don't exist
            missing_cameras = set(export_info.cameras) - set([c["Id"]for c in selected_cameras])
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Cameras with ids {missing_cameras} don't exist."
            )
        # Include areas' cameras
        areas_cameras = []
        areas_ids = [a[0] for a in areas]
        for area in [a for a in extract_config("areas").values() if a['Id'] in areas_ids]:
            areas_cameras.extend(area["Cameras"].split(","))
        selected_cameras.extend(
            [c for c in all_cameras if c["Id"] in areas_cameras and c["Id"] not in export_info.cameras]
        )
    if selected_cameras:
        return [(camera["Id"], camera["Name"]) for camera in selected_cameras]
    return []
def update_classifier_config(classifier: ClassifierDTO, reboot_processor: Optional[bool] = True):
    """
    Updates the classifier configuration of the processor
    """
    config_dict = extract_config()
    classifier_dict = map_to_config_file_format(classifier)
    config_dict["Classifier"] = classifier_dict
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(classifier_dict, success)
    return map_section_from_config("Classifier", extract_config())
Exemple #3
0
def update_core_config(core: CoreDTO, reboot_processor: Optional[bool] = True):
    """
    Updates the core configuration of the processor
    """
    config_dict = extract_config()
    core_dict = map_to_config_file_format(core)
    config_dict["CORE"] = core_dict
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(core_dict, success)
    return map_section_from_config("CORE", extract_config())
def update_detector_config(detector: DetectorDTO, reboot_processor: Optional[bool] = True):
    """
    Updates the detector configuration of the processor
    """
    config_dict = extract_config()
    detector_dict = map_to_config_file_format(detector)
    config_dict["Detector"] = detector_dict
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(detector_dict, success)
    return map_section_from_config("Detector", extract_config())
Exemple #5
0
def update_app_config(app: AppDTO, reboot_processor: Optional[bool] = True):
    """
    Updates the app configuration of the processor
    """
    config_dict = extract_config()
    app_dict = map_to_config_file_format(app)
    config_dict["App"] = app_dict
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(app_dict, success)
    return map_section_from_config("App", extract_config())
Exemple #6
0
async def edit_area(area_id: str,
                    edited_area: AreaConfigDTO,
                    reboot_processor: Optional[bool] = True):
    """
    Edits the configuration related to the area <area_id>
    """
    edited_area.id = area_id
    config_dict = extract_config()
    area_names = [x for x in config_dict.keys() if x.startswith("Area_")]
    areas = [map_section_from_config(x, config_dict) for x in area_names]
    areas_ids = [area["id"] for area in areas]
    try:
        index = areas_ids.index(area_id)
    except ValueError:
        raise HTTPException(status_code=404,
                            detail=f"The area: {area_id} does not exist")

    cameras = [x for x in config_dict.keys() if x.startswith("Source_")]
    cameras = [map_camera(x, config_dict, []) for x in cameras]
    camera_ids = [camera["id"] for camera in cameras]
    if not all(x in camera_ids for x in edited_area.cameras.split(",")):
        non_existent_cameras = set(
            edited_area.cameras.split(",")) - set(camera_ids)
        raise HTTPException(
            status_code=404,
            detail=f"The cameras: {non_existent_cameras} do not exist")

    area_dict = map_to_config_file_format(edited_area)
    config_dict[f"Area_{index}"] = area_dict

    success = update_config(config_dict, reboot_processor)
    return handle_response(area_dict, success)
Exemple #7
0
async def create_area(new_area: AreaConfigDTO,
                      reboot_processor: Optional[bool] = True):
    """
    Adds a new area to the processor.
    """
    config_dict = extract_config()
    areas_name = [x for x in config_dict.keys() if x.startswith("Area_")]
    areas = [map_section_from_config(x, config_dict) for x in areas_name]
    if new_area.id in [area["id"] for area in areas]:
        raise HTTPException(status_code=400, detail="Area already exists")

    cameras = [x for x in config_dict.keys() if x.startswith("Source_")]
    cameras = [map_camera(x, config_dict, []) for x in cameras]
    camera_ids = [camera["id"] for camera in cameras]
    if not all(x in camera_ids for x in new_area.cameras.split(",")):
        non_existent_cameras = set(
            new_area.cameras.split(",")) - set(camera_ids)
        raise HTTPException(
            status_code=404,
            detail=f"The cameras: {non_existent_cameras} do not exist")
    area_dict = map_to_config_file_format(new_area)
    config_dict[f"Area_{len(areas)}"] = area_dict

    success = update_config(config_dict, reboot_processor)
    return handle_response(area_dict, success, status.HTTP_201_CREATED)
async def edit_periodic_task(periodic_task_name: str,
                             edited_periodic_task: PeriodicTaskDTO,
                             reboot_processor: Optional[bool] = True):
    """
    Edits the configuration related to the periodic task <periodic_task_name>.
    """
    edited_periodic_task.name = periodic_task_name
    config_dict = extract_config()
    edited_periodic_task_section = next(
        (key for key, value in config_dict.items()
         if key.startswith("PeriodicTask_")
         and value["Name"] == periodic_task_name), None)
    if not edited_periodic_task_section:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"The periodic_task: {periodic_task_name} does not exist")
    try:
        validate_periodic_task(edited_periodic_task)
    except ValidationError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail=str(e))
    periodic_task_file = map_to_config_file_format(edited_periodic_task, True)
    config_dict[edited_periodic_task_section] = periodic_task_file
    success = update_config(config_dict, reboot_processor)
    return handle_response(periodic_task_file, success)
async def create_periodic_task(new_periodic_task: PeriodicTaskDTO,
                               reboot_processor: Optional[bool] = True):
    """
    Adds a periodic task.
    """
    config_dict = extract_config()
    periodic_tasks_index = [
        int(x[-1]) for x in config_dict.keys() if x.startswith("PeriodicTask_")
    ]
    periodic_tasks = get_periodic_tasks()
    try:
        validate_periodic_task(new_periodic_task)
    except ValidationError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail=str(e))
    if new_periodic_task.name in [ps["name"] for ps in periodic_tasks]:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail="Periodict task already exists")
    periodic_task_file = map_to_config_file_format(new_periodic_task, True)
    index = 0
    if periodic_tasks_index:
        index = max(periodic_tasks_index) + 1
    config_dict[f"PeriodicTask_{index}"] = periodic_task_file
    success = update_config(config_dict, reboot_processor)
    return handle_response(periodic_task_file, success,
                           status.HTTP_201_CREATED)
async def delete_area(area_id: str, reboot_processor: Optional[bool] = True):
    """
    Deletes the configuration related to the area <area_id>
    """
    if area_id.upper() == ALL_AREAS:
        delete_area_occupancy_rules(ALL_AREAS)
        raise HTTPException(
            status_code=status.HTTP_202_ACCEPTED,
            detail="Area with ID: 'ALL' cannot be deleted. However, its occupancy rules were deleted."
        )
    config_dict = extract_config()
    areas_name = [x for x in config_dict.keys() if x.startswith("Area_")]
    areas = [map_section_from_config(x, config_dict) for x in areas_name]
    areas_ids = [area["id"] for area in areas]
    try:
        index = areas_ids.index(area_id)
    except ValueError:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"The area: {area_id} does not exist")

    config_dict.pop(f"Area_{index}")
    config_dict = reestructure_areas(config_dict)

    success = update_config(config_dict, reboot_processor)

    delete_area_occupancy_rules(area_id)

    area_directory = os.path.join(os.getenv("AreaLogDirectory"), area_id)
    shutil.rmtree(area_directory)
    area_config_directory = os.path.join(os.getenv("AreaConfigDirectory"), area_id)
    shutil.rmtree(area_config_directory)

    return handle_response(None, success, status.HTTP_204_NO_CONTENT)
Exemple #11
0
async def create_area(new_area: AreaConfigDTO,
                      reboot_processor: Optional[bool] = True):
    """
    Adds a new area to the processor.
    """
    config_dict = extract_config()
    areas_name = [x for x in config_dict.keys() if x.startswith("Area_")]
    areas = [map_section_from_config(x, config_dict) for x in areas_name]
    if new_area.id in [area["id"] for area in areas]:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail=bad_request_serializer(
                                "Area already exists",
                                error_type="config duplicated area"))

    cameras = [x for x in config_dict.keys() if x.startswith("Source_")]
    cameras = [map_camera(x, config_dict, []) for x in cameras]
    camera_ids = [camera["id"] for camera in cameras]
    if not all(x in camera_ids for x in new_area.cameras.split(",")):
        non_existent_cameras = set(
            new_area.cameras.split(",")) - set(camera_ids)
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"The cameras: {non_existent_cameras} do not exist")
    area_dict = map_to_config_file_format(new_area)
    config_dict[f"Area_{len(areas)}"] = area_dict
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(area_dict, success, status.HTTP_201_CREATED)
    return next(
        (area for area in get_areas() if area["id"] == area_dict["Id"]), None)
Exemple #12
0
def sync_dashboard():
    """
    Sync the processor data in the cloud dashbord
    """
    config_dict = extract_config()
    dashboard_url = config_dict["App"]["DashboardURL"]
    endpoint_url = f"{dashboard_url}api/processor/sync/cameras"
    authorization_token = config_dict["App"].get("DashboardAuthorizationToken")
    headers = {
        "content-type": "application/json",
        "Authorization": authorization_token
    }
    if not authorization_token:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail="Missing authorization token")
    cameras = [{
        "processor_camera_id": camera["id"],
        "name": camera["name"],
        "has_been_calibrated": camera["has_been_calibrated"]
    } for camera in get_cameras()]
    response = requests.put(endpoint_url, json=cameras, headers=headers)
    if response.status_code != status.HTTP_200_OK:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                            detail="Error syncing processor with dashbord")
    return response.json()
Exemple #13
0
async def delete_area(area_id: str, reboot_processor: Optional[bool] = True):
    """
    Deletes the configuration related to the area <area_id>
    """
    if area_id.upper() == ALL_AREAS:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail=bad_request_serializer(
                                "Area with ID: 'ALL' cannot be deleted.",
                                error_type="Invalid ID"))
    config_dict = extract_config()
    areas_name = [x for x in config_dict.keys() if x.startswith("Area_")]
    areas = [map_section_from_config(x, config_dict) for x in areas_name]
    areas_ids = [area["id"] for area in areas]
    try:
        index = areas_ids.index(area_id)
    except ValueError:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
                            detail=f"The area: {area_id} does not exist")

    config_dict.pop(f"Area_{index}")
    config_dict = reestructure_areas((config_dict))

    success = update_config(config_dict, reboot_processor)

    area_directory = os.path.join(os.getenv("AreaLogDirectory"), area_id)
    shutil.rmtree(area_directory)

    return handle_response(None, success, status.HTTP_204_NO_CONTENT)
def get_areas(areas: str) -> Iterator[str]:
    if ALL_AREAS in areas.upper().split(","):
        return [ALL_AREAS]
    if areas:
        return areas.split(",")
    config = extract_config(config_type=AREAS)
    return [x["Id"] for x in config.values()]
Exemple #15
0
def get_cameras_for_areas(areas: Iterator[str]) -> Iterator[str]:
    config = extract_config(config_type=AREAS)
    cameras = []
    for area_config in config.values():
        if area_config["Id"] in areas:
            cameras.extend(area_config[CAMERAS].split(","))
    return cameras
Exemple #16
0
async def edit_logger(logger_name: str, edited_logger: SourceLoggerDTO,
                      reboot_processor: Optional[bool] = True):
    """
    Edits the configuration related to the logger <logger_name>
    """
    edited_logger.name = logger_name
    config_dict = extract_config()
    edited_logger_section = next((
        key for key, value in config_dict.items()
        if key.startswith("SourceLogger_") and value["Name"] == logger_name
    ), None)
    if not edited_logger_section:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"The logger: {logger_name} does not exist")
    try:
        validate_logger(edited_logger)
    except ValidationError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=bad_request_serializer(str(e))
        )
    logger_file = map_to_config_file_format(edited_logger, True)
    config_dict[edited_logger_section] = logger_file
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(logger_file, success)
    return next((ps for ps in get_source_loggers() if ps["name"] == logger_name), None)
async def create_camera(new_camera: CameraDTO,
                        reboot_processor: Optional[bool] = True):
    """
    Adds a new camera to the processor.
    """
    config_dict = extract_config()
    cameras_name = [x for x in config_dict.keys() if x.startswith("Source_")]
    cameras = [map_camera(x, config_dict) for x in cameras_name]

    if new_camera.id is None:
        ids = [camera["id"] for camera in cameras]
        new_camera.id = str(get_first_unused_id(ids))
    elif new_camera.id in [camera["id"] for camera in cameras]:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail=bad_request_serializer(
                                "Camera already exists",
                                error_type="config duplicated camera"))
    camera_dict = map_to_camera_file_format(new_camera)
    config_dict[f"Source_{len(cameras)}"] = camera_dict
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(camera_dict, success, status.HTTP_201_CREATED)

    camera_screenshot_directory = os.path.join(
        os.environ.get("ScreenshotsDirectory"), new_camera.id)
    Path(camera_screenshot_directory).mkdir(parents=True, exist_ok=True)
    heatmap_directory = os.path.join(os.getenv("SourceLogDirectory"),
                                     new_camera.id, "objects_log")
    Path(heatmap_directory).mkdir(parents=True, exist_ok=True)

    return next(
        (camera
         for camera in get_cameras() if camera["id"] == camera_dict["Id"]),
        None)
Exemple #18
0
async def create_logger(new_logger: SourceLoggerDTO, reboot_processor: Optional[bool] = True):
    """
    Adds a logger.
    """
    config_dict = extract_config()
    loggers_index = [int(x[-1]) for x in config_dict.keys() if x.startswith("SourceLogger_")]
    loggers = get_source_loggers()
    try:
        validate_logger(new_logger)
    except ValidationError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=bad_request_serializer(str(e))
        )
    if new_logger.name in [ps["name"] for ps in loggers]:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=bad_request_serializer("Logger already exists", error_type="config duplicated logger")
        )
    logger_file = map_to_config_file_format(new_logger, True)
    index = 0
    if loggers_index:
        index = max(loggers_index) + 1
    config_dict[f"SourceLogger_{index}"] = logger_file
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(logger_file, success, status.HTTP_201_CREATED)
    return next((ps for ps in get_source_loggers() if ps["name"] == logger_file["Name"]), None)
async def get_report_info():
    app_config = extract_config()["App"]
    return {
        "emails": app_config["GlobalReportingEmails"],
        "time": app_config["GlobalReportTime"],
        "daily": get_config().get_boolean("App", "DailyGlobalReport"),
        "weekly": get_config().get_boolean("App", "WeeklyGlobalReport")
    }
def get_cameras_for_areas(areas: Iterator[str]) -> Iterator[str]:
    if areas == [ALL_AREAS]:
        return get_all_cameras()
    config = extract_config(config_type=AREAS)
    cameras = []
    for area_config in config.values():
        if area_config["Id"] in areas:
            cameras.extend(area_config[CAMERAS.capitalize()].split(","))
    return cameras
Exemple #21
0
async def update_config_file(config: ConfigDTO, reboot_processor: Optional[bool] = True):
    """
    Overwrites the configuration used by the processor.
    """
    config_dict = map_to_file_format(config)
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(config_dict, success)
    return map_config(extract_config(), "")
async def get_video_live_feed_enabled(camera_id: str):
    """
    Returns *True* if the video live feed is enabled for the camera <camera_id>
    """
    config_dict = extract_config()
    index = get_camera_index(config_dict, camera_id)
    config = get_config()
    return {
        "enabled": config.get_boolean(f"Source_{index}", "LiveFeedEnabled")
    }
Exemple #23
0
def update_tracker_config(tracker: TrackerDTO,
                          reboot_processor: Optional[bool] = True):
    """
    Updates the tracker configuration of the processor
    """
    config_dict = extract_config()
    tracker_dict = map_to_config_file_format(tracker)
    config_dict["Tracker"] = tracker_dict
    success = update_config(config_dict, reboot_processor)
    return handle_response(tracker_dict, success)
Exemple #24
0
async def update_report_info(global_report_info: GlobalReportingEmailsInfo, reboot_processor: Optional[bool] = True):
    global_report_info = global_report_info.dict(exclude_unset=True, exclude_none=True)
    config_dict = extract_config()
    key_mapping = {"GlobalReportingEmails": "emails", "GlobalReportTime": "time",
                   "DailyGlobalReport": "daily", "WeeklyGlobalReport": "weekly"}
    for key, value in key_mapping.items():
        if value in global_report_info:
            config_dict["App"][key] = str(global_report_info[value])
    success = update_config(config_dict, reboot_processor)
    return handle_response(config_dict, success)
async def delete_camera(camera_id: str,
                        reboot_processor: Optional[bool] = True):
    """
    Deletes the configuration related to the camera <camera_id>
    """
    config_dict = extract_config()
    index = get_camera_index(config_dict, camera_id)
    config_dict = delete_camera_from_areas(camera_id, config_dict)
    config_dict.pop(f"Source_{index}")
    config_dict = reestructure_cameras((config_dict))
    success = update_config(config_dict, reboot_processor)
    return handle_response(None, success, status.HTTP_204_NO_CONTENT)
async def edit_camera(camera_id: str, edited_camera: CameraDTO, reboot_processor: Optional[bool] = True):
    """
    Edits the configuration related to the camera <camera_id>
    """
    edited_camera.id = camera_id
    config_dict = extract_config()
    index = get_camera_index(config_dict, camera_id)
    camera_dict = map_to_camera_file_format(edited_camera)
    config_dict[f"Source_{index}"] = map_to_camera_file_format(edited_camera)
    success = update_config(config_dict, reboot_processor)
    if not success:
        return handle_response(camera_dict, success)
    return next((camera for camera in get_cameras(["withImage"]) if camera["id"] == camera_id), None)
async def enable_video_live_feed(camera_id: str, disable_other_cameras: Optional[bool] = True):
    """
    Enables the video live feed for the camera <camera_id>.
    By default, the video live feed for the other cameras will be disabled. You can change that behavior sending the
    *disable_other_cameras* parameter in *False*.
    """
    config_dict = extract_config()
    index = get_camera_index(config_dict, camera_id)
    if disable_other_cameras:
        for camera_section in [x for x in config_dict.keys() if x.startswith("Source_")]:
            config_dict[camera_section]["LiveFeedEnabled"] = "False"
    config_dict[f"Source_{index}"]["LiveFeedEnabled"] = "True"
    success = update_config(config_dict, True)
    return handle_response(None, success, status.HTTP_204_NO_CONTENT)
Exemple #28
0
async def export_all_data(background_tasks: BackgroundTasks):
    """
    Returns a zip file containing the csv files for all cameras and areas
    """
    # TODO: Improve this function
    cameras = [(section_dict["Id"], section_dict["Name"]) for section_dict in extract_config("cameras").values()]
    areas = [(section_dict["Id"], section_dict["Name"]) for section_dict in extract_config("areas").values()]

    temp_dir = tempfile.mkdtemp()
    export_filename = f"export-{date.today()}.zip"
    zip_path = os.path.join(temp_dir, export_filename)
    with ZipFile(zip_path, 'w', compression=ZIP_DEFLATED) as export_zip:
        for (cam_id, name) in cameras:
            object_logs_path = os.path.join(os.getenv("SourceLogDirectory"), cam_id, "objects_log")
            social_ditancing_reports_folder = f"reports/{SocialDistancingMetric.reports_folder}"
            social_ditancing_reports_path = os.path.join(os.getenv("SourceLogDirectory"), cam_id,
                                                         social_ditancing_reports_folder)
            face_mask_reports_folder = f"reports/{FaceMaskUsageMetric.reports_folder}"
            face_mask_reports_path = os.path.join(os.getenv("SourceLogDirectory"), cam_id,
                                                  face_mask_reports_folder)
            export_folder_into_zip(object_logs_path, os.path.join(
                "cameras", f"{cam_id}-{name}", "raw_data"), export_zip)
            export_folder_into_zip(social_ditancing_reports_path, os.path.join(
                "cameras", f"{cam_id}-{name}", social_ditancing_reports_folder), export_zip)
            export_folder_into_zip(face_mask_reports_path, os.path.join(
                "cameras", f"{cam_id}-{name}", face_mask_reports_folder), export_zip)
        for (area_id, name) in areas:
            occupancy_logs_path = os.path.join(os.getenv("AreaLogDirectory"), area_id, "occupancy_log")
            occupancy_report_folder = f"reports/{OccupancyMetric.reports_folder}"
            occupancy_report_path = os.path.join(os.getenv("AreaLogDirectory"), area_id,
                                                 occupancy_report_folder)
            export_folder_into_zip(occupancy_logs_path, os.path.join(
                "areas", f"{area_id}-{name}", "raw_data"), export_zip)
            export_folder_into_zip(occupancy_report_path, os.path.join(
                "areas", f"{area_id}-{name}", occupancy_report_folder), export_zip)
    background_tasks.add_task(clean_up_file, temp_dir)
    return FileResponse(zip_path, filename=export_filename)
async def create_camera(new_camera: CameraDTO,
                        reboot_processor: Optional[bool] = True):
    """
    Adds a new camera to the processor.
    """
    config_dict = extract_config()
    cameras_name = [x for x in config_dict.keys() if x.startswith("Source_")]
    cameras = [map_camera(x, config_dict) for x in cameras_name]
    if new_camera.id in [camera["id"] for camera in cameras]:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail="Camera already exists")
    camera_dict = map_to_camera_file_format(new_camera)
    config_dict[f"Source_{len(cameras)}"] = camera_dict
    success = update_config(config_dict, reboot_processor)
    return handle_response(camera_dict, success, status.HTTP_201_CREATED)
async def edit_area(area_id: str, edited_area: AreaConfigDTO, reboot_processor: Optional[bool] = True):
    """
    Edits the configuration related to the area <area_id>
    """
    if area_id.upper() == ALL_AREAS:
        area = modify_area_all(edited_area)
        if edited_area.occupancy_rules:
            set_occupancy_rules(ALL_AREAS, edited_area.occupancy_rules)
        else:
            delete_area_occupancy_rules(ALL_AREAS)
        area["occupancy_rules"] = get_area_occupancy_rules(ALL_AREAS)
        return area

    edited_area.id = area_id
    config_dict = extract_config()
    area_names = [x for x in config_dict.keys() if x.startswith("Area_")]
    areas = [map_section_from_config(x, config_dict) for x in area_names]
    areas_ids = [area["id"] for area in areas]
    try:
        index = areas_ids.index(area_id)
    except ValueError:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"The area: {area_id} does not exist")

    cameras = [x for x in config_dict.keys() if x.startswith("Source_")]
    cameras = [map_camera(x, config_dict, []) for x in cameras]
    camera_ids = [camera["id"] for camera in cameras]
    if not all(x in camera_ids for x in edited_area.cameras.split(",")):
        non_existent_cameras = set(edited_area.cameras.split(",")) - set(camera_ids)
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"The cameras: {non_existent_cameras}"
                                                                          f"do not exist")

    occupancy_rules = edited_area.occupancy_rules
    del edited_area.occupancy_rules

    area_dict = map_to_config_file_format(edited_area)
    config_dict[f"Area_{index}"] = area_dict
    success = update_config(config_dict, reboot_processor)

    if occupancy_rules:
        set_occupancy_rules(edited_area.id, occupancy_rules)
    else:
        delete_area_occupancy_rules(area_id)

    if not success:
        return handle_response(area_dict, success)
    area = next((area for area in get_areas() if area["id"] == area_id), None)
    area["occupancy_rules"] = get_area_occupancy_rules(area["id"])
    return area