from cast import Cast
from player import Player
from script import Script
from move_actors_action import MoveActorsAction
from draw_actors_action import DrawActorsAction
from control_actors_action import ControlActorsAction
from director import Director
from output_service import OutputService
from input_service import InputService
from basic_item import basic_item
from point import Point
from load_assets_action import LoadAssetsAction
from unload_assets_action import UnloadAssetsAction
from basic_description import BasicDescription
from handle_collisions_action import HandleCollisionsAction
import constants
import random
from portrait import Portrait

def main():

    cast = Cast()
    player = Player()
    cast.add_actor("players", player)
    cast.add_actor("basic_description", BasicDescription())
    cast.add_actor("portrait", Portrait())
    #add more actors here
    print("added player")

    output_service = OutputService(player)
    #outputservice needs to be started with a player in order to calculate changes due to screen motion
    input_service = InputService()

    addRandomBasicItems(cast, output_service)

    script = Script()
    script.add_action("loading", LoadAssetsAction(output_service))
    script.add_action("unloading", UnloadAssetsAction(output_service))
    script.add_action("input", ControlActorsAction(input_service))
    script.add_action("update", MoveActorsAction(output_service))
    script.add_action("update", HandleCollisionsAction())
    #move actors needs output service too because actors need to get deltatime to update their positions
    #which is in output service
    script.add_action("output", DrawActorsAction(output_service))
    #add more scripts here, like collisions

    director = Director(output_service)
    director.start_game(cast, script)

def addBasicItemsForTesting(cast, output_service):
    positions = [[45, 45],
        [800, 800],
        [20, 800],
        [1500, 1500]]
    for position in positions:
        testItem = basic_item(Point(position[0], position[1]), output_service)
        cast.add_actor("basic_items", testItem)

def addRandomBasicItems(cast, output_service):
    column_start_y = 0 - constants.MAX_Y * 5
    sectors = []
    for i in range(0, 10):
        y_position = column_start_y + i * constants.MAX_Y
        addSectorRow(y_position, sectors)

    for sector in sectors:
        for position in sector:
            testItem = basic_item(Point(position._x, position._y), output_service)
            cast.add_actor("basic_items", testItem)

def testSector(cast, output_service):
    sec1 = createSector(0, 0)
    for position in sec1:
        testItem = basic_item(Point(position._x, position._y), output_service)
        cast.add_actor("basic_items", testItem)

def addSectorRow(y, sectors):
    row_start_x = 0 - constants.MAX_X * 5
    for i in range(0, 10):
        x_position = row_start_x + i * constants.MAX_X
        sector = createSector(x_position, y)
        sectors.append(sector)

def createSector(x, y):
    sector = []
    totalCount = random.randint(2, 4)
    for i in range(0, totalCount):
        randx = x + random.randint(0, constants.MAX_X)
        randy = y + random.randint(0, constants.MAX_Y)

        sector.append(Point(randx, randy))

    return sector

if __name__ == "__main__":
    main()


import cv2
import numpy as np
import time
import argparse

CONFIDENCE = 0.3
SCORE_THRESHOLD = 0.5
IOU_THRESHOLD = 0.5

config_path = "src/cgf.txt"
weights_path = "src/yolov3.weights"
labels = open("src/images.txt").read().strip().split("\n")
net = cv2.dnn.readNetFromDarknet(config_path, weights_path)

# generating colors for each object for later plotting
colors = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8")

# get all the layer names
ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]

