Examples & Tutorials
Examples & Tutorials
Learn sgraph through practical examples and real-world use cases.
Table of Contents
- Basic Usage Examples
- Software Architecture Analysis
- Dependency Analysis
- Visualization Examples
- Advanced Patterns
Basic Usage Examples
Example 1: Modeling a Simple C Project
from sgraph import SGraph, SElement, SElementAssociation
# Create model for nginx-like structure
model = SGraph(SElement(None, ''))
# Create the directory structure
nginx_root = model.createOrGetElementFromPath('/nginx')
src = model.createOrGetElementFromPath('/nginx/src')
core = model.createOrGetElementFromPath('/nginx/src/core')
# Create source files
nginx_c = model.createOrGetElementFromPath('/nginx/src/core/nginx.c')
nginx_h = model.createOrGetElementFromPath('/nginx/src/core/nginx.h')
config_h = model.createOrGetElementFromPath('/nginx/src/core/config.h')
# Add file types
nginx_c.addAttribute('type', 'c_source')
nginx_h.addAttribute('type', 'c_header')
config_h.addAttribute('type', 'c_header')
# Model #include relationships
include_nginx_h = SElementAssociation(nginx_c, nginx_h, 'includes')
include_config_h = SElementAssociation(nginx_c, config_h, 'includes')
include_nginx_h.initElems()
include_config_h.initElems()
# Export to see the structure
print("=== Dependencies ===")
print(model.to_deps())
print("\n=== XML Structure ===")
print(model.to_xml())
Example 2: Python Module Dependencies
from sgraph import SGraph, SElement, SElementAssociation
# Model a Python package
model = SGraph(SElement(None, ''))
# Create package structure
package = model.createOrGetElementFromPath('/myapp')
models = model.createOrGetElementFromPath('/myapp/models.py')
views = model.createOrGetElementFromPath('/myapp/views.py')
utils = model.createOrGetElementFromPath('/myapp/utils.py')
tests = model.createOrGetElementFromPath('/myapp/tests.py')
# Add metadata
models.addAttribute('type', 'python_module')
models.addAttribute('lines', 250)
views.addAttribute('type', 'python_module')
views.addAttribute('lines', 180)
utils.addAttribute('type', 'python_module')
utils.addAttribute('lines', 95)
# Model import relationships
views_imports_models = SElementAssociation(views, models, 'imports')
views_imports_utils = SElementAssociation(views, utils, 'imports')
tests_imports_models = SElementAssociation(tests, models, 'imports')
tests_imports_views = SElementAssociation(tests, views, 'imports')
# Initialize all relationships
for assoc in [views_imports_models, views_imports_utils,
tests_imports_models, tests_imports_views]:
assoc.initElems()
# Analyze the structure
print("=== Module Dependencies ===")
deps_output = model.to_deps()
print(deps_output)
# Find modules with most dependencies
all_elements = model.getAllElements()
modules = [e for e in all_elements if e.getAttribute('type') == 'python_module']
for module in modules:
imports = len(module.getAssociationsFrom())
imported_by = len(module.getAssociationsTo())
print(f"{module.name}: imports {imports}, imported by {imported_by}")
Software Architecture Analysis
Example 3: Analyzing Function Call Graphs
from sgraph.modelapi import ModelApi
# This example assumes you have a model file with function call data
# You can create such models using static analysis tools
def analyze_function_complexity(model_path):
api = ModelApi(filepath=model_path)
# Find all function elements
functions = api.getElementsByType('function')
complexity_data = []
for func in functions:
# Count incoming and outgoing calls
called_functions = api.getCalledFunctions(func)
calling_functions = api.getCallingFunctions(func)
# Calculate complexity metrics
fan_out = len(called_functions) # Functions this calls
fan_in = len(calling_functions) # Functions calling this
complexity_score = fan_out + fan_in
complexity_data.append({
'name': func.name,
'path': func.getPath(),
'fan_out': fan_out,
'fan_in': fan_in,
'complexity': complexity_score
})
# Sort by complexity
complexity_data.sort(key=lambda x: x['complexity'], reverse=True)
print("=== Top 10 Most Complex Functions ===")
for i, func_data in enumerate(complexity_data[:10]):
print(f"{i+1:2d}. {func_data['name']} (score: {func_data['complexity']})")
print(f" Fan-out: {func_data['fan_out']}, Fan-in: {func_data['fan_in']}")
print(f" Path: {func_data['path']}\n")
return complexity_data
# Usage (requires a model file with function data)
# complexity_analysis = analyze_function_complexity('codebase_model.xml')
Example 4: Finding Circular Dependencies
from sgraph.modelapi import ModelApi
from collections import defaultdict, deque
def find_circular_dependencies(model_path):
api = ModelApi(filepath=model_path)
# Build adjacency list of dependencies
dependencies = defaultdict(set)
all_elements = api.getAllElements()
for element in all_elements:
element_path = element.getPath()
for assoc in element.getAssociationsFrom():
if assoc.type in ['imports', 'includes', 'depends_on']:
target_path = assoc.toElement.getPath()
dependencies[element_path].add(target_path)
def has_cycle_from(start, visited, rec_stack):
visited.add(start)
rec_stack.add(start)
for neighbor in dependencies[start]:
if neighbor not in visited:
if has_cycle_from(neighbor, visited, rec_stack):
return True
elif neighbor in rec_stack:
return True
rec_stack.remove(start)
return False
# Find cycles
cycles = []
visited = set()
for node in dependencies:
if node not in visited:
rec_stack = set()
if has_cycle_from(node, visited, rec_stack):
cycles.append(node)
if cycles:
print("=== Circular Dependencies Found ===")
for cycle_node in cycles:
print(f"Cycle involving: {cycle_node}")
else:
print("No circular dependencies found!")
return cycles
# Usage
# circular_deps = find_circular_dependencies('project_model.xml')
Dependency Analysis
Example 5: Dependency Impact Analysis
from sgraph.modelapi import ModelApi
def analyze_dependency_impact(model_path, target_element_name):
"""
Analyze what would be affected if we changed a specific element
"""
api = ModelApi(filepath=model_path)
# Find the target element
targets = api.getElementsByName(target_element_name)
if not targets:
print(f"Element '{target_element_name}' not found!")
return
target = targets[0]
print(f"Analyzing impact of changes to: {target.getPath()}")
print("=" * 50)
# Direct dependencies (what uses this element)
direct_dependents = api.getCallingFunctions(target)
print(f"\nDirect Dependents ({len(direct_dependents)}):")
for dep in direct_dependents[:10]: # Show first 10
print(f" - {dep.getPath()}")
if len(direct_dependents) > 10:
print(f" ... and {len(direct_dependents) - 10} more")
# Transitive dependencies (what this element uses)
dependencies = api.getCalledFunctions(target)
print(f"\nDirect Dependencies ({len(dependencies)}):")
for dep in dependencies[:10]:
print(f" - {dep.getPath()}")
if len(dependencies) > 10:
print(f" ... and {len(dependencies) - 10} more")
# Calculate impact score
impact_score = len(direct_dependents) * 2 + len(dependencies)
print(f"\nImpact Score: {impact_score}")
print("(High score = high impact if changed)")
return {
'target': target,
'direct_dependents': direct_dependents,
'dependencies': dependencies,
'impact_score': impact_score
}
# Usage
# impact = analyze_dependency_impact('model.xml', 'authentication_module')
Example 6: Layer Violation Detection
from sgraph.modelapi import ModelApi
def detect_layer_violations(model_path):
"""
Detect architectural layer violations in a layered system
"""
api = ModelApi(filepath=model_path)
# Define architectural layers (customize for your project)
layers = {
'presentation': ['view', 'controller', 'ui'],
'business': ['service', 'domain', 'business'],
'data': ['repository', 'dao', 'database', 'persistence']
}
layer_order = ['presentation', 'business', 'data']
violations = []
all_elements = api.getAllElements()
for element in all_elements:
element_path = element.getPath().lower()
element_layer = None
# Determine element's layer
for layer_name, keywords in layers.items():
if any(keyword in element_path for keyword in keywords):
element_layer = layer_name
break
if element_layer:
# Check dependencies
for assoc in element.getAssociationsFrom():
target_path = assoc.toElement.getPath().lower()
target_layer = None
for layer_name, keywords in layers.items():
if any(keyword in target_path for keyword in keywords):
target_layer = layer_name
break
if target_layer:
element_idx = layer_order.index(element_layer)
target_idx = layer_order.index(target_layer)
# Violation: higher layer depending on lower layer
if element_idx > target_idx:
violations.append({
'from': element.getPath(),
'from_layer': element_layer,
'to': assoc.toElement.getPath(),
'to_layer': target_layer,
'type': assoc.type
})
if violations:
print("=== Layer Violations Detected ===")
for v in violations:
print(f"VIOLATION: {v['from_layer']} -> {v['to_layer']}")
print(f" From: {v['from']}")
print(f" To: {v['to']}")
print(f" Type: {v['type']}\n")
else:
print("No layer violations found!")
return violations
# Usage
# violations = detect_layer_violations('architecture_model.xml')
Visualization Examples
Example 7: Generate PlantUML Diagrams
from sgraph.modelapi import ModelApi
from sgraph.converters.xml_to_plantuml import XmlToPlantUml
def create_architecture_diagram(model_path, output_path):
"""
Generate a PlantUML architecture diagram
"""
# Convert model to PlantUML
converter = XmlToPlantUml()
converter.convert(model_path, output_path)
print(f"PlantUML diagram generated: {output_path}")
print("To generate PNG:")
print(f"plantuml {output_path}")
# You can also customize the PlantUML output
with open(output_path, 'r') as f:
content = f.read()
# Add styling
styled_content = """@startuml
!theme vibrant
skinparam backgroundColor #FAFAFA
skinparam class {
BackgroundColor #E8F4FD
BorderColor #1565C0
ArrowColor #1976D2
}
""" + content.replace("@startuml", "").replace("@enduml", "") + "\n@enduml"
styled_output = output_path.replace('.puml', '_styled.puml')
with open(styled_output, 'w') as f:
f.write(styled_content)
print(f"Styled PlantUML diagram: {styled_output}")
# Usage
# create_architecture_diagram('model.xml', 'architecture.puml')
Example 8: Create Interactive Web Visualization
from sgraph.converters.xml_to_3dforcegraph import XmlTo3DForceGraph
from sgraph.converters.sgraph_to_cytoscape import SGraphToCytoscape
def create_interactive_visualization(model_path):
"""
Create interactive web-based visualizations
"""
# 3D Force Graph (for large graphs)
force_converter = XmlTo3DForceGraph()
force_converter.convert(model_path, 'force_graph.html')
print("3D Force Graph created: force_graph.html")
# Cytoscape.js (for detailed analysis)
cyto_converter = SGraphToCytoscape()
cyto_converter.convert(model_path, 'cytoscape.html')
print("Cytoscape visualization created: cytoscape.html")
# Create a simple dashboard HTML
dashboard_html = """
<!DOCTYPE html>
<html>
<head>
<title>Project Architecture Dashboard</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.container { max-width: 1200px; margin: 0 auto; }
.viz-link {
display: inline-block;
margin: 10px;
padding: 15px 25px;
background: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
}
.viz-link:hover { background: #0056b3; }
</style>
</head>
<body>
<div class="container">
<h1>Project Architecture Visualizations</h1>
<p>Explore your project's architecture through interactive visualizations:</p>
<a href="force_graph.html" class="viz-link">🌐 3D Force Graph</a>
<a href="cytoscape.html" class="viz-link">🔍 Detailed Network</a>
<h2>About Your Architecture</h2>
<p>This dashboard provides different views of your software architecture:</p>
<ul>
<li><strong>3D Force Graph:</strong> Great for exploring large codebases and finding clusters</li>
<li><strong>Detailed Network:</strong> Perfect for analyzing specific components and relationships</li>
</ul>
</div>
</body>
</html>
"""
with open('dashboard.html', 'w') as f:
f.write(dashboard_html)
print("Dashboard created: dashboard.html")
print("\nOpen dashboard.html in your browser to explore!")
# Usage
# create_interactive_visualization('large_project_model.xml')
Advanced Patterns
Example 9: Model Comparison and Evolution Tracking
from sgraph.compare.modelcompare import ModelCompare
from sgraph.modelapi import ModelApi
def compare_model_versions(old_model_path, new_model_path):
"""
Compare two versions of a model to track changes
"""
comparer = ModelCompare()
comparison = comparer.compare(old_model_path, new_model_path)
print("=== Model Evolution Analysis ===")
print(f"Old model: {old_model_path}")
print(f"New model: {new_model_path}")
print("=" * 40)
# Analyze changes
added_elements = comparison.get('added_elements', [])
removed_elements = comparison.get('removed_elements', [])
modified_elements = comparison.get('modified_elements', [])
print(f"📈 Added elements: {len(added_elements)}")
for elem in added_elements[:5]:
print(f" + {elem}")
if len(added_elements) > 5:
print(f" ... and {len(added_elements) - 5} more")
print(f"\n📉 Removed elements: {len(removed_elements)}")
for elem in removed_elements[:5]:
print(f" - {elem}")
if len(removed_elements) > 5:
print(f" ... and {len(removed_elements) - 5} more")
print(f"\n🔄 Modified elements: {len(modified_elements)}")
for elem in modified_elements[:5]:
print(f" ~ {elem}")
if len(modified_elements) > 5:
print(f" ... and {len(modified_elements) - 5} more")
return comparison
# Usage
# evolution = compare_model_versions('v1.0_model.xml', 'v2.0_model.xml')
Example 10: Custom Metrics Calculation
from sgraph.modelapi import ModelApi
from sgraph.metricsapi import MetricsApi
def calculate_custom_metrics(model_path):
"""
Calculate custom architecture quality metrics
"""
api = ModelApi(filepath=model_path)
metrics = MetricsApi(model_path)
all_elements = api.getAllElements()
modules = [e for e in all_elements if 'module' in e.getPath()]
print("=== Custom Architecture Metrics ===")
# 1. Coupling Metrics
coupling_scores = []
for module in modules:
# Efferent coupling (outgoing dependencies)
outgoing = len(module.getAssociationsFrom())
# Afferent coupling (incoming dependencies)
incoming = len(module.getAssociationsTo())
# Instability = Outgoing / (Incoming + Outgoing)
total_coupling = incoming + outgoing
instability = outgoing / total_coupling if total_coupling > 0 else 0
coupling_scores.append({
'module': module.name,
'path': module.getPath(),
'efferent': outgoing,
'afferent': incoming,
'instability': instability
})
# Sort by instability (most unstable first)
coupling_scores.sort(key=lambda x: x['instability'], reverse=True)
print(f"\n📊 Coupling Analysis ({len(modules)} modules):")
print("Most Unstable Modules (high outgoing dependencies):")
for score in coupling_scores[:5]:
print(f" {score['module']:20} | "
f"Out: {score['efferent']:3} | "
f"In: {score['afferent']:3} | "
f"Instability: {score['instability']:.2f}")
# 2. Abstractness (if you have interface/abstract class info)
abstractness_scores = []
for module in modules:
# Count abstract elements (interfaces, abstract classes)
abstract_count = 0
concrete_count = 0
for child in module.getChildElements():
if child.getAttribute('type') in ['interface', 'abstract_class']:
abstract_count += 1
elif child.getAttribute('type') in ['class', 'function']:
concrete_count += 1
total = abstract_count + concrete_count
abstractness = abstract_count / total if total > 0 else 0
abstractness_scores.append({
'module': module.name,
'abstractness': abstractness,
'abstract_count': abstract_count,
'concrete_count': concrete_count
})
print(f"\n🎯 Abstractness Analysis:")
abstractness_scores.sort(key=lambda x: x['abstractness'], reverse=True)
for score in abstractness_scores[:5]:
print(f" {score['module']:20} | "
f"Abstract: {score['abstract_count']:2} | "
f"Concrete: {score['concrete_count']:2} | "
f"Abstractness: {score['abstractness']:.2f}")
# 3. Distance from Main Sequence (D = |A + I - 1|)
print(f"\n📏 Distance from Main Sequence:")
for i, coupling in enumerate(coupling_scores):
if i < len(abstractness_scores):
abstract = abstractness_scores[i]['abstractness']
instability = coupling['instability']
distance = abs(abstract + instability - 1)
print(f" {coupling['module']:20} | Distance: {distance:.2f}")
return {
'coupling': coupling_scores,
'abstractness': abstractness_scores
}
# Usage
# metrics = calculate_custom_metrics('enterprise_model.xml')
These examples show the power and flexibility of sgraph for analyzing software architectures. Each example can be adapted and extended for your specific use case!
Next Steps
- Try these examples with your own code models
- Combine multiple analyses for comprehensive insights
- Create custom visualizations for your specific needs
- Build automated architecture quality checks into your CI/CD pipeline
Happy analyzing! 🔍📊