Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elmira_doc #56

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 48 additions & 29 deletions Visualizer/Legend.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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)

18 changes: 18 additions & 0 deletions Visualizer/colors.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
37 changes: 12 additions & 25 deletions Visualizer/datasetCreator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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"]))
Expand All @@ -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)
Expand All @@ -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):
Expand Down
5 changes: 4 additions & 1 deletion Visualizer/nxToPyvis.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
Loading