def object_detect(image):
    h, w = image.shape[:2]

    # create 4D blob
    blob = cv2.dnn.blobFromImage(image, 1/255.0, (416, 416), swapRB=True, crop=False)

    # sets the blob as the input of the network
    net.setInput(blob)

    # feed forward (inference) and get the network output measure how much it took in seconds
    start = time.perf_counter()
    layer_outputs = net.forward(ln)
    time_took = time.perf_counter() - start
    print(f"Time took: {time_took:.2f}s")

    boxes, confidences, class_ids = [], [], []

    # loop over each of the layer outputs
    for output in layer_outputs:

        # loop over each of the object detections
        for detection in output:

            # extract the class id (label) and confidence (as a probability) of the current object detection
            scores = detection[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]

            # discard weak predictions by ensuring the detected probability is greater than the minimum probability
            if confidence > CONFIDENCE:

                # scale the bounding box coordinates back relative to the size of the image, keeping in mind that YOLO actually
                # returns the center (x, y)-coordinates of the bounding box followed by the boxes' width and height
                box = detection[:4] * np.array([w, h, w, h])
                (centerX, centerY, width, height) = box.astype("int")
                # use the center (x, y)-coordinates to derive the top and and left corner of the bounding box
                x = int(centerX - (width / 2))
                y = int(centerY - (height / 2))
                # update our list of bounding box coordinates, confidences,and class IDs
                boxes.append([x, y, int(width), int(height)])
                confidences.append(float(confidence))
                class_ids.append(class_id)

    # perform the non maximum suppression given the scores defined before
    idxs = cv2.dnn.NMSBoxes(boxes, confidences, SCORE_THRESHOLD, IOU_THRESHOLD)
    font_scale = 1
    thickness = 2

    # ensure at least one detection exists
    if len(idxs) > 0:

        # loop over the indexes we are keeping
        for i in idxs.flatten():

            # extract the bounding box coordinates
            x, y = boxes[i][0], boxes[i][1]
            w, h = boxes[i][2], boxes[i][3]

            # draw a bounding box rectangle and label on the image
            color = [int(c) for c in colors[class_ids[i]]]
            cv2.rectangle(image, (x, y), (x + w, y + h), color=color, thickness=thickness)
            text = f"{labels[class_ids[i]]}: {confidences[i]:.2f}"

            # calculate text width & height to draw the transparent boxes as background of the text
            (text_width, text_height) = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, fontScale=font_scale, thickness=thickness)[0]
            text_offset_x = x
            text_offset_y = y - 5
            box_coords = ((text_offset_x, text_offset_y), (text_offset_x + text_width + 2, text_offset_y - text_height))
            overlay = image.copy()
            cv2.rectangle(overlay, box_coords[0], box_coords[1], color=color, thickness=2)

            # add opacity (transparency to the box)
            image = cv2.addWeighted(overlay, 0.6, image, 0.4, 0)

            # now put the text (label: confidence %)
            cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX,fontScale=font_scale, color=(0, 0, 0), thickness=thickness)
    return image

if __name__=='__main__':
    ap = argparse.ArgumentParser()
    ap.add_argument("input_path", required=True, help="path to input image")
    ap.add_argument("output_path", required=True, help="path to output image")
    ap.add_argument("confidence",type=float, default=0.5,help="minimum probability to filter weak detections")
    ap.add_argument("threshold", type=float, default=0.3, help="threshold when applying non-maxima suppression")
    args = vars(ap.parse_args())
    global CONFIDENCE,IOU_THRESHOLD
    CONFIDENCE=args.confidence
    IOU_THRESHOLD=args.threshold
    img = cv2.imread(args.input_path)
    output=object_detect(img)
    cv2.imwrite(args.output_path, output)


from utils import get_db_collection
from bson.objectid import ObjectId
from Category import Category

COLLECTION_NAME = "Categories"

class CategoryAPI():
    #[input]  cat object
    #[return] _id of inserted category
    def insert(self, cat):
        collection = get_db_collection(COLLECTION_NAME)
        srchRlt = collection.find_one(cat.toQuery())
        if srchRlt is None:
            cat_id = collection.insert_one(cat.toQuery()).inserted_id
        else:
            cat_id = srchRlt['_id']
        return cat_id

    #[input]  cat object
    #[return] updated result
    def update(self, cat):
        collection = get_db_collection(COLLECTION_NAME)
        if cat.cat_id is None:
            print("Cat. ID cannot be None")
            return None
        elif collection.find_one({"_id": ObjectId(cat.cat_id)}) is None:
            print("Cat. ID does not exist, please create new cat. instead")
            return None
        else:
            return collection.update_one({ "_id": ObjectId(cat.cat_id)}, {'$set':cat.toQuery()})

    #[input]  cat id
    #[return] cat object
    def get(self, cat_id):
        collection = get_db_collection(COLLECTION_NAME)
        srchRlt = collection.find_one({"_id": ObjectId(cat_id)})
        if srchRlt == None:
            print("The Category corresponding the input cat. id does not exist")
            return None
        else:
            cat = Category(srchRlt["catName"], srchRlt["_id"])
            return cat

    #[input]  None
    #[return] list of cat object
    def list(self):
        collection = get_db_collection(COLLECTION_NAME)
        results = collection.find({})
        cats = []
        for result in results:
            cat = Category(result["catName"], result["_id"])
            cats.append(cat)
        return cats

    #[input]  cat id
    #[return] deleted result
    def delete(self, cat_id):
        collection = get_db_collection(COLLECTION_NAME)
        srchRlt = collection.find_one({"_id": ObjectId(cat_id)})
        if srchRlt is None:
            print("The Category corresponding the input cat. id does not exist")
            return None
        else:
            result = collection.delete_one({"_id": ObjectId(cat_id)})
            return result


