ZahirJS commited on
Commit
836921f
·
verified ·
1 Parent(s): f726186

Update entity_relationship_generator.py

Browse files
Files changed (1) hide show
  1. entity_relationship_generator.py +169 -93
entity_relationship_generator.py CHANGED
@@ -4,43 +4,6 @@ from tempfile import NamedTemporaryFile
4
  import os
5
 
6
  def generate_entity_relationship_diagram(json_input: str, output_format: str) -> str:
7
- """
8
- Generates an entity relationship diagram from JSON input.
9
-
10
- Args:
11
- json_input (str): A JSON string describing the entity relationship diagram structure.
12
-
13
- Expected JSON Format Example:
14
- {
15
- "entities": [
16
- {
17
- "name": "CUSTOMER",
18
- "type": "strong"
19
- },
20
- {
21
- "name": "ORDER",
22
- "type": "strong"
23
- },
24
- {
25
- "name": "REVIEW",
26
- "type": "weak"
27
- }
28
- ],
29
- "relationships": [
30
- {
31
- "name": "Places",
32
- "entities": ["CUSTOMER", "ORDER"],
33
- "cardinalities": {
34
- "CUSTOMER": "1",
35
- "ORDER": "N"
36
- }
37
- }
38
- ]
39
- }
40
-
41
- Returns:
42
- str: The filepath to the generated PNG image file.
43
- """
44
  try:
45
  if not json_input.strip():
46
  return "Error: Empty input"
@@ -50,104 +13,217 @@ def generate_entity_relationship_diagram(json_input: str, output_format: str) ->
50
  if 'entities' not in data:
51
  raise ValueError("Missing required field: entities")
52
 
53
- # Configuración ultra simple - dejar que Graphviz organice libremente
54
- dot = graphviz.Graph(comment='ER Diagram', engine='fdp')
55
  dot.attr(
56
  bgcolor='white',
57
- pad='1.0',
58
  overlap='false',
59
- splines='line',
60
- K='2'
 
61
  )
62
- dot.attr('node', fontname='Arial', fontsize='11')
63
- dot.attr('edge', fontname='Arial', fontsize='10')
64
 
65
  entities = data.get('entities', [])
66
  relationships = data.get('relationships', [])
67
 
68
- # 1. Crear todas las entidades como nodos simples
69
  for entity in entities:
70
  entity_name = entity.get('name')
71
  entity_type = entity.get('type', 'strong')
 
72
 
73
  if not entity_name:
74
  continue
75
 
76
  if entity_type == 'weak':
77
- # Entidad débil
78
  dot.node(
79
  entity_name,
80
  entity_name,
81
  shape='box',
82
  style='filled',
83
- fillcolor='#e8e8e8',
84
  color='#404040',
85
- penwidth='3'
 
 
86
  )
87
  else:
