From 54df55b3d0115ae740aaca8ded2f6c8b850184a0 Mon Sep 17 00:00:00 2001 From: ElmiraOn Date: Tue, 12 Dec 2023 22:00:02 -0500 Subject: [PATCH 1/4] added documentation --- Visualizer/colors.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Visualizer/colors.py b/Visualizer/colors.py index 535d53f..a7baffe 100644 --- a/Visualizer/colors.py +++ b/Visualizer/colors.py @@ -1,4 +1,22 @@ +# This class instantiate the colors used for different components of the network: +# aER: Activity ER | default: #FF7273 +# rER: rubric ER | default: #FF7273 +# iER: instructional ER | default: #F69159 +# general: any nodes that are not one of specified/ atomic ER with type zip | default: #ECD19A +# assesses: assess/ is assessed by relationship | default: #FF7273 +# comesAfter: comes after / comes before relation ship | default: #C0CB6B +# isPartOf: is part of/ has part relationship | default: #ECD19A +# start: start node | default: #C0CB6B +# end: end node | default: #C0CB6B +# requires: requires/ is required by relation | default: #BF87F2 +# aImg: atomic ERs with .png/.jpeg types | default: #A24052 +# aMov: atomic ERs with .mov/.mp4 | default: #FBF495 +# aSW: atomic ERs with exe/ipynd/app types | default: #93C539 +# aAudio: atomic ERs with mp3/wav types | default: #437C6C +# aText: atomic ERs with txt/pdf/html/md/pptx/dvi | default: #20C18B +# aDataset: atomic ERs with csv/ xlsx | default: #5FC7D3 + class Color: def __init__(self, aER, rER, iER, general, assesses, comesAfter, isPartOf, start, end, requires, aImg, aMov, aSW, aAudio, aText, aDataset): self.aER_node_color = aER From cff96e227931c46551c7b1f857583cb5acd3f73f Mon Sep 17 00:00:00 2001 From: ElmiraOn Date: Tue, 12 Dec 2023 22:34:29 -0500 Subject: [PATCH 2/4] added documentation --- Visualizer/Legend.py | 77 +++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/Visualizer/Legend.py b/Visualizer/Legend.py index deb6c34..314b5d6 100644 --- a/Visualizer/Legend.py +++ b/Visualizer/Legend.py @@ -1,22 +1,33 @@ +# This class creates The legend presented in the Legend container. +# the class creates a network using networkX, the network data +# is extracted using Network component of the pyvis library +# and passed to convertToHtml_Legend function of pyvisToHtml +# to create the html for the legend and visualized by the streamlit + import networkx as nx import pyvisToHtml from pyvis.network import Network class Legend: + # set the colors that are used for the network (when colors change so does the ones in the legend) def setColors(self, colors): self.colors = colors + # this function creates the legend data def create_legend(self, bg, font_color, has_icon): - G = nx.DiGraph() - Legend.__addNodes(self, G, has_icon) + G = nx.DiGraph() # initializing the network + Legend.__addComponent(self, G, has_icon) # adding the possible nodes and relation ships + # create the pyvis network G2 = Network(height="800px", width="100%", font_color= font_color, notebook=True,heading='', directed=True) - G2.from_nx(G) - Legend.__setOptions(self, G2) - data = G2.get_network_data() - pyvisToHtml.convertToHtml_Legend(data, bg) + G2.from_nx(G) # creating pyvis network based on networkX network + Legend.__setOptions(self, G2) # setting the options for the network for a consistent look + data = G2.get_network_data() # getting the network data from pyvis network to be passed for creating the html file + pyvisToHtml.convertToHtml_Legend(data, bg) # create the html file to be visualized by streamlit + # options for the network def __setOptions(self, G2): + # options for the edges/ relation edges = { "color": {"inherit": True}, "dashes": True, @@ -26,6 +37,7 @@ def __setOptions(self, G2): "selfReference": {"angle": 0.7853981633974483}, "smooth": {"roundness": 0.7} } + # options for the node/ ER. the fixed attr removes the ability to move the node. nodes = { "borderWidth": 3, "borderWidthSelected": 6, @@ -39,13 +51,17 @@ def __setOptions(self, G2): "shapeProperties": {"borderRadius": 4}, "fixed": {"x": True, "y": True} } + # possible interactions users can have the legend + # users cannot drag ERs, move view, or Zoom. + # any required interactions are done through the nav handlers interaction = { - "hover": True, - "dragNodes": False, + "hover": True, + "dragNodes": False, "dragView": False, "zoomView": False, "navigationButtons": True, } + # setting the options for the network G2.options.edges = edges G2.options.nodes = nodes G2.options.interaction = interaction @@ -64,81 +80,84 @@ def __get_atomic_node_color_property(color): }, } return res - def __addNodes(self, G, has_icon): + ## adding ERs and possible relatioships to the legend + def __addComponent(self, G, has_icon): #col1 + # aER G.add_node(1, label = "Activity ER ", shape = "text", title="legend", color= self.colors.aER_node_color, x = 0, y = 0) G.add_node(2, label = " ", shape="box", title="legend", color= self.colors.aER_node_color, x = 190, y = 0) - + #rER G.add_node(3, label = "Rubric ER ", shape="text", title="legend", color = self.colors.rER_node_color, x = 0, y = 50 ) G.add_node(4, label = "", shape="triangle", title="legend", color = self.colors.rER_node_color, x = 190, y = 50 ) - + #iER G.add_node(5, label = "Instructional ER ", shape="text", title="legend", color= self.colors.iER_node_color, x = 0, y = 100) G.add_node(6, label = " ", shape="circle", title="legend", color= self.colors.iER_node_color, x = 190, y = 100) - + # assess relation G.add_node(8, label = "Assesses relation", title="legend", shape = "text", color= self.colors.assess_relationship_color, x = 0, y = 150) G.add_node(9, label = " ",title="legend", shape="text", x = 140, y = 150) G.add_node(10, label = " ",title="legend", shape="text", x = 240, y = 150) G.add_edge(9, 10,color= self.colors.assess_relationship_color) - + # comesAfter/ Comes before relation G.add_node(11, label = " Comes Before relation", title="legend", shape = "text", color= self.colors.comesAfter_relationship_color, x = 0, y = 200) G.add_node(12, label = " ",title="legend", shape="text", x = 140, y = 200) G.add_node(13, label = " ",title="legend", shape="text", x = 240, y = 200) G.add_edge(12, 13,weight = 5, color= self.colors.comesAfter_relationship_color) - + # is part of relation G.add_node(14, label = " is Part Of relation", title="legend", shape = "text", color= self.colors.isPartOf_relationship_color, x = 0, y = 250) G.add_node(15, label = " ",title="legend", shape="text", x = 140, y = 250) G.add_node(16, label = " ",title="legend", shape="text", x = 240, y = 250) G.add_edge(15, 16, color = self.colors.isPartOf_relationship_color) - + # requires and is required by relation G.add_node(17, label = " is Required By relation", title="legend", shape = "text", color= self.colors.requires_node_color, x = 0, y = 300) G.add_node(18, label = " ",title="legend", shape="text", x = 140, y = 300) G.add_node(19, label = " ",title="legend", shape="text", x = 240, y = 300) G.add_edge(18, 19, weight = 5, color = self.colors.requires_node_color) - + # there are two method of visualization for the atomic nodes: color + small circle/ icons + bg color if(has_icon): #col2 - + # atomic ERs with .png/.jpeg types G.add_node(20, label = "Atomic ER: images", title="legend", shape = "text", color= self.colors.atomic_node_color_img, x = 400, y = 0) G.add_node(21, label = "", title="legend", shape="circularImage", image="https://raw.githubusercontent.com/LePa-YU/Visualizer/development/Visualizer/images/image.svg", color=Legend.__get_atomic_node_color_property(self.colors.atomic_node_color_img), x = 520, y = 0) - + # atomic ERs with .mov/.mp4 G.add_node(22, label = "Atomic ER: Videos", title="legend", shape = "text", color= self.colors.atomic_node_color_mov, x = 400, y = 50) G.add_node(23, label = "", title="legend", shape="circularImage", image="https://raw.githubusercontent.com/LePa-YU/Visualizer/development/Visualizer/images/video.svg", color=Legend.__get_atomic_node_color_property(self.colors.atomic_node_color_mov), x = 520, y = 50) - + # atomic ERs with exe/ipynd/app types G.add_node(24, label = " Atomic ER: Software", title="legend", shape = "text", color= self.colors.atomic_node_color_software, x = 400, y = 100) G.add_node(25, label = "", title="legend", shape="circularImage", image="https://raw.githubusercontent.com/LePa-YU/Visualizer/development/Visualizer/images/software.svg", color=Legend.__get_atomic_node_color_property(self.colors.atomic_node_color_software), x = 520, y = 100) - + # atomic ERs with mp3/wav types G.add_node(26, label = "Atomic ER: Audio ", title="legend", shape = "text", color= self.colors.atomic_node_color_audio, x = 400, y = 150) G.add_node(27, label = "", title="legend", shape="circularImage", image="https://raw.githubusercontent.com/LePa-YU/Visualizer/development/Visualizer/images/audio.svg", color=Legend.__get_atomic_node_color_property(self.colors.atomic_node_color_audio), x = 520, y = 150) - + # atomic node with type zip G.add_node(7, label = " Atomic ER: collection", title="legend", shape = "text", color= self.colors.atomic_node_color_coll, x = 400, y = 200) G.add_node(0, label = "", title="legend", shape="circularImage", image="https://raw.githubusercontent.com/LePa-YU/Visualizer/development/Visualizer/images/zip.svg", color=Legend.__get_atomic_node_color_property(self.colors.atomic_node_color_coll), x = 520, y = 200) - + # atomic ERs with txt/pdf/html/md/pptx/dvi G.add_node(28, label = "Atomic ER: Text ", title="legend", shape = "text", color= self.colors.atomic_node_color_text, x = 400, y = 250) G.add_node(29, label = "", title="legend", shape="circularImage", image = "https://raw.githubusercontent.com/LePa-YU/Visualizer/development/Visualizer/images/text.svg", color=Legend.__get_atomic_node_color_property(self.colors.atomic_node_color_text), x = 520, y = 250) - + # atomic ERs with csv/ xlsx G.add_node(30, label = "Atomic ER: Dataset", title="legend", shape = "text", color= self.colors.atomic_node_color_dataset, x = 400, y = 300) G.add_node(31, label = "", title="legend", shape="circularImage", image="https://raw.githubusercontent.com/LePa-YU/Visualizer/development/Visualizer/images/data.svg", color=Legend.__get_atomic_node_color_property(self.colors.atomic_node_color_dataset), x = 520, y = 300) else: #col2 + # atomic ERs with .png/.jpeg types G.add_node(20, label = "Atomic ER: images", title="legend", shape = "text", color= self.colors.atomic_node_color_img, x = 400, y = 0) G.add_node(21, label = "", title="legend", color= self.colors.atomic_node_color_img, x = 520, y = 0) - + # atomic ERs with .mov/.mp4 G.add_node(22, label = "Atomic ER: Videos", title="legend", shape = "text", color= self.colors.atomic_node_color_mov, x = 400, y = 50) G.add_node(23, label = "", title="legend", color= self.colors.atomic_node_color_mov, x = 520, y = 50) - + # atomic ERs with exe/ipynd/app types G.add_node(24, label = " Atomic ER: Software", title="legend", shape = "text", color= self.colors.atomic_node_color_software, x = 400, y = 100) G.add_node(25, label = "", title="legend", color= self.colors.atomic_node_color_software, x = 520, y = 100) - + # atomic ERs with mp3/wav types G.add_node(26, label = "Atomic ER: Audio ", title="legend", shape = "text", color= self.colors.atomic_node_color_audio, x = 400, y = 150) G.add_node(27, label = "", title="legend", color= self.colors.atomic_node_color_audio, x = 520, y = 150) - + # atomic node with type zip G.add_node(7, label = " Atomic ER: collection", title="legend", shape = "text", color= self.colors.atomic_node_color_coll, x = 400, y = 200) G.add_node(0, label = "", title="legend", color= self.colors.atomic_node_color_coll, x = 520, y = 200) - + # atomic ERs with txt/pdf/html/md/pptx/dvi G.add_node(28, label = "Atomic ER: Text ", title="legend", shape = "text", color= self.colors.atomic_node_color_text, x = 400, y = 250) G.add_node(29, label = "", title="legend", color= self.colors.atomic_node_color_text, x = 520, y = 250) - + # atomic ERs with csv/ xlsx G.add_node(30, label = "Atomic ER: Dataset", title="legend", shape = "text", color= self.colors.atomic_node_color_dataset, x = 400, y = 300) G.add_node(31, label = "", title="legend", color= self.colors.atomic_node_color_dataset, x = 520, y = 300) \ No newline at end of file From ec8e737a9c4b5f0892114fed0ecbdd85418667fa Mon Sep 17 00:00:00 2001 From: ElmiraOn Date: Tue, 12 Dec 2023 22:37:25 -0500 Subject: [PATCH 3/4] updated documentation --- Visualizer/nxToPyvis.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Visualizer/nxToPyvis.py b/Visualizer/nxToPyvis.py index d34c403..f332573 100644 --- a/Visualizer/nxToPyvis.py +++ b/Visualizer/nxToPyvis.py @@ -1,3 +1,6 @@ +# this class convert a given networkX to data and pass to pyvisToHtml.convertToHtml +# the length of node title are set here to 15 + # required imports import networkx as nx import pyvisToHtml @@ -13,7 +16,7 @@ def convert_to_pyvis(G, file_name, bg, font_color,file_label, view, physics, isH # if (physics): G2.show_buttons() G2.from_nx(G) - # wrap the long title aka node labels to fit in 15 + # wrap the long title aka node labels to fit in 15 character at each row for node in G2.nodes: id_string = node["label"] width = 15 From c9aff4a3de700acac543be73a638e1f62e220810 Mon Sep 17 00:00:00 2001 From: ElmiraOn Date: Wed, 20 Dec 2023 10:54:48 -0500 Subject: [PATCH 4/4] added documentations --- Visualizer/datasetCreator.py | 37 +++------ Visualizer/pyvisToHtml.py | 153 ++++++++++------------------------- 2 files changed, 55 insertions(+), 135 deletions(-) diff --git a/Visualizer/datasetCreator.py b/Visualizer/datasetCreator.py index a156256..04b1a67 100644 --- a/Visualizer/datasetCreator.py +++ b/Visualizer/datasetCreator.py @@ -10,8 +10,10 @@ def __init__(self, file_name): #pass col2 and initial and current option self.file_name = file_name + # initializing the DF if "df" not in st.session_state: col_list = ['identifier','title','description','url','type','isPartOf','assesses','comesAfter','requires','alternativeContent','references','isFormatOf','duration', "x values", "y values"] + # create the csv file if it doesnt exist if(not os.path.isfile(file_name)): st.session_state.df = pd.DataFrame(columns=col_list) # add start and end node @@ -41,10 +43,9 @@ def __init__(self, file_name): st.session_state.df[col] = "" st.session_state.df.to_csv(file_name, index=False) # assess, isPartOf, comesAfter must contain one value if not the case then set to empty -# <<<<<<< nias_branch + # cleaning the given dataset cleaning_file_name = file_name.replace(".csv", "_cleaning_report.txt") fi = open(cleaning_file_name, "a") - for i in range(len(st.session_state.df.index)): node_type = st.session_state.df["type"][i] title = st.session_state.df["title"][i] @@ -92,23 +93,6 @@ def __init__(self, file_name): else: st.session_state.df["assesses"][i] = ""; st.session_state.df["isPartOf"][i] = "" st.session_state.df["requires"][i] = "" - -# ======= -# for i in range(len(st.session_state.df.index)): -# node_type = st.session_state.df["type"][i] -# if node_type != "start" and node_type != "end": -# if node_type != "rER": st.session_state.df["assesses"][i] = "" # if node is not rER then it should not have assess field -# else: # if node is rER then it must not have comesAfter, requires, and isPartOf -# st.session_state.df["comesAfter"][i] = ""; st.session_state.df["isPartOf"][i] = "" -# st.session_state.df["requires"][i] = "" - -# # if a node is aER or iER --> might have comesAfter but no assesses, isPartof, requires -# if node_type != "aER" and node_type!="iER": st.session_state.df["comesAfter"][i] = "" # if node is not aER or rER then doesnt have comesAfter -# else: -# st.session_state.df["assesses"][i] = ""; st.session_state.df["isPartOf"][i] = "" -# st.session_state.df["requires"][i] = "" - -# >>>>>>> developmen ## only requires can have multi values for the rest of relation they are set to "" if they have more than one value a = st.session_state.df["assesses"][i]; ca = st.session_state.df["comesAfter"][i]; ipo = st.session_state.df["isPartOf"][i] if type(a) != int: @@ -123,16 +107,14 @@ def __init__(self, file_name): try: ipo = int(float(st.session_state.df["isPartOf"][i])) except: ipo = None if ipo == None: st.session_state.df["isPartOf"][i] = "" - -# <<<<<<< nias_branch + st.session_state.df.to_csv(file_name, index=False) fi.close() -# ======= -# st.session_state.df.to_csv(file_name, index=False) -# >>>>>>> development + # add a single node def add_node(self): datasetCreator.set_selected_node(self, None) + # create the fields required for a node return a dictionary of values for the new node node_dict = datasetCreator.__create_node_addition_fields(self) if(node_dict): end_node_comesAfter = st.session_state.df["comesAfter"].iloc[-1] # keep end node connected to its comesAfter @@ -141,10 +123,12 @@ def add_node(self): end_dict = {"identifier": node_dict["identifier"]+1, "title": "End", "description": "end", "url":"", "type":"end" ,"isPartOf": '', "assesses":'','comesAfter':end_node_comesAfter,"requires":'', "alternativeContent":'', "references":'', "isFormatOf": "", "duration": "", "x values":'', "y values":""} + # readd the end node to the end of csv datasetCreator.__add_node_from_dict(self, end_dict, len(st.session_state.df.index)) st.session_state.df.to_csv(self.file_name, index=False) st.session_state.df = pd.read_csv(self.file_name) - + + # given a dictionary of values and index in csv it adds the values of the node def __add_node_from_dict(self, node_dict, index): try: n_id = int(node_dict["identifier"]) except: n_id = int(float(node_dict["identifier"])) @@ -164,6 +148,7 @@ def __add_node_from_dict(self, node_dict, index): st.session_state.df.loc[index, "x values"] = node_dict["x values"] st.session_state.df.loc[index, "y values"] = node_dict["y values"] + # find and edit an existing node def edit_node(self): st.session_state.df = pd.read_csv(self.file_name) datasetCreator.set_selected_node(self, None) @@ -172,9 +157,11 @@ def edit_node(self): else: st.divider() st.text("find the node you want to edit:") + # find and return the id of node to be edited node = datasetCreator.__find_node_list(self) if(node != None): node = np.int16(node).item() + # highlight the selected node datasetCreator.set_selected_node(self, node) disable = False if("delete_node" in st.session_state): diff --git a/Visualizer/pyvisToHtml.py b/Visualizer/pyvisToHtml.py index 77e516b..76a0308 100644 --- a/Visualizer/pyvisToHtml.py +++ b/Visualizer/pyvisToHtml.py @@ -2,7 +2,7 @@ def convertToHtml(data, file_name, bg, file_label, view, isHorizontal, download_dataset_only, csvRows, needsStabilization, physics, select_edit_node_id, select_edit_node2_id, is_custom): file_html = open(file_name , "w") - # Adding the input data to the HTML file + # top part of html file_html.write(''' @@ -45,44 +45,42 @@ def convertToHtml(data, file_name, bg, file_label, view, isHorizontal, download_