'''
Definition of BaseGFSClient
class BaseGFSClient:
    def readChunk(self, filename, chunkIndex):
        # Read a chunk from GFS
    def writeChunk(self, filename, chunkIndex, content):
        # Write a chunk to GFS
'''

class GFSClient(BaseGFSClient):
    """
    @param: chunkSize: An integer
    """
    def __init__(self, chunkSize):
        self.baseGFSClient = BaseGFSClient()
        self.chunkSize = chunkSize
        self.chunk = {}

    """
    @param: filename: a file name
    @return: conetent of the file given from GFS
    """
    def read(self, filename):
        if filename not in self.chunk:
            return
        content = ""
        chunkNum = self.chunk[filename]
        for i in xrange(chunkNum):
            subContent = self.baseGFSClient.readChunk(filename, i)
            if subContent:
                content += subContent
        return content

    """
    @param: filename: a file name
    @param: content: a string
    @return: nothing
    """
    def write(self, filename, content):
        length = len(content)
        chunkNum = length / self.chunkSize + 1
        self.chunk[filename] = chunkNum
        for i in xrange(chunkNum):
            subContent = content[i*self.chunkSize: (i+1)*self.chunkSize]
            self.baseGFSClient.writeChunk(filename, i, subContent)


import sqlite3
from class_style import Styl
from tkinter import*
from tkinter import ttk
root = Tk()
    #style de notre treeview

style = Styl()

        #inventaire
