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

Update entity_relationship_generator.py

Browse files
Files changed (1) hide show
  1. entity_relationship_generator.py +52 -395
entity_relationship_generator.py CHANGED
@@ -1,304 +1,3 @@
1
- # import graphviz
2
- # import json
3
- # 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
- # It must follow the Expected JSON Format Example below.
13
-
14
- # Expected JSON Format Example:
15
- # {
16
- # "entities": [
17
- # {
18
- # "name": "Customer",
19
- # "type": "strong",
20
- # "attributes": [
21
- # {"name": "customer_id", "type": "primary_key"},
22
- # {"name": "first_name", "type": "regular"},
23
- # {"name": "last_name", "type": "regular"},
24
- # {"name": "email", "type": "regular"},
25
- # {"name": "phone", "type": "multivalued", "multivalued": true},
26
- # {"name": "age", "type": "derived", "derived": true},
27
- # {"name": "address", "type": "composite", "composite": true}
28
- # ]
29
- # },
30
- # {
31
- # "name": "Order",
32
- # "type": "strong",
33
- # "attributes": [
34
- # {"name": "order_id", "type": "primary_key"},
35
- # {"name": "customer_id", "type": "foreign_key"},
36
- # {"name": "order_date", "type": "regular"},
37
- # {"name": "total_amount", "type": "regular"},
38
- # {"name": "status", "type": "regular"},
39
- # {"name": "shipping_address", "type": "composite", "composite": true}
40
- # ]
41
- # },
42
- # {
43
- # "name": "Product",
44
- # "type": "strong",
45
- # "attributes": [
46
- # {"name": "product_id", "type": "primary_key"},
47
- # {"name": "name", "type": "regular"},
48
- # {"name": "description", "type": "regular"},
49
- # {"name": "price", "type": "regular"},
50
- # {"name": "category_id", "type": "foreign_key"},
51
- # {"name": "stock_quantity", "type": "regular"}
52
- # ]
53
- # },
54
- # {
55
- # "name": "Category",
56
- # "type": "strong",
57
- # "attributes": [
58
- # {"name": "category_id", "type": "primary_key"},
59
- # {"name": "name", "type": "regular"},
60
- # {"name": "description", "type": "regular"},
61
- # {"name": "parent_category_id", "type": "foreign_key"}
62
- # ]
63
- # },
64
- # {
65
- # "name": "OrderItem",
66
- # "type": "weak",
67
- # "attributes": [
68
- # {"name": "order_id", "type": "primary_key"},
69
- # {"name": "product_id", "type": "primary_key"},
70
- # {"name": "quantity", "type": "regular"},
71
- # {"name": "unit_price", "type": "regular"},
72
- # {"name": "discount", "type": "regular"}
73
- # ]
74
- # },
75
- # {
76
- # "name": "Payment",
77
- # "type": "strong",
78
- # "attributes": [
79
- # {"name": "payment_id", "type": "primary_key"},
80
- # {"name": "order_id", "type": "foreign_key"},
81
- # {"name": "payment_method", "type": "regular"},
82
- # {"name": "amount", "type": "regular"},
83
- # {"name": "payment_date", "type": "regular"},
84
- # {"name": "transaction_id", "type": "regular"}
85
- # ]
86
- # },
87
- # {
88
- # "name": "Supplier",
89
- # "type": "strong",
90
- # "attributes": [
91
- # {"name": "supplier_id", "type": "primary_key"},
92
- # {"name": "company_name", "type": "regular"},
93
- # {"name": "contact_person", "type": "regular"},
94
- # {"name": "email", "type": "regular"},
95
- # {"name": "phone", "type": "multivalued", "multivalued": true},
96
- # {"name": "address", "type": "composite", "composite": true}
97
- # ]
98
- # },
99
- # {
100
- # "name": "Review",
101
- # "type": "weak",
102
- # "attributes": [
103
- # {"name": "customer_id", "type": "primary_key"},
104
- # {"name": "product_id", "type": "primary_key"},
105
- # {"name": "rating", "type": "regular"},
106
- # {"name": "comment", "type": "regular"},
107
- # {"name": "review_date", "type": "regular"}
108
- # ]
109
- # }
110
- # ],
111
- # "relationships": [
112
- # {
113
- # "name": "Places",
114
- # "type": "regular",
115
- # "entities": ["Customer", "Order"],
116
- # "cardinalities": {
117
- # "Customer": "1",
118
- # "Order": "N"
119
- # }
120
- # },
121
- # {
122
- # "name": "Contains",
123
- # "type": "identifying",
124
- # "entities": ["Order", "OrderItem"],
125
- # "cardinalities": {
126
- # "Order": "1",
127
- # "OrderItem": "N"
128
- # }
129
- # },
130
- # {
131
- # "name": "Includes",
132
- # "type": "regular",
133
- # "entities": ["OrderItem", "Product"],
134
- # "cardinalities": {
135
- # "OrderItem": "N",
136
- # "Product": "1"
137
- # }
138
- # },
139
- # {
140
- # "name": "BelongsTo",
141
- # "type": "regular",
142
- # "entities": ["Product", "Category"],
143
- # "cardinalities": {
144
- # "Product": "N",
145
- # "Category": "1"
146
- # }
147
- # },
148
- # {
149
- # "name": "ProcessedBy",
150
- # "type": "regular",
151
- # "entities": ["Order", "Payment"],
152
- # "cardinalities": {
153
- # "Order": "1",
154
- # "Payment": "1"
155
- # }
156
- # },
157
- # {
158
- # "name": "Supplies",
159
- # "type": "regular",
160
- # "entities": ["Supplier", "Product"],
161
- # "cardinalities": {
162
- # "Supplier": "N",
163
- # "Product": "M"
164
- # }
165
- # },
166
- # {
167
- # "name": "Writes",
168
- # "type": "weak",
169
- # "entities": ["Customer", "Review"],
170
- # "cardinalities": {
171
- # "Customer": "1",
172
- # "Review": "N"
173
- # }
174
- # },
175
- # {
176
- # "name": "ReviewsProduct",
177
- # "type": "weak",
178
- # "entities": ["Review", "Product"],
179
- # "cardinalities": {
180
- # "Review": "N",
181
- # "Product": "1"
182
- # }
183
- # },
184
- # {
185
- # "name": "Subcategory",
186
- # "type": "regular",
187
- # "entities": ["Category", "Category"],
188
- # "cardinalities": {
189
- # "Category": "1",
190
- # "Category": "N"
191
- # }
192
- # }
193
- # ]
194
- # }
195
-
196
- # Returns:
197
- # str: The filepath to the generated PNG image file.
198
- # """
199
- # try:
200
- # if not json_input.strip():
201
- # return "Error: Empty input"
202
-
203
- # data = json.loads(json_input)
204
-
205
- # if 'entities' not in data:
206
- # raise ValueError("Missing required field: entities")
207
-
208
- # dot = graphviz.Graph(comment='ER Diagram', engine='dot')
209
- # dot.attr(rankdir='TB', bgcolor='white', pad='0.5')
210
- # dot.attr('node', fontname='Arial', fontsize='10', color='black')
211
- # dot.attr('edge', fontname='Arial', fontsize='9', color='black')
212
-
213
- # entities = data.get('entities', [])
214
- # relationships = data.get('relationships', [])
215
-
216
- # for entity in entities:
217
- # entity_name = entity.get('name')
218
- # entity_type = entity.get('type', 'strong')
219
- # attributes = entity.get('attributes', [])
220
-
221
- # if not entity_name:
222
- # continue
223
-
224
- # label_parts = [entity_name]
225
-
226
- # if attributes:
227
- # attr_lines = []
228
- # for attr in attributes:
229
- # attr_name = attr.get('name', '')
230
- # attr_type = attr.get('type', 'regular')
231
- # is_multivalued = attr.get('multivalued', False)
232
- # is_derived = attr.get('derived', False)
233
- # is_composite = attr.get('composite', False)
234
-
235
- # if attr_type == 'primary_key':
236
- # if is_multivalued:
237
- # attr_display = f"PK: {{ {attr_name} }}"
238
- # else:
239
- # attr_display = f"PK: {attr_name}"
240
- # elif attr_type == 'foreign_key':
241
- # attr_display = f"FK: {attr_name}"
242
- # else:
243
- # attr_display = attr_name
244
- # if is_derived:
245
- # attr_display = f"/ {attr_display} /"
246
- # if is_multivalued:
247
- # attr_display = f"{{ {attr_display} }}"
248
- # if is_composite:
249
- # attr_display = f"( {attr_display} )"
250
-
251
- # attr_lines.append(attr_display)
252
-
253
- # if attr_lines:
254
- # label_parts.extend(attr_lines)
255
-
256
- # label = "\\n".join(label_parts)
257
-
258
- # if entity_type == 'weak':
259
- # style = 'filled'
260
- # fillcolor = '#e8e8e8'
261
- # penwidth = '2'
262
- # else:
263
- # style = 'filled'
264
- # fillcolor = '#d0d0d0'
265
- # penwidth = '1'
266
-
267
- # dot.node(entity_name, label, shape='box', style=style, fillcolor=fillcolor, penwidth=penwidth)
268
-
269
- # for relationship in relationships:
270
- # rel_name = relationship.get('name')
271
- # rel_type = relationship.get('type', 'regular')
272
- # entities_involved = relationship.get('entities', [])
273
- # cardinalities = relationship.get('cardinalities', {})
274
-
275
- # if not rel_name or len(entities_involved) < 2:
276
- # continue
277
-
278
- # if rel_type == 'identifying':
279
- # style = 'filled'
280
- # fillcolor = '#c0c0c0'
281
- # penwidth = '2'
282
- # else:
283
- # style = 'filled'
284
- # fillcolor = '#c0c0c0'
285
- # penwidth = '1'
286
-
287
- # dot.node(rel_name, rel_name, shape='diamond', style=style, fillcolor=fillcolor, penwidth=penwidth)
288
-
289
- # for entity in entities_involved:
290
- # cardinality = cardinalities.get(entity, '1')
291
- # dot.edge(entity, rel_name, label=cardinality)
292
-
293
- # with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
294
- # dot.render(tmp.name, format=output_format, cleanup=True)
295
- # return f"{tmp.name}.{output_format}"
296
-
297
- # except json.JSONDecodeError:
298
- # return "Error: Invalid JSON format"
299
- # except Exception as e:
300
- # return f"Error: {str(e)}"
301
-
302
  import graphviz
