diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..51598c8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + backend: + build: + context: ./sge-backend + dockerfile: Dockerfile + ports: + - "8080:8080" + env_file: + - ./config.conf + environment: + SPRING_PROFILES_ACTIVE: docker + depends_on: + - database + restart: unless-stopped + + frontend: + build: + context: ./sge-frontend + dockerfile: Dockerfile + ports: + - "80:80" + env_file: + - ./config.conf + restart: unless-stopped + + database: + image: postgres:15 + environment: + POSTGRES_DB: sge + POSTGRES_USER: sge + POSTGRES_PASSWORD: 147 + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + postgres_data: diff --git a/sge-backend/message.json b/sge-backend/message.json index f109f80..9869d0a 100644 --- a/sge-backend/message.json +++ b/sge-backend/message.json @@ -1,547 +1,548 @@ { "id":111, "data_center":"new lab blc", - "projects":{ - "48966d34ff274def88db7f7b6d9f5cdc":{ - "main_optimization_space":{ - "vms":{ - "10.150.1.101":{ - "vm_name":"Bgreen", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"m1.medium", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "Bgreen", - 2, - 4, - 40, - "compute4" - ] + "physical_machines":{ + "10.150.1.33":{ + "tag":"main_optimization_space", + "name":"compute2", + "power":158.23672131147552, + "cloud_system":"open-stack", + "confg":[ + "compute2", + 11, + 101, + 91, + 114 + ], + "vms":{ + "10.150.1.112":{ + "vm_name":"nginx-proxyV2", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.33", + "host":"compute2", + "flavor_name":"m1.small", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 }, - "10.150.1.107":{ - "vm_name":"BLC-AYPOS-Development-1", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.34", - "host":"compute3", - "flavor_name":"aypos.test.flavour.8", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-1", - 3, - 3, - 8, - "compute3" - ] - }, - "10.150.1.112":{ - "vm_name":"nginx-proxyV2", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.33", - "host":"compute2", - "flavor_name":"m1.small", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "nginx-proxyV2", - 1, - 2, - 20, - "compute2" - ] - }, - "10.150.1.114":{ - "vm_name":"BLC-AYPOS-Development-5", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.33", - "host":"compute2", - "flavor_name":"aypos.test.flavour.19", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-5", - 2, - 3, - 8, - "compute2" - ] - }, - "10.150.1.123":{ - "vm_name":"SGE", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"ForJAVA", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "SGE", - 8, - 32, - 150, - "compute4" - ] - }, - "10.150.1.130":{ - "vm_name":"aybuke", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"m1.public", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "aybuke", - 8, - 8, - 50, - "compute4" - ] - }, - "10.150.1.133":{ - "vm_name":"BLC-AYPOS-Development-2", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.34", - "host":"compute3", - "flavor_name":"aypos.test.flavour.15", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-2", - 2, - 4, - 8, - "compute3" - ] - }, - "10.150.1.141":{ - "vm_name":"BLC-AYPOS-Development-0", - "power":12.178434411667777, - "state":"active", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"aypos.test.flavour.11", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-0", - 3, - 3, - 8, - "compute4" - ] - }, - "10.150.1.145":{ - "vm_name":"BLC-AYPOS-Development-9", - "power":14.950904196341009, - "state":"active", - "hosting_pm":"10.150.1.33", - "host":"compute2", - "flavor_name":"aypos.test.flavour.0", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-9", - 2, - 1, - 8, - "compute2" - ] - }, - "10.150.1.150":{ - "vm_name":"blc-cloud", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.32", - "host":"compute1", - "flavor_name":"m1.xlarge", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "blc-cloud", - 8, - 16, - 160, - "compute1" - ] - }, - "10.150.1.155":{ - "vm_name":"Aypos-Ulak", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"m1.vrealize", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "Aypos-Ulak", - 8, - 16, - 50, - "compute4" - ] - }, - "10.150.1.167":{ - "vm_name":"prometheus_serverV3", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.33", - "host":"compute2", - "flavor_name":"ayposYedek", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "prometheus_serverV3", - 2, - 6, - 60, - "compute2" - ] - }, - "10.150.1.171":{ - "vm_name":"blc-meet", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"m1.devstack", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "blc-meet", - 4, - 7, - 60, - "compute4" - ] - }, - "10.150.1.172":{ - "vm_name":"blc-office", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.32", - "host":"compute1", - "flavor_name":"m1.large", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "blc-office", - 4, - 8, - 80, - "compute1" - ] - }, - "10.150.1.174":{ - "vm_name":"mail-server", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"mail-server", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "mail-server", - 4, - 8, - 100, - "compute4" - ] - }, - "10.150.1.175":{ - "vm_name":"BLC-AYPOS-Development-7", - "power":19.256314103409117, - "state":"active", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"aypos.test.flavour.19", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-7", - 2, - 3, - 8, - "compute4" - ] - }, - "10.150.1.180":{ - "vm_name":"BLC-AYPOS-Development-8", - "power":26.738567147437937, - "state":"active", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"aypos.test.flavour.6", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-8", - 1, - 2, - 8, - "compute4" - ] - }, - "10.150.1.190":{ - "vm_name":"BLC-AYPOS-Development-6", - "power":42.36942549990342, - "state":"active", - "hosting_pm":"10.150.1.33", - "host":"compute2", - "flavor_name":"aypos.test.flavour.16", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-6", - 2, - 4, - 8, - "compute2" - ] - }, - "10.150.1.193":{ - "vm_name":"BLC-AYPOS-Development-4", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.34", - "host":"compute3", - "flavor_name":"aypos.test.flavour.4", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "BLC-AYPOS-Development-4", - 2, - 1, - 8, - "compute3" - ] - }, - "10.150.1.199":{ - "vm_name":"gitea-server", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.33", - "host":"compute2", - "flavor_name":"m1.large", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "gitea-server", - 4, - 8, - 80, - "compute2" - ] - }, - "10.150.1.200":{ - "vm_name":"ollama", - "power":0, - "state":"inactive", - "hosting_pm":"10.150.1.35", - "host":"compute4", - "flavor_name":"m1.vrealize", - "emissionsource":{ - "Dizel":10, - "Ham petrol":5, - "Orimulsion":15, - "Motor benzini":30, - "Jet benzini":40 - }, - "tag":"main_optimization_space", - "confg":[ - "ollama", - 8, - 16, - 50, - "compute4" - ] - } + "tag":"main_optimization_space", + "confg":[ + "nginx-proxyV2", + 1, + 2, + 20, + "compute2" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" }, - "pms":{ - "10.150.1.33":{ - "tag":"main_optimization_space", - "name":"compute2", - "power":159.86075409836062, - "confg":[ - "compute2", - 11, - 101, - 91, - 114 - ] + "10.150.1.114":{ + "vm_name":"BLC-AYPOS-Development-5", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.33", + "host":"compute2", + "flavor_name":"aypos.test.flavour.19", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 }, - "10.150.1.34":{ - "tag":"main_optimization_space", - "name":"compute3", - "power":21.31772131147542, - "confg":[ - "compute3", - 24, - 115, - 151, - 114 - ] + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-5", + 2, + 3, + 8, + "compute2" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.145":{ + "vm_name":"BLC-AYPOS-Development-9", + "power":8.857817947598239, + "state":"active", + "hosting_pm":"10.150.1.33", + "host":"compute2", + "flavor_name":"aypos.test.flavour.0", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 }, - "10.150.1.35":{ - "tag":"main_optimization_space", - "name":"compute4", - "power":189.92359016393448, - "confg":[ - "compute4", - -17, - 23, - 87, - 114 - ] + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-9", + 2, + 1, + 8, + "compute2" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.167":{ + "vm_name":"prometheus_serverV3", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.33", + "host":"compute2", + "flavor_name":"ayposYedek", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 }, - "10.150.1.32":{ - "tag":"main_optimization_space", - "name":"compute1", - "power":36.45219672131149, - "confg":[ - "compute1", - 10, - 97, - 211, - 114 - ] - } + "tag":"main_optimization_space", + "confg":[ + "prometheus_serverV3", + 2, + 6, + 60, + "compute2" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.190":{ + "vm_name":"BLC-AYPOS-Development-6", + "power":7.483440624373921, + "state":"active", + "hosting_pm":"10.150.1.33", + "host":"compute2", + "flavor_name":"aypos.test.flavour.16", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-6", + 2, + 4, + 8, + "compute2" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.199":{ + "vm_name":"gitea-server", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.33", + "host":"compute2", + "flavor_name":"m1.large", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "gitea-server", + 4, + 8, + 80, + "compute2" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + } + } + }, + "10.150.1.34":{ + "tag":"main_optimization_space", + "name":"compute3", + "power":22.16875409836066, + "cloud_system":"open-stack", + "confg":[ + "compute3", + 24, + 115, + 151, + 114 + ], + "vms":{ + "10.150.1.107":{ + "vm_name":"BLC-AYPOS-Development-1", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.34", + "host":"compute3", + "flavor_name":"aypos.test.flavour.8", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-1", + 3, + 3, + 8, + "compute3" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.133":{ + "vm_name":"BLC-AYPOS-Development-2", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.34", + "host":"compute3", + "flavor_name":"aypos.test.flavour.15", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-2", + 2, + 4, + 8, + "compute3" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.193":{ + "vm_name":"BLC-AYPOS-Development-4", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.34", + "host":"compute3", + "flavor_name":"aypos.test.flavour.4", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-4", + 2, + 1, + 8, + "compute3" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + } + } + }, + "10.150.1.35":{ + "tag":"main_optimization_space", + "name":"compute4", + "power":161.65611475409833, + "cloud_system":"open-stack", + "confg":[ + "compute4", + -19, + 19, + 87, + 114 + ], + "vms":{ + "10.150.1.101":{ + "vm_name":"Bgreen", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"m1.large", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "Bgreen", + 4, + 8, + 80, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.123":{ + "vm_name":"SGE", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"ForJAVA", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "SGE", + 8, + 32, + 150, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.130":{ + "vm_name":"aybuke", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"m1.public", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "aybuke", + 8, + 8, + 50, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.141":{ + "vm_name":"BLC-AYPOS-Development-0", + "power":1.1574191009150598, + "state":"active", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"aypos.test.flavour.11", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-0", + 3, + 3, + 8, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.155":{ + "vm_name":"Aypos-Ulak", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"m1.vrealize", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "Aypos-Ulak", + 8, + 16, + 50, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.171":{ + "vm_name":"blc-meet", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"m1.devstack", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "blc-meet", + 4, + 7, + 60, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.174":{ + "vm_name":"mail-server", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"mail-server", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "mail-server", + 4, + 8, + 100, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.175":{ + "vm_name":"BLC-AYPOS-Development-7", + "power":0.765597705736144, + "state":"active", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"aypos.test.flavour.19", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-7", + 2, + 3, + 8, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.180":{ + "vm_name":"BLC-AYPOS-Development-8", + "power":0.3963838074873964, + "state":"active", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"aypos.test.flavour.6", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "BLC-AYPOS-Development-8", + 1, + 2, + 8, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.200":{ + "vm_name":"ollama", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.35", + "host":"compute4", + "flavor_name":"m1.vrealize", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "ollama", + 8, + 16, + 50, + "compute4" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + } + } + }, + "10.150.1.32":{ + "tag":"main_optimization_space", + "name":"compute1", + "power":36.502836065573774, + "cloud_system":"open-stack", + "confg":[ + "compute1", + 10, + 97, + 211, + 114 + ], + "vms":{ + "10.150.1.150":{ + "vm_name":"blc-cloud", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.32", + "host":"compute1", + "flavor_name":"m1.xlarge", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "blc-cloud", + 8, + 16, + 160, + "compute1" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" + }, + "10.150.1.172":{ + "vm_name":"blc-office", + "power":0, + "state":"inactive", + "hosting_pm":"10.150.1.32", + "host":"compute1", + "flavor_name":"m1.large", + "emissionsource":{ + "Dizel":15, + "Ham petrol":10, + "Motor benzini":35, + "Jet benzini":40 + }, + "tag":"main_optimization_space", + "confg":[ + "blc-office", + 4, + 8, + 80, + "compute1" + ], + "project":"48966d34ff274def88db7f7b6d9f5cdc" } - }, - "sub_optimization_space":{ - } } } diff --git a/sge-backend/src/main/java/com/sgs/SgsApplication.java b/sge-backend/src/main/java/com/sgs/SgsApplication.java index 8d5d20c..0ce8d67 100644 --- a/sge-backend/src/main/java/com/sgs/SgsApplication.java +++ b/sge-backend/src/main/java/com/sgs/SgsApplication.java @@ -2,6 +2,8 @@ package com.sgs; import com.sgs.graphql.activitySubUnit.domain.ActivitySubUnit; import com.sgs.graphql.activitySubUnit.service.ActivitySubUnitService; +import com.sgs.graphql.area.domain.Area; +import com.sgs.graphql.area.service.AreaService; import com.sgs.graphql.city.domain.City; import com.sgs.graphql.city.repo.CityRepo; import com.sgs.graphql.city.service.CityService; @@ -73,6 +75,7 @@ public class SgsApplication implements CommandLineRunner { private final PermissionService permissionService; private final RoleService roleService; private final RoleRepo roleRepo; + private final AreaService areaService; private final NeighborhoodRepo neighborhoodRepo; private final NeighborhoodService neighborhoodService; private final CityService cityService; @@ -118,7 +121,7 @@ public class SgsApplication implements CommandLineRunner { @Autowired public SgsApplication(RoleService roleService, PermissionService permissionService, RoleRepo roleRepo, - NeighborhoodRepo neighborhoodRepo, NeighborhoodService neighborhoodService, CityService cityService, + AreaService areaService, NeighborhoodRepo neighborhoodRepo, NeighborhoodService neighborhoodService, CityService cityService, CityRepo cityRepo, DistrictRepo districtRepo, DistrictService districtService, CountryRepo countryRepo, CountryService countryService, OrganizationService organizationService, UserService userService, PasswordEncoder passwordEncoder, SectorService sectorService, SubSectorService subSectorService, @@ -133,6 +136,7 @@ public class SgsApplication implements CommandLineRunner { this.roleService = roleService; this.permissionService = permissionService; this.roleRepo = roleRepo; + this.areaService = areaService; this.neighborhoodRepo = neighborhoodRepo; this.neighborhoodService = neighborhoodService; this.cityService = cityService; @@ -426,6 +430,10 @@ public class SgsApplication implements CommandLineRunner { data_center_read.setTag(PermissionName.DATA_CENTER_READ); data_center_read.setDescription(PermissionDescription.DATA_CENTER_READ); + Permission paginate_datacenters_get = new Permission(); + paginate_datacenters_get.setTag(PermissionName.PAGINATE_DATACENTERS_GET); + paginate_datacenters_get.setDescription(PermissionDescription.PAGINATE_DATACENTERS_GET); + permissionService.saveAll(List.of( show_graphics, activities_get, paginate_user_histories, activity_sub_units_get, sub_sectors_get, sectors_get, deleted_items, undelete_items, user_create, user_update, user_delete, paginate_users_get, @@ -442,7 +450,7 @@ public class SgsApplication implements CommandLineRunner { question_create, question_delete, question_update, paginate_questions_get, published_survey_create, surveys_get, paginate_surveys_get, survey_add, survey_delete, survey_update, settings_access, data_center_create, data_center_delete, - data_center_update, data_center_read)); + data_center_update, data_center_read, paginate_datacenters_get)); } public void createDefaultRole() { @@ -771,6 +779,27 @@ public class SgsApplication implements CommandLineRunner { mailInfoService.save(mailInfo); } + public void ensureMissingPermissions() { + // Check if paginate_datacenters_get permission exists + Optional datacenterPermission = permissionService.findByTag("paginate_datacenters_get"); + if (!datacenterPermission.isPresent()) { + // Create the missing permission + Permission paginate_datacenters_get = new Permission(); + paginate_datacenters_get.setTag(PermissionName.PAGINATE_DATACENTERS_GET); + paginate_datacenters_get.setDescription(PermissionDescription.PAGINATE_DATACENTERS_GET); + permissionService.save(paginate_datacenters_get); + } + + // Ensure SUPER_ADMIN has all permissions + Optional superAdminRole = roleRepo.findByTag("SUPER_ADMIN"); + if (superAdminRole.isPresent()) { + Role role = superAdminRole.get(); + List allPermissions = permissionService.findAll(); + role.setPermissions(allPermissions); + roleService.save(role); + } + } + @Override public void run(String... args) throws Exception { if (mailInfoService.findAll().isEmpty()) { @@ -816,6 +845,8 @@ public class SgsApplication implements CommandLineRunner { if (roleService.findAll().isEmpty()) { createDefaultRole(); } + // Ensure all permissions are created and assigned to SUPER_ADMIN + ensureMissingPermissions(); if (organizationService.findAll().isEmpty()) { createDefaultOrganization(); } @@ -834,18 +865,34 @@ public class SgsApplication implements CommandLineRunner { if (neighborhoodService.findAll().isEmpty()) { createNeighborhoodsFromJson(); } - // Add new permission if it doesn't exist - // [Remove Later] - if (permissionService.findAll().stream().noneMatch(p -> PermissionName.SETTINGS_ACCESS.equals(p.getTag()))) { - Permission settingsAccess = new Permission(); - settingsAccess.setTag(PermissionName.SETTINGS_ACCESS); - settingsAccess.setDescription(PermissionDescription.SETTINGS_ACCESS); - settingsAccess.setDefault_permission(false); - settingsAccess.setDeleted(false); - permissionService.save(settingsAccess); + if (!cityService.findAll().isEmpty()) { + createDefaultArea(); + } + } + + void createDefaultArea() { + // Check if default area already exists + List existingAreas = areaService.findAll(); + boolean defaultAreaExists = existingAreas.stream() + .anyMatch(area -> "Turkiye".equals(area.getTag()) && area.isDefaultArea()); + + if (!defaultAreaExists) { + Area defaultArea = new Area(); + defaultArea.setTag("Turkiye"); + defaultArea.setDefaultArea(true); + defaultArea.setDeleted(false); + + // Get all cities to add to the default area + List allCities = cityService.findAll(); + defaultArea.setCities(allCities); + + // Get the Turkey country to add to the default area + List countries = countryService.findAll(); + if (!countries.isEmpty()) { + defaultArea.setCountries(countries); + } + + areaService.save(defaultArea); } - Permission settings_access = new Permission(); - settings_access.setTag(PermissionName.SETTINGS_ACCESS); - settings_access.setDescription(PermissionDescription.SETTINGS_ACCESS); } } \ No newline at end of file diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/DataCenter.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/DataCenter.java index ea02b59..6d49f23 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/DataCenter.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/DataCenter.java @@ -1,12 +1,11 @@ package com.sgs.graphql.dataCenter.domain; + import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import com.sgs.graphql.area.domain.Area; import com.sgs.graphql.sector.domain.Sector; import com.sgs.graphql.subSector.domain.SubSector; -import com.sgs.graphql.emissionSource.domain.EmissionSource; import com.sgs.graphql.emissionScope.domain.EmissionScope; -import com.sgs.graphql.consuptionUnit.domain.ConsuptionUnit; import com.sgs.graphql.activitySubUnit.domain.ActivitySubUnit; import com.sgs.lib.dao.domain.BaseDomain; @@ -14,7 +13,8 @@ import javax.persistence.*; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; -import org.springframework.transaction.annotation.Transactional; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; import java.util.ArrayList; import java.util.List; @@ -26,28 +26,25 @@ public class DataCenter extends BaseDomain { @Column(unique = true) private Integer externalId; - + private Integer number; private Area area; - private List projects = new ArrayList<>(); + private List physicalMachines = new ArrayList<>(); + private List dataCenterEmissionSources = new ArrayList<>(); private Sector sector; private SubSector subSector; - private EmissionSource emissionSource; private EmissionScope emissionScope; private Double consuptionAmount; - private ConsuptionUnit consuptionUnit; private ActivitySubUnit activitySubUnit; - + // New attributes private String ayposURL; private String address; private Double latitude; private Double longitude; - @Column(name = "data_center_name") - @Transactional public String getDataCenter() { return dataCenter; } @@ -63,12 +60,23 @@ public class DataCenter extends BaseDomain { @OneToMany(mappedBy = "dataCenter", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) @JsonManagedReference - public List getProjects() { - return projects; + public List getPhysicalMachines() { + return physicalMachines; } - public void setProjects(List projects) { - this.projects = projects; + public void setPhysicalMachines(List physicalMachines) { + this.physicalMachines = physicalMachines; + } + + @OneToMany(mappedBy = "dataCenter", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + @Fetch(FetchMode.SUBSELECT) + @JsonManagedReference + public List getDataCenterEmissionSources() { + return dataCenterEmissionSources; + } + + public void setDataCenterEmissionSources(List dataCenterEmissionSources) { + this.dataCenterEmissionSources = dataCenterEmissionSources; } public void setExternalId(Integer externalId) { @@ -84,7 +92,6 @@ public class DataCenter extends BaseDomain { this.number = number; } - @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "sector_id") public Sector getSector() { @@ -114,16 +121,6 @@ public class DataCenter extends BaseDomain { this.consuptionAmount = consuptionAmount; } - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "consuption_unit_id") - public ConsuptionUnit getConsuptionUnit() { - return consuptionUnit; - } - - public void setConsuptionUnit(ConsuptionUnit consuptionUnit) { - this.consuptionUnit = consuptionUnit; - } - @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "area_id") public Area getArea() { @@ -144,16 +141,6 @@ public class DataCenter extends BaseDomain { this.subSector = subSector; } - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "emission_source_id") - public EmissionSource getEmissionSource() { - return emissionSource; - } - - public void setEmissionSource(EmissionSource emissionSource) { - this.emissionSource = emissionSource; - } - @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "activity_sub_unit_id") public ActivitySubUnit getActivitySubUnit() { diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/DataCenterEmissionSource.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/DataCenterEmissionSource.java new file mode 100644 index 0000000..a92a385 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/DataCenterEmissionSource.java @@ -0,0 +1,68 @@ +package com.sgs.graphql.dataCenter.domain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.sgs.graphql.emissionSource.domain.EmissionSource; +import com.sgs.graphql.consuptionUnit.domain.ConsuptionUnit; +import com.sgs.lib.dao.domain.BaseDomain; + +import javax.persistence.*; + +@Entity +@Table(name = "data_center_emission_source") +public class DataCenterEmissionSource extends BaseDomain { + + private DataCenter dataCenter; + private EmissionSource emissionSource; + private ConsuptionUnit consuptionUnit; + private Boolean isDefault = false; // To mark which one is the default emission source + private Double percentage; // Percentage allocation for this emission source (optional) + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "data_center_id", nullable = false) + @JsonBackReference + public DataCenter getDataCenter() { + return dataCenter; + } + + public void setDataCenter(DataCenter dataCenter) { + this.dataCenter = dataCenter; + } + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "emission_source_id", nullable = false) + public EmissionSource getEmissionSource() { + return emissionSource; + } + + public void setEmissionSource(EmissionSource emissionSource) { + this.emissionSource = emissionSource; + } + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "consuption_unit_id", nullable = false) + public ConsuptionUnit getConsuptionUnit() { + return consuptionUnit; + } + + public void setConsuptionUnit(ConsuptionUnit consuptionUnit) { + this.consuptionUnit = consuptionUnit; + } + + @Column(name = "is_default") + public Boolean getIsDefault() { + return isDefault; + } + + public void setIsDefault(Boolean isDefault) { + this.isDefault = isDefault; + } + + @Column(name = "percentage") + public Double getPercentage() { + return percentage; + } + + public void setPercentage(Double percentage) { + this.percentage = percentage; + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/PhysicalMachine.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/PhysicalMachine.java index b3ece1a..3d53485 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/PhysicalMachine.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/PhysicalMachine.java @@ -14,9 +14,10 @@ public class PhysicalMachine extends BaseDomain { private String name; private String ip; private String tag; + private String cloudSystem; private Double power; private List vms = new ArrayList<>(); - private Project project; + private DataCenter dataCenter; @Column(name = "name") public String getName() { @@ -45,6 +46,15 @@ public class PhysicalMachine extends BaseDomain { this.tag = tag; } + @Column(name = "cloud_system") + public String getCloudSystem() { + return cloudSystem; + } + + public void setCloudSystem(String cloudSystem) { + this.cloudSystem = cloudSystem; + } + @Column(name = "power") public Double getPower() { return power; @@ -65,13 +75,13 @@ public class PhysicalMachine extends BaseDomain { } @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "project_id") + @JoinColumn(name = "data_center_id") @JsonBackReference - public Project getProject() { - return project; + public DataCenter getDataCenter() { + return dataCenter; } - public void setProject(Project project) { - this.project = project; + public void setDataCenter(DataCenter dataCenter) { + this.dataCenter = dataCenter; } } \ No newline at end of file diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/Project.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/Project.java deleted file mode 100644 index f4ca3e9..0000000 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/Project.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.sgs.graphql.dataCenter.domain; - -import java.util.List; - -import javax.persistence.*; - -import com.fasterxml.jackson.annotation.JsonBackReference; -import com.fasterxml.jackson.annotation.JsonManagedReference; -import com.sgs.lib.dao.domain.BaseDomain; - -@Entity -@Table(name = "project") -public class Project extends BaseDomain{ - - private String name; - - private List physicalMachines; - - - @OneToMany(mappedBy = "project", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) - @JsonManagedReference - public List getPhysicalMachines() { - return physicalMachines; - } - - public void setPhysicalMachines(List physicalMachines) { - this.physicalMachines = physicalMachines; - } - - // This creates a foreign key in the `project` table - private DataCenter dataCenter; - - - @Column(name = "name") - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - @ManyToOne - @JoinColumn(name = "data_center_id") - @JsonBackReference - public DataCenter getDataCenter() { - return dataCenter; - } - public void setDataCenter(DataCenter dataCenter) { - this.dataCenter = dataCenter; - } -} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/VM.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/VM.java index 9305f63..ee29795 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/VM.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/domain/VM.java @@ -23,6 +23,7 @@ public class VM extends BaseDomain { private String host; private String flavorName; private String tag; + private String project; // UUID of the project this VM belongs to private String emissionSourceData; // JSON string to store emission source map private Config config; private PhysicalMachine physicalMachine; @@ -100,6 +101,15 @@ public class VM extends BaseDomain { this.tag = tag; } + @Column(name = "project") + public String getProject() { + return project; + } + + public void setProject(String project) { + this.project = project; + } + @Column(name = "ip") public String getIp() { return ip; diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/DataCenterDto.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/DataCenterDto.java index 8893445..1973dbf 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/DataCenterDto.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/DataCenterDto.java @@ -17,7 +17,9 @@ public class DataCenterDto { private Integer externalId; private Integer number; private AreaDto area; - private Map projects; + + @JsonProperty("physical_machine") + private Map physicalMachine; // Emission calculation fields private SectorDto sector; @@ -68,12 +70,12 @@ public class DataCenterDto { this.area = area; } - public Map getProjects() { - return projects; + public Map getPhysicalMachine() { + return physicalMachine; } - public void setProjects(Map projects) { - this.projects = projects; + public void setPhysicalMachine(Map physicalMachine) { + this.physicalMachine = physicalMachine; } public SectorDto getSector() { diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/DataCenterEmissionSourceDto.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/DataCenterEmissionSourceDto.java new file mode 100644 index 0000000..d3cd4b6 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/DataCenterEmissionSourceDto.java @@ -0,0 +1,50 @@ +package com.sgs.graphql.dataCenter.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class DataCenterEmissionSourceDto { + + @JsonProperty("emission_source_id") + private String emissionSourceId; + + @JsonProperty("consuption_unit_id") + private String consuptionUnitId; + + @JsonProperty("is_default") + private Boolean isDefault = false; + + @JsonProperty("percentage") + private Double percentage; + + public String getEmissionSourceId() { + return emissionSourceId; + } + + public void setEmissionSourceId(String emissionSourceId) { + this.emissionSourceId = emissionSourceId; + } + + public String getConsuptionUnitId() { + return consuptionUnitId; + } + + public void setConsuptionUnitId(String consuptionUnitId) { + this.consuptionUnitId = consuptionUnitId; + } + + public Boolean getIsDefault() { + return isDefault; + } + + public void setIsDefault(Boolean isDefault) { + this.isDefault = isDefault; + } + + public Double getPercentage() { + return percentage; + } + + public void setPercentage(Double percentage) { + this.percentage = percentage; + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/MainOptimizationSpaceDto.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/MainOptimizationSpaceDto.java deleted file mode 100644 index 1086696..0000000 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/MainOptimizationSpaceDto.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sgs.graphql.dataCenter.dto; - -import java.util.Map; - -public class MainOptimizationSpaceDto { - private Map vms; - private Map pms; - - public Map getVms() { - return vms; - } - - public void setVms(Map vms) { - this.vms = vms; - } - - public Map getPms() { - return pms; - } - - public void setPms(Map pms) { - this.pms = pms; - } -} \ No newline at end of file diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/PhysicalMachineDto.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/PhysicalMachineDto.java index 1f730b3..f4bc349 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/PhysicalMachineDto.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/PhysicalMachineDto.java @@ -2,6 +2,7 @@ package com.sgs.graphql.dataCenter.dto; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; +import java.util.Map; public class PhysicalMachineDto { @JsonProperty("name") @@ -10,11 +11,17 @@ public class PhysicalMachineDto { @JsonProperty("tag") private String tag; + @JsonProperty("cloud_system") + private String cloudSystem; + @JsonProperty("power") private double power; @JsonProperty("confg") // Note: keeping the typo from JSON private List confg; + + @JsonProperty("vms") + private Map vms; private String ip; // IP address from the message key @@ -34,6 +41,14 @@ public class PhysicalMachineDto { this.tag = tag; } + public String getCloudSystem() { + return cloudSystem; + } + + public void setCloudSystem(String cloudSystem) { + this.cloudSystem = cloudSystem; + } + public double getPower() { return power; } @@ -57,4 +72,12 @@ public class PhysicalMachineDto { public void setIp(String ip) { this.ip = ip; } + + public Map getVms() { + return vms; + } + + public void setVms(Map vms) { + this.vms = vms; + } } diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/ProjectDto.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/ProjectDto.java deleted file mode 100644 index 1103893..0000000 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/ProjectDto.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.sgs.graphql.dataCenter.dto; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ProjectDto { - @JsonProperty("main_optimization_space") - private MainOptimizationSpaceDto mainOptimizationSpace; - - @JsonProperty("sub_optimization_space") - private SubOptimizationSpaceDto subOptimizationSpace; - - // Legacy field - kept for backward compatibility with older message formats - @JsonProperty("physical_machines") - private List physicalMachines; - - // These fields are derived from the map key and entity relationships, not from the message - private String id; // This will be set from the map key - private String name; // This will be derived or set separately - - public MainOptimizationSpaceDto getMainOptimizationSpace() { - return mainOptimizationSpace; - } - - public void setMainOptimizationSpace(MainOptimizationSpaceDto mainOptimizationSpace) { - this.mainOptimizationSpace = mainOptimizationSpace; - } - - public SubOptimizationSpaceDto getSubOptimizationSpace() { - return subOptimizationSpace; - } - - public void setSubOptimizationSpace(SubOptimizationSpaceDto subOptimizationSpace) { - this.subOptimizationSpace = subOptimizationSpace; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - - public List getPhysicalMachines() { - return physicalMachines; - } - public void setPhysicalMachines(List physicalMachines) { - this.physicalMachines = physicalMachines; - } -} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/SubOptimizationSpaceDto.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/SubOptimizationSpaceDto.java deleted file mode 100644 index ddce932..0000000 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/SubOptimizationSpaceDto.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sgs.graphql.dataCenter.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -/** - * DTO for sub_optimization_space in the message format. - * Currently appears to be empty in the message structure. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class SubOptimizationSpaceDto { - // Currently empty based on the message format - // Can be extended if needed in the future - - // Add a placeholder field to make Jackson happy - private boolean isEmpty = true; - - public boolean isEmpty() { - return isEmpty; - } - - public void setEmpty(boolean empty) { - this.isEmpty = empty; - } -} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/VMDto.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/VMDto.java index 4a106b0..92f026d 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/VMDto.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/dto/VMDto.java @@ -26,6 +26,9 @@ public class VMDto { @JsonProperty("tag") private String tag; + @JsonProperty("project") + private String project; + @JsonProperty("confg") // Note: keeping the typo from JSON private List confg; @@ -101,6 +104,14 @@ public class VMDto { this.tag = tag; } + public String getProject() { + return project; + } + + public void setProject(String project) { + this.project = project; + } + public List getConfg() { return confg; } diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/DataCenterMutation.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/DataCenterMutation.java index 5129da5..0400428 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/DataCenterMutation.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/DataCenterMutation.java @@ -1,18 +1,26 @@ package com.sgs.graphql.dataCenter.mutation; +import com.sgs.exception.GraphQLCustomException; import com.sgs.graphql.dataCenter.mutation.mapper.DataCenterMapper; import com.sgs.graphql.dataCenter.service.DataCenterService; import com.sgs.graphql.dataCenter.domain.DataCenter; import com.sgs.graphql.dataCenter.mutation.input.DataCenterCreateInput; import com.sgs.graphql.dataCenter.mutation.input.DataCenterUpdateInput; import com.sgs.graphql.permission.domain.PermissionName; +import com.sgs.graphql.systemHistory.enums.LogType; +import com.sgs.graphql.systemHistory.mutation.SystemLogger; +import com.sgs.graphql.userHistory.mutation.UserLogger; +import com.sgs.graphql.userNotification.mutation.UserNotificationMutation; import graphql.kickstart.tools.GraphQLMutationResolver; +import graphql.schema.DataFetchingEnvironment; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; +import javax.transaction.Transactional; import javax.validation.Valid; +import java.util.NoSuchElementException; import java.util.UUID; @Validated @@ -21,57 +29,128 @@ public class DataCenterMutation implements GraphQLMutationResolver { private final DataCenterService dataCenterService; private final DataCenterMapper dataCenterMapper; + private final SystemLogger systemLogger; + private final UserLogger userLogger; + private final UserNotificationMutation notificationMutation; @Autowired - public DataCenterMutation(DataCenterService dataCenterService, DataCenterMapper dataCenterMapper) { + public DataCenterMutation(DataCenterService dataCenterService, DataCenterMapper dataCenterMapper, + SystemLogger systemLogger, UserLogger userLogger, + UserNotificationMutation notificationMutation) { this.dataCenterService = dataCenterService; this.dataCenterMapper = dataCenterMapper; + this.systemLogger = systemLogger; + this.userLogger = userLogger; + this.notificationMutation = notificationMutation; } @PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_CREATE + "')") - public DataCenter createDataCenter(@Valid DataCenterCreateInput input) { - // Check for duplicate externalId - if (input.getExternalId() != null && dataCenterService.existsByExternalId(input.getExternalId())) { - throw new RuntimeException("This external id already exists: " + input.getExternalId()); + @Transactional + public DataCenter createDataCenter(@Valid DataCenterCreateInput input, DataFetchingEnvironment environment) { + try { + // Check for duplicate externalId + if (input.getExternalId() != null && dataCenterService.existsByExternalId(input.getExternalId())) { + throw new GraphQLCustomException("2010", input.getExternalId().toString()); + } + + DataCenter dataCenter = dataCenterMapper.toEntity(input); + + // Set auto-generated number if not provided + if (dataCenter.getNumber() == null) { + Integer maxNumber = dataCenterService.findMaxNumber(); + dataCenter.setNumber((maxNumber == null) ? 1 : maxNumber + 1); + } + + DataCenter savedDataCenter = dataCenterService.save(dataCenter); + + // Log and notify + String dataCenterName = savedDataCenter.getDataCenter() != null ? savedDataCenter.getDataCenter() + : "DataCenter #" + savedDataCenter.getNumber(); + systemLogger.createSystemLog(LogType.INFO, dataCenterName + " adlı veri merkezi oluşturuldu"); + userLogger.createUserLog(LogType.INFO, dataCenterName + " adlı veri merkezi oluşturuldu", environment); + notificationMutation.createNotification(environment, "Veri Merkezi", + dataCenterName + " adlı veri merkezi oluşturuldu", "Yeni kayıt"); + + return savedDataCenter; + } catch (GraphQLCustomException e) { + throw e; + } catch (Exception e) { + systemLogger.createSystemLog(LogType.ERROR, + "Yeni veri merkezi oluşturulurken hata oluştu: " + e.getMessage()); + throw e; } - - DataCenter dataCenter = dataCenterMapper.toEntity(input); - - // Set auto-generated number if not provided - if (dataCenter.getNumber() == null) { - Integer maxNumber = dataCenterService.getRepository().findMaxNumber(); - dataCenter.setNumber((maxNumber == null) ? 1 : maxNumber + 1); - } - - return dataCenterService.save(dataCenter); } @PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_UPDATE + "')") - public DataCenter updateDataCenter(UUID id, @Valid DataCenterUpdateInput input) { - return dataCenterService.findById(id) - .map(dataCenter -> { - // Check for duplicate externalId if it's being updated - if (input.getExternalId() != null && - !input.getExternalId().equals(dataCenter.getExternalId()) && - dataCenterService.existsByExternalId(input.getExternalId())) { - throw new RuntimeException("This external id already exists: " + input.getExternalId()); - } - return dataCenterService.update(dataCenterMapper.updateEntity(dataCenter, input)); - }) - .orElseThrow(() -> new RuntimeException("Data center not found with ID: " + id)); + @Transactional + public DataCenter updateDataCenter(UUID id, @Valid DataCenterUpdateInput input, + DataFetchingEnvironment environment) { + try { + DataCenter dataCenter = dataCenterService.findById(id) + .orElseThrow(() -> new NoSuchElementException("Data center not found with ID: " + id)); + + // Check for duplicate externalId if it's being updated + if (input.getExternalId() != null && + !input.getExternalId().equals(dataCenter.getExternalId()) && + dataCenterService.existsByExternalId(input.getExternalId())) { + throw new GraphQLCustomException("2010", input.getExternalId().toString()); + } + + DataCenter updatedDataCenter = dataCenterService.update(dataCenterMapper.updateEntity(dataCenter, input)); + + // Log and notify + String dataCenterName = updatedDataCenter.getDataCenter() != null ? updatedDataCenter.getDataCenter() + : "DataCenter #" + updatedDataCenter.getNumber(); + systemLogger.createSystemLog(LogType.INFO, dataCenterName + " adlı veri merkezi güncellendi"); + userLogger.createUserLog(LogType.INFO, dataCenterName + " adlı veri merkezi güncellendi", environment); + notificationMutation.createNotification(environment, "Veri Merkezi", + dataCenterName + " adlı veri merkezi güncellendi", "Güncelleme"); + + return updatedDataCenter; + } catch (GraphQLCustomException e) { + throw e; + } catch (NoSuchElementException e) { + systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi bulunamadı: " + id); + throw e; + } catch (Exception e) { + systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi güncellenirken hata oluştu: " + e.getMessage()); + throw e; + } } @PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_DELETE + "')") - public Boolean deleteDataCenter(UUID id) { + @Transactional + public String deleteDataCenter(UUID id, DataFetchingEnvironment environment) { + String dataCenterName = ""; try { DataCenter dataCenter = dataCenterService.findById(id) - .orElseThrow(() -> new RuntimeException("Data center not found with ID: " + id)); - + .orElseThrow(() -> new NoSuchElementException("Data center not found with ID: " + id)); + + dataCenterName = dataCenter.getDataCenter() != null ? dataCenter.getDataCenter() + : "DataCenter #" + dataCenter.getNumber(); + + // Check if data center has physical machines or other dependencies + if (dataCenter.getPhysicalMachines() != null && !dataCenter.getPhysicalMachines().isEmpty()) { + String message = "Silinmeye çalışılan veri merkezinin fiziksel makineleri olduğu için silinememektedir"; + systemLogger.createSystemLog(LogType.WARN, dataCenterName + ": " + message); + return message; + } + dataCenterService.delete(dataCenter); - return true; + + // Log and notify + systemLogger.createSystemLog(LogType.INFO, dataCenterName + " adlı veri merkezi silindi"); + userLogger.createUserLog(LogType.INFO, dataCenterName + " adlı veri merkezi silindi", environment); + notificationMutation.createNotification(environment, "Veri Merkezi", + dataCenterName + " adlı veri merkezi silindi", "Silme"); + + return "true"; + } catch (NoSuchElementException e) { + systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi bulunamadı: " + id); + return "false"; } catch (Exception e) { - System.out.println("DataCenter deletion error: " + e.getMessage()); - return false; + systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi silinirken hata oluştu: " + e.getMessage()); + return "false"; } } } \ No newline at end of file diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterCreateInput.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterCreateInput.java index b3defbb..e9e856d 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterCreateInput.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterCreateInput.java @@ -5,6 +5,7 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Min; import java.util.UUID; +import java.util.List; public class DataCenterCreateInput extends BaseCreateInput { @NotBlank(message = "Data center adı gereklidir") @@ -21,11 +22,12 @@ public class DataCenterCreateInput extends BaseCreateInput { private UUID sectorId; private UUID subSectorId; - private UUID emissionSourceId; private UUID emissionScopeId; - private UUID consuptionUnitId; private UUID activitySubUnitId; + // Multiple emission sources support - each with exactly one unit + private List dataCenterEmissionSources; + // New attributes private String ayposURL; private String address; @@ -54,18 +56,15 @@ public class DataCenterCreateInput extends BaseCreateInput { public UUID getSubSectorId() { return subSectorId; } public void setSubSectorId(UUID subSectorId) { this.subSectorId = subSectorId; } - public UUID getEmissionSourceId() { return emissionSourceId; } - public void setEmissionSourceId(UUID emissionSourceId) { this.emissionSourceId = emissionSourceId; } - public UUID getEmissionScopeId() { return emissionScopeId; } public void setEmissionScopeId(UUID emissionScopeId) { this.emissionScopeId = emissionScopeId; } - public UUID getConsuptionUnitId() { return consuptionUnitId; } - public void setConsuptionUnitId(UUID consuptionUnitId) { this.consuptionUnitId = consuptionUnitId; } - public UUID getActivitySubUnitId() { return activitySubUnitId; } public void setActivitySubUnitId(UUID activitySubUnitId) { this.activitySubUnitId = activitySubUnitId; } + public List getDataCenterEmissionSources() { return dataCenterEmissionSources; } + public void setDataCenterEmissionSources(List dataCenterEmissionSources) { this.dataCenterEmissionSources = dataCenterEmissionSources; } + // New attribute getters and setters public String getAyposURL() { return ayposURL; } public void setAyposURL(String ayposURL) { this.ayposURL = ayposURL; } diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterEmissionSourceInput.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterEmissionSourceInput.java new file mode 100644 index 0000000..e5a9b11 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterEmissionSourceInput.java @@ -0,0 +1,40 @@ +package com.sgs.graphql.dataCenter.mutation.input; + +public class DataCenterEmissionSourceInput { + private String emissionSourceId; + private String consuptionUnitId; + private Boolean isDefault = false; + private Double percentage; + + public String getEmissionSourceId() { + return emissionSourceId; + } + + public void setEmissionSourceId(String emissionSourceId) { + this.emissionSourceId = emissionSourceId; + } + + public String getConsuptionUnitId() { + return consuptionUnitId; + } + + public void setConsuptionUnitId(String consuptionUnitId) { + this.consuptionUnitId = consuptionUnitId; + } + + public Boolean getIsDefault() { + return isDefault; + } + + public void setIsDefault(Boolean isDefault) { + this.isDefault = isDefault; + } + + public Double getPercentage() { + return percentage; + } + + public void setPercentage(Double percentage) { + this.percentage = percentage; + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterUpdateInput.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterUpdateInput.java index 9e34544..803a5c0 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterUpdateInput.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/input/DataCenterUpdateInput.java @@ -1,9 +1,11 @@ package com.sgs.graphql.dataCenter.mutation.input; import com.sgs.lib.dao.mutation.input.BaseUpdateInput; -import javax.validation.constraints.NotNull; +import java.util.List; import java.util.UUID; +import javax.validation.constraints.NotNull; + public class DataCenterUpdateInput extends BaseUpdateInput { private String dataCenter; private Integer externalId; @@ -14,11 +16,12 @@ public class DataCenterUpdateInput extends BaseUpdateInput { @NotNull(message = "Sektör ID gereklidir") private UUID sectorId; private UUID subSectorId; - private UUID emissionSourceId; private UUID emissionScopeId; - private UUID consuptionUnitId; private UUID activitySubUnitId; + // Multiple emission sources with units + private List dataCenterEmissionSources; + // New attributes private String ayposURL; private String address; @@ -47,18 +50,19 @@ public class DataCenterUpdateInput extends BaseUpdateInput { public UUID getSubSectorId() { return subSectorId; } public void setSubSectorId(UUID subSectorId) { this.subSectorId = subSectorId; } - public UUID getEmissionSourceId() { return emissionSourceId; } - public void setEmissionSourceId(UUID emissionSourceId) { this.emissionSourceId = emissionSourceId; } - public UUID getEmissionScopeId() { return emissionScopeId; } public void setEmissionScopeId(UUID emissionScopeId) { this.emissionScopeId = emissionScopeId; } - public UUID getConsuptionUnitId() { return consuptionUnitId; } - public void setConsuptionUnitId(UUID consuptionUnitId) { this.consuptionUnitId = consuptionUnitId; } - public UUID getActivitySubUnitId() { return activitySubUnitId; } public void setActivitySubUnitId(UUID activitySubUnitId) { this.activitySubUnitId = activitySubUnitId; } + public List getDataCenterEmissionSources() { + return dataCenterEmissionSources; + } + public void setDataCenterEmissionSources(List dataCenterEmissionSources) { + this.dataCenterEmissionSources = dataCenterEmissionSources; + } + // New attribute getters and setters public String getAyposURL() { return ayposURL; } public void setAyposURL(String ayposURL) { this.ayposURL = ayposURL; } diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/mapper/DataCenterMapper.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/mapper/DataCenterMapper.java index eb6e446..1419cf2 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/mapper/DataCenterMapper.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/mutation/mapper/DataCenterMapper.java @@ -1,8 +1,10 @@ package com.sgs.graphql.dataCenter.mutation.mapper; import com.sgs.graphql.dataCenter.domain.DataCenter; +import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource; import com.sgs.graphql.dataCenter.mutation.input.DataCenterCreateInput; import com.sgs.graphql.dataCenter.mutation.input.DataCenterUpdateInput; +import com.sgs.graphql.dataCenter.mutation.input.DataCenterEmissionSourceInput; import com.sgs.graphql.area.service.AreaService; import com.sgs.graphql.sector.service.SectorService; import com.sgs.graphql.subSector.service.SubSectorService; @@ -13,6 +15,9 @@ import com.sgs.graphql.activitySubUnit.service.ActivitySubUnitService; import com.sgs.lib.dao.mutation.mapper.BaseCreateUpdateMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.List; +import java.util.ArrayList; +import java.util.UUID; @Component public class DataCenterMapper extends BaseCreateUpdateMapper { @@ -62,18 +67,10 @@ public class DataCenterMapper extends BaseCreateUpdateMapper dataCenterEmissionSources = new ArrayList<>(); + + for (DataCenterEmissionSourceInput emissionSourceInput : input.getDataCenterEmissionSources()) { + DataCenterEmissionSource dcEmissionSource = new DataCenterEmissionSource(); + dcEmissionSource.setDataCenter(entity); + + // Set EmissionSource + if (emissionSourceInput.getEmissionSourceId() != null) { + dcEmissionSource.setEmissionSource(emissionSourceService.findById(UUID.fromString(emissionSourceInput.getEmissionSourceId())).orElse(null)); + } + + // Set ConsuptionUnit + if (emissionSourceInput.getConsuptionUnitId() != null) { + dcEmissionSource.setConsuptionUnit(consuptionUnitService.findById(UUID.fromString(emissionSourceInput.getConsuptionUnitId())).orElse(null)); + } + + // Set optional fields + dcEmissionSource.setIsDefault(emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false); + dcEmissionSource.setPercentage(emissionSourceInput.getPercentage()); + + dataCenterEmissionSources.add(dcEmissionSource); + } + + entity.setDataCenterEmissionSources(dataCenterEmissionSources); + } + return entity; } @@ -119,22 +144,40 @@ public class DataCenterMapper extends BaseCreateUpdateMapper dataCenterEmissionSources = new ArrayList<>(); + for (DataCenterEmissionSourceInput emissionSourceInput : input.getDataCenterEmissionSources()) { + DataCenterEmissionSource dcEmissionSource = new DataCenterEmissionSource(); + dcEmissionSource.setDataCenter(entity); + + if (emissionSourceInput.getEmissionSourceId() != null) { + dcEmissionSource.setEmissionSource(emissionSourceService.findById(UUID.fromString(emissionSourceInput.getEmissionSourceId())).orElse(null)); + } + + if (emissionSourceInput.getConsuptionUnitId() != null) { + dcEmissionSource.setConsuptionUnit(consuptionUnitService.findById(UUID.fromString(emissionSourceInput.getConsuptionUnitId())).orElse(null)); + } + + dcEmissionSource.setIsDefault(emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false); + dcEmissionSource.setPercentage(emissionSourceInput.getPercentage()); + + dataCenterEmissionSources.add(dcEmissionSource); + } + entity.setDataCenterEmissionSources(dataCenterEmissionSources); + } + // New attributes (partial update - only if provided) if (input.getAyposURL() != null) { entity.setAyposURL(input.getAyposURL()); diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/query/DataCenterQueryResolver.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/query/DataCenterQueryResolver.java index a763132..ba0420e 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/query/DataCenterQueryResolver.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/query/DataCenterQueryResolver.java @@ -11,15 +11,16 @@ import javax.transaction.Transactional; import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Sort; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; -import com.sgs.graphql.auth.service.AuthorizationService; import com.sgs.graphql.dataCenter.domain.DataCenter; +import com.sgs.graphql.dataCenter.domain.PhysicalMachine; import com.sgs.graphql.dataCenter.query.pagination.DataCenterPageable; -import com.sgs.graphql.dataCenter.repo.DataCenterRepo; import com.sgs.graphql.dataCenter.repo.criteria.DataCenterCriteria; import com.sgs.graphql.dataCenter.service.DataCenterService; -import com.sgs.graphql.systemHistory.mutation.SystemLogger; +import com.sgs.graphql.dataCenter.service.PhysicalMachineService; +import com.sgs.graphql.permission.domain.PermissionName; import com.sgs.lib.dao.query.pagination.Pagination; import com.sgs.lib.dao.query.sort.SortBy; @@ -28,42 +29,46 @@ import graphql.kickstart.tools.GraphQLQueryResolver; @Component public class DataCenterQueryResolver implements GraphQLQueryResolver { - private final DataCenterService DataCenterService; - private final DataCenterRepo dataCenterRepo; - private final AuthorizationService authorizationService; - private final SystemLogger systemLogger; + private final DataCenterService dataCenterService; + private final PhysicalMachineService physicalMachineService; @Autowired - public DataCenterQueryResolver(AuthorizationService authorizationService, SystemLogger systemLogger, DataCenterService DataCenterService, DataCenterRepo dataCenterRepo) { - this.DataCenterService = DataCenterService; - this.dataCenterRepo = dataCenterRepo; - this.authorizationService = authorizationService; - this.systemLogger = systemLogger; + public DataCenterQueryResolver(DataCenterService dataCenterService, PhysicalMachineService physicalMachineService) { + this.dataCenterService = dataCenterService; + this.physicalMachineService = physicalMachineService; } + @PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')") public DataCenter dataCenter(UUID id) { - return DataCenterService.findById(id).orElse(null); + return dataCenterService.findById(id).orElse(null); } @Transactional + @PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')") public List dataCenters(DataCenterCriteria criteria, List sortBy) { - List dataCenters = DataCenterService.filterWithSort(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()), + return dataCenterService.filterWithSort(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()), Sort.by(ObjectUtils.defaultIfNull(sortBy, new ArrayList()) .stream() .map(SortBy::toOrder) .collect(Collectors.toList()))); - - return dataCenters; } - public DataCenterPageable paginateDataCenters(Pagination pagination, DataCenterCriteria criteria, List sortBy) { - return new DataCenterPageable(DataCenterService.filterWithPaginate(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()), - Pagination.toPageRequest(pagination, sortBy))); + @PreAuthorize("hasAuthority('" + PermissionName.PAGINATE_DATACENTERS_GET + "')") + public DataCenterPageable paginateDataCenters(Pagination pagination, DataCenterCriteria criteria, + List sortBy) { + return new DataCenterPageable( + dataCenterService.filterWithPaginate(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()), + Pagination.toPageRequest(pagination, sortBy))); } + @PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')") + public Optional getByNumber(Integer number) { + return dataCenterService.findByNumber(number); + } - public Optional getByNumber(Integer id) { - return dataCenterRepo.findByNumber(id).stream().findFirst(); + @PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')") + public List physicalMachines(UUID datacenterId) { + return physicalMachineService.findByDatacenterId(datacenterId); } } diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/DataCenterEmissionSourceRepo.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/DataCenterEmissionSourceRepo.java new file mode 100644 index 0000000..2a41342 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/DataCenterEmissionSourceRepo.java @@ -0,0 +1,25 @@ +package com.sgs.graphql.dataCenter.repo; + +import com.sgs.graphql.dataCenter.domain.DataCenter; +import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource; +import com.sgs.lib.dao.repo.BaseRepo; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface DataCenterEmissionSourceRepo extends BaseRepo { + + List findByDataCenter(DataCenter dataCenter); + + Optional findByDataCenterAndIsDefaultTrue(DataCenter dataCenter); + + @Query("SELECT dces FROM DataCenterEmissionSource dces WHERE dces.dataCenter.id = :dataCenterId") + List findByDataCenterId(@Param("dataCenterId") String dataCenterId); + + void deleteByDataCenter(DataCenter dataCenter); +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/PhysicalMachineRepo.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/PhysicalMachineRepo.java new file mode 100644 index 0000000..a08b7be --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/PhysicalMachineRepo.java @@ -0,0 +1,22 @@ +package com.sgs.graphql.dataCenter.repo; + +import com.sgs.graphql.dataCenter.domain.PhysicalMachine; +import com.sgs.lib.dao.repo.BaseRepo; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.UUID; + +@Repository +public interface PhysicalMachineRepo extends BaseRepo { + + /** + * Find all physical machines by datacenter ID + * @param datacenterId Datacenter ID + * @return List of physical machines + */ + @Query("SELECT pm FROM PhysicalMachine pm WHERE pm.dataCenter.id = :datacenterId") + List findByDatacenterId(@Param("datacenterId") UUID datacenterId); +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/DataCenterEmissionSourceCriteria.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/DataCenterEmissionSourceCriteria.java new file mode 100644 index 0000000..e0400b5 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/DataCenterEmissionSourceCriteria.java @@ -0,0 +1,6 @@ +package com.sgs.graphql.dataCenter.repo.criteria; + +import com.sgs.lib.dao.repo.criteria.BaseCriteria; + +public class DataCenterEmissionSourceCriteria extends BaseCriteria { +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/PhysicalMachineCriteria.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/PhysicalMachineCriteria.java new file mode 100644 index 0000000..557d90d --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/PhysicalMachineCriteria.java @@ -0,0 +1,6 @@ +package com.sgs.graphql.dataCenter.repo.criteria; + +import com.sgs.lib.dao.repo.criteria.BaseCriteria; + +public class PhysicalMachineCriteria extends BaseCriteria { +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/VMCriteria.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/VMCriteria.java new file mode 100644 index 0000000..7a62fb2 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/VMCriteria.java @@ -0,0 +1,6 @@ +package com.sgs.graphql.dataCenter.repo.criteria; + +import com.sgs.lib.dao.repo.criteria.BaseCriteria; + +public class VMCriteria extends BaseCriteria { +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/DataCenterEmissionSourceCriteriaSpec.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/DataCenterEmissionSourceCriteriaSpec.java new file mode 100644 index 0000000..a4d1e09 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/DataCenterEmissionSourceCriteriaSpec.java @@ -0,0 +1,17 @@ +package com.sgs.graphql.dataCenter.repo.criteria.spec; + +import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource; +import com.sgs.graphql.dataCenter.repo.criteria.DataCenterEmissionSourceCriteria; +import com.sgs.lib.dao.repo.criteria.spec.BaseCriteriaSpec; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; + +@Component +public class DataCenterEmissionSourceCriteriaSpec + extends BaseCriteriaSpec { + + @Override + public Specification createForAll(DataCenterEmissionSourceCriteria criteria) { + return null; + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/PhysicalMachineCriteriaSpec.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/PhysicalMachineCriteriaSpec.java new file mode 100644 index 0000000..07540f9 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/PhysicalMachineCriteriaSpec.java @@ -0,0 +1,16 @@ +package com.sgs.graphql.dataCenter.repo.criteria.spec; + +import com.sgs.graphql.dataCenter.domain.PhysicalMachine; +import com.sgs.graphql.dataCenter.repo.criteria.PhysicalMachineCriteria; +import com.sgs.lib.dao.repo.criteria.spec.BaseCriteriaSpec; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; + +@Component +public class PhysicalMachineCriteriaSpec extends BaseCriteriaSpec { + + @Override + public Specification createForAll(PhysicalMachineCriteria criteria) { + return null; + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/VMCriteriaSpec.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/VMCriteriaSpec.java new file mode 100644 index 0000000..4ca6e5b --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/repo/criteria/spec/VMCriteriaSpec.java @@ -0,0 +1,16 @@ +package com.sgs.graphql.dataCenter.repo.criteria.spec; + +import com.sgs.graphql.dataCenter.domain.VM; +import com.sgs.graphql.dataCenter.repo.criteria.VMCriteria; +import com.sgs.lib.dao.repo.criteria.spec.BaseCriteriaSpec; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; + +@Component +public class VMCriteriaSpec extends BaseCriteriaSpec { + + @Override + public Specification createForAll(VMCriteria criteria) { + return null; + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/DataCenterEmissionSourceService.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/DataCenterEmissionSourceService.java new file mode 100644 index 0000000..5bc2bef --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/DataCenterEmissionSourceService.java @@ -0,0 +1,40 @@ +package com.sgs.graphql.dataCenter.service; + +import com.sgs.graphql.dataCenter.domain.DataCenter; +import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource; +import com.sgs.graphql.dataCenter.repo.DataCenterEmissionSourceRepo; +import com.sgs.graphql.dataCenter.repo.criteria.DataCenterEmissionSourceCriteria; +import com.sgs.graphql.dataCenter.repo.criteria.spec.DataCenterEmissionSourceCriteriaSpec; +import com.sgs.lib.dao.service.BaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class DataCenterEmissionSourceService extends + BaseService { + + @Autowired + public DataCenterEmissionSourceService(DataCenterEmissionSourceRepo repository, + DataCenterEmissionSourceCriteriaSpec criteriaSpec) { + super(repository, criteriaSpec); + } + + public List findByDataCenter(DataCenter dataCenter) { + return getRepository().findByDataCenter(dataCenter); + } + + public Optional findByDataCenterAndIsDefaultTrue(DataCenter dataCenter) { + return getRepository().findByDataCenterAndIsDefaultTrue(dataCenter); + } + + public List findByDataCenterId(String dataCenterId) { + return getRepository().findByDataCenterId(dataCenterId); + } + + public void deleteByDataCenter(DataCenter dataCenter) { + getRepository().deleteByDataCenter(dataCenter); + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/DataCenterService.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/DataCenterService.java index 5edc838..d27ee72 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/DataCenterService.java +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/DataCenterService.java @@ -8,15 +8,26 @@ import com.sgs.lib.dao.service.BaseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Optional; + @Service -public class DataCenterService extends BaseService { +public class DataCenterService + extends BaseService { @Autowired public DataCenterService(DataCenterRepo repository, DataCenterCriteriaSpec criteriaSpec) { super(repository, criteriaSpec); } - + public boolean existsByExternalId(Integer externalId) { return getRepository().existsByExternalId(externalId); } + + public Optional findByNumber(Integer number) { + return getRepository().findByNumber(number); + } + + public Integer findMaxNumber() { + return getRepository().findMaxNumber(); + } } \ No newline at end of file diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/PhysicalMachineService.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/PhysicalMachineService.java new file mode 100644 index 0000000..b8eebf1 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/PhysicalMachineService.java @@ -0,0 +1,26 @@ +package com.sgs.graphql.dataCenter.service; + +import com.sgs.graphql.dataCenter.domain.PhysicalMachine; +import com.sgs.graphql.dataCenter.repo.PhysicalMachineRepo; +import com.sgs.graphql.dataCenter.repo.criteria.PhysicalMachineCriteria; +import com.sgs.graphql.dataCenter.repo.criteria.spec.PhysicalMachineCriteriaSpec; +import com.sgs.lib.dao.service.BaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class PhysicalMachineService extends + BaseService { + + @Autowired + public PhysicalMachineService(PhysicalMachineRepo repository, PhysicalMachineCriteriaSpec criteriaSpec) { + super(repository, criteriaSpec); + } + + public List findByDatacenterId(UUID datacenterId) { + return getRepository().findByDatacenterId(datacenterId); + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/VMService.java b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/VMService.java new file mode 100644 index 0000000..600b515 --- /dev/null +++ b/sge-backend/src/main/java/com/sgs/graphql/dataCenter/service/VMService.java @@ -0,0 +1,29 @@ +package com.sgs.graphql.dataCenter.service; + +import com.sgs.graphql.dataCenter.domain.VM; +import com.sgs.graphql.dataCenter.repo.VMRepo; +import com.sgs.graphql.dataCenter.repo.criteria.VMCriteria; +import com.sgs.graphql.dataCenter.repo.criteria.spec.VMCriteriaSpec; +import com.sgs.lib.dao.service.BaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class VMService extends BaseService { + + @Autowired + public VMService(VMRepo repository, VMCriteriaSpec criteriaSpec) { + super(repository, criteriaSpec); + } + + public List findAllByVmName(String vmName) { + return getRepository().findAllByVmName(vmName); + } + + public Optional findFirstByVmNameOrderByIdDesc(String vmName) { + return getRepository().findFirstByVmNameOrderByIdDesc(vmName); + } +} diff --git a/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/query/MainDataTableQueryResolver.java b/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/query/MainDataTableQueryResolver.java index 3f56e9c..2fb2246 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/query/MainDataTableQueryResolver.java +++ b/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/query/MainDataTableQueryResolver.java @@ -51,9 +51,11 @@ public class MainDataTableQueryResolver implements GraphQLQueryResolver { /** * GraphQL query to get VM emission summaries with hierarchy information + * @param datacenterId Optional datacenter ID to filter VMs by datacenter + * @param projectId Optional project ID to filter VMs by project * @return List of VM emission summaries including datacenter, project, aggregate, and physical machine info */ - public List vmEmissionSummary() { - return mainDataTableService.getVMEmissionSummaries(); + public List vmEmissionSummary(UUID datacenterId, UUID projectId) { + return mainDataTableService.getVMEmissionSummaries(datacenterId, projectId); } } diff --git a/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/service/MainDataTableService.java b/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/service/MainDataTableService.java index ff87c71..67809e7 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/service/MainDataTableService.java +++ b/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/service/MainDataTableService.java @@ -9,6 +9,7 @@ import com.sgs.lib.dao.service.BaseService; import com.sgs.graphql.mainDataTable.dto.VMEmissionSummary; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -21,7 +22,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service -public class MainDataTableService extends BaseService { +public class MainDataTableService + extends BaseService { @PersistenceContext private EntityManager entityManager; @@ -32,68 +34,97 @@ public class MainDataTableService extends BaseService getVMEmissionSummaries() { + return getVMEmissionSummaries(null, null); + } + + public List getVMEmissionSummaries(UUID datacenterId) { + return getVMEmissionSummaries(datacenterId, null); + } + + public List getVMEmissionSummaries(UUID datacenterId, UUID projectId) { + List whereConditions = new ArrayList<>(); + + if (datacenterId != null) { + whereConditions.add("dc.id = decode(replace(:datacenterId, '-', ''), 'hex')"); + } + + if (projectId != null) { + whereConditions.add("v.project = :projectId"); + } + + String whereClause = whereConditions.isEmpty() ? "" : "WHERE " + String.join(" AND ", whereConditions) + " "; + String sql = """ - SELECT - CAST(v.id AS VARCHAR) as vm_id, - v.name as vm_name, - v.power as vm_power, - v.status as vm_status, - mdt.total_emission, - mdt.created_date, - pm.name as physical_machine_name, - p.name as project_name, - dc.data_center_name as datacenter_name, - mdt.co2, - mdt.ch4, - mdt.n2o - FROM main_data_table mdt - JOIN vm v ON mdt.vm_id = v.id - LEFT JOIN vms vms_active ON v.active_vms_id = vms_active.id - LEFT JOIN vms vms_inactive ON v.inactive_vms_id = vms_inactive.id - LEFT JOIN physical_machine pm ON (pm.vms_id = vms_active.id OR pm.vms_id = vms_inactive.id) - LEFT JOIN project p ON pm.project_id = p.id - LEFT JOIN data_center dc ON p.data_center_id = dc.id - ORDER BY mdt.created_date DESC, v.name - """; - + SELECT + CAST(v.id AS VARCHAR) as vm_id, + v.vm_name as vm_name, + v.power as vm_power, + v.state as vm_status, + mdt.total_emission, + mdt.created_date, + pm.name as physical_machine_name, + v.project as project_name, + dc.data_center_name as datacenter_name, + mdt.co2, + mdt.ch4, + mdt.n2o + FROM main_data_table mdt + JOIN vm v ON mdt.vm_id = v.id + LEFT JOIN physical_machine pm ON v.physical_machine_id = pm.id + LEFT JOIN data_center dc ON pm.data_center_id = dc.id + """ + whereClause + """ + ORDER BY mdt.created_date DESC, v.vm_name + """; + Query query = entityManager.createNativeQuery(sql); + + // Add parameters if provided + if (datacenterId != null) { + query.setParameter("datacenterId", datacenterId.toString()); + } + + if (projectId != null) { + query.setParameter("projectId", projectId.toString()); + } + @SuppressWarnings("unchecked") List results = query.getResultList(); - + return results.stream().map(row -> { VMEmissionSummary summary = new VMEmissionSummary(); - + // Handle UUID conversion from hex format String uuidStr = (String) row[0]; UUID vmId = convertHexToUUID(uuidStr); summary.setVmId(vmId); - + summary.setVmName((String) row[1]); summary.setVmPower((Double) row[2]); summary.setVmStatus((String) row[3]); summary.setTotalEmission((Double) row[4]); - + // Convert Timestamp to LocalDateTime for created_date Timestamp timestamp = (Timestamp) row[5]; if (timestamp != null) { summary.setCreatedDate(timestamp.toLocalDateTime()); } - + summary.setPhysicalMachine((String) row[6]); summary.setProject((String) row[7]); summary.setDataCenter((String) row[8]); - + // Individual emission values summary.setCo2((Double) row[9]); summary.setCh4((Double) row[10]); summary.setN2o((Double) row[11]); - + return summary; }).collect(Collectors.toList()); } - + /** * Converts PostgreSQL hex format UUID to proper UUID format + * * @param hexUuid UUID in hex format (e.g., \x6205c18b8d1e4f0fa5154212fb44050b) * @return UUID object */ @@ -103,11 +134,11 @@ public class MainDataTableService extends BaseService { @@ -16,4 +18,8 @@ public class PermissionService extends BaseService findByTag(String tag) { + return getRepository().findByTag(tag); + } } diff --git a/sge-backend/src/main/java/com/sgs/utils/rabbitMQ/MessageListener.java b/sge-backend/src/main/java/com/sgs/utils/rabbitMQ/MessageListener.java index 43af254..12302f3 100644 --- a/sge-backend/src/main/java/com/sgs/utils/rabbitMQ/MessageListener.java +++ b/sge-backend/src/main/java/com/sgs/utils/rabbitMQ/MessageListener.java @@ -74,19 +74,16 @@ public class MessageListener { System.out.println(" External ID: " + dto.getId()); System.out.println(" Number: " + dto.getNumber()); System.out.println(" Consumption Amount: " + dto.getConsuptionAmount()); - System.out.println(" Projects Count: " + (dto.getProjects() != null ? dto.getProjects().size() : 0)); + System.out.println(" Physical Machines Count: " + (dto.getPhysicalMachine() != null ? dto.getPhysicalMachine().size() : 0)); - // Log MainOptimizationSpace info for each project - if (dto.getProjects() != null) { - for (Map.Entry projectEntry : dto.getProjects().entrySet()) { - String projectId = projectEntry.getKey(); - ProjectDto project = projectEntry.getValue(); - if (project.getMainOptimizationSpace() != null) { - MainOptimizationSpaceDto mainOpt = project.getMainOptimizationSpace(); - System.out.println(" Project " + projectId + ":"); - System.out.println(" PMs: " + (mainOpt.getPms() != null ? mainOpt.getPms().size() : 0)); - System.out.println(" VMs: " + (mainOpt.getVms() != null ? mainOpt.getVms().size() : 0)); - } + // Log Physical Machine info + if (dto.getPhysicalMachine() != null) { + for (Map.Entry pmEntry : dto.getPhysicalMachine().entrySet()) { + String pmIp = pmEntry.getKey(); + PhysicalMachineDto pm = pmEntry.getValue(); + System.out.println(" Physical Machine " + pm.getName() + " (IP: " + pmIp + "):"); + System.out.println(" Power: " + pm.getPower()); + System.out.println(" VMs: " + (pm.getVms() != null ? pm.getVms().size() : 0)); } } @@ -103,18 +100,18 @@ public class MessageListener { // System.out.println(entity.toString()); System.out.println("✅ Raw JSON message:\n" + message); - System.out.println("✅ DTO parsed:\n" + objectMapper.writeValueAsString(dto)); + //System.out.println("✅ DTO parsed:\n" + objectMapper.writeValueAsString(dto)); //System.out.println("✅ Entity:\n" + objectMapper.writeValueAsString(entity)); System.out.println("🚨 DataCenter name: " + dataCenter.getDataCenter()); System.out.println("🚨 External ID: " + dataCenter.getExternalId()); - System.out.println("🚨 Projects count: " + (dataCenter.getProjects() != null ? dataCenter.getProjects().size() : 0)); - if (dataCenter.getProjects() != null && !dataCenter.getProjects().isEmpty()) { - Project firstProject = dataCenter.getProjects().get(0); - System.out.println("🚨 PMs in first project: " + firstProject.getPhysicalMachines().size()); + System.out.println("🚨 Physical Machines count: " + (dataCenter.getPhysicalMachines() != null ? dataCenter.getPhysicalMachines().size() : 0)); + if (dataCenter.getPhysicalMachines() != null && !dataCenter.getPhysicalMachines().isEmpty()) { + PhysicalMachine firstPM = dataCenter.getPhysicalMachines().get(0); + System.out.println("🚨 VMs in first PM: " + firstPM.getVms().size()); // Show VM assignment summary int totalVMs = 0; - for (PhysicalMachine pm : firstProject.getPhysicalMachines()) { + for (PhysicalMachine pm : dataCenter.getPhysicalMachines()) { int vmCount = pm.getVms() != null ? pm.getVms().size() : 0; totalVMs += vmCount; System.out.println(" PM " + pm.getName() + " (IP: " + pm.getIp() + "): " + vmCount + " VMs"); @@ -157,6 +154,7 @@ public class MessageListener { entity.setHost(dto.getHost()); entity.setFlavorName(dto.getFlavorName()); entity.setTag(dto.getTag()); + entity.setProject(dto.getProject()); // Set calcOn - you may want to derive this from state or other logic entity.setCalcOn(dto.isCalcOn()); @@ -168,7 +166,7 @@ public class MessageListener { entity.setEmissionSource(dto.getEmissionSource()); // Debug logging - System.out.println("🔍 VM Entity Created: " + dto.getVmName() + " - state = " + dto.getState() + " - calcOn = " + dto.isCalcOn()); + System.out.println("🔍 VM Entity Created: " + dto.getVmName() + " - state = " + dto.getState() + " - calcOn = " + dto.isCalcOn() + " - project = " + dto.getProject()); if (dto.getEmissionSource() != null && !dto.getEmissionSource().isEmpty()) { System.out.println(" Emission Sources: " + dto.getEmissionSource()); } @@ -184,6 +182,7 @@ public class MessageListener { entity.setName(dto.getName()); entity.setIp(dto.getIp()); entity.setTag(dto.getTag()); + entity.setCloudSystem(dto.getCloudSystem()); entity.setPower(dto.getPower()); // VMs are now processed separately from the new message format @@ -207,112 +206,42 @@ public class MessageListener { // ConsuptionUnit, and ActivitySubUnit are no longer received in the message // These will need to be set via the DataCenter CRUD operations - // Convert Projects - if (dto.getProjects() != null) { - List projects = new ArrayList<>(); - for (Map.Entry projectEntry : dto.getProjects().entrySet()) { - String projectId = projectEntry.getKey(); - ProjectDto projectDto = projectEntry.getValue(); + // Convert Physical Machines directly + if (dto.getPhysicalMachine() != null) { + List physicalMachines = new ArrayList<>(); + for (Map.Entry pmEntry : dto.getPhysicalMachine().entrySet()) { + String pmIp = pmEntry.getKey(); + PhysicalMachineDto pmDto = pmEntry.getValue(); - // Set the project ID and a default name if not provided - projectDto.setId(projectId); - if (projectDto.getName() == null || projectDto.getName().isEmpty()) { - projectDto.setName("Project-" + projectId.substring(0, 8)); // Use first 8 chars of ID as name + PhysicalMachine pm = new PhysicalMachine(); + pm.setName(pmDto.getName()); + pm.setIp(pmIp); // Use the IP from the map key + pm.setTag(pmDto.getTag()); + pm.setPower(pmDto.getPower()); + pm.setDataCenter(entity); + + // Process VMs for this PM + if (pmDto.getVms() != null) { + List vms = new ArrayList<>(); + for (Map.Entry vmEntry : pmDto.getVms().entrySet()) { + String vmIp = vmEntry.getKey(); + VMDto vmDto = vmEntry.getValue(); + + VM vm = toVMEntity(vmDto); + vm.setIp(vmIp); // Use the IP from the map key + vm.setPhysicalMachine(pm); + vms.add(vm); + } + pm.setVms(vms); } - Project project = toProjectEntity(projectDto); - project.setDataCenter(entity); - projects.add(project); + physicalMachines.add(pm); } - entity.setProjects(projects); - } - - return entity; - } - - public Project toProjectEntity(ProjectDto dto){ - if (dto == null) - return null; - - Project entity = new Project(); - entity.setName(dto.getName()); - - // Process MainOptimizationSpace instead of physical machines list - if (dto.getMainOptimizationSpace() != null) { - List physicalMachines = processMainOptimizationSpace(dto.getMainOptimizationSpace()); - // Set project reference for each PM - physicalMachines.forEach(pm -> pm.setProject(entity)); entity.setPhysicalMachines(physicalMachines); } - + return entity; } - - /** - * Process MainOptimizationSpaceDto and assign VMs to their hosting PMs - */ - private List processMainOptimizationSpace(MainOptimizationSpaceDto mainOptSpace) { - List physicalMachines = new ArrayList<>(); - - if (mainOptSpace.getPms() == null || mainOptSpace.getVms() == null) { - System.out.println("⚠️ MainOptimizationSpace has null PMs or VMs"); - return physicalMachines; - } - - System.out.println("🔍 Processing MainOptimizationSpace with " + - mainOptSpace.getPms().size() + " PMs and " + - mainOptSpace.getVms().size() + " VMs"); - - // Convert PMs from DTO to Entity - for (Map.Entry pmEntry : mainOptSpace.getPms().entrySet()) { - String pmIp = pmEntry.getKey(); - PhysicalMachineDto pmDto = pmEntry.getValue(); - - // Set the IP from the map key - pmDto.setIp(pmIp); - - PhysicalMachine pm = toPhysicalMachineEntity(pmDto); - physicalMachines.add(pm); - - System.out.println("✅ Created PM: " + pm.getName() + " (IP: " + pm.getIp() + ")"); - } - - // Assign VMs to their hosting PMs - for (Map.Entry vmEntry : mainOptSpace.getVms().entrySet()) { - String vmIp = vmEntry.getKey(); - VMDto vmDto = vmEntry.getValue(); - - // Set the IP from the map key - vmDto.setIp(vmIp); - - VM vm = toVMEntity(vmDto); - - // Find the hosting PM by IP - String hostingPmIp = vmDto.getHostingPm(); - PhysicalMachine hostingPm = physicalMachines.stream() - .filter(pm -> pm.getIp().equals(hostingPmIp)) - .findFirst() - .orElse(null); - - if (hostingPm != null) { - // Assign VM to PM - vm.setPhysicalMachine(hostingPm); - if (hostingPm.getVms() == null) { - hostingPm.setVms(new ArrayList<>()); - } - hostingPm.getVms().add(vm); - - System.out.println("✅ Assigned VM: " + vm.getVmName() + " (IP: " + vm.getIp() + - ") to PM: " + hostingPm.getName() + " (IP: " + hostingPm.getIp() + ")"); - } else { - System.err.println("❌ Could not find hosting PM with IP: " + hostingPmIp + - " for VM: " + vm.getVmName()); - } - } - - return physicalMachines; - } - @Transactional public DataCenter createDataCenter(DataCenter newDc) { @@ -335,123 +264,116 @@ public class MessageListener { // Note: DataCenter name and emission-related fields are no longer updated from the message // These are now managed via DataCenter CRUD operations - for (Project newProject : newDc.getProjects()) { - Optional existingProjOpt = dc.getProjects().stream() - .filter(p -> p.getName().equalsIgnoreCase(newProject.getName())) - .findFirst(); + // Ensure datacenter has initialized physical machines list + if (dc.getPhysicalMachines() == null) { + dc.setPhysicalMachines(new ArrayList<>()); + } - Project project; - if (existingProjOpt.isPresent()) { - project = existingProjOpt.get(); - project.setName(newProject.getName()); - System.out.println("✅ Updated existing project: " + project.getName()); - } else { - project = newProject; - project.setDataCenter(dc); - dc.getProjects().add(project); - System.out.println("✅ Created new project: " + project.getName()); + // Process Physical Machines directly from the message structure + for (PhysicalMachine newPm : newDc.getPhysicalMachines()) { + Optional existingPmOpt = dc.getPhysicalMachines().stream() + .filter(pm -> pm.getIp().equals(newPm.getIp())) // Match by IP instead of name + .findFirst(); + + PhysicalMachine pm; + if (existingPmOpt.isPresent()) { + // Update existing PM + pm = existingPmOpt.get(); + pm.setName(newPm.getName()); + pm.setIp(newPm.getIp()); + pm.setTag(newPm.getTag()); + pm.setPower(newPm.getPower()); + + System.out.println("✅ Updated existing PM: " + pm.getName() + " (IP: " + pm.getIp() + ")"); + } else { + // Create new PM + pm = newPm; + pm.setDataCenter(dc); + dc.getPhysicalMachines().add(pm); + + System.out.println("✅ Created new PM: " + pm.getName() + " (IP: " + pm.getIp() + ")"); + } + + // Process VMs that are already assigned to this PM + if (newPm.getVms() != null && !newPm.getVms().isEmpty()) { + System.out.println("🔍 Processing " + newPm.getVms().size() + " VMs for PM: " + newPm.getName()); + + // Ensure PM has VM list initialized + if (pm.getVms() == null) { + pm.setVms(new ArrayList<>()); } - - // Ensure project has initialized physical machines list - if (project.getPhysicalMachines() == null) { - project.setPhysicalMachines(new ArrayList<>()); - } - - // Process Physical Machines (VMs are already assigned to PMs from MainOptimizationSpace) - for (PhysicalMachine newPm : newProject.getPhysicalMachines()) { - Optional existingPmOpt = project.getPhysicalMachines().stream() - .filter(pm -> pm.getIp().equals(newPm.getIp())) // Match by IP instead of name - .findFirst(); - - PhysicalMachine pm; - if (existingPmOpt.isPresent()) { - // Update existing PM - pm = existingPmOpt.get(); - pm.setName(newPm.getName()); - pm.setIp(newPm.getIp()); - pm.setTag(newPm.getTag()); - pm.setPower(newPm.getPower()); - - System.out.println("✅ Updated existing PM: " + pm.getName() + " (IP: " + pm.getIp() + ")"); - } else { - // Create new PM - pm = newPm; - pm.setProject(project); - project.getPhysicalMachines().add(pm); - - System.out.println("✅ Created new PM: " + pm.getName() + " (IP: " + pm.getIp() + ")"); + + // Clear existing VMs to replace with new ones + pm.getVms().clear(); + + // Process each VM that's already assigned to this PM + for (VM newVm : newPm.getVms()) { + if (newVm == null) continue; + + // Find existing VM by vmName + Optional existingVmOpt = Optional.empty(); + String vmLookupName = newVm.getVmName(); + if (vmLookupName != null) { + existingVmOpt = vmRepo.findFirstByVmNameOrderByIdDesc(vmLookupName); } - - // Process VMs that are already assigned to this PM - if (newPm.getVms() != null && !newPm.getVms().isEmpty()) { - System.out.println("🔍 Processing " + newPm.getVms().size() + " VMs for PM: " + newPm.getName()); + + VM vm; + if (existingVmOpt.isPresent()) { + // Update existing VM + vm = existingVmOpt.get(); - // Ensure PM has VM list initialized - if (pm.getVms() == null) { - pm.setVms(new ArrayList<>()); + // IMPORTANT: Remove VM from its current Physical Machine first + if (vm.getPhysicalMachine() != null) { + PhysicalMachine currentPM = vm.getPhysicalMachine(); + if (currentPM.getVms() != null) { + currentPM.getVms().remove(vm); + System.out.println("🔄 Removed VM " + vm.getVmName() + " from previous PM: " + currentPM.getName()); + } } - // Clear existing VMs to replace with new ones - pm.getVms().clear(); + vm.setState(newVm.getState()); + vm.setVmName(newVm.getVmName()); + vm.setIp(newVm.getIp()); + vm.setPower(newVm.getPower()); + vm.setCalcOn(newVm.getCalcOn()); + vm.setHostingPm(newVm.getHostingPm()); + vm.setHost(newVm.getHost()); + vm.setFlavorName(newVm.getFlavorName()); + vm.setTag(newVm.getTag()); + vm.setEmissionSource(newVm.getEmissionSource()); + vm.setProject(newVm.getProject()); - // Process each VM that's already assigned to this PM - for (VM newVm : newPm.getVms()) { - if (newVm == null) continue; - - // Find existing VM by vmName - Optional existingVmOpt = Optional.empty(); - String vmLookupName = newVm.getVmName(); - if (vmLookupName != null) { - existingVmOpt = vmRepo.findFirstByVmNameOrderByIdDesc(vmLookupName); - } - - VM vm; - if (existingVmOpt.isPresent()) { - // Update existing VM - vm = existingVmOpt.get(); - vm.setState(newVm.getState()); - vm.setVmName(newVm.getVmName()); - vm.setIp(newVm.getIp()); - vm.setPower(newVm.getPower()); - vm.setCalcOn(newVm.getCalcOn()); - vm.setHostingPm(newVm.getHostingPm()); - vm.setHost(newVm.getHost()); - vm.setFlavorName(newVm.getFlavorName()); - vm.setTag(newVm.getTag()); - vm.setEmissionSource(newVm.getEmissionSource()); - - System.out.println("✅ Updated existing VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn()); - } else { - // Use new VM - vm = newVm; - - System.out.println("✅ Created new VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn()); - } - - // Set physical machine relationship - vm.setPhysicalMachine(pm); - pm.getVms().add(vm); + System.out.println("✅ Updated existing VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn()); + } else { + // Use new VM + vm = newVm; + + System.out.println("✅ Created new VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn()); + } + + // Set physical machine relationship + vm.setPhysicalMachine(pm); + pm.getVms().add(vm); - // Update config - if (newVm.getConfig() != null) { - if (vm.getConfig() == null) { - vm.setConfig(newVm.getConfig()); - vm.getConfig().setVm(vm); - } else { - vm.getConfig().setCpu(newVm.getConfig().getCpu()); - vm.getConfig().setRam(newVm.getConfig().getRam()); - vm.getConfig().setDisk(newVm.getConfig().getDisk()); - } - } + // Update config + if (newVm.getConfig() != null) { + if (vm.getConfig() == null) { + vm.setConfig(newVm.getConfig()); + vm.getConfig().setVm(vm); + } else { + vm.getConfig().setCpu(newVm.getConfig().getCpu()); + vm.getConfig().setRam(newVm.getConfig().getRam()); + vm.getConfig().setDisk(newVm.getConfig().getDisk()); } } } } + } System.out.println("Before Save: DataCenter=" + dc.getDataCenter()); System.out.println("External ID=" + dc.getExternalId()); System.out.println("Number=" + dc.getNumber()); - System.out.println("Projects=" + (dc.getProjects() != null ? dc.getProjects().size() : 0)); + System.out.println("Physical Machines=" + (dc.getPhysicalMachines() != null ? dc.getPhysicalMachines().size() : 0)); DataCenter saved = dataCenterService.save(dc); System.out.println("✅ Saved: ID=" + saved.getId()); @@ -525,41 +447,29 @@ public class MessageListener { int failedCalculations = 0; int totalEmissionSources = 0; - for (Project project : dataCenter.getProjects()) { - for (PhysicalMachine pm : project.getPhysicalMachines()) { - if (pm.getVms() != null) { - // Calculate for all VMs (only those with calcOn = true) - for (VM vm : pm.getVms()) { - totalVMs++; - if (vm.getCalcOn() != null && vm.getCalcOn()) { - eligibleVMs++; - if (vm.getPower() != null && vm.getPower() > 0) { - System.out.println("✅ Processing VM " + vm.getVmName() + " (calcOn = true)"); - processedVMs++; - - // Check if VM has emission sources - Map emissionSources = vm.getEmissionSource(); - if (emissionSources != null && !emissionSources.isEmpty()) { - // Create separate emission record for each emission source - System.out.println("🔍 VM has " + emissionSources.size() + " emission sources"); - totalEmissionSources += emissionSources.size(); - for (Map.Entry sourceEntry : emissionSources.entrySet()) { - String sourceName = sourceEntry.getKey(); - Integer percentage = sourceEntry.getValue(); - System.out.println(" - " + sourceName + ": " + percentage + "%"); - - boolean success = createVMEmissionRecordForSource(dataCenter, vm, project, pm, sourceName, percentage); - if (success) { - successfulCalculations++; - } else { - failedCalculations++; - } - } - } else { - // Fallback to default emission source if VM has no emission sources - System.out.println("⚠️ VM has no emission sources, using default"); - totalEmissionSources++; - boolean success = createVMEmissionRecord(dataCenter, vm, project, pm); + for (PhysicalMachine pm : dataCenter.getPhysicalMachines()) { + if (pm.getVms() != null) { + // Calculate for all VMs (only those with calcOn = true) + for (VM vm : pm.getVms()) { + totalVMs++; + if (vm.getCalcOn() != null && vm.getCalcOn()) { + eligibleVMs++; + if (vm.getPower() != null && vm.getPower() > 0) { + System.out.println("✅ Processing VM " + vm.getVmName() + " (calcOn = true)"); + processedVMs++; + + // Check if VM has emission sources + Map emissionSources = vm.getEmissionSource(); + if (emissionSources != null && !emissionSources.isEmpty()) { + // Create separate emission record for each emission source + System.out.println("🔍 VM has " + emissionSources.size() + " emission sources"); + totalEmissionSources += emissionSources.size(); + for (Map.Entry sourceEntry : emissionSources.entrySet()) { + String sourceName = sourceEntry.getKey(); + Integer percentage = sourceEntry.getValue(); + System.out.println(" - " + sourceName + ": " + percentage + "%"); + + boolean success = createVMEmissionRecordForSource(dataCenter, vm, pm, sourceName, percentage); if (success) { successfulCalculations++; } else { @@ -567,16 +477,26 @@ public class MessageListener { } } } else { - System.out.println("⚠️ Skipping VM " + vm.getVmName() + " (calcOn = true) - no power consumption data"); + // Fallback to default emission source if VM has no emission sources + System.out.println("⚠️ VM has no emission sources, using default"); + totalEmissionSources++; + boolean success = createVMEmissionRecord(dataCenter, vm, pm); + if (success) { + successfulCalculations++; + } else { + failedCalculations++; + } } } else { - System.out.println("⏭️ Skipping VM " + vm.getVmName() + " - calcOn = " + vm.getCalcOn()); + System.out.println("⚠️ Skipping VM " + vm.getVmName() + " (calcOn = true) - no power consumption data"); } + } else { + System.out.println("⏭️ Skipping VM " + vm.getVmName() + " - calcOn = " + vm.getCalcOn()); } } } } - + System.out.println("🎯 VM Emission Calculation Summary:"); System.out.println(" Total VMs found: " + totalVMs); System.out.println(" VMs with calcOn = true: " + eligibleVMs); @@ -587,7 +507,7 @@ public class MessageListener { System.out.println(" Failed emission calculations: " + failedCalculations); } - private boolean createVMEmissionRecord(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm) { + private boolean createVMEmissionRecord(DataCenter dataCenter, VM vm, PhysicalMachine pm) { try { // Check if VM has an ID (is persisted) if (vm.getId() == null) { @@ -595,7 +515,7 @@ public class MessageListener { return false; } - MainDataTableCreateInput input = createVMMainDataTableInput(dataCenter, vm, project, pm); + MainDataTableCreateInput input = createVMMainDataTableInput(dataCenter, vm, pm); System.out.println("🔍 Creating emission record for VM: " + vm.getVmName() + " (Power: " + vm.getPower() + "W)"); MainDataTable result = callMainDataTableMutation(input); @@ -619,7 +539,7 @@ public class MessageListener { } } - private boolean createVMEmissionRecordForSource(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm, String emissionSourceName, Integer percentage) { + private boolean createVMEmissionRecordForSource(DataCenter dataCenter, VM vm, PhysicalMachine pm, String emissionSourceName, Integer percentage) { try { // Check if VM has an ID (is persisted) if (vm.getId() == null) { @@ -639,7 +559,13 @@ public class MessageListener { // Calculate power consumption for this emission source (percentage of total VM power) double sourceSpecificPower = vm.getPower() * (percentage / 100.0); - MainDataTableCreateInput input = createVMMainDataTableInputForSource(dataCenter, vm, project, pm, emissionSource, sourceSpecificPower, percentage); + MainDataTableCreateInput input = createVMMainDataTableInputForSource(dataCenter, vm, pm, emissionSource, sourceSpecificPower, percentage); + + if (input == null) { + System.err.println("❌ Failed to create input for VM emission calculation - skipping"); + return false; + } + System.out.println("🔍 Creating emission record for VM: " + vm.getVmName() + " - Source: " + emissionSourceName + " (" + percentage + "%) - Power: " + sourceSpecificPower + "W"); @@ -665,7 +591,7 @@ public class MessageListener { } } - private MainDataTableCreateInput createVMMainDataTableInput(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm) { + private MainDataTableCreateInput createVMMainDataTableInput(DataCenter dataCenter, VM vm, PhysicalMachine pm) { MainDataTableCreateInput input = new MainDataTableCreateInput(); // Copy datacenter-level information (if available) @@ -692,8 +618,15 @@ public class MessageListener { input.setSubSector(dataCenter.getSubSector().getId()); } - if (dataCenter.getEmissionSource() != null) { - input.setEmissionSource(dataCenter.getEmissionSource().getId()); + // Handle multiple emission sources - use the default one for emission calculations + if (dataCenter.getDataCenterEmissionSources() != null && !dataCenter.getDataCenterEmissionSources().isEmpty()) { + // Find the default emission source or use the first one + DataCenterEmissionSource defaultEmissionSource = dataCenter.getDataCenterEmissionSources().stream() + .filter(DataCenterEmissionSource::getIsDefault) + .findFirst() + .orElse(dataCenter.getDataCenterEmissionSources().get(0)); + + input.setEmissionSource(defaultEmissionSource.getEmissionSource().getId()); } else { // Fallback to default emission source for electricity try { @@ -711,8 +644,16 @@ public class MessageListener { input.setActivitySubUnit(dataCenter.getActivitySubUnit().getId()); } - if (dataCenter.getConsuptionUnit() != null) { - input.setConsuptionUnit(dataCenter.getConsuptionUnit().getId()); + // Handle consumption unit from emission sources - use the default one for VM calculations + if (dataCenter.getDataCenterEmissionSources() != null && !dataCenter.getDataCenterEmissionSources().isEmpty()) { + DataCenterEmissionSource defaultEmissionSource = dataCenter.getDataCenterEmissionSources().stream() + .filter(DataCenterEmissionSource::getIsDefault) + .findFirst() + .orElse(dataCenter.getDataCenterEmissionSources().get(0)); + + if (defaultEmissionSource.getConsuptionUnit() != null) { + input.setConsuptionUnit(defaultEmissionSource.getConsuptionUnit().getId()); + } } // Default to Kapsam-3 if no emission scope is set @@ -739,68 +680,134 @@ public class MessageListener { System.out.println(" VM Name: " + vm.getVmName()); System.out.println(" Power: " + vm.getPower() + "W"); System.out.println(" Physical Machine: " + pm.getName()); - System.out.println(" Project: " + project.getName()); System.out.println(" DataCenter Sector: " + (dataCenter.getSector() != null ? dataCenter.getSector().getTag() : "NOT SET")); return input; } - private MainDataTableCreateInput createVMMainDataTableInputForSource(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm, EmissionSource emissionSource, double sourceSpecificPower, Integer percentage) { + private MainDataTableCreateInput createVMMainDataTableInputForSource(DataCenter dataCenter, VM vm, PhysicalMachine pm, EmissionSource emissionSource, double sourceSpecificPower, Integer percentage) { MainDataTableCreateInput input = new MainDataTableCreateInput(); // Copy datacenter-level information (if available) input.setYear("2025"); input.setMonth("07"); + // Validate required fields + if (dataCenter == null) { + System.err.println("❌ DataCenter is null - cannot create emission record"); + return null; + } + + if (vm == null) { + System.err.println("❌ VM is null - cannot create emission record"); + return null; + } + + if (emissionSource == null) { + System.err.println("❌ EmissionSource is null - cannot create emission record"); + return null; + } + // Note: These fields are no longer received in the message // They need to be set via DataCenter CRUD operations first if (dataCenter.getArea() != null && !dataCenter.getArea().getCities().isEmpty()) { input.setCity(dataCenter.getArea().getCities().get(0).getId()); + System.out.println("🔍 Setting City: " + dataCenter.getArea().getCities().get(0).getId()); + } else { + System.out.println("⚠️ Warning: No city available for DataCenter"); } if (dataCenter.getArea() != null && !dataCenter.getArea().getDistricts().isEmpty()) { input.setDistrict(dataCenter.getArea().getDistricts().get(0).getId()); + System.out.println("🔍 Setting District: " + dataCenter.getArea().getDistricts().get(0).getId()); + } else { + System.out.println("⚠️ Warning: No district available for DataCenter"); } if (dataCenter.getSector() != null) { input.setSector(dataCenter.getSector().getId()); + System.out.println("🔍 Setting Sector: " + dataCenter.getSector().getId() + " (" + dataCenter.getSector().getTag() + ")"); } else { - System.out.println("⚠️ Warning: DataCenter has no sector set - emission calculation may fail"); + System.err.println("❌ Error: DataCenter has no sector set - this is required for emission calculation"); + return null; } if (dataCenter.getSubSector() != null) { input.setSubSector(dataCenter.getSubSector().getId()); + System.out.println("🔍 Setting SubSector: " + dataCenter.getSubSector().getId()); + } else { + System.out.println("⚠️ Warning: DataCenter has no subsector set"); } // Use the specific emission source for this calculation input.setEmissionSource(emissionSource.getId()); + System.out.println("🔍 Setting EmissionSource: " + emissionSource.getId() + " (" + emissionSource.getTag() + ")"); if (dataCenter.getActivitySubUnit() != null) { input.setActivitySubUnit(dataCenter.getActivitySubUnit().getId()); + System.out.println("🔍 Setting ActivitySubUnit: " + dataCenter.getActivitySubUnit().getId()); + } else { + System.out.println("⚠️ Warning: DataCenter has no activity sub unit set"); } - if (dataCenter.getConsuptionUnit() != null) { - input.setConsuptionUnit(dataCenter.getConsuptionUnit().getId()); + // Handle consumption unit from emission sources - use the emission source's unit + if (emissionSource != null) { + // Find the DataCenterEmissionSource that matches this emissionSource + System.out.println("🔍 Looking for consumption unit for emission source: " + emissionSource.getTag()); + + if (dataCenter.getDataCenterEmissionSources() == null || dataCenter.getDataCenterEmissionSources().isEmpty()) { + System.err.println("❌ Error: DataCenter has no emission sources configured - cannot find consumption unit"); + return null; + } + + boolean foundUnit = false; + for (DataCenterEmissionSource dces : dataCenter.getDataCenterEmissionSources()) { + if (dces.getEmissionSource() != null && dces.getEmissionSource().getId().equals(emissionSource.getId())) { + if (dces.getConsuptionUnit() != null) { + input.setConsuptionUnit(dces.getConsuptionUnit().getId()); + System.out.println("🔍 Setting ConsumptionUnit: " + dces.getConsuptionUnit().getId() + " (" + dces.getConsuptionUnit().getTag() + ")"); + foundUnit = true; + break; + } else { + System.err.println("❌ Error: DataCenterEmissionSource has no consumption unit set for emission source: " + emissionSource.getTag()); + return null; + } + } + } + + if (!foundUnit) { + System.err.println("❌ Error: Could not find matching DataCenterEmissionSource for emission source: " + emissionSource.getTag()); + return null; + } } // Default to Kapsam-3 if no emission scope is set input.setScope(dataCenter.getEmissionScope() != null ? dataCenter.getEmissionScope().getTag().equals("Kapsam-3") : true); + System.out.println("🔍 Setting Scope: " + (dataCenter.getEmissionScope() != null ? + dataCenter.getEmissionScope().getTag() : "Kapsam-3 (default)")); try { List organizations = organizationRepo.findAll(); if (!organizations.isEmpty()) { input.setOrganization(organizations.get(0).getId()); + System.out.println("🔍 Setting Organization: " + organizations.get(0).getId()); + } else { + System.err.println("❌ Error: No organizations found in database - this is required"); + return null; } } catch (Exception e) { System.err.println("❌ Error finding organization: " + e.getMessage()); + return null; } // Set VM-specific fields input.setVmId(vm.getId()); + System.out.println("🔍 Setting VM ID: " + vm.getId()); // Use the source-specific power consumption (percentage of total VM power) input.setConsuptionAmount(String.valueOf(sourceSpecificPower)); + System.out.println("🔍 Setting Consumption Amount: " + sourceSpecificPower + "W"); System.out.println("🔍 VM Emission Input for Source:"); System.out.println(" VM ID: " + vm.getId()); @@ -809,7 +816,6 @@ public class MessageListener { System.out.println(" Percentage: " + percentage + "%"); System.out.println(" Source Power: " + sourceSpecificPower + "W"); System.out.println(" Physical Machine: " + pm.getName()); - System.out.println(" Project: " + project.getName()); System.out.println(" DataCenter Sector: " + (dataCenter.getSector() != null ? dataCenter.getSector().getTag() : "NOT SET")); return input; @@ -828,6 +834,34 @@ public class MessageListener { try { System.out.println("🔄 Calling mainDataTableMutation.createMainDataTable..."); + // Validate input fields before making the call + if (input.getSector() == null) { + System.err.println("❌ Sector is null - cannot create emission record"); + return null; + } + + if (input.getEmissionSource() == null) { + System.err.println("❌ EmissionSource is null - cannot create emission record"); + return null; + } + + if (input.getConsuptionUnit() == null) { + System.err.println("❌ ConsumptionUnit is null - cannot create emission record"); + return null; + } + + if (input.getOrganization() == null) { + System.err.println("❌ Organization is null - cannot create emission record"); + return null; + } + + if (input.getVmId() == null) { + System.err.println("❌ VM ID is null - cannot create emission record"); + return null; + } + + System.out.println("🔍 Input validation passed - proceeding with mutation call"); + // Call the mutation method - pass null environment since we've already patched the logging MainDataTable result = mainDataTableMutation.createMainDataTable(input, null); diff --git a/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls b/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls index bae61e8..4c5cb3f 100644 --- a/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls +++ b/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls @@ -5,8 +5,10 @@ input DataCenterCreateInput { sectorId: ID subSectorId: ID activitySubUnitId: ID - emissionSourceId: ID - consuptionUnitId: ID + + # Multiple emission sources support - each with exactly one unit + dataCenterEmissionSources: [DataCenterEmissionSourceInput!] + consuptionAmount: Float areaId: ID number: Int @@ -23,8 +25,10 @@ input DataCenterUpdateInput { sectorId: ID subSectorId: ID activitySubUnitId: ID - emissionSourceId: ID - consuptionUnitId: ID + + # Multiple emission sources support - each with exactly one unit + dataCenterEmissionSources: [DataCenterEmissionSourceInput!] + consuptionAmount: Float areaId: ID number: Int @@ -33,3 +37,10 @@ input DataCenterUpdateInput { latitude: Float longitude: Float } + +input DataCenterEmissionSourceInput { + emissionSourceId: ID! + consuptionUnitId: ID! + isDefault: Boolean + percentage: Float +} diff --git a/sge-backend/src/main/resources/graphql/dataCenter/query.graphqls b/sge-backend/src/main/resources/graphql/dataCenter/query.graphqls index 5dab060..db03553 100644 --- a/sge-backend/src/main/resources/graphql/dataCenter/query.graphqls +++ b/sge-backend/src/main/resources/graphql/dataCenter/query.graphqls @@ -3,4 +3,5 @@ extend type Query{ dataCenters(criteria: DataCenterCriteria, sortBy: [SortBy!]): [DataCenter!] paginateDataCenters(pagination : Pagination!, criteria: DataCenterCriteria, sortBy:[SortBy!] ) : DataCenterPageable! getByNumber(number: Int!): DataCenter + physicalMachines(datacenterId: ID!): [PhysicalMachine!]! } \ No newline at end of file diff --git a/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls b/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls index a96fb1e..c6b5929 100644 --- a/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls +++ b/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls @@ -7,11 +7,13 @@ type DataCenter { sector: Sector subSector: SubSector activitySubUnit: ActivitySubUnit - emissionSource: EmissionSource - consuptionUnit: ConsuptionUnit + + # Multiple emission sources support - each with exactly one unit + dataCenterEmissionSources: [DataCenterEmissionSource] + consuptionAmount: Float - projects: [Project] + physicalMachines: [PhysicalMachine] area: Area number: Int @@ -21,10 +23,12 @@ type DataCenter { longitude: Float } -type Project { +type DataCenterEmissionSource { id: ID - name: String - physicalMachines: [PhysicalMachine] + emissionSource: EmissionSource + consuptionUnit: ConsuptionUnit + isDefault: Boolean + percentage: Float } type PhysicalMachine { @@ -32,11 +36,13 @@ type PhysicalMachine { name: String ip: String tag: String + cloudSystem: String power: Float - vms: [Vm] + vms: [VM] + dataCenter: DataCenter } -type Vm { +type VM { id: ID state: String vmName: String @@ -47,8 +53,10 @@ type Vm { host: String flavorName: String tag: String + project: String emissionSource: EmissionSourceMap config: Config + physicalMachine: PhysicalMachine } scalar EmissionSourceMap diff --git a/sge-backend/src/main/resources/graphql/mainDataTable/query.graphqls b/sge-backend/src/main/resources/graphql/mainDataTable/query.graphqls index f06327c..5a05347 100644 --- a/sge-backend/src/main/resources/graphql/mainDataTable/query.graphqls +++ b/sge-backend/src/main/resources/graphql/mainDataTable/query.graphqls @@ -2,5 +2,5 @@ extend type Query{ mainDataTable(id: ID!): MainDataTable! mainDataTables(criteria: MainDataTableCriteria, sortBy: [SortBy!]): [MainDataTable!] paginateMainDataTables(pagination : Pagination!, criteria: MainDataTableCriteria, sortBy:[SortBy!] ) : MainDataTablePageable! - vmEmissionSummary: [VMEmissionSummary!]! + vmEmissionSummary(datacenterId: ID, projectId: ID): [VMEmissionSummary!]! } \ No newline at end of file diff --git a/sge-backend/src/main/resources/graphql/mainDataTable/type.graphqls b/sge-backend/src/main/resources/graphql/mainDataTable/type.graphqls index 416bd06..0424e35 100644 --- a/sge-backend/src/main/resources/graphql/mainDataTable/type.graphqls +++ b/sge-backend/src/main/resources/graphql/mainDataTable/type.graphqls @@ -26,11 +26,11 @@ type MainDataTable { proteinAmount:Float burnOrOpenBurn:Boolean scopeCheck:Boolean - vm: Vm + vm: VM } -type Vm { +type VM { id: ID state: String vmName: String @@ -41,6 +41,7 @@ type Vm { host: String flavorName: String tag: String + project: String config: Config } @@ -51,22 +52,22 @@ type Config { disk: Int } -type VMEmissionSummary { - vmId: ID! - vmName: String! - vmPower: Float - vmStatus: String - totalEmission: Float! - createdDate: LocalDateTime! - physicalMachine: String - project: String - dataCenter: String - # Individual emission values per record - co2: Float! - ch4: Float! - n2o: Float! - reportGeneratedTime: LocalDateTime -} + type VMEmissionSummary { + vmId: ID! + vmName: String + vmPower: Float + vmStatus: String + totalEmission: Float! + createdDate: LocalDateTime! + physicalMachine: String + project: String + dataCenter: String + # Individual emission values per record + co2: Float! + ch4: Float! + n2o: Float! + reportGeneratedTime: LocalDateTime + } type SolidWasteSupplement { id: ID! diff --git a/sge-frontend/src/redux/actions/dataCenter/index.js b/sge-frontend/src/redux/actions/dataCenter/index.js index e439d5e..a8df393 100644 --- a/sge-frontend/src/redux/actions/dataCenter/index.js +++ b/sge-frontend/src/redux/actions/dataCenter/index.js @@ -58,41 +58,42 @@ export const getDataCenters = () => { id tag } - emissionSource { + dataCenterEmissionSources { id - tag - } - consuptionUnit { - id - description + emissionSource { + id + tag + } + consuptionUnit { + id + description + } + isDefault + percentage } activitySubUnit { id tag } - projects { + physicalMachines { id name - physicalMachines { + vms { id - name - vms { - id - vmName - state - power - calcOn - hostingPm - host - flavorName - tag - config { - id - cpu - ram - disk - } - } + vmName + state + power + calcOn + hostingPm + host + flavorName + tag + config { + id + cpu + ram + disk + } } } } @@ -111,7 +112,7 @@ export const getDataCenters = () => { console.log("GraphQL Response:", { status: response.status, data: response.data, - errors: response.data?.errors + errors: response.data?.errors, }); // Check for GraphQL errors @@ -125,7 +126,7 @@ export const getDataCenters = () => { } const dataCenters = response.data.data.dataCenters; - + // Validate dataCenters is an array if (!Array.isArray(dataCenters)) { throw new Error("Invalid response: dataCenters is not an array"); @@ -144,7 +145,7 @@ export const getDataCenters = () => { message: error.message, response: error.response?.data, status: error.response?.status, - stack: error.stack + stack: error.stack, }); // Check for specific error types @@ -213,13 +214,18 @@ export const createDataCenter = (dataCenterData) => { id tag } - emissionSource { + dataCenterEmissionSources { id - tag - } - consuptionUnit { - id - description + emissionSource { + id + tag + } + consuptionUnit { + id + description + } + isDefault + percentage } activitySubUnit { id @@ -236,16 +242,20 @@ export const createDataCenter = (dataCenterData) => { number: parseInt(dataCenterData.number) || 1, areaId: dataCenterData.areaId || null, address: dataCenterData.address || "", - latitude: dataCenterData.latitude ? parseFloat(dataCenterData.latitude) : null, - longitude: dataCenterData.longitude ? parseFloat(dataCenterData.longitude) : null, + latitude: dataCenterData.latitude + ? parseFloat(dataCenterData.latitude) + : null, + longitude: dataCenterData.longitude + ? parseFloat(dataCenterData.longitude) + : null, emissionScopeId: dataCenterData.emissionScopeId || null, sectorId: dataCenterData.sectorId || null, subSectorId: dataCenterData.subSectorId || null, - emissionSourceId: dataCenterData.emissionSourceId || null, - consuptionUnitId: dataCenterData.consuptionUnitId || null, - activitySubUnitId: dataCenterData.activitySubUnitId || null - } - } + dataCenterEmissionSources: + dataCenterData.dataCenterEmissionSources || [], + activitySubUnitId: dataCenterData.activitySubUnitId || null, + }, + }, }, { headers: { @@ -258,7 +268,7 @@ export const createDataCenter = (dataCenterData) => { console.log("Create Response:", { status: response.status, data: response.data, - errors: response.data?.errors + errors: response.data?.errors, }); if (response.data?.errors) { @@ -334,13 +344,18 @@ export const updateDataCenter = (id, dataCenterData) => { id tag } - emissionSource { + dataCenterEmissionSources { id - tag - } - consuptionUnit { - id - description + emissionSource { + id + tag + } + consuptionUnit { + id + description + } + isDefault + percentage } activitySubUnit { id @@ -358,16 +373,20 @@ export const updateDataCenter = (id, dataCenterData) => { number: parseInt(dataCenterData.number) || 1, areaId: dataCenterData.areaId || null, address: dataCenterData.address || "", - latitude: dataCenterData.latitude ? parseFloat(dataCenterData.latitude) : null, - longitude: dataCenterData.longitude ? parseFloat(dataCenterData.longitude) : null, + latitude: dataCenterData.latitude + ? parseFloat(dataCenterData.latitude) + : null, + longitude: dataCenterData.longitude + ? parseFloat(dataCenterData.longitude) + : null, emissionScopeId: dataCenterData.emissionScopeId || null, sectorId: dataCenterData.sectorId || null, subSectorId: dataCenterData.subSectorId || null, - emissionSourceId: dataCenterData.emissionSourceId || null, - consuptionUnitId: dataCenterData.consuptionUnitId || null, - activitySubUnitId: dataCenterData.activitySubUnitId || null - } - } + dataCenterEmissionSources: + dataCenterData.dataCenterEmissionSources || [], + activitySubUnitId: dataCenterData.activitySubUnitId || null, + }, + }, }, { headers: { @@ -382,7 +401,7 @@ export const updateDataCenter = (id, dataCenterData) => { dispatch({ type: "UPDATE_DATA_CENTER_SUCCESS", - payload: response.data.data.updateDataCenter + payload: response.data.data.updateDataCenter, }); return response.data.data.updateDataCenter; @@ -415,8 +434,8 @@ export const deleteDataCenter = (id) => { } `, variables: { - id: id - } + id: id, + }, }, { headers: { @@ -431,7 +450,7 @@ export const deleteDataCenter = (id) => { dispatch({ type: "DELETE_DATA_CENTER_SUCCESS", - payload: id + payload: id, }); return true; @@ -466,7 +485,7 @@ export const getEmissionScopes = () => { description } } - ` + `, }, { headers: { @@ -481,7 +500,7 @@ export const getEmissionScopes = () => { dispatch({ type: "GET_EMISSION_SCOPES_SUCCESS", - payload: response.data.data.emissionScopes + payload: response.data.data.emissionScopes, }); return response.data.data.emissionScopes; @@ -502,65 +521,59 @@ export const getDataCenterVMs = (dataCenterId) => { return new Promise(async (resolve, reject) => { // Don't make the request if dataCenterId is undefined, null, or empty if (!dataCenterId || dataCenterId === "undefined") { - console.log('getDataCenterVMs: No dataCenterId provided'); + console.log("getDataCenterVMs: No dataCenterId provided"); resolve([]); return; } try { - console.log('getDataCenterVMs: Fetching VMs for data center:', dataCenterId); - const response = await ApplicationService.http() - .post( - "/graphql", - { - query: ` + console.log( + "getDataCenterVMs: Fetching VMs for data center:", + dataCenterId + ); + const response = await ApplicationService.http().post( + "/graphql", + { + query: ` { dataCenter(id: "${dataCenterId}") { id dataCenter - projects { + physicalMachines { id name - physicalMachines { + vms { id - name - vms { - id - name - status - power - } + vmName + state + power } } } } `, + }, + { + headers: { + Authorization: "Bearer " + localStorage.getItem("accessToken"), }, - { - headers: { - Authorization: "Bearer " + localStorage.getItem("accessToken"), - }, - } - ); + } + ); const dataCenter = response?.data?.data?.dataCenter; - console.log('getDataCenterVMs: Data center response:', dataCenter); - + console.log("getDataCenterVMs: Data center response:", dataCenter); + let allVMs = []; - - if (dataCenter && dataCenter.projects) { - dataCenter.projects.forEach(project => { - if (project.physicalMachines) { - project.physicalMachines.forEach(pm => { - if (pm.vms) { - allVMs = allVMs.concat(pm.vms); - } - }); + + if (dataCenter && dataCenter.physicalMachines) { + dataCenter.physicalMachines.forEach((pm) => { + if (pm.vms) { + allVMs = allVMs.concat(pm.vms); } }); } - - console.log('getDataCenterVMs: Found VMs:', allVMs); + + console.log("getDataCenterVMs: Found VMs:", allVMs); resolve(allVMs); } catch (error) { console.error("Error fetching VMs by data center:", error); diff --git a/sge-frontend/src/views/DataCenterManagement.js b/sge-frontend/src/views/DataCenterManagement.js index 620e25d..5db4a64 100644 --- a/sge-frontend/src/views/DataCenterManagement.js +++ b/sge-frontend/src/views/DataCenterManagement.js @@ -28,26 +28,37 @@ import { Edit } from "@mui/icons-material"; import { useSnackbar } from "notistack"; import { default as SweetAlert } from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; -import { getDataCenters, createDataCenter, updateDataCenter, deleteDataCenter, getEmissionScopes } from "../redux/actions/dataCenter"; +import { + getDataCenters, + createDataCenter, + updateDataCenter, + deleteDataCenter, + getEmissionScopes, +} from "../redux/actions/dataCenter"; import { getAreas, getAreasWithCriteria } from "../redux/actions/areas"; import { useTranslation } from "react-i18next"; -import { getSectors, getSectorById, getSubSectorById, getConsuptionUnits } from "../redux/actions/datas"; +import { + getSectors, + getSectorById, + getSubSectorById, + getConsuptionUnits, +} from "../redux/actions/datas"; import { getAllEmissionSources } from "../redux/actions/emissionSources"; import { permissionCheck } from "../components/permission-check"; import { customFilterForSelect } from "../utility/Utils"; -import { MapContainer, TileLayer, Marker, useMapEvents } from 'react-leaflet'; -import 'leaflet/dist/leaflet.css'; -import L from 'leaflet'; -import axios from 'axios'; -import { debounce } from 'lodash'; +import { MapContainer, TileLayer, Marker, useMapEvents } from "react-leaflet"; +import "leaflet/dist/leaflet.css"; +import L from "leaflet"; +import axios from "axios"; +import { debounce } from "lodash"; // Add Nominatim service configuration -const NOMINATIM_BASE_URL = 'https://nominatim.openstreetmap.org'; +const NOMINATIM_BASE_URL = "https://nominatim.openstreetmap.org"; const nominatimAxios = axios.create({ baseURL: NOMINATIM_BASE_URL, headers: { - 'User-Agent': 'SGE-DataCenter-Management' // Required by Nominatim's usage policy - } + "User-Agent": "SGE-DataCenter-Management", // Required by Nominatim's usage policy + }, }); const Swal = withReactContent(SweetAlert); @@ -55,9 +66,9 @@ const Swal = withReactContent(SweetAlert); // Fix Leaflet marker icon issue delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ - iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), - iconUrl: require('leaflet/dist/images/marker-icon.png'), - shadowUrl: require('leaflet/dist/images/marker-shadow.png') + iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"), + iconUrl: require("leaflet/dist/images/marker-icon.png"), + shadowUrl: require("leaflet/dist/images/marker-shadow.png"), }); // Map marker component that handles clicks @@ -66,30 +77,33 @@ const MapMarker = ({ position, setPosition, setSelectedDataCenter }) => { click(e) { setPosition([e.latlng.lat, e.latlng.lng]); // Use Nominatim reverse geocoding directly - nominatimAxios.get(`/reverse?format=json&lat=${e.latlng.lat}&lon=${e.latlng.lng}`) - .then(response => { + nominatimAxios + .get(`/reverse?format=json&lat=${e.latlng.lat}&lon=${e.latlng.lng}`) + .then((response) => { const address = response.data.display_name; - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, address, latitude: e.latlng.lat, - longitude: e.latlng.lng + longitude: e.latlng.lng, })); }) - .catch(error => { - console.error('Error getting address:', error); + .catch((error) => { + console.error("Error getting address:", error); // Still update coordinates even if address lookup fails - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, latitude: e.latlng.lat, - longitude: e.latlng.lng + longitude: e.latlng.lng, })); }); - } + }, }); // Only render marker if position exists and has valid coordinates - return position && position[0] && position[1] ? : null; + return position && position[0] && position[1] ? ( + + ) : null; }; const DataCenterManagement = () => { @@ -115,15 +129,14 @@ const DataCenterManagement = () => { emissionScopeId: null, sectorId: null, subSectorId: null, - emissionSourceId: null, - consuptionUnitId: null, - activitySubUnitId: null + dataCenterEmissionSources: [], // Array of emission sources with consumption units + activitySubUnitId: null, }); const [mapPosition, setMapPosition] = useState(null); const dataCenterStore = useSelector((state) => { - console.log('DataCenter Store:', state.dataCenter); + console.log("DataCenter Store:", state.dataCenter); return state.dataCenter; }); const emissionScopeStore = useSelector((state) => state.emissionScope); @@ -138,7 +151,7 @@ const DataCenterManagement = () => { const [emissionScopesOptions, setEmissionScopesOptions] = useState([]); const [areasOptions, setAreasOptions] = useState([]); const [citiesOptions, setCitiesOptions] = useState([]); - + // Add state for selected sector and sub sector like in data input const [selectedSector, setSelectedSector] = useState(null); const [selectedSubSector, setSelectedSubSector] = useState(null); @@ -170,7 +183,7 @@ const DataCenterManagement = () => { @@ -200,7 +213,9 @@ const DataCenterManagement = () => { onClick={() => handleEditDataCenter(row)} > - {t("Cruds.edit")} + + {t("Cruds.edit")} + )} {permissionCheck("data_center_delete") && ( @@ -210,7 +225,9 @@ const DataCenterManagement = () => { onClick={() => handleDeleteDataCenter(row)} > - {t("Cruds.delete")} + + {t("Cruds.delete")} + )} @@ -269,16 +286,41 @@ const DataCenterManagement = () => { ); }, [emissionSourceStore?.emissionSources]); + // Remove the old emission source effect since we now handle it in the emission sources component + // useEffect(() => { + // if (selectedDataCenter?.emissionSourceId) { + // dispatch( + // getConsuptionUnits({ + // id: selectedDataCenter?.emissionSourceId, + // sector: selectedDataCenter?.sectorId, + // }) + // ); + // } + // }, [selectedDataCenter?.emissionSourceId]); + + // Ensure there's always at least one emission source entry for new data centers useEffect(() => { - if (selectedDataCenter?.emissionSourceId) { - dispatch( - getConsuptionUnits({ - id: selectedDataCenter?.emissionSourceId, - sector: selectedDataCenter?.sectorId, - }) - ); + if ( + showAddModal && + !editingDataCenter && + selectedDataCenter.dataCenterEmissionSources.length === 0 + ) { + setSelectedDataCenter((prev) => ({ + ...prev, + dataCenterEmissionSources: [ + { + emissionSourceId: null, + consuptionUnitId: null, + isDefault: true, + }, + ], + })); } - }, [selectedDataCenter?.emissionSourceId]); + }, [ + showAddModal, + editingDataCenter, + selectedDataCenter.dataCenterEmissionSources.length, + ]); useEffect(() => { if (selectedSubSector != null) { @@ -352,8 +394,18 @@ const DataCenterManagement = () => { }, [selectedDataCenter.areaId, areasStore?.areas]); const handleEditDataCenter = (row) => { - console.log('Editing data center:', row); + console.log("Editing data center:", row); setEditingDataCenter(row); + + // Convert dataCenterEmissionSources to the format expected by the form + const emissionSources = row.dataCenterEmissionSources + ? row.dataCenterEmissionSources.map((dces) => ({ + emissionSourceId: dces.emissionSource?.id, + consuptionUnitId: dces.consuptionUnit?.id, + isDefault: dces.isDefault || false, + })) + : []; + setSelectedDataCenter({ name: row.dataCenter, externalId: row.externalId?.toString(), @@ -368,17 +420,20 @@ const DataCenterManagement = () => { emissionScopeId: row.emissionScope?.id || null, sectorId: row.sector?.id || null, subSectorId: row.subSector?.id || null, - emissionSourceId: row.emissionSource?.id || null, - consuptionUnitId: row.consuptionUnit?.id || null, - activitySubUnitId: row.activitySubUnit?.id || null + dataCenterEmissionSources: emissionSources, + activitySubUnitId: row.activitySubUnit?.id || null, }); - + // Set the selected sector and sub sector for cascading dropdowns setSelectedSector(row.sector?.id); setSelectedSubSector(row.subSector?.id); - + // Only set map position if we have both address and valid coordinates - setMapPosition(row.address && row.latitude && row.longitude ? [row.latitude, row.longitude] : null); + setMapPosition( + row.address && row.latitude && row.longitude + ? [row.latitude, row.longitude] + : null + ); setShowAddModal(true); }; @@ -403,17 +458,16 @@ const DataCenterManagement = () => { enqueueSnackbar(t("DataCenter.deleteSuccess"), { variant: "success" }); } catch (error) { console.error("Delete error:", error); - enqueueSnackbar( - error?.message || t("DataCenter.deleteError"), - { variant: "error" } - ); + enqueueSnackbar(error?.message || t("DataCenter.deleteError"), { + variant: "error", + }); } } }; const validateForm = () => { const errors = []; - + // Required fields validation if (!selectedDataCenter.name?.trim()) { errors.push(t("DataCenter.nameRequired")); @@ -448,7 +502,7 @@ const DataCenterManagement = () => { try { const lat = parseFloat(selectedDataCenter.latitude); const lng = parseFloat(selectedDataCenter.longitude); - + if (isNaN(lat) || lat < -90 || lat > 90) { errors.push(t("DataCenter.invalidLatitude")); } @@ -473,23 +527,60 @@ const DataCenterManagement = () => { if (selectedDataCenter.subSectorId && !selectedDataCenter.sectorId) { errors.push(t("DataCenter.sectorRequired")); } - if (selectedDataCenter.emissionSourceId && !selectedDataCenter.subSectorId) { - errors.push(t("DataCenter.subSectorRequired")); - } - if (selectedDataCenter.consuptionUnitId && !selectedDataCenter.emissionSourceId) { - errors.push(t("DataCenter.emissionSourceRequired")); - } - if (selectedDataCenter.activitySubUnitId && !selectedDataCenter.subSectorId) { + if ( + selectedDataCenter.activitySubUnitId && + !selectedDataCenter.subSectorId + ) { errors.push(t("DataCenter.subSectorRequiredForActivity")); } + // Emission sources validations + if (selectedDataCenter.dataCenterEmissionSources.length > 0) { + const validSources = selectedDataCenter.dataCenterEmissionSources.filter( + (source) => source.emissionSourceId && source.consuptionUnitId + ); + + if (validSources.length === 0) { + errors.push(t("DataCenter.atLeastOneEmissionSource")); + } + + // Check for incomplete emission sources + selectedDataCenter.dataCenterEmissionSources.forEach((source, index) => { + if ( + (source.emissionSourceId && !source.consuptionUnitId) || + (!source.emissionSourceId && source.consuptionUnitId) + ) { + errors.push( + t("DataCenter.incompleteEmissionSource", { index: index + 1 }) + ); + } + }); + + // Check for duplicate emission sources + const sourceIds = validSources.map((s) => s.emissionSourceId); + const duplicates = sourceIds.filter( + (id, index) => sourceIds.indexOf(id) !== index + ); + if (duplicates.length > 0) { + errors.push(t("DataCenter.duplicateEmissionSources")); + } + + // Ensure exactly one default emission source + const defaultSources = validSources.filter((s) => s.isDefault); + if (defaultSources.length === 0) { + errors.push(t("DataCenter.oneDefaultEmissionSourceRequired")); + } else if (defaultSources.length > 1) { + errors.push(t("DataCenter.onlyOneDefaultEmissionSourceAllowed")); + } + } + return errors; }; const handleSubmit = async () => { const validationErrors = validateForm(); if (validationErrors.length > 0) { - validationErrors.forEach(error => { + validationErrors.forEach((error) => { enqueueSnackbar(error, { variant: "error" }); }); return; @@ -498,20 +589,26 @@ const DataCenterManagement = () => { try { // Format data according to GraphQL input type const dataToSubmit = { - dataCenter: selectedDataCenter.name, + name: selectedDataCenter.name, externalId: parseInt(selectedDataCenter.externalId), number: parseInt(selectedDataCenter.number || "1"), address: selectedDataCenter.address, areaId: selectedDataCenter.areaId, - latitude: selectedDataCenter.latitude ? parseFloat(selectedDataCenter.latitude) : null, - longitude: selectedDataCenter.longitude ? parseFloat(selectedDataCenter.longitude) : null, + latitude: selectedDataCenter.latitude + ? parseFloat(selectedDataCenter.latitude) + : null, + longitude: selectedDataCenter.longitude + ? parseFloat(selectedDataCenter.longitude) + : null, ayposURL: selectedDataCenter.ayposURL, emissionScopeId: selectedDataCenter.emissionScopeId, sectorId: selectedDataCenter.sectorId, subSectorId: selectedDataCenter.subSectorId, - emissionSourceId: selectedDataCenter.emissionSourceId, - consuptionUnitId: selectedDataCenter.consuptionUnitId, - activitySubUnitId: selectedDataCenter.activitySubUnitId + dataCenterEmissionSources: + selectedDataCenter.dataCenterEmissionSources.filter( + (source) => source.emissionSourceId && source.consuptionUnitId + ), + activitySubUnitId: selectedDataCenter.activitySubUnitId, }; if (editingDataCenter) { @@ -523,26 +620,27 @@ const DataCenterManagement = () => { await dispatch(createDataCenter(dataToSubmit)); enqueueSnackbar(t("DataCenter.createSuccess"), { variant: "success" }); } - + // Refresh the data centers list await dispatch(getDataCenters()); handleCloseModal(); } catch (error) { console.error("Submit error:", error); - + // Handle specific error cases if (error.message?.includes("duplicate")) { - enqueueSnackbar(t("DataCenter.duplicateExternalId"), { variant: "error" }); + enqueueSnackbar(t("DataCenter.duplicateExternalId"), { + variant: "error", + }); } else if (error.message?.includes("permission")) { enqueueSnackbar(t("Common.noPermission"), { variant: "error" }); } else if (error.message?.includes("not found")) { enqueueSnackbar(t("DataCenter.resourceNotFound"), { variant: "error" }); } else { - enqueueSnackbar( - error?.message || t("DataCenter.submitError"), - { variant: "error" } - ); + enqueueSnackbar(error?.message || t("DataCenter.submitError"), { + variant: "error", + }); } } }; @@ -554,13 +652,28 @@ const DataCenterManagement = () => { externalId: "", number: "", address: "", + areaId: null, + cityId: null, latitude: null, longitude: null, ayposURL: "", - city: "" + city: "", + emissionScopeId: null, + sectorId: null, + subSectorId: null, + dataCenterEmissionSources: [ + { + emissionSourceId: null, + consuptionUnitId: null, + isDefault: true, + }, + ], + activitySubUnitId: null, }); setMapPosition(null); setEditingDataCenter(null); + setSelectedSector(null); + setSelectedSubSector(null); }; const handleFilter = (e) => { @@ -594,45 +707,47 @@ const DataCenterManagement = () => { const geocodeAddress = useCallback(async (address) => { if (!address || !address.trim()) { setMapPosition(null); - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, latitude: null, - longitude: null + longitude: null, })); return false; } try { - const response = await nominatimAxios.get(`/search?format=json&q=${encodeURIComponent(address)}`); + const response = await nominatimAxios.get( + `/search?format=json&q=${encodeURIComponent(address)}` + ); if (response.data && response.data[0]) { const { lat, lon } = response.data[0]; const newPosition = [parseFloat(lat), parseFloat(lon)]; setMapPosition(newPosition); - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, latitude: parseFloat(lat), - longitude: parseFloat(lon) + longitude: parseFloat(lon), })); return true; } // If no results found, clear the coordinates setMapPosition(null); - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, latitude: null, - longitude: null + longitude: null, })); return false; } catch (error) { - console.error('Error geocoding address:', error); + console.error("Error geocoding address:", error); // On error, clear the coordinates setMapPosition(null); - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, latitude: null, - longitude: null + longitude: null, })); return false; } @@ -641,18 +756,18 @@ const DataCenterManagement = () => { // Update the address input handler const handleAddressChange = (e) => { const newAddress = e.target.value; - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, - address: newAddress + address: newAddress, })); // If address is empty, clear the coordinates if (!newAddress.trim()) { setMapPosition(null); - setSelectedDataCenter(prev => ({ + setSelectedDataCenter((prev) => ({ ...prev, latitude: null, - longitude: null + longitude: null, })); } else { // If address is not empty, try to geocode it after a delay @@ -675,9 +790,7 @@ const DataCenterManagement = () => { size="lg" > - {editingDataCenter - ? t("DataCenter.edit") - : t("DataCenter.add")} + {editingDataCenter ? t("DataCenter.edit") : t("DataCenter.add")}
@@ -685,7 +798,8 @@ const DataCenterManagement = () => { { { - + {/* Emission Scope Section */} -
Emission Scope Configuration
+
+ Emission Scope Configuration +
@@ -828,7 +945,8 @@ const DataCenterManagement = () => { placeholder="Select emission scope" options={emissionScopesOptions} value={emissionScopesOptions?.find( - (option) => option.value === selectedDataCenter.emissionScopeId + (option) => + option.value === selectedDataCenter.emissionScopeId )} onChange={(option) => setSelectedDataCenter({ @@ -843,7 +961,9 @@ const DataCenterManagement = () => { - + option.value === selectedDataCenter.emissionSourceId + +
+ + Configure emission sources for this data center. At least + one emission source with consumption unit is required. + + {selectedDataCenter.dataCenterEmissionSources.map( + (source, index) => ( + + + + + option.value === source.consuptionUnitId + )} + onChange={(option) => { + const updatedSources = [ + ...selectedDataCenter.dataCenterEmissionSources, + ]; + updatedSources[index] = { + ...updatedSources[index], + consuptionUnitId: option?.value, + }; + setSelectedDataCenter({ + ...selectedDataCenter, + dataCenterEmissionSources: updatedSources, + }); + }} + isClearable + filterOption={customFilterForSelect} + isDisabled={!source.emissionSourceId} + styles={{ + placeholder: (provided) => ({ + ...provided, + color: "#6e6b7b", + }), + }} + menuPlacement="top" + /> + + + +
+ + +
+ +
+ ) )} - onChange={(option) => { - setSelectedDataCenter({ - ...selectedDataCenter, - emissionSourceId: option?.value, - consuptionUnitId: null, - }); - }} - isClearable - filterOption={customFilterForSelect} - isDisabled={!selectedDataCenter.subSectorId} - /> - - - - - - option.value === selectedDataCenter.activitySubUnitId + (option) => + option.value === selectedDataCenter.activitySubUnitId )} onChange={(option) => { setSelectedDataCenter({ @@ -962,10 +1236,11 @@ const DataCenterManagement = () => { isClearable filterOption={customFilterForSelect} isDisabled={!selectedDataCenter.subSectorId} + menuPlacement="top" /> - + @@ -986,7 +1261,7 @@ const DataCenterManagement = () => { setSelectedDataCenter({ ...selectedDataCenter, latitude: pos[0], - longitude: pos[1] + longitude: pos[1], }); }} setSelectedDataCenter={setSelectedDataCenter} @@ -1002,14 +1277,13 @@ const DataCenterManagement = () => { - @@ -1083,9 +1357,7 @@ const DataCenterManagement = () => { progressPending={dataCenterStore?.loading} progressComponent={
Loading...
} noDataComponent={ -
- {t("Common.noDataAvailable")} -
+
{t("Common.noDataAvailable")}
} />
@@ -1094,4 +1366,4 @@ const DataCenterManagement = () => { ); }; -export default memo(DataCenterManagement); \ No newline at end of file +export default memo(DataCenterManagement); diff --git a/sge-frontend/src/views/Map.js b/sge-frontend/src/views/Map.js index c044980..2b7b4fd 100644 --- a/sge-frontend/src/views/Map.js +++ b/sge-frontend/src/views/Map.js @@ -32,10 +32,10 @@ import { getCities } from "../redux/actions/cities"; import { v4 as uuidv4 } from "uuid"; import { getCity } from "../redux/actions/city"; import { getDistrict } from "../redux/actions/district"; -import { - getOrganisations, - getOrganisationById, -} from "../redux/actions/organisations"; +// import { +// getOrganisations, +// getOrganisationById, +// } from "../redux/actions/organisations"; import { getAreasWithCriteria } from "../redux/actions/areas"; import { ChromePicker } from "react-color"; import { customFilterForSelect } from "../utility/Utils"; @@ -117,20 +117,20 @@ const Map = () => { const citiesStore = useSelector((state) => state.cities); const cityStore = useSelector((state) => state.city); const districtStore = useSelector((state) => state.district); - const OrganisationsStore = useSelector((state) => state.organizations); + // const OrganisationsStore = useSelector((state) => state.organizations); const areasStore = useSelector((state) => state.areas); const dataCenterStore = useSelector((state) => state.dataCenter); const [cities, setCities] = useState([]); const [districts, setDistricts] = useState([]); const [neighborhoods, setNeighborhoods] = useState([]); - const [organizationOptions, setOrganizationOptions] = useState([]); - const organizationId = localStorage.getItem("organizationId"); - const roleTag = localStorage.getItem("roleTag"); - const [selectedOrganization, setSelectedOrganization] = useState({ - label: localStorage.getItem("organizationName"), - value: organizationId, - }); + // const [organizationOptions, setOrganizationOptions] = useState([]); + // const organizationId = localStorage.getItem("organizationId"); + // const roleTag = localStorage.getItem("roleTag"); + // const [selectedOrganization, setSelectedOrganization] = useState({ + // label: localStorage.getItem("organizationName"), + // value: organizationId, + // }); const [areasOptions, setAreasOptions] = useState([]); const [done, setDone] = useState(false); @@ -146,7 +146,7 @@ const Map = () => { const [showDataInputModal, setShowDataInputModal] = useState(false); const [inputData, setInputData] = useState({ - organization: selectedOrganization, + // organization: selectedOrganization, }); const [referance, setReferance] = useState( Number(localStorage.getItem("referance")) || 1000 @@ -176,15 +176,17 @@ const Map = () => { dispatch(getDataCenters()); }, []); - useEffect(() => { - if (selectedOrganization?.value != "undefined") { - dispatch(getAreasWithCriteria(selectedOrganization.value)); - } - }, [selectedOrganization]); + // useEffect(() => { + // if (selectedOrganization?.value != "undefined") { + // dispatch(getAreasWithCriteria(selectedOrganization.value)); + // } + // }, [selectedOrganization]); useEffect(() => { setAreasOptions([]); + // Load all areas without organization filter for now + // You may want to add organization filtering back later if needed const citiesOptions = areasStore?.areasWithCriteria ?.map((area) => @@ -235,13 +237,13 @@ const Map = () => { ]); }, [areasStore]); - useEffect(() => { - if (roleTag === "SUPER_ADMIN") { - dispatch(getOrganisations()); - } else { - dispatch(getOrganisationById(organizationId)); - } - }, []); + // useEffect(() => { + // if (roleTag === "SUPER_ADMIN") { + // dispatch(getOrganisations()); + // } else { + // dispatch(getOrganisationById(organizationId)); + // } + // }, []); const handleDataInputButtonPressed = ({ area, type }) => { const areaName = @@ -348,38 +350,38 @@ const Map = () => { }); }, [selectedDistrict, districtStore?.district, year.value]); - useEffect(() => { - let organizationOptions = []; + // useEffect(() => { + // let organizationOptions = []; - if ( - OrganisationsStore.organization && - OrganisationsStore.organization.length !== 0 - ) { - organizationOptions.push({ - value: OrganisationsStore.organization.id, - label: OrganisationsStore.organization.tag, - }); + // if ( + // OrganisationsStore.organization && + // OrganisationsStore.organization.length !== 0 + // ) { + // organizationOptions.push({ + // value: OrganisationsStore.organization.id, + // label: OrganisationsStore.organization.tag, + // }); - if (OrganisationsStore.organization.children) { - organizationOptions = [ - ...organizationOptions, - ...OrganisationsStore.organization.children.map((organization) => ({ - value: organization.child.id, - label: organization.child.tag, - })), - ]; - } - } else { - organizationOptions = OrganisationsStore.dataOrganization.map( - (organization) => ({ - value: organization.id, - label: organization.tag, - }) - ); - } + // if (OrganisationsStore.organization.children) { + // organizationOptions = [ + // ...organizationOptions, + // ...OrganisationsStore.organization.children.map((organization) => ({ + // value: organization.child.id, + // label: organization.child.tag, + // })), + // ]; + // } + // } else { + // organizationOptions = OrganisationsStore.dataOrganization.map( + // (organization) => ({ + // value: organization.id, + // label: organization.tag, + // }) + // ); + // } - setOrganizationOptions(organizationOptions); - }, [OrganisationsStore]); + // setOrganizationOptions(organizationOptions); + // }, [OrganisationsStore]); const renderDataInputModal = () => { return ( @@ -623,29 +625,40 @@ const Map = () => { {dataCenterStore.dataCenters.map((dc) => { if (!dc.latitude || !dc.longitude) return null; - + return ( - +
{dc.dataCenter}
-

{t('DataCenter.number')}: {dc.number}

-

{t('DataCenter.externalId')}: {dc.externalId}

-

{t('DataCenter.city')}: {dc.area?.cities?.map(city => city.name).join(', ') || "-"}

- {dc.area &&

{t('Area')}: {dc.area.tag}

} - {dc.projects?.length > 0 && ( +

+ {t("DataCenter.number")}: {dc.number} +

+

+ {t("DataCenter.externalId")}:{" "} + {dc.externalId} +

+

+ {t("DataCenter.city")}:{" "} + {dc.area?.cities?.map((city) => city.name).join(", ") || + "-"} +

+ {dc.area && (

- {t('Projects')}: {dc.projects.length} + {t("Area")}: {dc.area.tag} +

+ )} + {dc.dataCenterEmissionSources?.length > 0 && ( +

+ {t("EmissionSources")}:{" "} + {dc.dataCenterEmissionSources.length}

)} {dc.ayposURL && ( @@ -699,7 +712,9 @@ const Map = () => { setDistrictView(true); setZoom(8.0); - let convertCordinates = convertCoordinates(city.coordinates); + let convertCordinates = convertCoordinates( + city.coordinates + ); let length = convertCordinates[0][0][0].length; let mlength = ((length + 1) / 2).toFixed(0); let lat1 = convertCordinates[0][0][0][0]; @@ -710,7 +725,7 @@ const Map = () => { lat: ((lat1 + lat2) / 2).toFixed(2), lng: ((lng1 + lng2) / 2).toFixed(2), }); - } + }, }} >