88
- # Entidad fuerte
89
  dot.node(
90
  entity_name,
91
  entity_name,
92
  shape='box',
93
  style='filled',
94
  fillcolor='#e8e8e8',
95
- color='#404040'
96
- )
97
-
98
- # 2. Crear todas las relaciones como nodos rombos
99
- for relationship in relationships:
100
- rel_name = relationship.get('name')
101
- rel_type = relationship.get('type', 'regular')
102
-
103
- if not rel_name:
104
- continue
105
-
106
- if rel_type == 'identifying':
107
- # Relación identificadora
108
- dot.node(
109
- rel_name,
110
- rel_name,
111
- shape='diamond',
112
- style='filled',
113
- fillcolor='#d8d8d8',
114
  color='#404040',
115
- penwidth='3'
116
- )
117
- else:
118
- # Relación regular
119
- dot.node(
120
- rel_name,
121
- rel_name,
122
- shape='diamond',
123
- style='filled',
124
- fillcolor='#d8d8d8',
125
- color='#404040'
126
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
- # 3. Conectar entidades con relaciones (simple y directo)
129
  for relationship in relationships:
130
  rel_name = relationship.get('name')
 
131
  entities_involved = relationship.get('entities', [])
132
  cardinalities = relationship.get('cardinalities', {})
 
133
 
134
- if not rel_name or len(entities_involved) < 2:
135
  continue
136
 
137
- # Conectar cada entidad con la relación
138
- for entity in entities_involved:
139
- cardinality = cardinalities.get(entity, '1')
140
 
141
- # Conexión simple: entidad -> relación
142
- dot.edge(
143
- entity,
144
- rel_name,
145
- label=cardinality,
146
- color='#404040',
147
- fontcolor='#000000'
148
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
- # Renderizar
151
  with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
152
  dot.render(tmp.name, format=output_format, cleanup=True)
153
  return f"{tmp.name}.{output_format}"
 
4
  import os
5
 
6
  def generate_entity_relationship_diagram(json_input: str, output_format: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  try:
8
  if not json_input.strip():
9
  return "Error: Empty input"
 
13
  if 'entities' not in data:
14
  raise ValueError("Missing required field: entities")
15
 
16
+ dot = graphviz.Graph(comment='ER Diagram', engine='neato')
 
17
  dot.attr(
18
  bgcolor='white',
19
+ pad='1.5',
20
  overlap='false',
21
+ splines='true',
22
+ sep='+25',
23
+ esep='+15'
24
  )
25
+ dot.attr('node', fontname='Arial', fontsize='10', color='#404040')
26
+ dot.attr('edge', fontname='Arial', fontsize='9', color='#404040')
27
 
28
  entities = data.get('entities', [])
29
  relationships = data.get('relationships', [])
30
 
 
31
  for entity in entities:
32
  entity_name = entity.get('name')
33
  entity_type = entity.get('type', 'strong')
34
+ attributes = entity.get('attributes', [])
35
 
36
  if not entity_name:
37
  continue
38
 
39
  if entity_type == 'weak':
 
40
  dot.node(
41
  entity_name,
42
  entity_name,
43
  shape='box',
44
  style='filled',
45
+ fillcolor='#f0f0f0',
46
  color='#404040',
47
+ penwidth='3',
48
+ width='1.8',
49
+ height='0.8'
50
  )
51
  else:
 
52
  dot.node(
53
  entity_name,
54
  entity_name,
55
  shape='box',
56
  style='filled',
57
  fillcolor='#e8e8e8',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  color='#404040',
59
+ penwidth='1',
60
+ width='1.8',
61
+ height='0.8'
 
 
 
 
 
 
 
 
62
  )
63
+
64
+ for i, attr in enumerate(attributes):
65
+ attr_name = attr.get('name', '')
66
+ attr_type = attr.get('type', 'regular')
67
+
68
+ attr_id = f"{entity_name}_attr_{i}"
69
+
70
+ if attr_type == 'primary_key':
71
+ dot.node(
72
+ attr_id,
73
+ f'<U><B>{attr_name}</B></U>',
74
+ shape='ellipse',
75
+ style='filled',
76
+ fillcolor='#d8d8d8',
77
+ color='#404040',
78
+ width='1.2',
79
+ height='0.6'
80
+ )
81
+ elif attr_type == 'partial_key':
82
+ dot.node(
83
+ attr_id,
84
+ f'<U>{attr_name}</U>',
85
+ shape='ellipse',
86
+ style='filled,dashed',
87
+ fillcolor='#d8d8d8',
88
+ color='#404040',
89
+ width='1.2',
90
+ height='0.6'
91
+ )
92
+ elif attr_type == 'multivalued':
93
+ dot.node(
94
+ attr_id,
95
+ attr_name,
96
+ shape='ellipse',
97
+ style='filled',
98
+ fillcolor='#d8d8d8',
99
+ color='#404040',
100
+ penwidth='3',
101
+ width='1.2',
102
+ height='0.6'
103
+ )
104
+ elif attr_type == 'derived':
105
+ dot.node(
106
+ attr_id,
107
+ f'/{attr_name}/',
108
+ shape='ellipse',
109
+ style='filled,dashed',
110
+ fillcolor='#d8d8d8',
111
+ color='#404040',
112
+ width='1.2',
113
+ height='0.6'
114
+ )
115
+ elif attr_type == 'composite':
116
+ dot.node(
117
+ attr_id,
118
+ attr_name,
119
+ shape='ellipse',
120
+ style='filled',
121
+ fillcolor='#d8d8d8',
122
+ color='#404040',
123
+ width='1.2',
124
+ height='0.6'
125
+ )
126
+ else:
127
+ dot.node(
128
+ attr_id,
129
+ attr_name,
130
+ shape='ellipse',
131
+ style='filled',
132
+ fillcolor='#d8d8d8',
133
+ color='#404040',
134
+ width='1.2',
135
+ height='0.6'
136
+ )
137
+
138
+ dot.edge(entity_name, attr_id, color='#404040', len='1.5')
139
 
 
140
  for relationship in relationships:
141
  rel_name = relationship.get('name')
142
+ rel_type = relationship.get('type', 'regular')
143
  entities_involved = relationship.get('entities', [])
144
  cardinalities = relationship.get('cardinalities', {})
145
+ rel_attributes = relationship.get('attributes', [])
146
 
147
+ if not rel_name:
148
  continue
149
 
150
+ if rel_type == 'isa':
151
+ parent = relationship.get('parent')
152
+ children = relationship.get('children', [])
153
 
154
+ if parent and children:
155
+ isa_id = f"isa_{rel_name}"
156
+ dot.node(
157
+ isa_id,
158
+ 'ISA',
159
+ shape='triangle',
160
+ style='filled',
161
+ fillcolor='#d0d0d0',
162
+ color='#404040',
163
+ penwidth='2',
164
+ width='1.0',
165
+ height='0.8'
166
+ )
167
+
168
+ dot.edge(parent, isa_id, color='#404040', len='2.0')
169
+
170
+ for child in children:
171
+ dot.edge(isa_id, child, color='#404040', len='2.0')
172
+
173
+ elif len(entities_involved) >= 2:
174
+ if rel_type == 'identifying':
175
+ dot.node(
176
+ rel_name,
177
+ rel_name,
178
+ shape='diamond',
179
+ style='filled',
180
+ fillcolor='#c8c8c8',
181
+ color='#404040',
182
+ penwidth='3',
183
+ width='1.8',
184
+ height='1.0'
185
+ )
186
+ else:
187
+ dot.node(
188
+ rel_name,
189
+ rel_name,
190
+ shape='diamond',
191
+ style='filled',
192
+ fillcolor='#c8c8c8',
193
+ color='#404040',
194
+ penwidth='1',
195
+ width='1.8',
196
+ height='1.0'
197
+ )
198
+
199
+ for j, attr in enumerate(rel_attributes):
200
+ attr_name = attr.get('name', '')
201
+ attr_id = f"{rel_name}_attr_{j}"
202
+
203
+ dot.node(
204
+ attr_id,
205
+ attr_name,
206
+ shape='ellipse',
207
+ style='filled',
208
+ fillcolor='#d8d8d8',
209
+ color='#404040',
210
+ width='1.0',
211
+ height='0.5'
212
+ )
213
+ dot.edge(rel_name, attr_id, color='#404040', len='1.0')
214
+
215
+ for entity in entities_involved:
216
+ cardinality = cardinalities.get(entity, '1')
217
+ dot.edge(
218
+ entity,
219
+ rel_name,
220
+ label=f' {cardinality} ',
221
+ color='#404040',
222
+ len='2.5',
223
+ fontcolor='#000000',
224
+ fontsize='11'
225
+ )
226
 
 
227
  with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
228
  dot.render(tmp.name, format=output_format, cleanup=True)
229
  return f"{tmp.name}.{output_format}"