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); } } 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/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/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 1419cf2..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 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 +127,89 @@ 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 diff --git a/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/dto/VMEmissionSummary.java b/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/dto/VMEmissionSummary.java index c8da404..ab5517a 100644 --- a/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/dto/VMEmissionSummary.java +++ b/sge-backend/src/main/java/com/sgs/graphql/mainDataTable/dto/VMEmissionSummary.java @@ -15,7 +15,7 @@ public class VMEmissionSummary { private Double totalEmission; // Individual record's total emission private LocalDateTime createdDate; // When this specific record was created private String physicalMachine; - private String project; + private String cloudSystem; // From physical machine private String dataCenter; // Individual emission values for this specific record @@ -31,7 +31,7 @@ public class VMEmissionSummary { public VMEmissionSummary(UUID vmId, String vmName, Double vmPower, String vmStatus, Double totalEmission, LocalDateTime createdDate, - String physicalMachine, String project, String dataCenter, + String physicalMachine, String cloudSystem, String dataCenter, Double co2, Double ch4, Double n2o) { this.vmId = vmId; this.vmName = vmName; @@ -40,7 +40,7 @@ public class VMEmissionSummary { this.totalEmission = totalEmission; this.createdDate = createdDate; this.physicalMachine = physicalMachine; - this.project = project; + this.cloudSystem = cloudSystem; this.dataCenter = dataCenter; this.co2 = co2; this.ch4 = ch4; @@ -70,8 +70,8 @@ public class VMEmissionSummary { public String getPhysicalMachine() { return physicalMachine; } public void setPhysicalMachine(String physicalMachine) { this.physicalMachine = physicalMachine; } - public String getProject() { return project; } - public void setProject(String project) { this.project = project; } + public String getCloudSystem() { return cloudSystem; } + public void setCloudSystem(String cloudSystem) { this.cloudSystem = cloudSystem; } public String getDataCenter() { return dataCenter; } public void setDataCenter(String dataCenter) { this.dataCenter = dataCenter; } 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 2fb2246..1bb22c3 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 @@ -55,7 +55,7 @@ public class MainDataTableQueryResolver implements GraphQLQueryResolver { * @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(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 12302f3..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); @@ -805,10 +838,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()); 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! diff --git a/sge-frontend/src/redux/actions/dataCenter/index.js b/sge-frontend/src/redux/actions/dataCenter/index.js index a8df393..dba7253 100644 --- a/sge-frontend/src/redux/actions/dataCenter/index.js +++ b/sge-frontend/src/redux/actions/dataCenter/index.js @@ -45,6 +45,10 @@ export const getDataCenters = () => { name } } + city { + id + name + } emissionScope { id tag @@ -201,6 +205,10 @@ export const createDataCenter = (dataCenterData) => { name } } + city { + id + name + } emissionScope { id tag @@ -241,6 +249,7 @@ export const createDataCenter = (dataCenterData) => { ayposURL: dataCenterData.ayposURL || "", number: parseInt(dataCenterData.number) || 1, areaId: dataCenterData.areaId || null, + cityId: dataCenterData.cityId || null, address: dataCenterData.address || "", latitude: dataCenterData.latitude ? parseFloat(dataCenterData.latitude) @@ -331,6 +340,10 @@ export const updateDataCenter = (id, dataCenterData) => { name } } + city { + id + name + } emissionScope { id tag @@ -372,6 +385,7 @@ export const updateDataCenter = (id, dataCenterData) => { ayposURL: dataCenterData.ayposURL || "", number: parseInt(dataCenterData.number) || 1, areaId: dataCenterData.areaId || null, + cityId: dataCenterData.cityId || null, address: dataCenterData.address || "", latitude: dataCenterData.latitude ? parseFloat(dataCenterData.latitude) diff --git a/sge-frontend/src/redux/actions/mainDataTables/index.js b/sge-frontend/src/redux/actions/mainDataTables/index.js index 0db497f..8f34bc4 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,7 @@ export const getVMEmissionSummary = () => { totalEmission createdDate physicalMachine - project + cloudSystem dataCenter co2 ch4 @@ -25,7 +25,10 @@ export const getVMEmissionSummary = () => { reportGeneratedTime } } - ` + `, + variables: { + datacenterId: datacenterId + } }, { headers: { diff --git a/sge-frontend/src/views/DataCenter.js b/sge-frontend/src/views/DataCenter.js index b1b9d60..c867941 100644 --- a/sge-frontend/src/views/DataCenter.js +++ b/sge-frontend/src/views/DataCenter.js @@ -18,17 +18,16 @@ 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 || []; + return pms; }; // Table columns following your pattern const initialColumns = [ { - name: "Number", - selector: (row) => row.number, + name: "External ID", + selector: (row) => row.externalId, sortable: true, minWidth: "100px", }, @@ -38,28 +37,33 @@ const DataCenter = () => { sortable: true, minWidth: "200px", }, - // Projects - { - name: "Projects", - selector: (row) => (row.projects || []).length, - sortable: true, - minWidth: "200px", - cell: (row) => ( -
- {(row.projects || []).length > 0 ? ( -
- {row.projects.map((project, index) => ( -
0 ? 'mt-1' : ''}`}> - {project.name} -
- ))} -
- ) : ( - - - )} -
- ), - }, + // Projects - Based on API response, this field might not exist or be structured differently + // { + // name: "Projects", + // selector: (row) => row.projects?.length || 0, + // sortable: true, + // minWidth: "200px", + // cell: (row) => ( + //
+ // {row.projects && row.projects.length > 0 ? ( + //
+ // {row.projects.map((project, index) => ( + //
0 ? "mt-1" : "" + // }`} + // > + // {project.name} + //
+ // ))} + //
+ // ) : ( + // - + // )} + //
+ // ), + // }, // Physical Machines { name: "Physical Machines", @@ -80,27 +84,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 +125,9 @@ const DataCenter = () => {
{vms.active} Active - {vms.total - vms.active} Inactive + + {vms.total - vms.active} Inactive +
@@ -183,14 +201,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 +231,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 +293,6 @@ const DataCenter = () => { ); }; - - return (
diff --git a/sge-frontend/src/views/DataCenterManagement.js b/sge-frontend/src/views/DataCenterManagement.js index 5db4a64..964684f 100644 --- a/sge-frontend/src/views/DataCenterManagement.js +++ b/sge-frontend/src/views/DataCenterManagement.js @@ -412,11 +412,11 @@ const DataCenterManagement = () => { number: row.number?.toString(), address: row.address || "", areaId: row.area?.id || null, - cityId: null, + cityId: row.city?.id || null, latitude: row.latitude, longitude: row.longitude, ayposURL: row.ayposURL || "", - city: row.city || "", + city: row.city?.name || "", emissionScopeId: row.emissionScope?.id || null, sectorId: row.sector?.id || null, subSectorId: row.subSector?.id || null, @@ -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 @@ -594,6 +611,7 @@ const DataCenterManagement = () => { number: parseInt(selectedDataCenter.number || "1"), address: selectedDataCenter.address, areaId: selectedDataCenter.areaId, + cityId: selectedDataCenter.cityId, latitude: selectedDataCenter.latitude ? parseFloat(selectedDataCenter.latitude) : null, @@ -1029,189 +1047,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 + + )} +
+ +
+ +
+
) )} - +
+ +
diff --git a/sge-frontend/src/views/DataSet/MainDataTables.js b/sge-frontend/src/views/DataSet/MainDataTables.js index 88c7b11..e772f2c 100644 --- a/sge-frontend/src/views/DataSet/MainDataTables.js +++ b/sge-frontend/src/views/DataSet/MainDataTables.js @@ -1,56 +1,157 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } 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(() => { - console.log('Current store data:', mainDataTablesStore); - }, [mainDataTablesStore]); + if (dataCenterStore?.dataCenters?.length > 0) { + const options = dataCenterStore.dataCenters.map((dataCenter) => ({ + value: dataCenter.id, + label: dataCenter.dataCenter, + externalId: dataCenter.externalId, + })); + setDataCenterOptions(options); + } + }, [dataCenterStore?.dataCenters]); - const [loading, setLoading] = useState(true); + // 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]); - const columns = [ - { header: t("VM ID"), accessorKey: "vmId", Cell: ({ cell }) => {cell.getValue() || "-"} }, - { header: t("VM Name"), accessorKey: "vmName", Cell: ({ cell }) => {cell.getValue() || "-"} }, - { header: t("VM Power"), accessorKey: "vmPower", Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"} }, - { header: t("VM Status"), accessorKey: "vmStatus", Cell: ({ cell }) => {cell.getValue() || "-"} }, - { 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()) || "-"} }, - ]; + // Memoize columns to prevent re-renders + const columns = useMemo( + () => [ + { + header: t("VM ID"), + accessorKey: "vmId", + size: 150, + Cell: ({ cell }) => {cell.getValue() || "-"}, + }, + { + header: t("VM Name"), + accessorKey: "vmName", + size: 200, + Cell: ({ cell }) => {cell.getValue() || "-"}, + }, + { + header: t("VM Power"), + accessorKey: "vmPower", + size: 120, + Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"}, + }, + { + header: t("VM Status"), + accessorKey: "vmStatus", + size: 100, + Cell: ({ cell }) => {cell.getValue() || "-"}, + }, + { + header: t("Total Emission"), + accessorKey: "totalEmission", + size: 150, + Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"}, + }, + { + header: t("Physical Machine"), + accessorKey: "physicalMachine", + size: 150, + Cell: ({ cell }) => {cell.getValue() || "-"}, + }, + { + header: t("Cloud System"), + accessorKey: "cloudSystem", + size: 150, + Cell: ({ cell }) => {cell.getValue() || "-"}, + }, + { + header: t("Data Center"), + accessorKey: "dataCenter", + size: 150, + Cell: ({ cell }) => {cell.getValue() || "-"}, + }, + { + header: "CO2", + accessorKey: "co2", + size: 100, + Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"}, + }, + { + header: "CH4", + accessorKey: "ch4", + size: 100, + Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"}, + }, + { + header: "N2O", + accessorKey: "n2o", + size: 100, + Cell: ({ cell }) => {editNumbers(cell.getValue()) || "-"}, + }, + { + header: t("Created Date"), + accessorKey: "createdDate", + size: 180, + Cell: ({ cell }) => ( + + {cell.getValue() ? new Date(cell.getValue()).toLocaleString() : "-"} + + ), + sortable: true, + }, + ], + [t] + ); - const tableData = mainDataTablesStore?.vmEmissionSummary || []; - console.log('VM Emission data:', tableData); + // Memoize table data to prevent unnecessary re-renders + const tableData = useMemo(() => { + const data = mainDataTablesStore?.vmEmissionSummary || []; + console.log("VM Emission data count:", data.length); + return data; + }, [mainDataTablesStore?.vmEmissionSummary]); if (error) { return ( @@ -65,38 +166,108 @@ function MainDataTables() { {t("Raw Data")} + {tableData.length > 0 && ( + + {tableData.length} records loaded + + )} - + + {/* Datacenter Selection */} +
+ + + +