11from graphviz import Digraph
22import os
33
4- # Create docs folder if not exists
5- os .makedirs ("docs " , exist_ok = True )
4+ # Create folders if they don't exist
5+ os .makedirs ("images " , exist_ok = True )
66
7- dot = Digraph ("ERD_PortalEmprego " , format = "png" )
7+ dot = Digraph ("ERD_ClientProduct " , format = "png" )
88dot .attr (rankdir = "TB" , fontsize = "12" , fontname = "Arial" )
99
10- # Helper: add table-style nodes with optional FK cardinalities
10+ # Helper function to add table nodes
1111def add_table (name , title , fields , color = "lightgray" ):
1212 label = f'''<
1313 <TABLE BORDER="1" CELLBORDER="1" CELLSPACING="0">
1414 <TR><TD BGCOLOR="{ color } " COLSPAN="2"><B>{ title } </B></TD></TR>'''
1515 for f in fields :
16- label += f'\n <TR><TD ALIGN="LEFT">{ f } </TD></TR>'
16+ label += f'\n <TR><TD ALIGN="LEFT">{ f } </TD></TR>'
1717 label += "\n </TABLE>>"
1818 dot .node (name , label = label , shape = "none" )
1919
20- # Tables with FK cardinalities
21- add_table ("user_profiles" , "user_profiles" , [
22- "id (PK)" ,
20+ # Add tables
21+ add_table ("Clients" , "Clients" , [
22+ "id_client (PK)" ,
23+ "name" ,
24+ "tax_id (UNIQUE)" ,
2325 "email (UNIQUE)" ,
24- "nome_completo" ,
25- "perfil" ,
26- "telefone" ,
27- "criado_em" ,
28- "atualizado_em"
26+ "phone_number (UNIQUE)" ,
27+ "date_of_birth" ,
28+ "newsletter" ,
29+ "authorization" ,
30+ "acquisition_channel" ,
31+ "modified_at" ,
32+ "created_at"
2933], color = "lightblue" )
3034
31- add_table ("candidate_profiles" , "candidate_profiles" , [
32- "id (PK)" ,
33- "id_utilizador (FK → user_profiles.id) 1:1" ,
34- "curriculo_url" ,
35- "competencias" ,
36- "anos_experiencia" ,
37- "formacao" ,
38- "salario_min/max" ,
39- "localizacoes_preferidas" ,
40- "linkedin/github/portfolio"
35+ add_table ("Products" , "Products" , [
36+ "id_product (PK)" ,
37+ "name (UNIQUE)" ,
38+ "description" ,
39+ "product_type" ,
40+ "sale_price" ,
41+ "current_stock" ,
42+ "minimum_stock" ,
43+ "purchase_price" ,
44+ "modified_at" ,
45+ "created_at"
46+ ], color = "lightgreen" )
47+
48+ add_table ("Employees" , "Employees" , [
49+ "id_employee (PK)" ,
50+ "name" ,
51+ "identification_doc" ,
52+ "tax_id (UNIQUE)" ,
53+ "email (UNIQUE)" ,
54+ "phone_number (UNIQUE)" ,
55+ "role" ,
56+ "date_of_birth" ,
57+ "address" ,
58+ "iban" ,
59+ "modified_at" ,
60+ "created_at"
4161], color = "lightyellow" )
4262
43- add_table ("company_profiles" , "company_profiles" , [
44- "id (PK)" ,
45- "id_utilizador (FK → user_profiles.id) 1:1" ,
46- "nome_empresa" ,
47- "descricao_empresa" ,
48- "sector_atividade" ,
49- "dimensao_empresa" ,
50- "numero_contribuinte" ,
51- "morada/cidade/pais faturacao" ,
52- "stripe_customer_id"
63+ add_table ("Suppliers" , "Suppliers" , [
64+ "id_supplier (PK)" ,
65+ "company_name" ,
66+ "tax_id (UNIQUE)" ,
67+ "email (UNIQUE)" ,
68+ "phone_number (UNIQUE)" ,
69+ "contact_person" ,
70+ "address" ,
71+ "country" ,
72+ "modified_at" ,
73+ "created_at"
74+ ], color = "orange" )
75+
76+ add_table ("Reviews" , "Reviews" , [
77+ "id_client (PK, FK → Clients.id_client)" ,
78+ "rating" ,
79+ "comment" ,
80+ "visibility" ,
81+ "review_date"
82+ ], color = "lightgray" )
83+
84+ add_table ("Events" , "Events" , [
85+ "id_event (PK)" ,
86+ "name" ,
87+ "description" ,
88+ "event_type" ,
89+ "event_date" ,
90+ "event_time" ,
91+ "location" ,
92+ "capacity" ,
93+ "private_event" ,
94+ "price" ,
95+ "modified_at" ,
96+ "created_at"
97+ ], color = "lightcoral" )
98+
99+ add_table ("Event_Participations" , "Event_Participations" , [
100+ "id_event (PK, FK → Events.id_event)" ,
101+ "id_client (PK, FK → Clients.id_client)" ,
102+ "registration_date"
103+ ], color = "lightcyan" )
104+
105+ add_table ("Payments" , "Payments" , [
106+ "id_payment (PK)" ,
107+ "id_client (FK → Clients.id_client)" ,
108+ "id_employee (FK → Employees.id_employee)" ,
109+ "amount" ,
110+ "currency" ,
111+ "payment_method" ,
112+ "payment_status" ,
113+ "payment_date"
114+ ], color = "lightsteelblue" )
115+
116+ add_table ("Payment_Items" , "Payment_Items" , [
117+ "id_payment (PK, FK → Payments.id_payment)" ,
118+ "id_product (PK, FK → Products.id_product)" ,
119+ "quantity" ,
120+ "unit_price"
53121], color = "lightpink" )
54122
55- add_table ("jobs " , "jobs " , [
56- "id (PK)" ,
57- "id_empresa (FK → company_profiles.id) 1:N " ,
58- "titulo " ,
59- "descricao " ,
60- "salario_min/max " ,
61- "localizacao " ,
62- "tipo_contrato " ,
63- "estado " ,
64- "experiencia_requerida " ,
65- "categoria "
66- ], color = "lightgreen " )
123+ add_table ("Orders " , "Orders " , [
124+ "id_order (PK)" ,
125+ "id_supplier (FK → Suppliers.id_supplier) " ,
126+ "id_employee (FK → Employees.id_employee) " ,
127+ "amount " ,
128+ "currency " ,
129+ "payment_method " ,
130+ "payment_status " ,
131+ "order_status " ,
132+ "order_date " ,
133+ "modified_at "
134+ ], color = "lightgoldenrod " )
67135
68- add_table ("job_applications" , "job_applications" , [
69- "id (PK)" ,
70- "id_oferta (FK → jobs.id) 1:N" ,
71- "id_candidato (FK → candidate_profiles.id) 1:N" ,
72- "carta_apresentacao_url" ,
73- "curriculo_url" ,
74- "estado"
75- ], color = "orange" )
136+ add_table ("Order_Items" , "Order_Items" , [
137+ "id_order (PK, FK → Orders.id_order)" ,
138+ "id_product (PK, FK → Products.id_product)" ,
139+ "quantity" ,
140+ "unit_value"
141+ ], color = "lightsalmon" )
76142
77- add_table ("payment_history" , "payment_history" , [
78- "id (PK)" ,
79- "id_empresa (FK → company_profiles.id) 1:N" ,
80- "stripe_payment_intent_id" ,
81- "valor" ,
82- "moeda" ,
83- "estado" ,
84- "metodo_pagamento" ,
85- "iva_percentual" ,
86- "valor_com_iva"
87- ], color = "lightgray" )
143+ # Foreign key edges with cardinalities
144+ dot .edge ("Clients" , "Reviews" , label = "1 → 1" )
145+
146+ dot .edge ("Events" , "Event_Participations" , label = "1 → N" )
147+ dot .edge ("Clients" , "Event_Participations" , label = "1 → N" )
148+
149+ dot .edge ("Clients" , "Payments" , label = "1 → N" )
150+ dot .edge ("Employees" , "Payments" , label = "1 → N" )
151+
152+ dot .edge ("Payments" , "Payment_Items" , label = "1 → N" )
153+ dot .edge ("Products" , "Payment_Items" , label = "1 → N" )
154+
155+ dot .edge ("Suppliers" , "Orders" , label = "1 → N" )
156+ dot .edge ("Employees" , "Orders" , label = "1 → N" )
88157
89- # Simple edge labels for readability (no crow foots)
90- dot .edge ("user_profiles" , "candidate_profiles" , label = "1 → 1" )
91- dot .edge ("user_profiles" , "company_profiles" , label = "1 → 1" )
92- dot .edge ("company_profiles" , "jobs" , label = "1 → *" )
93- dot .edge ("jobs" , "job_applications" , label = "1 → *" )
94- dot .edge ("candidate_profiles" , "job_applications" , label = "1 → *" )
95- dot .edge ("company_profiles" , "payment_history" , label = "1 → *" )
158+ dot .edge ("Orders" , "Order_Items" , label = "1 → N" )
159+ dot .edge ("Products" , "Order_Items" , label = "1 → N" )
96160
97- # Export
98- output_path = "./images/ERD_PortalEmprego "
161+ # Render and save
162+ output_path = "./images/ERD_ClientProduct "
99163dot .render (output_path , cleanup = True )
100164
101165print (f"ERD saved to { output_path } .png" )
0 commit comments