tree_frame_inventaire = Frame(root)
my_scrollbar = Scrollbar(tree_frame_inventaire, orient=VERTICAL)
my_tree_inventaire = ttk.Treeview(tree_frame_inventaire, yscrollcommand=my_scrollbar.set)
my_scrollbar.config(command=my_tree_inventaire.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
#Colonne
my_tree_inventaire['columns'] = ("article", "prix", "nombre")
my_tree_inventaire.column("#0", width=90, minwidth=35)
my_tree_inventaire.column("article", anchor=W, width=200,minwidth=190)
my_tree_inventaire.column("prix", anchor=CENTER,width=110,minwidth=100)
my_tree_inventaire.column("nombre", anchor=CENTER, width=75,minwidth=70)

#en tete
my_tree_inventaire.heading("#0", text="Numero", anchor=W)
my_tree_inventaire.heading("article", text="Artcile", anchor=W)
my_tree_inventaire.heading("prix", text="Prix/Ar", anchor=CENTER)
my_tree_inventaire.heading("nombre", text="Nombre", anchor=CENTER)
my_tree_inventaire.tag_configure('font', font=('Arial','15','bold italic'))
#les themes
my_tree_inventaire.tag_configure('defaut',background="#C0C0C0")
my_tree_inventaire.tag_configure('white',background="white")
my_tree_inventaire.tag_configure('blue',background="#0080C0")
my_tree_inventaire.tag_configure('red',background="#FF0000")
my_tree_inventaire.tag_configure('green',background="#00FF00")

#Corps
frame_inventaire = Frame(root)
entrer_article_inventaire = Entry(frame_inventaire,font=('Arial',20))
entrer_prix_inventaire = Entry(frame_inventaire,font=('Arial',20),width=15)
entrer_nombre_inventaire = Entry(frame_inventaire,font=('Arial',20),width=10)

frame_bouton_inventaire = Frame(root)

        #commande
tree_frame_commande = Frame(root)
my_scrollbar = Scrollbar(tree_frame_commande, orient=VERTICAL)
my_tree_commande = ttk.Treeview(tree_frame_commande, yscrollcommand=my_scrollbar.set)
my_scrollbar.config(command=my_tree_commande.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
#Colonne
my_tree_commande['columns'] = ("article", "prix", "quantiter", "total")
my_tree_commande.column("#0", width=90, minwidth=35)
my_tree_commande.column("article", anchor=W, width=200,minwidth=190)
my_tree_commande.column("prix", anchor=CENTER,width=110,minwidth=100)
my_tree_commande.column("quantiter", anchor=CENTER, width=75,minwidth=70)
my_tree_commande.column("total", anchor=CENTER, width=75,minwidth=70)

#en tete
my_tree_commande.heading("#0", text="Numero", anchor=W)
my_tree_commande.heading("article", text="Artcile", anchor=W)
my_tree_commande.heading("prix", text="Prix", anchor=CENTER)
my_tree_commande.heading("quantiter", text="Quantiter", anchor=CENTER)
my_tree_commande.heading("total", text="Total", anchor=CENTER)
my_tree_commande.tag_configure('font', font=('Arial','15','bold italic'))

frame_commande = Frame(root)
frame_client = Frame(root)
entrer_article_commande = Entry(frame_commande,font=('Arial',20))
entrer_quantiter_commande = Entry(frame_commande,font=('Arial',20),width=15)
entrer_nom_de_client = Entry(frame_client,font=('Arial',20),width=25)
resultat = Listbox(root,bg="#D8D8D8",height=1,width=10,font=("Helvetica",25))

frame_box = Frame(root)
my_scrollbar_box = Scrollbar(frame_box, orient=VERTICAL)
liste_box = Listbox(frame_box,width=50, yscrollcommand=my_scrollbar_box.set)
my_scrollbar_box.config(command=liste_box.yview)
toppings = []

frame_bouton_commande = Frame(root)


"""
So I came upon this Google Video and saw an exercise
https://www.youtube.com/watch?v=XKu_SEDAykw

I thought this technique would be nice to have in my
back pocket for the future.

Challenge:
Given a sorted array of integers, you must find two
numbers that sum up to the given target value.

O(n)
"""

import time

def find_sums(array, value):
  head = 0
  tail = len(array) - 1
  while True:
    summed = array[head] + array[tail]
    if summed > value:
      tail -= 1
    elif summed < value:
      head += 1
    elif summed == value:
      return (array[head], array[tail])
    elif head >= tail:
      return (None, None)

def debug(array, value):
  print("========== Start ==========")
  start_time = time.time()
  (head, tail) = find_sums(array, value)
  end_time = time.time()
  print("target: {0}".format(value))
  print("found in array: {0} and {1}".format(head, tail))
  print("time: {0}".format(end_time - start_time))
  print("==========  End  ==========\n\n")

# start here

array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
target = 8

debug(array, target)


import functools
from os import PathLike

from pydra import Workflow
from pydra.engine.core import TaskBase

import clinica.pydra.engine_utils as pu
from clinica.pydra.interfaces import bids_reader, bids_writer, caps_reader

def clinica_io(func):
    """Decorator to add BIDS reader/writer to any Pydra workflow."""

    @functools.wraps(func)
    def run_wrapper(
        name: str, parameters: dict, input_dir: PathLike, output_dir: PathLike
    ) -> Workflow:

        core_workflow = func(parameters=parameters)

        pipeline = Workflow(
            name=name,
            input_spec=["bids_dir", "caps_dir"],
            bids_dir=input_dir,
            caps_dir=output_dir,
        )

        build_input_workflow(pipeline, core_workflow)

        # TODO: define condition on split if multiple fields
        pipeline.add(core_workflow)

        build_output_workflow(pipeline, core_workflow, output_dir)

        return pipeline

    return run_wrapper

def add_input_reading_task(
    pipeline: Workflow,
    core_workflow: Workflow,
    query_type: str,
    reader: TaskBase,
) -> Workflow:
    """Configure and add the reading tasks of input workflow.

    Parameters
    ----------
    pipeline : Workflow
        The main Workflow to which the readers should be added.

    core_workflow : Workflow
        The core workflow. It defines, through its inputs,
        what the reader workflow should read and these two
        workflows should connect.

    query_type : {"bids", "caps_file", "caps_group"}
        The type of query that should be run.

    reader : TaskBase
        Task responsible for reading data.

    Returns
    -------
    pipeline : Workflow
        The main Workflow with readers added to it.
    """
    from clinica.pydra.query import query_factory

    query = query_factory(pu.list_workflow_inputs(core_workflow), query_type=query_type)
    if len(query) == 0:
        return pipeline
    input_dir = "bids_dir" if "bids" in reader.__name__ else "caps_dir"
    input_workflow = Workflow(
        name=f"input_workflow_{query_type}_reader",
        input_spec=["input_dir"],
    )
    try:
        input_workflow.inputs.input_dir = getattr(pipeline.lzin, input_dir)
    except AttributeError:
        raise AttributeError(
            f"Workflow has no {input_dir} input. Please verify your input specifications."
        )
    input_workflow = add_input_task(
        input_workflow,
        reader(query=query, input_dir=input_workflow.lzin.input_dir),
    )
    pipeline.add(input_workflow)

    # connect input workflow to core workflow
    for field, _ in query.query.items():
        read_data = getattr(input_workflow.lzout, field)
        setattr(core_workflow.inputs, field, read_data)

    return pipeline

add_input_reading_task_bids = functools.partial(
    add_input_reading_task,
    query_type="bids",
    reader=bids_reader,
)

add_input_reading_task_caps_file = functools.partial(
    add_input_reading_task,
    query_type="caps_file",
    reader=caps_reader,
)

add_input_reading_task_caps_group = functools.partial(
    add_input_reading_task,
    query_type="caps_group",
    reader=caps_reader,
)

def build_input_workflow(pipeline: Workflow, core_workflow: Workflow) -> Workflow:
    """Setup for an input workflow.

    For now, the input workflow is responsible for:

        - reading BIDS data
        - reading CAPS data with clinica_file_reader
        - reading CAPS data with clinica_group_reader

    Parameters
    ----------
    pipeline :  Workflow
         The high level workflow containing (input -> core -> output).

    core_workflow : Workflow
         The functional workflow.

    Returns
    -------
    pipeline : Workflow
        The pipeline with the input workflow.
    """
    pipeline = add_input_reading_task_bids(pipeline, core_workflow)
    pipeline = add_input_reading_task_caps_file(pipeline, core_workflow)
    pipeline = add_input_reading_task_caps_group(pipeline, core_workflow)
    return pipeline

def add_input_task(input_workflow: Workflow, task: TaskBase) -> Workflow:
    """Add a task to the input workflow and define the workflow outputs.

    Parameters
    ----------
    input_workflow : Workflow
        The high level workflow containing (input -> core -> output).

    task : TaskBase
        The task to be added to the input_workflow.

    Returns
    -------
    input_workflow : Workflow
         The input workflow with the task added to it.
    """
    input_workflow.add(task)
    input_workflow.set_output(
        [
            (
                field,
                getattr(getattr(input_workflow, f"{task.name}").lzout, field),
            )
            for field in task.output_names
        ]
    )
    return input_workflow

def build_output_workflow(
    pipeline: Workflow, core_workflow: Workflow, output_dir: PathLike
) -> Workflow:
    """Example of an output workflow.

    Parameters
    ----------
    pipeline : Workflow
        the resulting workflow consisting of (input/core/output)
    core_workflow : Workflow
        contains the core interfaces
    output_dir : PathLike
        Path of the directory to be written to

    Returns
    -------
    Workflow
        The pipeline with the output workflow.
    """

    output_attrs = []

    for i, field in enumerate(pu.list_out_fields(core_workflow)):
        pipeline.add(bids_writer(name="bids_writer_task_" + str(field)))

        writer_task = getattr(pipeline, "bids_writer_task_" + str(field))
        writer_task_inputs = getattr(writer_task, "inputs")

        output_data = getattr(core_workflow.lzout, field)

        setattr(writer_task_inputs, "output_file", output_data)
        setattr(writer_task_inputs, "output_dir", output_dir)

        output_attrs.append((field, output_data))

    pipeline.set_output(output_attrs)

    return pipeline


import my_enums
from tigr import AbstractDrawer
from tkinter import *

class DrawerTkinter(AbstractDrawer):

    def __init__(self, set_width=1000, set_height=1000):
        self.window = Tk()
        self.canvas = Canvas(self.window, width=set_width, height=set_height)
        self.canvas.grid(row=0, column=0)
        self.colour = ''
        self.src_x = 500
        self.src_y = 500
        self.des_x = 0
        self.des_y = 0
        self.penIsDown = False

    def select_pen(self, pen_num):
        self.colour = my_enums.Pen.colours[pen_num]
        print("Current colour is " + self.colour)

    def pen_down(self):
        self.penIsDown = True
        print("penIsDown == " + str(self.penIsDown))

    def pen_up(self):
        self.penIsDown = False
        print("penIsDown == " + str(self.penIsDown))

    def go_along(self, along):
        self.src_x += along
        print("Move " + str(along) + " along")
        print("source_x == " + str(self.src_x) + "source_y == " + str(self.src_y))

    def go_down(self, down):
        self.src_y += down
        print("Move " + str(down) + " along")
        print("source_x == " + str(self.src_x) + "source_y == " + str(self.src_y))

    def draw_line(self, direction, distance):
        if direction == 90:
            self.des_y = self.src_y - distance
            self.des_x = self.src_x
            print("going UP " + str(distance))
        if direction == 270:
            self.des_y = self.src_y + distance
            self.des_x = self.src_x
            print("going DOWN " + str(distance))
        if direction == 0:
            self.des_x = self.src_x + distance
            self.des_y = self.src_y
            print("going RIGHT " + str(distance))
        if direction == 180:
            self.des_x = self.src_x - distance
            self.des_y = self.src_y
            print("going LEFT  " + str(distance))

        if self.penIsDown:
            print("src_x == " + str(self.src_x) + "/ src_y == " + str(self.src_y) + "des_x == " + str(
                self.des_x) + "/ des_y == " + str(self.des_y))
            self.canvas.create_line(self.src_x, self.src_y, self.des_x, self.des_y, fill=self.colour)
            print("just drew a line")

        self.src_x, self.src_y = self.des_x, self.des_y
        print("source_x == " + str(self.src_x) + "source_y == " + str(self.src_y))

    def update(self):
        self.window.mainloop()


"""Module to create zoning configuration related DataFrames"""

import utilities.database_operations as dbop
import utilities.dataframe_operations as dfop
import utilities.module_execution as meop
import utilities.report_operations as report

from .report_zoning import zoning_report_main
from .zoning_aggregation import verify_cfg_type, zoning_aggregated
from .zoning_alias_dashboard import alias_dashboard
from .zoning_cfg_dashboard import cfg_dashborad
from .zoning_statistics import zonemember_statistics

def zoning_analysis(switch_params_aggregated_df, portshow_aggregated_df,
                            cfg_df, zone_df, alias_df, cfg_effective_df,
                            fcrfabric_df, lsan_df, peerzone_df,
                            project_constants_lst):
    """Main function to analyze zoning configuration"""

    # imported project constants required for module execution
    project_steps_df, max_title, io_data_names_df, _, report_headers_df, report_columns_usage_sr, *_ = project_constants_lst

    # data titles obtained after module execution (output data)
    # data titles which module is dependent on (input data)
    data_names, analyzed_data_names = dfop.list_from_dataframe(io_data_names_df, 'zoning_analysis_out', 'zoning_analysis_in')
    # module information
    meop.show_module_info(project_steps_df, data_names)
    # read data from database if they were saved on previos program execution iteration
    data_lst = dbop.read_database(project_constants_lst, *data_names)

    # force run when any output data from data_lst is not found in database or
    # procedure execution explicitly requested (force_run flag is on) for any output or input data
    force_run = meop.verify_force_run(data_names, data_lst, project_steps_df,
                                            max_title, analyzed_data_names)
    if force_run:
        # current operation information string
        info = f'Generating zoning table'
        print(info, end =" ")

        # aggregated DataFrames
        zoning_aggregated_df, alias_aggregated_df \
            = zoning_aggregated(switch_params_aggregated_df, portshow_aggregated_df,
                                    cfg_df, zone_df, alias_df, cfg_effective_df, fcrfabric_df, lsan_df, peerzone_df)

        # create comprehensive statistics DataFrame with Fabric summaries and
        # zones statistics DataFrame without summaries
        zonemember_statistics_df, zonemember_zonelevel_stat_df = zonemember_statistics(zoning_aggregated_df)
        # add zoning statistics notes, zone duplicates and zone pairs to zoning aggregated DataFrame
        zoning_aggregated_df = statistics_to_aggregated_zoning(zoning_aggregated_df, zonemember_zonelevel_stat_df)
        # check all fabric devices (Wwnp) for usage in zoning configuration
        portshow_zoned_aggregated_df = verify_cfg_type(portshow_aggregated_df, zoning_aggregated_df, ['PortName'])
        # create alias configuration statistics
        alias_statistics_df = alias_dashboard(alias_aggregated_df, portshow_zoned_aggregated_df)
        # create Effective zoning configuration summary statistics
        effective_cfg_statistics_df = cfg_dashborad(zonemember_statistics_df, portshow_zoned_aggregated_df, zoning_aggregated_df, alias_aggregated_df)
        # after finish display status
        meop.status_info('ok', max_title, len(info))

        # report tables
        zoning_report_df, alias_report_df, zoning_compare_report_df, \
            unzoned_device_report_df, no_alias_device_report_df, zoning_absent_device_report_df, \
                zonemember_statistics_report_df, alias_statistics_report_df, effective_cfg_statistics_report_df = \
                    zoning_report_main(zoning_aggregated_df, alias_aggregated_df, portshow_zoned_aggregated_df,
                                        zonemember_statistics_df, alias_statistics_df, effective_cfg_statistics_df,
                                        data_names, report_headers_df, report_columns_usage_sr)

        # create list with partitioned DataFrames
        data_lst = [zoning_aggregated_df, alias_aggregated_df, portshow_zoned_aggregated_df,
                    zonemember_statistics_df, alias_statistics_df, effective_cfg_statistics_df,
                    zoning_report_df, alias_report_df, zoning_compare_report_df, unzoned_device_report_df,
                    no_alias_device_report_df, zoning_absent_device_report_df, zonemember_statistics_report_df,
                    alias_statistics_report_df, effective_cfg_statistics_report_df]

        # writing data to sql
        dbop.write_database(project_constants_lst, data_names, *data_lst)
    # verify if loaded data is empty and replace information string with empty DataFrame
    else:
        data_lst = dbop.verify_read_data(max_title, data_names, *data_lst)
        zoning_aggregated_df, alias_aggregated_df, portshow_zoned_aggregated_df, *_ = data_lst

    # save data to service file if it's required
    for data_name, data_frame in zip(data_names, data_lst):
        report.dataframe_to_excel(data_frame, data_name, project_constants_lst)
    return zoning_aggregated_df, alias_aggregated_df, portshow_zoned_aggregated_df

def statistics_to_aggregated_zoning(zoning_aggregated_df, zonemember_zonelevel_stat_df):
    """
    Function to compliment zoning_aggregated_df DataFrame with 'Target_Initiator'and 'Target_model' notes
    obtained from zone statistics DataFrame analysis
    """

    # create DataFrame with note columns only
    zone_columns = ['Fabric_name', 'Fabric_label', 'cfg', 'cfg_type', 'zone']
    note_columns = ['Zone_name_device_names_ratio', 'Zone_name_device_names_related',
                    'zone_duplicated', 'zone_absorber', 'zone_paired',
                    'Zone_and_Pairzone_names_ratio', 'Zone_and_Pairzone_names_related',
                    'Pair_zone_note',
                    'Target_Initiator_note', 'Target_model_note', 'Mixed_zone_note', 'Effective_cfg_usage_note']
    note_columns = [column for column in note_columns if column in zonemember_zonelevel_stat_df.columns]
    zonenote_df = zonemember_zonelevel_stat_df.reindex(columns = [*zone_columns, *note_columns]).copy()
    # compliment aggregated DataFrame
    zoning_aggregated_df = zoning_aggregated_df.merge(zonenote_df, how='left', on=zone_columns)
    return zoning_aggregated_df