303
  import json
304
  from tempfile import NamedTemporaryFile
@@ -321,12 +20,15 @@ def generate_entity_relationship_diagram(json_input: str, output_format: str) ->
321
  {
322
  "name": "ORDER",
323
  "type": "strong"
 
 
 
 
324
  }
325
  ],
326
  "relationships": [
327
  {
328
  "name": "Places",
329
- "type": "regular",
330
  "entities": ["CUSTOMER", "ORDER"],
331
  "cardinalities": {
332
  "CUSTOMER": "1",
@@ -348,22 +50,22 @@ def generate_entity_relationship_diagram(json_input: str, output_format: str) ->
348
  if 'entities' not in data:
349
  raise ValueError("Missing required field: entities")
350
 
351
- # Configuración simple y funcional
352
- dot = graphviz.Graph(comment='ER Diagram', engine='neato')
353
  dot.attr(
354
  bgcolor='white',
355
  pad='1.0',
356
  overlap='false',
357
- splines='true',
358
- sep='+15'
359
  )
360
- dot.attr('node', fontname='Arial', fontsize='11', color='#404040')
361
- dot.attr('edge', fontname='Arial', fontsize='10', color='#404040')
362
 
363
  entities = data.get('entities', [])
364
  relationships = data.get('relationships', [])
365
 
366
- # Crear solo las entidades principales (sin atributos por ahora)
367
  for entity in entities:
368
  entity_name = entity.get('name')
369
  entity_type = entity.get('type', 'strong')
@@ -372,7 +74,7 @@ def generate_entity_relationship_diagram(json_input: str, output_format: str) ->
372
  continue
373
 
374
  if entity_type == 'weak':
375
- # Entidad débil: rectángulo con doble borde
376
  dot.node(
377
  entity_name,
378
  entity_name,
@@ -380,114 +82,69 @@ def generate_entity_relationship_diagram(json_input: str, output_format: str) ->
380
  style='filled',
381
  fillcolor='#e8e8e8',
382
  color='#404040',
383
- penwidth='3',
384
- width='1.5',
385
- height='0.8'
386
  )
387
  else:
388
- # Entidad fuerte: rectángulo simple
389
  dot.node(
390
  entity_name,
391
  entity_name,
392
  shape='box',
393
  style='filled',
394
  fillcolor='#e8e8e8',
395
- color='#404040',
396
- penwidth='1',
397
- width='1.5',
398
- height='0.8'
399
  )
400
 
401
- # Crear relaciones
402
  for relationship in relationships:
403
  rel_name = relationship.get('name')
404
  rel_type = relationship.get('type', 'regular')
405
- entities_involved = relationship.get('entities', [])
406
- cardinalities = relationship.get('cardinalities', {})
407
 
408
  if not rel_name:
409
  continue
410
 
411
- if rel_type == 'isa':
412
- # Relación ISA - crear triángulo y conectar correctamente
413
- parent = relationship.get('parent')
414
- children = relationship.get('children', [])
415
-
416
- if parent and children:
417
- # Crear triángulo ISA
418
- isa_node = f"ISA_{rel_name}"
419
- dot.node(
420
- isa_node,
421
- 'ISA',
422
- shape='triangle',
423
- style='filled',
424
- fillcolor='#d0d0d0',
425
- color='#404040',
426
- penwidth='2',
427
- width='0.8',
428
- height='0.6'
429
- )
430
-
431
- # Conectar padre con triángulo ISA
432
- dot.edge(parent, isa_node, color='#404040', penwidth='2')
433
-
434
- # Conectar triángulo ISA con cada hijo
435
- for child in children:
436
- dot.edge(isa_node, child, color='#404040', penwidth='2')
437
-
438
- elif len(entities_involved) >= 2:
439
- # Relaciones regulares e identificadoras
440
- entity1, entity2 = entities_involved[0], entities_involved[1]
441
- card1 = cardinalities.get(entity1, '1')
442
- card2 = cardinalities.get(entity2, '1')
443
-
444
- if rel_type == 'identifying':
445
- # Relación identificadora - rombo con doble borde
446
- dot.node(
447
- rel_name,
448
- rel_name,
449
- shape='diamond',
450
- style='filled',
451
- fillcolor='#d8d8d8',
452
- color='#404040',
453
- penwidth='3',
454
- width='1.5',
455
- height='0.8'
456
- )
457
- else:
458
- # Relación regular - rombo simple
459
- dot.node(
460
- rel_name,
461
- rel_name,
462
- shape='diamond',
463
- style='filled',
464
- fillcolor='#d8d8d8',
465
- color='#404040',
466
- penwidth='1',
467
- width='1.5',
468
- height='0.8'
469
- )
470
-
471
- # Conectar primera entidad con relación
472
- dot.edge(
473
- entity1,
474
  rel_name,
475
- taillabel=f' {card1} ',
 
 
 
476
  color='#404040',
477
- penwidth='2',
478
- fontcolor='#000000',
479
- fontsize='12'
480
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
 
482
- # Conectar relación con segunda entidad
483
  dot.edge(
484
- rel_name,
485
- entity2,
486
- headlabel=f' {card2} ',
487
  color='#404040',
488
- penwidth='2',
489
- fontcolor='#000000',
490
- fontsize='12'
491
  )
492
 
493
  # Renderizar
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import graphviz
2
  import json
3
  from tempfile import NamedTemporaryFile
 
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",
 
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')
 
74
  continue
75
 
76
  if entity_type == 'weak':
77
+ # Entidad débil
78
  dot.node(
79
  entity_name,
80
  entity_name,
 
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