From 17d77fcda722bd876ac622ccb0d7c14d53615670 Mon Sep 17 00:00:00 2001 From: Khaled Elagamy Date: Tue, 19 Aug 2025 00:07:20 +0300 Subject: [PATCH 01/13] Fix message queue data misnaming --- .../graphql/dataCenter/dto/DataCenterDto.java | 16 ++++++++-------- .../com/sgs/utils/rabbitMQ/MessageListener.java | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) 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 1973dbf..7c8febf 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,10 +17,10 @@ public class DataCenterDto { private Integer externalId; private Integer number; private AreaDto area; - - @JsonProperty("physical_machine") + + @JsonProperty("physical_machines") private Map physicalMachine; - + // Emission calculation fields private SectorDto sector; private SubSectorDto subSector; @@ -62,12 +62,12 @@ public class DataCenterDto { this.number = number; } - public AreaDto getArea() { - return area; + public AreaDto getArea() { + return area; } - - public void setArea(AreaDto area) { - this.area = area; + + public void setArea(AreaDto area) { + this.area = area; } public Map getPhysicalMachine() { 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 12302f3..d26845c 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 @@ -805,10 +805,19 @@ public class MessageListener { 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"); + // Use the source-specific power consumption (percentage of total VM power) + // Format to 6 decimal places to avoid very long strings + String formattedPower = String.format("%.6f", sourceSpecificPower); + input.setConsuptionAmount(formattedPower); + System.out.println("πŸ” Setting Consumption Amount: " + formattedPower + "W"); + // Validate field lengths to prevent database errors + System.out.println("πŸ” Field length validation:"); + System.out.println(" Year: " + (input.getYear() != null ? input.getYear().length() : "null")); + System.out.println(" Month: " + (input.getMonth() != null ? input.getMonth().length() : "null")); + System.out.println(" ConsuptionAmount: " + (input.getConsuptionAmount() != null ? input.getConsuptionAmount().length() : "null")); + + System.out.println("πŸ” VM Emission Input for Source:"); System.out.println(" VM ID: " + vm.getId()); System.out.println(" VM Name: " + vm.getVmName()); From 2257ec79f6e8d1295fd958959255b226354c5c56 Mon Sep 17 00:00:00 2001 From: Khaled Elagamy Date: Tue, 19 Aug 2025 00:08:01 +0300 Subject: [PATCH 02/13] Make creation of default area nested in city initialization --- .../src/main/java/com/sgs/SgsApplication.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sge-backend/src/main/java/com/sgs/SgsApplication.java b/sge-backend/src/main/java/com/sgs/SgsApplication.java index 0ce8d67..d150b6a 100644 --- a/sge-backend/src/main/java/com/sgs/SgsApplication.java +++ b/sge-backend/src/main/java/com/sgs/SgsApplication.java @@ -121,7 +121,8 @@ public class SgsApplication implements CommandLineRunner { @Autowired public SgsApplication(RoleService roleService, PermissionService permissionService, RoleRepo roleRepo, - AreaService areaService, 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, @@ -789,7 +790,7 @@ public class SgsApplication implements CommandLineRunner { 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()) { @@ -858,6 +859,7 @@ public class SgsApplication implements CommandLineRunner { } if (cityService.findAll().isEmpty()) { createCitiesFromJson(); + createDefaultArea(); } if (districtService.findAll().isEmpty()) { createDistrictFromJson(); @@ -865,33 +867,30 @@ public class SgsApplication implements CommandLineRunner { if (neighborhoodService.findAll().isEmpty()) { createNeighborhoodsFromJson(); } - 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()); - + .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); } } From 47959fb35f9b92d994165cae3e9d354bfe87b8a0 Mon Sep 17 00:00:00 2001 From: Khaled Elagamy Date: Tue, 19 Aug 2025 00:08:42 +0300 Subject: [PATCH 03/13] Make the datacenter creation modal mobile friendly --- .../src/views/DataCenterManagement.js | 390 ++++++++++-------- 1 file changed, 225 insertions(+), 165 deletions(-) diff --git a/sge-frontend/src/views/DataCenterManagement.js b/sge-frontend/src/views/DataCenterManagement.js index 5db4a64..4cbe2c1 100644 --- a/sge-frontend/src/views/DataCenterManagement.js +++ b/sge-frontend/src/views/DataCenterManagement.js @@ -428,6 +428,23 @@ const DataCenterManagement = () => { setSelectedSector(row.sector?.id); setSelectedSubSector(row.subSector?.id); + // If there are existing emission sources, fetch consumption units for each + if ( + row.dataCenterEmissionSources && + row.dataCenterEmissionSources.length > 0 + ) { + row.dataCenterEmissionSources.forEach((dces) => { + if (dces.emissionSource && dces.emissionSource.id) { + dispatch( + getConsuptionUnits({ + id: dces.emissionSource.id, + sector: row.sector?.id, + }) + ); + } + }); + } + // Only set map position if we have both address and valid coordinates setMapPosition( row.address && row.latitude && row.longitude @@ -1029,189 +1046,232 @@ const DataCenterManagement = () => { -
- - Configure emission sources for this data center. At least - one emission source with consumption unit is required. - +
+
+ + 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" - /> - - - -
- -
- -
+ isClearable + filterOption={customFilterForSelect} + isDisabled={!source.emissionSourceId} + styles={{ + placeholder: (provided) => ({ + ...provided, + color: "#6e6b7b", + }), + }} + menuPlacement="auto" + /> + + + + +
+
+ + {source.isDefault && ( + + Default Source + + )} +
+ +
+ +
+
) )} - +
+ +
From 82f86a62cae64e63b481365e9177f6460139e449 Mon Sep 17 00:00:00 2001 From: Khaled Elagamy Date: Tue, 19 Aug 2025 00:15:51 +0300 Subject: [PATCH 04/13] Fix issue in updating datacenter Found a hibernate cascade issue and was fixed by reuse the existing collection instaed of creating a new one in updating the datacenter entity --- .../mutation/mapper/DataCenterMapper.java | 102 +++++++++--------- 1 file changed, 54 insertions(+), 48 deletions(-) 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 1419cf2..f40b1fb 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 @@ -31,10 +31,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)); + 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)); + dcEmissionSource.setConsuptionUnit(consuptionUnitService + .findById(UUID.fromString(emissionSourceInput.getConsuptionUnitId())).orElse(null)); } - + // Set optional fields - dcEmissionSource.setIsDefault(emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false); + dcEmissionSource.setIsDefault( + emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false); dcEmissionSource.setPercentage(emissionSourceInput.getPercentage()); - + dataCenterEmissionSources.add(dcEmissionSource); } - + entity.setDataCenterEmissionSources(dataCenterEmissionSources); } - + return entity; } @@ -118,83 +121,86 @@ public class DataCenterMapper extends BaseCreateUpdateMapper dataCenterEmissionSources = new ArrayList<>(); + + // Add new emission sources to the same managed collection 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)); + 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.setConsuptionUnit(consuptionUnitService + .findById(UUID.fromString(emissionSourceInput.getConsuptionUnitId())).orElse(null)); } - - dcEmissionSource.setIsDefault(emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false); + + dcEmissionSource.setIsDefault( + emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false); dcEmissionSource.setPercentage(emissionSourceInput.getPercentage()); - - dataCenterEmissionSources.add(dcEmissionSource); + + // Add to the existing managed collection instead of creating a new one + entity.getDataCenterEmissionSources().add(dcEmissionSource); } - entity.setDataCenterEmissionSources(dataCenterEmissionSources); } - + // New attributes (partial update - only if provided) if (input.getAyposURL() != null) { entity.setAyposURL(input.getAyposURL()); } - + if (input.getAddress() != null) { entity.setAddress(input.getAddress()); } - + if (input.getLatitude() != null) { entity.setLatitude(input.getLatitude()); } - + if (input.getLongitude() != null) { entity.setLongitude(input.getLongitude()); } - + return entity; } } \ No newline at end of file From b5901a049cdab2e22b2982ca226d87e948deb463 Mon Sep 17 00:00:00 2001 From: Ali Sadeghi <220201944@ostimteknik.edu.tr> Date: Tue, 19 Aug 2025 04:15:05 +0300 Subject: [PATCH 05/13] Merge: Add city to datacenter, cloudsystem to summary, fix listener --- .../graphql/dataCenter/domain/DataCenter.java | 12 ++++ .../mutation/input/DataCenterCreateInput.java | 5 ++ .../mutation/input/DataCenterUpdateInput.java | 4 ++ .../mutation/mapper/DataCenterMapper.java | 21 ++++-- .../mainDataTable/dto/VMEmissionSummary.java | 10 +-- .../query/MainDataTableQueryResolver.java | 4 +- .../service/MainDataTableService.java | 67 ++++++++----------- .../sgs/utils/rabbitMQ/MessageListener.java | 47 +++++++++++-- .../graphql/dataCenter/input.graphqls | 2 + .../graphql/dataCenter/type.graphqls | 1 + .../graphql/mainDataTable/query.graphqls | 2 +- .../graphql/mainDataTable/type.graphqls | 2 +- 12 files changed, 116 insertions(+), 61 deletions(-) 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 6d49f23..30527cf 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 @@ -3,6 +3,7 @@ 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.city.domain.City; import com.sgs.graphql.sector.domain.Sector; import com.sgs.graphql.subSector.domain.SubSector; import com.sgs.graphql.emissionScope.domain.EmissionScope; @@ -29,6 +30,7 @@ public class DataCenter extends BaseDomain { private Integer number; private Area area; + private City city; private List physicalMachines = new ArrayList<>(); private List dataCenterEmissionSources = new ArrayList<>(); @@ -131,6 +133,16 @@ public class DataCenter extends BaseDomain { this.area = area; } + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "city_id") + public City getCity() { + return city; + } + + public void setCity(City city) { + this.city = city; + } + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "sub_sector_id") public SubSector getSubSector() { 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 e9e856d..720f4e5 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 @@ -18,6 +18,8 @@ public class DataCenterCreateInput extends BaseCreateInput { private UUID areaId; + private UUID cityId; + @NotNull(message = "SektΓΆr ID gereklidir") private UUID sectorId; @@ -50,6 +52,9 @@ public class DataCenterCreateInput extends BaseCreateInput { public UUID getAreaId() { return areaId; } public void setAreaId(UUID areaId) { this.areaId = areaId; } + public UUID getCityId() { return cityId; } + public void setCityId(UUID cityId) { this.cityId = cityId; } + public UUID getSectorId() { return sectorId; } public void setSectorId(UUID sectorId) { this.sectorId = sectorId; } 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 803a5c0..0c732e5 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 @@ -13,6 +13,7 @@ public class DataCenterUpdateInput extends BaseUpdateInput { private Double consuptionAmount; private UUID areaId; + private UUID cityId; @NotNull(message = "SektΓΆr ID gereklidir") private UUID sectorId; private UUID subSectorId; @@ -44,6 +45,9 @@ public class DataCenterUpdateInput extends BaseUpdateInput { public UUID getAreaId() { return areaId; } public void setAreaId(UUID areaId) { this.areaId = areaId; } + public UUID getCityId() { return cityId; } + public void setCityId(UUID cityId) { this.cityId = cityId; } + public UUID getSectorId() { return sectorId; } public void setSectorId(UUID sectorId) { this.sectorId = sectorId; } 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 f40b1fb..98a5f65 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 @@ -12,6 +12,7 @@ import com.sgs.graphql.emissionSource.service.EmissionSourceService; import com.sgs.graphql.emissionScope.service.EmissionScopeService; import com.sgs.graphql.consuptionUnit.service.ConsuptionUnitService; import com.sgs.graphql.activitySubUnit.service.ActivitySubUnitService; +import com.sgs.graphql.city.service.CityService; import com.sgs.lib.dao.mutation.mapper.BaseCreateUpdateMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -29,12 +30,13 @@ public class DataCenterMapper extends BaseCreateUpdateMapper vmEmissionSummary(UUID datacenterId, UUID projectId) { - return mainDataTableService.getVMEmissionSummaries(datacenterId, projectId); + public List vmEmissionSummary(UUID datacenterId) { + return mainDataTableService.getVMEmissionSummaries(datacenterId); } } 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 67809e7..0a04afb 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 @@ -34,59 +34,48 @@ public class MainDataTableService } public List getVMEmissionSummaries() { - return getVMEmissionSummaries(null, null); + return getVMEmissionSummaries(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 whereClause = whereConditions.isEmpty() ? "" : + "WHERE " + String.join(" AND ", whereConditions) + " "; + String sql = """ - 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 - """; - + 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, + pm.cloud_system as cloud_system, + 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(); @@ -110,7 +99,7 @@ public class MainDataTableService } summary.setPhysicalMachine((String) row[6]); - summary.setProject((String) row[7]); + summary.setCloudSystem((String) row[7]); summary.setDataCenter((String) row[8]); // Individual emission values 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 d26845c..5c768e7 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 @@ -217,6 +217,7 @@ public class MessageListener { pm.setName(pmDto.getName()); pm.setIp(pmIp); // Use the IP from the map key pm.setTag(pmDto.getTag()); + pm.setCloudSystem(pmDto.getCloudSystem()); pm.setPower(pmDto.getPower()); pm.setDataCenter(entity); @@ -282,16 +283,17 @@ public class MessageListener { pm.setName(newPm.getName()); pm.setIp(newPm.getIp()); pm.setTag(newPm.getTag()); + pm.setCloudSystem(newPm.getCloudSystem()); pm.setPower(newPm.getPower()); - System.out.println("βœ… Updated existing PM: " + pm.getName() + " (IP: " + pm.getIp() + ")"); + System.out.println("βœ… Updated existing PM: " + pm.getName() + " (IP: " + pm.getIp() + ") - CloudSystem: " + pm.getCloudSystem()); } else { // Create new PM pm = newPm; pm.setDataCenter(dc); dc.getPhysicalMachines().add(pm); - System.out.println("βœ… Created new PM: " + pm.getName() + " (IP: " + pm.getIp() + ")"); + System.out.println("βœ… Created new PM: " + pm.getName() + " (IP: " + pm.getIp() + ") - CloudSystem: " + pm.getCloudSystem()); } // Process VMs that are already assigned to this PM @@ -547,15 +549,46 @@ public class MessageListener { return false; } - // Find the emission source by name/tag - List emissionSources = emissionSourceRepo.findByTag(emissionSourceName); - if (emissionSources.isEmpty()) { + // Find the emission source by name/tag from datacenter's configured emission sources + EmissionSource emissionSource = null; + + // First, try to find the emission source from datacenter's configured sources + if (dataCenter.getDataCenterEmissionSources() != null && !dataCenter.getDataCenterEmissionSources().isEmpty()) { + for (DataCenterEmissionSource dces : dataCenter.getDataCenterEmissionSources()) { + if (dces.getEmissionSource() != null && + emissionSourceName.equalsIgnoreCase(dces.getEmissionSource().getTag())) { + emissionSource = dces.getEmissionSource(); + System.out.println("βœ… Found emission source '" + emissionSourceName + + "' in datacenter's configured sources (ID: " + emissionSource.getId() + ")"); + break; + } + } + } + + // If not found in datacenter's sources, fall back to subsector-specific search + if (emissionSource == null && dataCenter.getSubSector() != null) { + emissionSource = emissionSourceRepo.findByTagAndSubSectorIgnoreCase(emissionSourceName, dataCenter.getSubSector()); + if (emissionSource != null) { + System.out.println("⚠️ Using subsector fallback for emission source '" + emissionSourceName + + "' (ID: " + emissionSource.getId() + ") - Consider configuring it for datacenter"); + } + } + + // Last resort: global search + if (emissionSource == null) { + List emissionSources = emissionSourceRepo.findByTag(emissionSourceName); + if (!emissionSources.isEmpty()) { + emissionSource = emissionSources.get(0); + System.out.println("⚠️ Using global fallback for emission source '" + emissionSourceName + + "' (ID: " + emissionSource.getId() + ") - This may cause incorrect calculations!"); + } + } + + if (emissionSource == null) { System.err.println("❌ Could not find emission source: " + emissionSourceName); return false; } - EmissionSource emissionSource = emissionSources.get(0); - // Calculate power consumption for this emission source (percentage of total VM power) double sourceSpecificPower = vm.getPower() * (percentage / 100.0); diff --git a/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls b/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls index 4c5cb3f..ec46a32 100644 --- a/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls +++ b/sge-backend/src/main/resources/graphql/dataCenter/input.graphqls @@ -11,6 +11,7 @@ input DataCenterCreateInput { consuptionAmount: Float areaId: ID + cityId: ID number: Int ayposURL: String address: String @@ -31,6 +32,7 @@ input DataCenterUpdateInput { consuptionAmount: Float areaId: ID + cityId: ID number: Int ayposURL: String address: String diff --git a/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls b/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls index c6b5929..7554f2d 100644 --- a/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls +++ b/sge-backend/src/main/resources/graphql/dataCenter/type.graphqls @@ -15,6 +15,7 @@ type DataCenter { physicalMachines: [PhysicalMachine] area: Area + city: City number: Int ayposURL: String diff --git a/sge-backend/src/main/resources/graphql/mainDataTable/query.graphqls b/sge-backend/src/main/resources/graphql/mainDataTable/query.graphqls index 5a05347..fa85239 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(datacenterId: ID, projectId: ID): [VMEmissionSummary!]! + vmEmissionSummary(datacenterId: 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 0424e35..57e6360 100644 --- a/sge-backend/src/main/resources/graphql/mainDataTable/type.graphqls +++ b/sge-backend/src/main/resources/graphql/mainDataTable/type.graphqls @@ -60,7 +60,7 @@ type Config { totalEmission: Float! createdDate: LocalDateTime! physicalMachine: String - project: String + cloudSystem: String dataCenter: String # Individual emission values per record co2: Float! From 8bd6d94174c909c2d1d4a7c8a46c4bc30829c1b4 Mon Sep 17 00:00:00 2001 From: Khaled Elagamy Date: Tue, 19 Aug 2025 05:39:02 +0300 Subject: [PATCH 06/13] Fix the DataCenterOverview tab display by removing project relation --- sge-frontend/src/views/DataCenter.js | 123 +++++++++++++++++++-------- 1 file changed, 87 insertions(+), 36 deletions(-) diff --git a/sge-frontend/src/views/DataCenter.js b/sge-frontend/src/views/DataCenter.js index b1b9d60..374b2be 100644 --- a/sge-frontend/src/views/DataCenter.js +++ b/sge-frontend/src/views/DataCenter.js @@ -18,10 +18,10 @@ const DataCenter = () => { const [refreshInterval, setRefreshInterval] = useState(null); const getAllPhysicalMachines = (dataCenter) => { - if (!dataCenter.projects) return []; - return dataCenter.projects.flatMap(project => - project.physicalMachines || [] - ); + // Physical machines are directly in the dataCenter object, not in projects + const pms = dataCenter.physicalMachines || []; + console.log(`Physical machines for ${dataCenter.dataCenter}:`, pms); + return pms; }; // Table columns following your pattern @@ -38,18 +38,23 @@ const DataCenter = () => { sortable: true, minWidth: "200px", }, - // Projects + // Projects - Based on API response, this field might not exist or be structured differently { name: "Projects", - selector: (row) => (row.projects || []).length, + selector: (row) => row.projects?.length || 0, sortable: true, minWidth: "200px", cell: (row) => (
- {(row.projects || []).length > 0 ? ( + {row.projects && row.projects.length > 0 ? (
{row.projects.map((project, index) => ( -
0 ? 'mt-1' : ''}`}> +
0 ? "mt-1" : "" + }`} + > {project.name}
))} @@ -80,27 +85,39 @@ const DataCenter = () => { name: "Virtual Machines", selector: (row) => { const pms = getAllPhysicalMachines(row); - const vms = pms.reduce((acc, pm) => { - if (!pm.vms) return acc; - return { - active: acc.active + pm.vms.filter(vm => vm.state?.toLowerCase() === "active").length, - total: acc.total + pm.vms.length - }; - }, { active: 0, total: 0 }); + const vms = pms.reduce( + (acc, pm) => { + if (!pm.vms) return acc; + return { + active: + acc.active + + pm.vms.filter((vm) => vm.state?.toLowerCase() === "active") + .length, + total: acc.total + pm.vms.length, + }; + }, + { active: 0, total: 0 } + ); return vms.total; }, sortable: true, minWidth: "200px", cell: (row) => { const pms = getAllPhysicalMachines(row); - const vms = pms.reduce((acc, pm) => { - if (!pm.vms) return acc; - return { - active: acc.active + pm.vms.filter(vm => vm.state?.toLowerCase() === "active").length, - total: acc.total + pm.vms.length - }; - }, { active: 0, total: 0 }); - + const vms = pms.reduce( + (acc, pm) => { + if (!pm.vms) return acc; + return { + active: + acc.active + + pm.vms.filter((vm) => vm.state?.toLowerCase() === "active") + .length, + total: acc.total + pm.vms.length, + }; + }, + { active: 0, total: 0 } + ); + return (
@@ -109,7 +126,9 @@ const DataCenter = () => {
{vms.active} Active β€’ - {vms.total - vms.active} Inactive + + {vms.total - vms.active} Inactive +
@@ -183,14 +202,23 @@ const DataCenter = () => { {pm.vms.map((vm) => { - const isActive = vm.state && ["ACTIVE", "active"].includes(vm.state); + const isActive = + vm.state && ["ACTIVE", "active"].includes(vm.state); return ( - {vm.vmName || vm.vm_name} + + {vm.vmName || vm.vm_name} + -
+
{vm.state}
@@ -204,23 +232,48 @@ const DataCenter = () => {
- CPU - {(vm.config?.cpu || (vm.confg && vm.confg[1])) || '-'} + + CPU + + + {vm.config?.cpu || + (vm.confg && vm.confg[1]) || + "-"} +
- RAM - {(vm.config?.ram || (vm.confg && vm.confg[2])) || '-'} GB + + RAM + + + {vm.config?.ram || + (vm.confg && vm.confg[2]) || + "-"}{" "} + GB +
- Disk - {(vm.config?.disk || (vm.confg && vm.confg[3])) || '-'} GB + + Disk + + + {vm.config?.disk || + (vm.confg && vm.confg[3]) || + "-"}{" "} + GB +
- {vm.host || vm.hostingPm || (vm.confg && vm.confg[4]) || '-'} + + {vm.host || + vm.hostingPm || + (vm.confg && vm.confg[4]) || + "-"} +
@@ -241,8 +294,6 @@ const DataCenter = () => { ); }; - - return (
From 21b757cf77ca81763b67416cb44413a6aff71419 Mon Sep 17 00:00:00 2001 From: Khaled Elagamy Date: Tue, 19 Aug 2025 05:39:16 +0300 Subject: [PATCH 07/13] Fix small translation in Map --- sge-frontend/src/views/Map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sge-frontend/src/views/Map.js b/sge-frontend/src/views/Map.js index 2b7b4fd..5fb9b2b 100644 --- a/sge-frontend/src/views/Map.js +++ b/sge-frontend/src/views/Map.js @@ -650,7 +650,7 @@ const Map = () => { )} {dc.dataCenterEmissionSources?.length > 0 && (

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

)} From 2d39883b7f58c30c101904b7949b6ab4323757f6 Mon Sep 17 00:00:00 2001 From: k3n9achi Date: Tue, 19 Aug 2025 06:17:37 +0300 Subject: [PATCH 08/13] filtering raw data page --- .../src/redux/actions/mainDataTables/index.js | 12 +- .../src/views/DataSet/MainDataTables.js | 149 ++++++++++++------ 2 files changed, 108 insertions(+), 53 deletions(-) diff --git a/sge-frontend/src/redux/actions/mainDataTables/index.js b/sge-frontend/src/redux/actions/mainDataTables/index.js index 0db497f..e3999b1 100644 --- a/sge-frontend/src/redux/actions/mainDataTables/index.js +++ b/sge-frontend/src/redux/actions/mainDataTables/index.js @@ -1,6 +1,6 @@ import ApplicationService from "../../../services/ApplicationService"; -export const getVMEmissionSummary = () => { +export const getVMEmissionSummary = (datacenterId) => { return async (dispatch) => { try { const response = await ApplicationService.http() @@ -8,8 +8,8 @@ export const getVMEmissionSummary = () => { "/graphql", { query: ` - { - vmEmissionSummary { + query GetVMEmissions($datacenterId: ID) { + vmEmissionSummary(datacenterId: $datacenterId) { vmId vmName vmPower @@ -17,7 +17,6 @@ export const getVMEmissionSummary = () => { totalEmission createdDate physicalMachine - project dataCenter co2 ch4 @@ -25,7 +24,10 @@ export const getVMEmissionSummary = () => { reportGeneratedTime } } - ` + `, + variables: { + datacenterId: datacenterId + } }, { headers: { diff --git a/sge-frontend/src/views/DataSet/MainDataTables.js b/sge-frontend/src/views/DataSet/MainDataTables.js index 88c7b11..88ce4fa 100644 --- a/sge-frontend/src/views/DataSet/MainDataTables.js +++ b/sge-frontend/src/views/DataSet/MainDataTables.js @@ -2,38 +2,63 @@ import React, { useState, useEffect } from "react"; import { MaterialReactTable } from "material-react-table"; import { useDispatch, useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; -import { Card, CardHeader, CardTitle, Alert } from "reactstrap"; +import { Card, CardHeader, CardTitle, Alert, Row, Col, Label } from "reactstrap"; import { getVMEmissionSummary } from "../../redux/actions/mainDataTables/index"; +import { getDataCenters } from "../../redux/actions/dataCenter"; import { editNumbers } from "../../components/edit-numbers"; +import Select from "react-select"; function MainDataTables() { const { t } = useTranslation(); const dispatch = useDispatch(); const mainDataTablesStore = useSelector((state) => state.mainDataTables); + const dataCenterStore = useSelector((state) => state.dataCenter); const [error, setError] = useState(null); + const [selectedDataCenter, setSelectedDataCenter] = useState(null); + const [dataCenterOptions, setDataCenterOptions] = useState([]); + const [loading, setLoading] = useState(false); + // Fetch datacenters on component mount useEffect(() => { - const fetchData = async () => { - try { - setLoading(true); - await dispatch(getVMEmissionSummary()); - } catch (err) { - console.error('Error in MainDataTables:', err); - setError(err.message); - } finally { - setLoading(false); - } - }; - fetchData(); + dispatch(getDataCenters()); }, [dispatch]); - // Debug log for store data + // Update datacenter options when datacenters are loaded + useEffect(() => { + if (dataCenterStore?.dataCenters?.length > 0) { + const options = dataCenterStore.dataCenters.map((dataCenter) => ({ + value: dataCenter.id, + label: dataCenter.dataCenter, + externalId: dataCenter.externalId, + })); + setDataCenterOptions(options); + } + }, [dataCenterStore?.dataCenters]); + + // Fetch VM emission data when datacenter is selected + useEffect(() => { + if (selectedDataCenter?.value) { + const fetchData = async () => { + try { + setLoading(true); + setError(null); + await dispatch(getVMEmissionSummary(selectedDataCenter.value)); + } catch (err) { + console.error('Error in MainDataTables:', err); + setError(err.message); + } finally { + setLoading(false); + } + }; + fetchData(); + } + }, [selectedDataCenter, dispatch]); + + // Debug log for store data useEffect(() => { console.log('Current store data:', mainDataTablesStore); }, [mainDataTablesStore]); - const [loading, setLoading] = useState(true); - const columns = [ { header: t("VM ID"), accessorKey: "vmId", Cell: ({ cell }) => {cell.getValue() || "-"} }, { header: t("VM Name"), accessorKey: "vmName", Cell: ({ cell }) => {cell.getValue() || "-"} }, @@ -42,13 +67,15 @@ function MainDataTables() { { header: t("Total Emission"), accessorKey: "totalEmission", Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"} }, { header: t("Created Date"), accessorKey: "createdDate", Cell: ({ cell }) => ({cell.getValue() ? new Date(cell.getValue()).toLocaleString() : "-"}), sortable: true }, { header: t("Physical Machine"), accessorKey: "physicalMachine", Cell: ({ cell }) => {cell.getValue() || "-"} }, - { header: t("Project"), accessorKey: "project", Cell: ({ cell }) => {cell.getValue() || "-"} }, { header: t("Data Center"), accessorKey: "dataCenter", Cell: ({ cell }) => {cell.getValue() || "-"} }, { header: "CO2", accessorKey: "co2", Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"} }, { header: "CH4", accessorKey: "ch4", Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"} }, { header: "N2O", accessorKey: "n2o", Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"} }, ]; + + + const tableData = mainDataTablesStore?.vmEmissionSummary || []; console.log('VM Emission data:', tableData); @@ -66,37 +93,63 @@ function MainDataTables() { {t("Raw Data")} - + + {/* Datacenter Selection */} +
+ + + +