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

Automated ER diagram generation #83

Merged
merged 12 commits into from
Dec 21, 2024
Merged
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ db.sqlite3
# Build artifacts
bundle.tar.gz

# Documentation generated files
documentation/entity_relationship_diagram.html
documentation/model.dot
documentation/model.svg
documentation/model.SVG

.env
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ build:
bundle: build
tar -czv --exclude-vcs --exclude-vcs-ignores --exclude '*/bundle.tar.gz' --exclude '*/node_modules' --exclude 'carshare/media' --exclude '*/__pycache__' -f bundle.tar.gz ../carshare/* ../crispy-tailwind/*

generate-er-diagram:
@echo Generating DOT file
poetry run python manage.py graph_models -a > documentation/model.dot
@echo Converting to SVG
dot -Tsvg documentation/model.dot -o documentation/model.svg
@echo Inserting into html viewer
poetry run python documentation/insert-diagram.py documentation/template_entity_relationship_diagram.html documentation/model.svg documentation/entity_relationship_diagram.html

setup-crispy-tailwind:
@echo Setting up our modified version of crispy tailwind...
test -s crispy_tailwind || (\
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,15 @@ We use the _black_ python code style. You can reformat your code changes before

$ make format

In order to use the diagram generation script you must install graphviz:

https://graphviz.org/download/

To generate an entity relationship diagram run:

$ make generate-er-diagram

View the diagram by opening the generated html file which allows you to pan and zoom.

Happy hacking!

1 change: 1 addition & 0 deletions carshare/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def env_get_list(variable, default):
"django.contrib.postgres",
"django.contrib.sites",
"django.contrib.staticfiles",
"django_extensions",
"allauth",
"allauth.account",
"allauth.socialaccount",
Expand Down
34 changes: 34 additions & 0 deletions documentation/insert-diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import sys


def replace_placeholder(html_file, svg_file, output_file):
try:
# Read the HTML file
with open(html_file, "r", encoding="utf-8") as html:
html_content = html.read()

# Read the SVG file
with open(svg_file, "r", encoding="utf-8") as svg:
svg_content = svg.read()

# Replace the placeholder with the SVG content
updated_html = html_content.replace("PLACE_HOLDER", svg_content)

# Write the updated HTML to a new file
with open(output_file, "w", encoding="utf-8") as output:
output.write(updated_html)

print(f"Updated HTML file has been saved to {output_file}.")
except Exception as e:
print(f"An error occurred: {e}")


# Example usage:
# replace_placeholder('index.html', 'graphic.svg', 'updated_index.html')

# If running from the command line:
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python script.py <html_file> <svg_file> <output_file>")
else:
replace_placeholder(sys.argv[1], sys.argv[2], sys.argv[3])
92 changes: 92 additions & 0 deletions documentation/template_entity_relationship_diagram.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Car Share Entity Relationship Diagram</title>
</head>
<body>
PLACE_HOLDER
<script>
const svg = document.querySelector("svg");
let viewBox = svg.viewBox.baseVal;

// Initial ViewBox setup
viewBox.x = 1350;
viewBox.y = 1000;
viewBox.width = 1000; // Width of the viewBox
viewBox.height = 1000; // Height of the viewBox

let isPanning = false;
let startPoint = { x: 0, y: 0 };
let endPoint = { x: 0, y: 0 };
let scale = 1;

// Mouse down event for panning
svg.addEventListener("mousedown", (event) => {
isPanning = true;
startPoint = { x: event.clientX, y: event.clientY };
svg.style.cursor = "move";
});

// Mouse move event for dragging
svg.addEventListener("mousemove", (event) => {
if (!isPanning) return;

endPoint = { x: event.clientX, y: event.clientY };

const dx = (startPoint.x - endPoint.x) * (viewBox.width / svg.clientWidth);
const dy = (startPoint.y - endPoint.y) * (viewBox.height / svg.clientHeight);

viewBox.x += dx;
viewBox.y += dy;

startPoint = endPoint;
svg.setAttribute("viewBox", `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`);
});

// Mouse up or leave event to stop panning
svg.addEventListener("mouseup", () => {
isPanning = false;
svg.style.cursor = "default";
});

svg.addEventListener("mouseleave", () => {
isPanning = false;
svg.style.cursor = "default";
});

// Zoom functionality
svg.addEventListener("wheel", (event) => {
event.preventDefault();

const { width, height } = viewBox;

const zoomFactor = 1.01;
const mouseX = event.clientX - svg.getBoundingClientRect().left;
const mouseY = event.clientY - svg.getBoundingClientRect().top;

const svgX = (mouseX / svg.clientWidth) * width + viewBox.x;
const svgY = (mouseY / svg.clientHeight) * height + viewBox.y;

if (event.deltaY < 0) {
scale *= zoomFactor; // Zoom in
} else {
scale /= zoomFactor; // Zoom out
}

const newWidth = width * scale;
const newHeight = height * scale;

viewBox.x = svgX - (mouseX / svg.clientWidth) * newWidth;
viewBox.y = svgY - (mouseY / svg.clientHeight) * newHeight;
viewBox.width = newWidth;
viewBox.height = newHeight;

svg.setAttribute("viewBox", `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`);
scale=1;
});

</script>
</body>
</html>
120 changes: 67 additions & 53 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading