Skip to content

Commit

Permalink
Add implementations for dictionaries
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahtang7 committed May 5, 2023
1 parent fbbdeee commit c23d8a1
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 1 deletion.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ So far, the seealgo library provides visualization for the following data struct
- List: `append(value)`, `insert(index, value)`, `remove(value)`, `__setitem__(index, value)`
- Binary Search Tree (provided as a nested dictionary): `insert(child)`, `remove(value)`
- Set: `add(value)`, `remove(value)`, `clear()`, `update(values)`
- Dictionary (nested and non-nested): `update(iterable)`, `pop(key)`

## Installation
This library requires you to have `graphviz` installed on your system using the [instructions](https://graphviz.org/download/) appropriate for your system, or by using the following command if you are on a macOS device:
Expand All @@ -27,6 +28,8 @@ pip install seealgo
```

## Using seealgo
(for examples of all data structures available with seealgo, visit our [GitHub Pages](https://sarahtang7.github.io/seealgo/)!)

This is an example of using the List module to visualize appending 5 to a list:

```python
Expand Down
12 changes: 12 additions & 0 deletions outputFiles/initDictOutput.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
digraph Dict {
rankdir=LR
subgraph cluster_Dict {
node [color=white style=filled]
label=Dictionary
style=filled
color=lightgrey
key1 [label="key1: one"]
key2 [label="key2: 2"]
key3 [label="key3: three"]
}
}
15 changes: 15 additions & 0 deletions outputFiles/nestedDictOutput.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
digraph Dict {
rankdir=LR
subgraph cluster_Dict {
node [color=white style=filled]
label=Dictionary
style=filled
color=lightgrey
key1 [label="key1: value1"]
key2 [label=key2 color=lightblue2 shape=rectangle style=filled]
nested_key1 [label="nested_key1: nested_value1"]
key2 -> nested_key1
nested_key2 [label="nested_key2: nested_value2"]
key2 -> nested_key2
}
}
13 changes: 13 additions & 0 deletions outputFiles/popDictOutput1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
digraph Dict {
rankdir=LR
subgraph cluster_Dict {
node [color=white style=filled]
label=Dictionary
style=filled
color=lightgrey
apple [label="apple: red"]
banana [label="banana: yellow"]
goldfish [label="goldfish: gold"]
kiwi [label="kiwi: green"]
}
}
12 changes: 12 additions & 0 deletions outputFiles/popDictOutput2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
digraph Dict {
rankdir=LR
subgraph cluster_Dict {
node [color=white style=filled]
label=Dictionary
style=filled
color=lightgrey
apple [label="apple: red"]
banana [label="banana: yellow"]
kiwi [label="kiwi: green"]
}
}
13 changes: 13 additions & 0 deletions outputFiles/updateDictOutput1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
digraph Dict {
rankdir=LR
subgraph cluster_Dict {
node [color=white style=filled]
label=Dictionary
style=filled
color=lightgrey
1 [label="1: 1"]
2 [label="2: 4"]
3 [label="3: 9"]
4 [label="4: 16"]
}
}
16 changes: 16 additions & 0 deletions outputFiles/updateDictOutput2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
digraph Dict {
rankdir=LR
subgraph cluster_Dict {
node [color=white style=filled]
label=Dictionary
style=filled
color=lightgrey
1 [label="1: 1"]
2 [label="2: 4"]
3 [label="3: 9"]
4 [label="4: 16"]
5 [label="5: 25" color=green style=filled]
6 [label="6: 36" color=green style=filled]
7 [label="7: 49" color=green style=filled]
}
}
4 changes: 3 additions & 1 deletion seealgo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from seealgo.see_tree_algo import Tree
from seealgo.see_set_algo import Set
from .see_list_algo import List
from seealgo.see_dict_algo import Dict

myList = List()
myTree = Tree()
mySet = Set()
myDict = Dict()

__all__ = [
'List', 'Tree', 'Set'
'List', 'Tree', 'Set', 'Dict'
]
116 changes: 116 additions & 0 deletions seealgo/see_dict_algo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
This module contains the functionality to visualize a dictionary
data structure as it changes throughout a function.
This involves the following classes:
* `TrackedDict(dict)`: detects changes made to a given dictionary
data structure and triggers creation of a new visualization for each change.
* `Dict`: uses graphviz to construct a visualization of the dictionary using a table.
"""

from graphviz import Digraph
import webbrowser

class TrackedDict(dict):
"""
Tracks changes to a dictionary data structure and triggers creation of new visualization
Args:
dict: the dictionary data structure to track
"""

def update(self, iterable):
"""
Sets the value of a key-value pair and checks the keys of the dictionary
Args:
key (Any): key of the key-value pair to be set
value (Any): value to be set for the specified key
"""
super().update(iterable)
keys = list(iterable.keys())
Dict.create_viz(self, self, keys)


def pop(self, key):
"""
Removes a key-value pair given the key.
Args:
key (Any): key of the key-value pair to be removed
Raises:
KeyError: If the element to be removed is not in the dictionary.
"""
super().pop(key)
Dict.create_viz(self, self)


class Dict:
"""
Create graphviz visualization for dictionary data structure
"""

filenum = 1

def see(self, func, data):
"""
Creates a visualization for the initial dictionary and starts tracking
a given dictionary as it changes throughout a given function.
Args:
func (function): function that the dictionary is being altered through
data (dict): dictionary to track
"""
Dict.create_viz(self, data)
data = TrackedDict(data)
if func is not None:
func(data)


def create_viz(self, data, keys={}):
"""
Creates and renders a visualization of the dictionary using graphviz
Args:
data (dict): dictionary that is being visualized
key (iterable): optional list of keys of new key-value pairs
"""

di_graph = Digraph('Dict', filename=f'dict{Dict.filenum}.gv')
Dict.filenum += 1
di_graph.attr(rankdir='LR')

with di_graph.subgraph(name='cluster_Dict') as subgraph:
subgraph.attr(label='Dictionary')
subgraph.attr(style='filled')
subgraph.attr(color='lightgrey')
subgraph.node_attr.update(style='filled', color='white')

# Create nodes for each key-value pair in the dictionary
for k, v in data.items():

if isinstance(v, dict): # if value is a nested dictionary
subgraph.node(str(k), label=str(k), shape='rectangle', style='filled', color='lightblue2')

for nested_k, nested_v in v.items():
if nested_k in keys:
subgraph.node(str(nested_k), label=f"{str(nested_k)}: {str(nested_v)}", style='filled', color='green')
else:
subgraph.node(str(nested_k), label=f"{str(nested_k)}: {str(nested_v)}")
# Add an edge from parent node to child node
subgraph.edge(str(k), str(nested_k))

else: # if value is not a nested dictionary

if k in keys:
subgraph.node(str(k), label=f"{str(k)}: {str(v)}", style='filled', color='green')
else:
subgraph.node(str(k), label=f"{str(k)}: {str(v)}")

# Render the graph to a file
di_graph.view()
109 changes: 109 additions & 0 deletions seealgo/tests/test_dict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
This file contains unit tests for
visualizations involving dictionary data structures.
"""

from seealgo import Dict


### UNIT TESTS

def test_initdict():
"""
Test the visualization of an unchanged dictionary.
"""

init_dict = {'key1': 'one', 'key2': 2, 'key3': 'three'}
viz0 = Dict()
viz0.see(None, init_dict)

with open('dict1.gv', 'r', encoding='utf-8') as file:
viz_contents = file.read()

with open('../../outputFiles/initDictOutput.txt', 'r', encoding='utf-8') as true_file:
true_contents = true_file.read()

assert viz_contents == true_contents


def test_nesteddict():
"""
Test the visualization of a nested dictionary.
"""

nested_dict = {
'key1': 'value1',
'key2': {
'nested_key1': 'nested_value1',
'nested_key2': 'nested_value2'
}
}
vizN = Dict()
vizN.see(None, nested_dict)

with open('dict2.gv', 'r', encoding='utf-8') as file:
viz_contents = file.read()

with open('../../outputFiles/nestedDictOutput.txt', 'r', encoding='utf-8') as true_file:
true_contents = true_file.read()

assert viz_contents == true_contents


def test_updatedict():
"""
Test the visualization of adding
a key:value pair to a dictionary.
"""

dict1 = {'1': 1, '2': 4, '3': 9, '4':16}
dict_new = {'5': 25, '6': 36, '7': 49}
viz1 = Dict()

def update_func(user_dict):
user_dict.update(dict_new)
return user_dict

viz1.see(update_func, dict1)

with open('dict3.gv', 'r', encoding='utf-8') as file:
viz_contents1 = file.read()
with open('dict4.gv', 'r', encoding='utf-8') as file:
viz_contents2 = file.read()

with open('../../outputFiles/updateDictOutput1.txt', 'r', encoding='utf-8') as true_file:
true_contents1 = true_file.read()
with open('../../outputFiles/updateDictOutput2.txt', 'r', encoding='utf-8') as true_file:
true_contents2 = true_file.read()

assert viz_contents1 == true_contents1
assert viz_contents2 == true_contents2


def test_popfromdict():
"""
Test the visualization of removing
a value from a dictionary.
"""

dict2 = {'apple': 'red', 'banana': 'yellow', 'goldfish': 'gold', 'kiwi': 'green'}
viz2 = Dict()

def pop_func(user_dict):
user_dict.pop('goldfish')
return user_dict

viz2.see(pop_func, dict2)

with open('dict5.gv', 'r', encoding='utf-8') as file:
viz_contents1 = file.read()
with open('dict6.gv', 'r', encoding='utf-8') as file:
viz_contents2 = file.read()

with open('../../outputFiles/popDictOutput1.txt', 'r', encoding='utf-8') as true_file:
true_contents1 = true_file.read()
with open('../../outputFiles/popDictOutput2.txt', 'r', encoding='utf-8') as true_file:
true_contents2 = true_file.read()

assert viz_contents1 == true_contents1
assert viz_contents2 == true_contents2

0 comments on commit c23d8a1

Please sign in to comment.