From d7a31e95ebbe5af257166a3e5aa158acb5fd2289 Mon Sep 17 00:00:00 2001 From: Aris Mamo Date: Mon, 16 Dec 2024 18:53:28 +0100 Subject: [PATCH] - Added new widget map - Finished configuration logic - Finished rendering --- .../cruise/controller/DataController.java | 2 +- .../cruise/controller/QueryController.java | 3 +- .../controller/StructureController.java | 48 +- .../cruise/controller/UserController.java | 97 +- .../java/it/enea/cruise/dto/CommunityDTO.java | 5 +- .../it/enea/cruise/model/user/Community.java | 17 +- .../model/user/LocalQueryParameter.java | 77 +- .../cruise/model/user/ParametricQuery.java | 1 + .../enea/cruise/repositories/DTOManager.java | 8 +- .../user/LocalQueryParameterRepository.java | 3 +- .../ParametricQueryManager.java | 2 - .../enea/cruise/service/shared/UserUtil.java | 24 + .../src/main/resources/application.properties | 2 +- lec/README.md | 6 + lec/angular.json | 1 + lec/package.json | 2 + lec/src/app/app.module.ts | 6 +- .../datasource-configuration.component.html | 63 +- .../datasource-configuration.component.ts | 91 +- ...rce-configuration-parameter.component.html | 107 ++- ...ource-configuration-parameter.component.ts | 6 +- .../components/header/header.component.html | 2 +- .../app/components/menu/menu.component.html | 4 +- lec/src/app/components/menu/menu.component.ts | 3 +- .../app/components/menubardemo/menubardemo.ts | 2 +- .../page-editor/page-editor.component.css | 12 +- .../page-editor/page-editor.component.html | 287 +++--- .../page-editor/page-editor.component.ts | 25 +- .../page-properties-editor.component.ts | 18 +- .../app/components/page/page.component.html | 1 + .../app/components/pages/pages.component.ts | 9 +- .../interceptors/http-request.interceptor.ts | 1 - lec/src/app/models/Community.ts | 11 + lec/src/app/models/enea/LocalUserDTO.ts | 2 + lec/src/app/models/enea/ParametricQuery.ts | 1 + .../app/services/community.service.spec.ts | 16 + lec/src/app/services/community.service.ts | 17 + lec/src/app/services/data.service.ts | 14 +- .../parameter-resolvers/IParameterResolver.ts | 1 - lec/src/app/services/user.service.ts | 24 +- lec/src/app/services/widget.service.ts | 2 + .../chart-widget-configuration.component.html | 188 ++-- .../components/chart-widget.component.html | 174 ++-- .../components/chart-widget.component.ts | 888 +++++++++--------- .../image-widget-configuration.component.ts | 5 +- .../app/widgets/image/models/ImageWidget.ts | 4 +- .../map-widget-configuration.component.css | 0 .../map-widget-configuration.component.html | 82 ++ ...map-widget-configuration.component.spec.ts | 25 + .../map-widget-configuration.component.ts | 75 ++ .../map-widget/map-widget.component.css | 31 + .../map-widget/map-widget.component.html | 4 + .../map-widget/map-widget.component.spec.ts | 25 + .../map-widget/map-widget.component.ts | 202 ++++ .../map/components/models/map-widget.ts | 26 + lec/src/assets/i18n/en.json | 76 +- lec/src/assets/i18n/it.json | 48 +- .../configuration/StartUpRunner.java | 2 +- .../lecservice/models/dto/CommunityDTO.java | 6 +- .../lecservice/security/dto/UserInfo.java | 3 +- .../lecservice/services/RegisterService.java | 2 +- .../java/enea/lecservice/widgets/Widget.java | 7 +- .../lecservice/widgets/map/MapWidget.java | 30 + .../src/main/resources/application.properties | 2 +- 64 files changed, 1738 insertions(+), 1190 deletions(-) create mode 100644 gecoregistration/src/main/java/it/enea/cruise/service/shared/UserUtil.java create mode 100644 lec/src/app/services/community.service.spec.ts create mode 100644 lec/src/app/services/community.service.ts create mode 100644 lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.css create mode 100644 lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.html create mode 100644 lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.spec.ts create mode 100644 lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.ts create mode 100644 lec/src/app/widgets/map/components/map-widget/map-widget.component.css create mode 100644 lec/src/app/widgets/map/components/map-widget/map-widget.component.html create mode 100644 lec/src/app/widgets/map/components/map-widget/map-widget.component.spec.ts create mode 100644 lec/src/app/widgets/map/components/map-widget/map-widget.component.ts create mode 100644 lec/src/app/widgets/map/components/models/map-widget.ts create mode 100644 lecservice/src/main/java/enea/lecservice/widgets/map/MapWidget.java diff --git a/gecoregistration/src/main/java/it/enea/cruise/controller/DataController.java b/gecoregistration/src/main/java/it/enea/cruise/controller/DataController.java index 0912847..2b35e23 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/controller/DataController.java +++ b/gecoregistration/src/main/java/it/enea/cruise/controller/DataController.java @@ -185,7 +185,7 @@ public class DataController { list = resultList.get(key); for (String columnName : list) { HashMap metadata = new HashMap(); - tmdata = lqpInterface.findAllByName(columnName); + tmdata = lqpInterface.findAllByNameAndUseInFrontEndTrue(columnName); for (LocalQueryParameter lqpTemp : tmdata) { if (lqpTemp.getName() == null || "".equals(lqpTemp.getName().trim())) continue; metadata.put("name", lqpTemp.getName()); diff --git a/gecoregistration/src/main/java/it/enea/cruise/controller/QueryController.java b/gecoregistration/src/main/java/it/enea/cruise/controller/QueryController.java index a86fc8b..5a3b619 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/controller/QueryController.java +++ b/gecoregistration/src/main/java/it/enea/cruise/controller/QueryController.java @@ -12,7 +12,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; @@ -97,7 +96,7 @@ public class QueryController { lpq.setDescription(pDTO.getDescription()); lpq.setExample(pDTO.getExample()); lpq.setFamilytype(pDTO.getFamilyType()); - lpq.setInout(pDTO.getInOut()); + lpq.setUseInFrontEnd("1".equalsIgnoreCase(pDTO.getInOut())); lpq.setParamId(pDTO.getParameterId()); lpq.setType(pDTO.getType()); lpq.setUnitofmeasure(pDTO.getUnitOfMeasure()); diff --git a/gecoregistration/src/main/java/it/enea/cruise/controller/StructureController.java b/gecoregistration/src/main/java/it/enea/cruise/controller/StructureController.java index 44990de..a104f67 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/controller/StructureController.java +++ b/gecoregistration/src/main/java/it/enea/cruise/controller/StructureController.java @@ -1,23 +1,23 @@ package it.enea.cruise.controller; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import it.enea.cruise.dto.*; +import it.enea.cruise.model.structure.ForeignKey; +import it.enea.cruise.model.structure.VwTableMetadata; +import it.enea.cruise.model.user.User; +import it.enea.cruise.repositories.DTOManager; +import it.enea.cruise.repositories.user.ForeignKeyRepository; +import it.enea.cruise.repositories.user.TableMetadataRepository; +import it.enea.cruise.service.shared.UserUtil; import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.Table; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; - +import lombok.extern.log4j.Log4j2; import org.reflections.Reflections; -import static org.reflections.scanners.Scanners.*; - import org.springframework.beans.PropertyAccessor; import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -32,22 +32,10 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.lang.reflect.Method; +import java.util.*; -import it.enea.cruise.dto.FieldDTO; -import it.enea.cruise.dto.FormDTO; -import it.enea.cruise.dto.MetadataDTO; -import it.enea.cruise.dto.TableMetadataDTO; -import it.enea.cruise.dto.DescriptionDTO; -import it.enea.cruise.model.structure.ForeignKey; -import it.enea.cruise.dto.TableCommentDTO; -import it.enea.cruise.model.structure.VwTableMetadata; -import it.enea.cruise.model.user.User; -import it.enea.cruise.repositories.user.ForeignKeyRepository; -import it.enea.cruise.repositories.user.TableMetadataRepository; -import it.enea.cruise.repositories.DTOManager; -import lombok.extern.log4j.Log4j2; +import static org.reflections.scanners.Scanners.TypesAnnotated; @Controller @Log4j2 @@ -70,10 +58,8 @@ public class StructureController { @Autowired ObjectMapper objectMapper; - - @Autowired - UserController userController; - + + private UserUtil userUtil; private TableMetadataDTO tDTO; @@ -250,7 +236,7 @@ public class StructureController { log.info("Body: " + body); - User user = userController.getLocalUserFromToken(token); + User user = userUtil.getLocalUserFromToken(token); if(user == null) { new ResponseEntity("User not valid", HttpStatus.FORBIDDEN); diff --git a/gecoregistration/src/main/java/it/enea/cruise/controller/UserController.java b/gecoregistration/src/main/java/it/enea/cruise/controller/UserController.java index df15ce9..d199aa4 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/controller/UserController.java +++ b/gecoregistration/src/main/java/it/enea/cruise/controller/UserController.java @@ -1,64 +1,39 @@ package it.enea.cruise.controller; -import java.io.IOException; -import java.sql.Date; -import java.util.ArrayList; -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import freemarker.template.TemplateException; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.mail.MessagingException; - -import ch.qos.logback.core.util.StringCollectionUtil; -import it.enea.scp.idp.sdk.model.UserDto; -import it.enea.cruise.service.idp.IdpProvider; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import org.json.JSONObject; - -import freemarker.template.TemplateException; import it.enea.cruise.dto.CommunityDTO; import it.enea.cruise.dto.LocalUserDTO; -import it.enea.cruise.model.user.Community; -import it.enea.cruise.model.user.Device; -import it.enea.cruise.model.user.Role; -import it.enea.cruise.model.user.User; -import it.enea.cruise.model.user.UserCommunity; -import it.enea.cruise.model.user.UserRole; import it.enea.cruise.dto.UserToken; -import it.enea.cruise.repositories.user.TableMetadataRepository; -import it.enea.cruise.repositories.user.CommunityRepository; -import it.enea.cruise.repositories.user.UserRepository; +import it.enea.cruise.model.user.*; import it.enea.cruise.repositories.DTOManager; -import it.enea.cruise.repositories.user.RoleRepository; -import it.enea.cruise.repositories.user.UserCommunityRepository; -import it.enea.cruise.repositories.user.UserRoleRepository; +import it.enea.cruise.repositories.user.*; +import it.enea.cruise.service.idp.IdpProvider; +import it.enea.scp.idp.sdk.model.UserDto; +import jakarta.mail.MessagingException; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.sql.Date; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** @@ -155,16 +130,16 @@ public class UserController { /** * Returns the list of the users in the DB This method will be removed soon - * because the lackness of secutiry. In fact, it does not evaluate the role of + * because the lack of security. In fact, it does not evaluate the role of * the requester and does not select the community the users belong to. * * @param: null * @exception: Return an Internal Server error in case of exception */ - @GetMapping("/community/listAllCommunities") + @GetMapping("/user/community/listAllCommunities") public ResponseEntity> getAllCommunities() { - List communities = new ArrayList(); + List communities = new ArrayList<>(); try { List communitiesTemp = communityInterface.findAll(); for (Community c : communitiesTemp) { @@ -172,11 +147,9 @@ public class UserController { communities.add(cDTO); } } catch (Exception e) { - return new ResponseEntity(e.getLocalizedMessage(), - // headers, - HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity(e.getLocalizedMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } - return new ResponseEntity>(communities, HttpStatus.OK); + return new ResponseEntity<>(communities, HttpStatus.OK); } @@ -287,7 +260,7 @@ public class UserController { @Parameter(name = "email", example = "", required = true) @RequestParam("email") String email) { UserDto user = idpClientManager.getUserByEmail(email); - return new ResponseEntity(user, + return new ResponseEntity<>(user, // headers, HttpStatus.CREATED); } @@ -409,7 +382,7 @@ public class UserController { user.setSubject(uDto.getUuid()); // Inserisco le comunita for (CommunityDTO cDto : user.getUsercommunities()) { - Community c = communityInterface.findById(cDto.getCommunityid()).get(); + Community c = communityInterface.findById(Long.valueOf(cDto.getCommunityid())).get(); UserCommunity uc1 = new UserCommunity(); uc1.setCommunity(c); uc1.setUser(tmpUser); @@ -1106,7 +1079,7 @@ public class UserController { } for (CommunityDTO cDto : userDTO.getUsercommunities()) { - Community c = communityInterface.findById(cDto.getCommunityid()).get(); + Community c = communityInterface.findById(Long.valueOf(cDto.getCommunityid())).get(); if (tmpUser.getCommunities() == null || !tmpUser.getCommunities().contains(c)) { UserCommunity uc1 = new UserCommunity(); uc1.setCommunity(c); @@ -1247,18 +1220,6 @@ public class UserController { } - public User getLocalUserFromToken(String token) { - User user = new User(); - Boolean isValid = idpClientManager.isValid(token); - - if (isValid) { - User userdto = idpClientManager.getUserByToken(token); - - user = userInterface.findBySubject(userdto.getUuid()); - } - return user; - } - private Boolean checkIsJWTToken(String token) { String[] chunks = token.split("\\."); @@ -1269,6 +1230,4 @@ public class UserController { return false; } - - } diff --git a/gecoregistration/src/main/java/it/enea/cruise/dto/CommunityDTO.java b/gecoregistration/src/main/java/it/enea/cruise/dto/CommunityDTO.java index 53e7246..1a857a3 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/dto/CommunityDTO.java +++ b/gecoregistration/src/main/java/it/enea/cruise/dto/CommunityDTO.java @@ -16,9 +16,10 @@ import lombok.ToString; @JsonInclude(JsonInclude.Include.NON_NULL) public class CommunityDTO { - private Long communityid; + private String communityid; private String communityName; private String description; private String wallet; - + private Double georeferencex; + private Double georeferencey; } diff --git a/gecoregistration/src/main/java/it/enea/cruise/model/user/Community.java b/gecoregistration/src/main/java/it/enea/cruise/model/user/Community.java index 7fea9c4..9f3c670 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/model/user/Community.java +++ b/gecoregistration/src/main/java/it/enea/cruise/model/user/Community.java @@ -1,9 +1,6 @@ package it.enea.cruise.model.user; -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.annotation.*; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.Fetch; @@ -47,6 +44,18 @@ public class Community { @JsonInclude(JsonInclude.Include.NON_NULL) private String description; + + @JsonIgnore + private String georeferenceformat; + + @JsonProperty("georeferencex") + private Double georeferencex; + + @JsonProperty("georeferencey") + private Double georeferencey; + + @JsonProperty("georeferencez") + private Double georeferencez; /* * Questo crea il legame con la tabella users_communities. Essendo diff --git a/gecoregistration/src/main/java/it/enea/cruise/model/user/LocalQueryParameter.java b/gecoregistration/src/main/java/it/enea/cruise/model/user/LocalQueryParameter.java index a621e26..349147e 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/model/user/LocalQueryParameter.java +++ b/gecoregistration/src/main/java/it/enea/cruise/model/user/LocalQueryParameter.java @@ -4,9 +4,14 @@ package it.enea.cruise.model.user; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + import java.util.List; +@Getter +@Setter @Entity @Table (name= "localqueryparameters", schema = "cruise") public class LocalQueryParameter { @@ -26,7 +31,7 @@ public class LocalQueryParameter { private String familytype; - private String inout; + private boolean useInFrontEnd; private String unitofmeasure; @@ -34,64 +39,6 @@ public class LocalQueryParameter { //@JsonBackReference(value = "parametricquery-parameter") @JsonIgnoreProperties("parameters") private List parametricquery; - - public Long getParamId() { - return paramId; - } - - public void setParamId(Long paramId) { - this.paramId = paramId; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getExample() { - return example; - } - - public void setExample(String example) { - this.example = example; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getFamilytype() { - return familytype; - } - - public void setFamilytype(String familytype) { - this.familytype = familytype; - } - - public String getInout() { - return inout; - } - - public void setInout(String inout) { - this.inout = inout; - } - - public String getUnitofmeasure() { - return unitofmeasure; - } - - public void setUnitofmeasure(String unitofmeasure) { - this.unitofmeasure = unitofmeasure; - } - - public String getDescription(String language) { @@ -100,7 +47,7 @@ public class LocalQueryParameter { if (language.contains("it")) { return (result[0] + " | " + result[1]); } else { - if (result.length > 2) { + if (result.length > 3) { return (result[2] + " | " + result[3]); } else return (result[0] + " | " + result[1]); @@ -108,14 +55,4 @@ public class LocalQueryParameter { } else return ""; } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - } diff --git a/gecoregistration/src/main/java/it/enea/cruise/model/user/ParametricQuery.java b/gecoregistration/src/main/java/it/enea/cruise/model/user/ParametricQuery.java index 87c2c15..421bf17 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/model/user/ParametricQuery.java +++ b/gecoregistration/src/main/java/it/enea/cruise/model/user/ParametricQuery.java @@ -24,6 +24,7 @@ public class ParametricQuery { @Id @Column(name = "queryid") + @JsonProperty("queryid") @GeneratedValue(strategy = GenerationType.AUTO) private Long id; diff --git a/gecoregistration/src/main/java/it/enea/cruise/repositories/DTOManager.java b/gecoregistration/src/main/java/it/enea/cruise/repositories/DTOManager.java index 74fe390..6f7e86e 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/repositories/DTOManager.java +++ b/gecoregistration/src/main/java/it/enea/cruise/repositories/DTOManager.java @@ -53,10 +53,12 @@ public class DTOManager { public CommunityDTO communityDTO(UserCommunity c) { CommunityDTO communityDTO = new CommunityDTO(); log.info("preparing CommunityTO for export"); - communityDTO.setCommunityid(c.getCommunity().getId()); + communityDTO.setCommunityid(c.getCommunity().getId().toString()); communityDTO.setDescription(c.getCommunity().getDescription()); communityDTO.setWallet(c.getWallet() != null ? c.getWallet().toString() : "noWallet"); communityDTO.setCommunityName(c.getCommunity().getCommunityName()); + communityDTO.setGeoreferencex(c.getCommunity().getGeoreferencex()); + communityDTO.setGeoreferencey(c.getCommunity().getGeoreferencey()); return communityDTO; } @@ -64,10 +66,12 @@ public class DTOManager { public CommunityDTO communityDTO(Community c) { CommunityDTO communityDTO = new CommunityDTO(); log.info("preparing CommunityTO for export"); - communityDTO.setCommunityid(c.getId()); + communityDTO.setCommunityid(c.getId().toString()); communityDTO.setDescription(c.getDescription()); communityDTO.setWallet("noWallet"); communityDTO.setCommunityName(c.getCommunityName()); + communityDTO.setGeoreferencex(c.getGeoreferencex()); + communityDTO.setGeoreferencey(c.getGeoreferencey()); return communityDTO; } diff --git a/gecoregistration/src/main/java/it/enea/cruise/repositories/user/LocalQueryParameterRepository.java b/gecoregistration/src/main/java/it/enea/cruise/repositories/user/LocalQueryParameterRepository.java index bbe3546..dcf401b 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/repositories/user/LocalQueryParameterRepository.java +++ b/gecoregistration/src/main/java/it/enea/cruise/repositories/user/LocalQueryParameterRepository.java @@ -8,6 +8,7 @@ import it.enea.cruise.model.user.LocalQueryParameter; public interface LocalQueryParameterRepository extends JpaRepository{ - List findAllByName(String paramName); + List findAllByName(String paramName); + List findAllByNameAndUseInFrontEndTrue(String paramName); } diff --git a/gecoregistration/src/main/java/it/enea/cruise/service/parametricQuery/ParametricQueryManager.java b/gecoregistration/src/main/java/it/enea/cruise/service/parametricQuery/ParametricQueryManager.java index edec73c..d3e0ffe 100644 --- a/gecoregistration/src/main/java/it/enea/cruise/service/parametricQuery/ParametricQueryManager.java +++ b/gecoregistration/src/main/java/it/enea/cruise/service/parametricQuery/ParametricQueryManager.java @@ -91,12 +91,10 @@ public class ParametricQueryManager { metadata.put("type", lqpTemp.getType()); metadata.put("example", lqpTemp.getExample()); metadata.put("unitOfMeasure", lqpTemp.getUnitofmeasure()); - } resultWithMetadata.setMetadata(metadata); } - } pq.setParameterMetadata(parameterMetadata); diff --git a/gecoregistration/src/main/java/it/enea/cruise/service/shared/UserUtil.java b/gecoregistration/src/main/java/it/enea/cruise/service/shared/UserUtil.java new file mode 100644 index 0000000..0ca812b --- /dev/null +++ b/gecoregistration/src/main/java/it/enea/cruise/service/shared/UserUtil.java @@ -0,0 +1,24 @@ +package it.enea.cruise.service.shared; + +import it.enea.cruise.model.user.User; +import it.enea.cruise.repositories.user.UserRepository; +import it.enea.cruise.service.idp.IdpProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserUtil { + private final IdpProvider idpClientManager; + private final UserRepository userRepository; + public User getLocalUserFromToken(String token) { + User user = new User(); + Boolean isValid = idpClientManager.isValid(token); + + if (isValid) { + User userdto = idpClientManager.getUserByToken(token); + user = userRepository.findBySubject(userdto.getUuid()); + } + return user; + } +} diff --git a/gecoregistration/src/main/resources/application.properties b/gecoregistration/src/main/resources/application.properties index 64425b2..a358e9e 100644 --- a/gecoregistration/src/main/resources/application.properties +++ b/gecoregistration/src/main/resources/application.properties @@ -76,4 +76,4 @@ es.password=${ES_PSWD} es.host=${ES_HOST} es.port=${ES_PORT} # Data query -data.start_from_year=3 +data.start_from_year=2 diff --git a/lec/README.md b/lec/README.md index ce92325..6ae4696 100644 --- a/lec/README.md +++ b/lec/README.md @@ -2,6 +2,12 @@ This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.1.1. +## Install Leaflet +Run `npm install -D @types/leaflet` if you encounter "type" error when running the app + +## Pages view url +http://localhost:4200/pages + ## Development server Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. diff --git a/lec/angular.json b/lec/angular.json index 826725e..4daa1de 100644 --- a/lec/angular.json +++ b/lec/angular.json @@ -38,6 +38,7 @@ "node_modules/primeflex/primeflex.min.css", "node_modules/quill/dist/quill.core.css", "node_modules/quill/dist/quill.snow.css", + "node_modules/leaflet/dist/leaflet.css", "src/styles.css" ], "scripts": [ diff --git a/lec/package.json b/lec/package.json index bf23240..41804ff 100644 --- a/lec/package.json +++ b/lec/package.json @@ -29,6 +29,7 @@ "angular-oauth2-oidc": "^17.0.2", "chart.js": "^4.4.4", "chartjs-plugin-zoom": "^2.0.1", + "leaflet": "^1.9.4", "material-icons": "^1.13.12", "moment": "^2.30.1", "ng2-charts": "^6.0.1", @@ -46,6 +47,7 @@ "@angular-devkit/build-angular": "~18.2.6", "@angular/compiler-cli": "~18.2.6", "@types/jasmine": "~5.1.4", + "@types/leaflet": "^1.9.14", "@types/node": "^22.7.4", "jasmine-core": "~5.3.0", "karma": "~6.4.4", diff --git a/lec/src/app/app.module.ts b/lec/src/app/app.module.ts index 9cc4c45..dc0cdd2 100644 --- a/lec/src/app/app.module.ts +++ b/lec/src/app/app.module.ts @@ -89,6 +89,8 @@ import {MatButtonModule} from "@angular/material/button"; import {OAuthModule, OAuthStorage} from "angular-oauth2-oidc"; import { LoginOidComponent } from './components/login-oid/login-oid.component'; import { LogoutOidComponent } from './components/logout-oid/logout-oid.component'; +import { MapWidgetComponent } from './widgets/map/components/map-widget/map-widget.component'; +import { MapWidgetConfigurationComponent } from './widgets/map/components/map-widget-configuration/map-widget-configuration.component'; @@ -139,7 +141,9 @@ export function storageFactory() : OAuthStorage { AccountActivateComponent, MenuComponent, LoginOidComponent, - LogoutOidComponent + LogoutOidComponent, + MapWidgetComponent, + MapWidgetConfigurationComponent ], exports: [ RawHtmlPipe, diff --git a/lec/src/app/components/datasource-configuration/datasource-configuration.component.html b/lec/src/app/components/datasource-configuration/datasource-configuration.component.html index eff8f94..ebae4ea 100644 --- a/lec/src/app/components/datasource-configuration/datasource-configuration.component.html +++ b/lec/src/app/components/datasource-configuration/datasource-configuration.component.html @@ -1,55 +1,54 @@
- -
- + +
-
{{datasourceConfiguration.parametricQuery!.name}}
+
{{ datasourceConfiguration.parametricQuery!.name }}
-
{{query?.name}}
+
{{ query?.name }}
-
- + type="button" + > +
-
+ -
- -
- -
-
- +
+
-
diff --git a/lec/src/app/components/datasource-configuration/datasource-configuration.component.ts b/lec/src/app/components/datasource-configuration/datasource-configuration.component.ts index 899f740..ec36e8c 100644 --- a/lec/src/app/components/datasource-configuration/datasource-configuration.component.ts +++ b/lec/src/app/components/datasource-configuration/datasource-configuration.component.ts @@ -1,67 +1,66 @@ -import { Component, Input, OnInit } from '@angular/core'; +import {Component, Input, OnInit} from '@angular/core'; import {LocalQueryParameter, ParametricQuery} from 'src/app/models/enea/ParametricQuery'; -import { Parameter } from 'src/app/models/Parameter'; -import { DatasourceConfiguration } from 'src/app/widgets/Widget'; -import { DataService } from 'src/app/services/data.service'; -import { UtilsService } from 'src/app/services/utils.service'; +import {Parameter} from 'src/app/models/Parameter'; +import {DatasourceConfiguration} from 'src/app/widgets/Widget'; +import {DataService} from 'src/app/services/data.service'; +import {UtilsService} from 'src/app/services/utils.service'; import {ParameterResolverService} from "../../services/parameter-resolvers/parameter-resolver.service"; @Component({ - selector: 'app-datasource-configuration', - templateUrl: './datasource-configuration.component.html', - styleUrls: ['./datasource-configuration.component.css'] + selector: 'app-datasource-configuration', + templateUrl: './datasource-configuration.component.html', + styleUrls: ['./datasource-configuration.component.css'] }) export class DatasourceConfigurationComponent implements OnInit { - @Input() datasourceConfiguration: DatasourceConfiguration; + @Input() datasourceConfiguration: DatasourceConfiguration; - @Input() parametricQueries: ParametricQuery[] = []; - @Input() showSelectDatasourceLabel: boolean = true; + @Input() parametricQueries: ParametricQuery[] = []; + @Input() showSelectDatasourceLabel: boolean = true; - debug: Boolean = false; + debug: Boolean = false; - selectedQuery : ParametricQuery = new ParametricQuery(); + selectedQuery: ParametricQuery = new ParametricQuery(); - constructor( - private dataService: DataService, - private utils: UtilsService, + constructor( + private dataService: DataService, + private utils: UtilsService, private parameterResolver: ParameterResolverService) { this.datasourceConfiguration = new DatasourceConfiguration(); - } - - ngOnInit(): void { - if (!this.datasourceConfiguration) - this.datasourceConfiguration = new DatasourceConfiguration(); - if (this.parametricQueries.length === 0) - this.loadParametricQueries(); - } + } - loadParametricQueries() { - this.dataService.getListOfFunctions().subscribe( - parametricQueries => this.parametricQueries = parametricQueries, - error => this.utils.showErrorMessage(error) - ); - } + ngOnInit(): void { + if (!this.datasourceConfiguration) + this.datasourceConfiguration = new DatasourceConfiguration(); + if (this.parametricQueries.length === 0) + this.loadParametricQueries(); + } - updateParametricQueries(){ - this.parametricQueries = []; - this.loadParametricQueries(); - } + loadParametricQueries() { + this.dataService.getListOfFunctions().subscribe( + parametricQueries => this.parametricQueries = parametricQueries, + error => this.utils.showErrorMessage(error) + ); + } - resetParameters() { - if (this.datasourceConfiguration) { - this.datasourceConfiguration.parameters = []; - if (this.datasourceConfiguration.parametricQuery && this.datasourceConfiguration.parametricQuery.parameters) { - this.datasourceConfiguration.parameters = this.datasourceConfiguration.parametricQuery.parameters.map(o => new Parameter(o.name || '', '', undefined)); - if (this.debug) this.utils.showErrorMessage("DatasourceConfiguration " + JSON.stringify(this.datasourceConfiguration)); + updateParametricQueries() { + this.parametricQueries = []; + this.loadParametricQueries(); + } - } + resetParameters() { + if (this.datasourceConfiguration) { + this.datasourceConfiguration.parameters = []; + if (this.datasourceConfiguration.parametricQuery && this.datasourceConfiguration.parametricQuery.parameters) { + this.datasourceConfiguration.parameters = this.datasourceConfiguration.parametricQuery.parameters.filter(o => o.useInFrontEnd).map(o => new Parameter(o.name || '', '', undefined)); + if (this.debug) this.utils.showErrorMessage("DatasourceConfiguration " + JSON.stringify(this.datasourceConfiguration)); + } + } } - } - updateQuery(query?: ParametricQuery){ - this.datasourceConfiguration!.parametricQuery = query; - this.resetParameters(); - } + updateQuery(query?: ParametricQuery) { + this.datasourceConfiguration!.parametricQuery = query; + this.resetParameters(); + } } diff --git a/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.html b/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.html index fab5b26..212bee5 100644 --- a/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.html +++ b/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.html @@ -1,5 +1,4 @@ - -
+
-
{{query?.description}}
+
{{ query?.description }}
@@ -23,61 +22,61 @@
+ filterBy="description" + optionLabel="description" + optionValue="value" + placeholder="{{ 'DATASOURCE_CONFIGURATION_PARAMETER.SELECT_AN_INTERVAL' | translate }}" + [options]="intervals" + [filter]="true" + [showClear]="false" + [(ngModel)]="parameter!.value" + >
-
+
- + + filterBy="description" + optionLabel="description" + optionValue="value" + placeholder="{{ 'DATASOURCE_CONFIGURATION_PARAMETER.SELECT_AN_INTERVAL' | translate }}" + [options]="intervals" + [filter]="true" + [showClear]="false" + [(ngModel)]="parameter!.value" + > +
+
+ + +
+
+ + +
+
+ +
+
+ +
-
- - -
-
- - -
-
- - -
-
- -
-
diff --git a/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.ts b/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.ts index e314d0e..6b724aa 100644 --- a/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.ts +++ b/lec/src/app/components/datasource-configuration/parameters/datasource-configuration-parameter.component.ts @@ -41,12 +41,12 @@ export class DatasourceConfigurationParameterComponent implements OnInit { if(inputString === "") return "Empty String"; const lang = this.translate.currentLang; - var resultString = ""; - var array = inputString.split('|'); + let resultString = ""; + const array = inputString.split('|'); if (lang === "it") {resultString = array[0]} else{ resultString = array[3]; - }; + } return resultString; } diff --git a/lec/src/app/components/header/header.component.html b/lec/src/app/components/header/header.component.html index a736516..99587b1 100644 --- a/lec/src/app/components/header/header.component.html +++ b/lec/src/app/components/header/header.component.html @@ -12,7 +12,7 @@ (click)="menu.toggle($event)" style=" background: #003183;"> - {{ userService?.user?.name }} {{ userService?.user?.surname }} + {{ userService.user.name || '' }} {{ userService.user.surname || '' }}
{{ 'LABEL.SELECT_A_COMMUNITY' | translate }}
-
- + !o.data || !o.data.dynamic)); this.isAdmin = false; this.isManager = false; + this.authService.removeAuthorization(); // Detach from ngZone - setTimeout(() => this.authService.removeAuthorization(), 200); + //setTimeout(() => this.authService.removeAuthorization(), 200); } setActualLanguage(lang: string) { diff --git a/lec/src/app/components/menubardemo/menubardemo.ts b/lec/src/app/components/menubardemo/menubardemo.ts index bf08d7a..e62e379 100644 --- a/lec/src/app/components/menubardemo/menubardemo.ts +++ b/lec/src/app/components/menubardemo/menubardemo.ts @@ -8,7 +8,7 @@ import {MenuItem} from 'primeng/api'; }) export class MenubarDemo { - items: MenuItem[]; + items: MenuItem[] = []; ngOnInit() { diff --git a/lec/src/app/components/page-editor/page-editor.component.css b/lec/src/app/components/page-editor/page-editor.component.css index 810fb00..d54e491 100644 --- a/lec/src/app/components/page-editor/page-editor.component.css +++ b/lec/src/app/components/page-editor/page-editor.component.css @@ -3,6 +3,7 @@ /*height: calc( 100vh - 30px);*/ height: calc(100% - 7.7rem); + /* calculate the height. Header is 30px */ } @@ -16,11 +17,14 @@ /*transition: min-width 0.5s; transition: width 0.5s;*/ transition: all 0.5s; + height: 100%; + overflow-y: auto; } #content { background: rgb(255, 255, 255); flex: 1; + overflow-y: auto; /*flex: 1 0 auto;*/ /* enable grow, disable shrink */ } @@ -28,7 +32,6 @@ .p-button.button-close { font-size: 0.5rem; padding: 0.3rem 0.3rem; - margin-top: -5px; background: rgb(236, 236, 236); border-top-left-radius: 0px; border-bottom-right-radius: 0px; @@ -44,7 +47,6 @@ padding: 0.1rem 0.1rem; background: rgb(236, 236, 236); border-radius: 0px; - top: -15px; } .p-button.button-widget:nth-child(1) { @@ -78,12 +80,6 @@ background-color: #f0f0f082; } -.widgetContainer { - border: 1px solid #e0e0e0; - border-radius: 5px; - cursor: pointer; -} - .widget-selected { border: 1px dotted #777; border-radius: 5px; diff --git a/lec/src/app/components/page-editor/page-editor.component.html b/lec/src/app/components/page-editor/page-editor.component.html index 231672a..279a402 100644 --- a/lec/src/app/components/page-editor/page-editor.component.html +++ b/lec/src/app/components/page-editor/page-editor.component.html @@ -1,182 +1,177 @@ - +
-
+
-
{{ 'PAGE_EDITOR.STATIC_PAGE_TITLE' | translate }}
-
{{ 'PAGE_EDITOR.STATIC_PAGE_SUBTITLE' | translate }}
+
{{ 'PAGE_EDITOR.STATIC_PAGE_TITLE' | translate }}
+
{{ 'PAGE_EDITOR.STATIC_PAGE_SUBTITLE' | translate }}
-
-
-
-
- - -
-
- -
-
-
+ +
+
+ + +
+ +
+
-
- -
+ class="p-button"> + +
-
-
- - -
-
- -
-
-
- -
- -
- - - -
- - - - - +
+
+ + +
+ +
+
+
+
+ + +
-
- - -
- - -
- -
- - -
-
-
- -
+ + + + + +
+
+ + +
+ + +
+
+
+
- - -
- {{ 'PAGE_EDITOR.NO_CONFIG' | translate }} -
-
-
+ +
+ + +
+ {{ 'PAGE_EDITOR.NO_CONFIG' | translate }} +
+
diff --git a/lec/src/app/components/page-editor/page-editor.component.ts b/lec/src/app/components/page-editor/page-editor.component.ts index bfd438f..b403fe2 100644 --- a/lec/src/app/components/page-editor/page-editor.component.ts +++ b/lec/src/app/components/page-editor/page-editor.component.ts @@ -10,6 +10,7 @@ import {TextWidget} from 'src/app/widgets/text/models/TextWidget'; import {UtilsService} from 'src/app/services/utils.service'; import {WidgetService} from "../../services/widget.service"; import {ImageWidget} from "../../widgets/image/models/ImageWidget"; +import {MapWidget} from "../../widgets/map/components/models/map-widget"; @Component({ selector: 'app-page-editor', @@ -40,7 +41,7 @@ export class PageEditorComponent implements OnInit { } private initWidgetsMenuItems() { - this.translate.get(["LABEL.EMPTY", "LABEL.TEXT", "LABEL.TABLE", "LABEL.CHART", "LABEL.LAYOUT", "LABEL.IMAGE"]).subscribe(o => { + this.translate.get(["LABEL.EMPTY", "LABEL.TEXT", "LABEL.TABLE", "LABEL.CHART", "LABEL.LAYOUT", "LABEL.IMAGE", "LABEL.MAP"]).subscribe(o => { this.widgetsMenuItems = [{ label: o["LABEL.TEXT"], icon: 'pi pi-align-left', @@ -84,6 +85,17 @@ export class PageEditorComponent implements OnInit { } this.addWidgetToColumn(this.selectedColumn, "IMAGE"); } + }, + { + label: o["LABEL.MAP"], + icon: 'pi pi-map', + command: () => { + if (!this.selectedColumn) { + this.utils.showWarnMessage("No column selected."); + return; + } + this.addWidgetToColumn(this.selectedColumn, "MAP"); + } } // ,{ // label: o["LABEL.LAYOUT"], @@ -150,12 +162,6 @@ export class PageEditorComponent implements OnInit { } updateSelectedWidget(column: LayoutColumn, i: number) { - /* const index = column.widgets.indexOf(widget); - if (index === -1) { - this.utils.showErrorMessage("Widget not found"); - return; - } - */ this.selectedWidget = column.widgets[i]; } @@ -170,12 +176,13 @@ export class PageEditorComponent implements OnInit { else if (type === "CHART") widget = new ChartWidget(); else if (type === "IMAGE") - widget = new ImageWidget() + widget = new ImageWidget(); + else if (type === "MAP") + widget = new MapWidget(); else // TEXT widget = new TextWidget({content: "HTML widget. Insert your text here."}); column.widgets.push(widget); this.selectedWidget = widget; } - } diff --git a/lec/src/app/components/page-editor/page-properties-editor/page-properties-editor.component.ts b/lec/src/app/components/page-editor/page-properties-editor/page-properties-editor.component.ts index fbbd2ed..2014f18 100644 --- a/lec/src/app/components/page-editor/page-properties-editor/page-properties-editor.component.ts +++ b/lec/src/app/components/page-editor/page-properties-editor/page-properties-editor.component.ts @@ -7,6 +7,7 @@ import {MessageService} from "primeng/api"; import {Community} from 'src/app/models/Community'; import {HttpClient} from '@angular/common/http'; import {environment} from "../../../../environments/environment"; +import {CommunityService} from "../../../services/community.service"; @Component({ selector: 'app-page-properties-editor', @@ -20,18 +21,17 @@ export class PagePropertiesEditorComponent implements OnInit { permissions: Permission[] = []; communities: Community[] = []; - community: Community = new Community(); constructor( private translate: TranslateService, private permissionService: PermissionService, private messageService: MessageService, - private http: HttpClient) { - } + private communityService: CommunityService + ) {} ngOnInit(): void { - this.getCommunities(); + //this.getCommunities(); this.languages = this.translate.getLangs(); this.permissionService.getAll().subscribe( permissions => { @@ -40,7 +40,7 @@ export class PagePropertiesEditorComponent implements OnInit { error => { this.messageService.add({severity: 'error', summary: 'Permissions list', detail: error}) }); - this.getCommunities().subscribe( + this.communityService.getCommunities().subscribe( response => { this.communities = response; } @@ -49,13 +49,5 @@ export class PagePropertiesEditorComponent implements OnInit { communityChange(id: Number = 0){ if (id === null) this.page!.communityId = 0; - } - - - getCommunities() { - return this.http.get(environment.baseUrl + "/lecservice/listCommunities"); - - } - } diff --git a/lec/src/app/components/page/page.component.html b/lec/src/app/components/page/page.component.html index c42a763..f0057c4 100644 --- a/lec/src/app/components/page/page.component.html +++ b/lec/src/app/components/page/page.component.html @@ -21,6 +21,7 @@ +
diff --git a/lec/src/app/components/pages/pages.component.ts b/lec/src/app/components/pages/pages.component.ts index b0b009f..83398cb 100644 --- a/lec/src/app/components/pages/pages.component.ts +++ b/lec/src/app/components/pages/pages.component.ts @@ -171,14 +171,13 @@ export class PagesComponent implements OnInit, OnDestroy { this.utils.showErrorMessage("No page found."); return; } - this.pageService.save(this.page) - .subscribe(_ => { + this.pageService.save(this.page).subscribe({ + next: _ => { this.lastSavedPageHash = this.page!.getHash(); this.utils.showInfoMessageTrans("PAGES.NEW.PAGE_SAVED"); }, - error => { - this.utils.showErrorMessage(error); - }); + error: err => this.utils.showErrorMessage(err) + }) } deletePage(page?: Page) { diff --git a/lec/src/app/interceptors/http-request.interceptor.ts b/lec/src/app/interceptors/http-request.interceptor.ts index dcc9681..436d32e 100644 --- a/lec/src/app/interceptors/http-request.interceptor.ts +++ b/lec/src/app/interceptors/http-request.interceptor.ts @@ -45,7 +45,6 @@ export class HttpRequestInterceptor implements HttpInterceptor, OnDestroy { private handleAuthError(err: any): Observable { if (err.status === 401 || err.status === 403) { - this.token = ""; this.authService.removeAuthorization(); } diff --git a/lec/src/app/models/Community.ts b/lec/src/app/models/Community.ts index 7442ffe..da7d0dc 100644 --- a/lec/src/app/models/Community.ts +++ b/lec/src/app/models/Community.ts @@ -1,12 +1,23 @@ +import {MessageService} from "primeng/api"; + export class Community { communityid?: string; communityName?: string; description?: string; wallet?: string; + georeferencex?: number; + georeferencey?: number; public constructor(init?:Partial) { Object.assign(this, init); } + + static createDefaultUserCommunityEntry(name: string) { + const result = new Community(); + result.communityid = "my_communities"; + result.communityName = name + return result; + } } diff --git a/lec/src/app/models/enea/LocalUserDTO.ts b/lec/src/app/models/enea/LocalUserDTO.ts index 7f65806..2ed6d88 100644 --- a/lec/src/app/models/enea/LocalUserDTO.ts +++ b/lec/src/app/models/enea/LocalUserDTO.ts @@ -3,6 +3,8 @@ export interface CommunityDTO { communityName?: string; description?: string; wallet?: string; + georeferencex?: number; + georeferencey?: number; } export interface Token { diff --git a/lec/src/app/models/enea/ParametricQuery.ts b/lec/src/app/models/enea/ParametricQuery.ts index 24267fd..d46295f 100644 --- a/lec/src/app/models/enea/ParametricQuery.ts +++ b/lec/src/app/models/enea/ParametricQuery.ts @@ -18,6 +18,7 @@ export class LocalQueryParameter { name?: string; paramId?: number; type?: string; + useInFrontEnd?: boolean } export class Metadata { diff --git a/lec/src/app/services/community.service.spec.ts b/lec/src/app/services/community.service.spec.ts new file mode 100644 index 0000000..7d92e04 --- /dev/null +++ b/lec/src/app/services/community.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { CommunityService } from './community.service'; + +describe('CommunityService', () => { + let service: CommunityService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(CommunityService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/lec/src/app/services/community.service.ts b/lec/src/app/services/community.service.ts new file mode 100644 index 0000000..75eec5a --- /dev/null +++ b/lec/src/app/services/community.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import {Community} from "../models/Community"; +import {environment} from "../../environments/environment"; +import {HttpClient} from "@angular/common/http"; + +@Injectable({ + providedIn: 'root' +}) +export class CommunityService { + + constructor(private http: HttpClient) { } + + getCommunities() { + return this.http.get(environment.baseUrl + "/lecservice/listCommunities"); + + } +} diff --git a/lec/src/app/services/data.service.ts b/lec/src/app/services/data.service.ts index 61e2e77..12acd33 100644 --- a/lec/src/app/services/data.service.ts +++ b/lec/src/app/services/data.service.ts @@ -23,16 +23,18 @@ import {environment} from "../../environments/environment"; export class DataService { constructor(private http: HttpClient, private parameterResolver: ParameterResolverService, private utils: UtilsService) {} - - public getData(configuration: DatasourceConfiguration): Observable { - - console.log("GetData Configuration : " + JSON.stringify( configuration )); + public getData(configuration: DatasourceConfiguration, parameterValues?: any): Observable { + console.info("Configuration: ", configuration, " Parameter values: ", parameterValues); const data = { "queryId": configuration.parametricQuery?.queryid, - "parameters": configuration.parameters.map(o => { name: o.name, value: this.parameterResolver.resolve(o) }) + "parameters": configuration.parameters.map(o => { name: o.name, value: parameterValues && parameterValues[o.name] ? parameterValues[o.name] : this.parameterResolver.resolve(o)}) }; - + + return this.doDataRequest(data); + } + + private doDataRequest(data: any) { return this.http.post(environment.baseUrl + "/lecservice/data/getData", data).pipe( tap( (serviceResponse: ServiceResponse) => { diff --git a/lec/src/app/services/parameter-resolvers/IParameterResolver.ts b/lec/src/app/services/parameter-resolvers/IParameterResolver.ts index fb5cb19..49d3195 100644 --- a/lec/src/app/services/parameter-resolvers/IParameterResolver.ts +++ b/lec/src/app/services/parameter-resolvers/IParameterResolver.ts @@ -171,7 +171,6 @@ export class AnyStringParameterResolver extends BaseParameterResolver implements type = "any_string"; allowedDataTypes = ["String"]; description = "Numero specifico"; - for(parameter: Parameter, queryParams: Params): IParameterResolver { return new AnyNumberParameterResolver({ parameter, queryParams}); diff --git a/lec/src/app/services/user.service.ts b/lec/src/app/services/user.service.ts index 16382bb..25f1c96 100644 --- a/lec/src/app/services/user.service.ts +++ b/lec/src/app/services/user.service.ts @@ -7,11 +7,12 @@ import {LocalUserDTO} from "../models/enea/LocalUserDTO"; import {Observable, Subscription} from "rxjs"; import {UtilsService} from './utils.service'; import {environment} from "../../environments/environment"; -import {AuthConfig, OAuthService} from "angular-oauth2-oidc"; +import {AuthConfig, NullValidationHandler, OAuthService} from "angular-oauth2-oidc"; import {Community} from "../models/Community"; import {User} from "../models/User"; import {AuthService} from "./auth.service"; import {Router} from "@angular/router"; +import {MessageService} from "primeng/api"; @Injectable({ providedIn: 'root' @@ -23,7 +24,7 @@ export class UserService implements OnDestroy { private readonly authSubscription?: Subscription; - constructor(private http: HttpClient, private router: Router, private auth: AuthService, private utils: UtilsService, private oauthService: OAuthService) { + constructor(private http: HttpClient, private messageService: MessageService, private router: Router, private auth: AuthService, private utils: UtilsService, private oauthService: OAuthService) { this.authSubscription = this.auth.onAuthChange().subscribe((authorization) => { this.updateUserInfo(authorization); this.checkIfShouldLogout(authorization); @@ -74,7 +75,7 @@ export class UserService implements OnDestroy { .then(o => { console.info("loadDiscoveryDocumentAndTryLogin() -> ", o); }) - .catch(err => console.error(err)); + .catch(err => this.messageService.add({severity: 'warn', summary: 'Login', detail: err.message})); } login() { @@ -145,14 +146,17 @@ export class UserService implements OnDestroy { logout() { if (environment.authenticationType === 'oid') { - return this.oauthService.revokeTokenAndLogout( - { - client_id: this.oauthService.clientId, - returnTo: this.oauthService.redirectUri - } - ); + console.log(this.oauthService.getIdToken()); + console.log(this.oauthService.getAccessTokenExpiration()); + console.log(this.oauthService.hasValidAccessToken()); + debugger + if (this.oauthService.clientId && this.oauthService.redirectUri) + return this.oauthService.revokeTokenAndLogout({ + clientId: this.oauthService.clientId, + redirectUri: this.oauthService.redirectUri + }); + else return null; } else { - console.info("Local logout"); return this.http.post(environment.baseUrl + "/lecservice/auth/logout", {}) .pipe( tap( diff --git a/lec/src/app/services/widget.service.ts b/lec/src/app/services/widget.service.ts index 0597a98..d28f6f5 100644 --- a/lec/src/app/services/widget.service.ts +++ b/lec/src/app/services/widget.service.ts @@ -10,4 +10,6 @@ export class WidgetService { onConfigurationChanged = new Subject(); constructor() { } + + } \ No newline at end of file diff --git a/lec/src/app/widgets/chart/components/chart-widget-configuration/chart-widget-configuration.component.html b/lec/src/app/widgets/chart/components/chart-widget-configuration/chart-widget-configuration.component.html index 6b768ee..f6b9f59 100644 --- a/lec/src/app/widgets/chart/components/chart-widget-configuration/chart-widget-configuration.component.html +++ b/lec/src/app/widgets/chart/components/chart-widget-configuration/chart-widget-configuration.component.html @@ -10,120 +10,110 @@
-
+
- +
-
+
- +
-
- - - - - - - - - +
+
+
+ +
Serie {{i+1}}
+
+ +

{{'CHART_WIDGET_CONFIGURATION.SELECTED_DATASOURCE' | translate}}:

-
-
- -
Serie {{i+1}}
-
- -

{{'CHART_WIDGET_CONFIGURATION.SELECTED_DATASOURCE' - | translate}}:

+
+ + +
+
-
- - -
-
- - - + + - + -

{{'CHART_WIDGET_CONFIGURATION.SELECTED_COLUMNS' - | translate}}:

+

{{'CHART_WIDGET_CONFIGURATION.SELECTED_COLUMNS' + | translate}}:

- -

{{'CHART_WIDGET_CONFIGURATION.X_AXIS_COLUMN' - | translate}}:

+ +

{{'CHART_WIDGET_CONFIGURATION.X_AXIS_COLUMN' + | translate}}:

- - - -
+ + + +
-
{{style.column}}
- {{'CHART_WIDGET_CONFIGURATION.STYLE' - | translate}}: - - - {{ 'CHART_WIDGET_CONFIGURATION.INTERPOLATE' | translate }} -
- - {{ 'CHART_WIDGET_CONFIGURATION.FILL' | translate }} -
-
- -
- -
-
+
{{style.column}}
+ {{'CHART_WIDGET_CONFIGURATION.STYLE' + | translate}}: + + + {{ 'CHART_WIDGET_CONFIGURATION.INTERPOLATE' | translate }} +
+ + {{ 'CHART_WIDGET_CONFIGURATION.FILL' | translate }} +
+
+ +
+ +
+
-
-
- - -
+
+ +
+ +
- + -
+
\ No newline at end of file diff --git a/lec/src/app/widgets/chart/components/chart-widget.component.html b/lec/src/app/widgets/chart/components/chart-widget.component.html index 144c4a7..5fae3d8 100644 --- a/lec/src/app/widgets/chart/components/chart-widget.component.html +++ b/lec/src/app/widgets/chart/components/chart-widget.component.html @@ -1,103 +1,103 @@ +
+
{{ widget?.name }}
+ + +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
- - -
-
{{widget?.name}}
- - +
+ +
+
- -
- - -
-
- - -
- - Update serie: + +
+
+
+ + +
+ + Update serie: - - - + + + +
+ +
+ {{ "PARAMETERS." + parameter.name | translate }}
- -
- {{"PARAMETERS." + parameter.name | translate}} -
-
+
- -
-
- {{"PARAMETERS." + parameter.name | translate}} -
-
- - -
- + +
+
+ {{ "PARAMETERS." + parameter.name | translate }} +
+
+ + +
-
- -
+
+
+
-
-
-
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
-
-
-
{{'CHART_WIDGET.NO_DATA' | translate}}
+
+
{{ 'CHART_WIDGET.NO_DATA' | translate }}
diff --git a/lec/src/app/widgets/chart/components/chart-widget.component.ts b/lec/src/app/widgets/chart/components/chart-widget.component.ts index 82ccd4a..3241f3e 100644 --- a/lec/src/app/widgets/chart/components/chart-widget.component.ts +++ b/lec/src/app/widgets/chart/components/chart-widget.component.ts @@ -1,328 +1,375 @@ // @ts-nocheck -import {Component, Input, OnDestroy, OnInit, ViewChild, NgZone} from "@angular/core"; +import {Component, Input, NgZone, OnDestroy, OnInit, ViewChild} from "@angular/core"; import {ChartWidget, Series, SeriesMetadata, SeriesStyle} from "../models/ChartWidget"; import {DataService} from "../../../services/data.service"; import {MessageService} from "primeng/api"; import {WidgetService} from "../../../services/widget.service"; -import {Subscription, forkJoin, interval} from "rxjs"; +import {forkJoin, Subscription} from "rxjs"; -import { UtilsService } from 'src/app/services/utils.service'; +import {UtilsService} from 'src/app/services/utils.service'; import {ActivatedRoute, Params, Router} from "@angular/router"; -//import {QueryStringParameterResolver} from "../../../services/parameter-resolvers/IParameterResolver"; - import {ChartDataLocal} from "../models/ChartDataLocal"; -import { ChartData, ChartConfiguration, ChartType} from 'chart.js/auto'; - -import { NgChartsModule } from 'ng2-charts'; -import { BaseChartDirective } from 'ng2-charts'; +import {ChartConfiguration, ChartData, ChartType} from 'chart.js/auto'; -import { Chart, ChartElement, ChartEvent, Element } from 'chart.js'; - -//import { Moment } from 'moment'; -//import * as moment from "moment"; -//import { DatasourceConfiguration } from "../../Widget"; -import { Intervals } from "src/app/models/Parameter"; +import {BaseChartDirective} from 'ng2-charts'; +import {Chart, Element} from 'chart.js'; +import {Intervals} from "src/app/models/Parameter"; import zoomPlugin from 'chartjs-plugin-zoom'; -Chart.register(zoomPlugin); +Chart.register(zoomPlugin); @Component({ -selector: 'chart-widget', -templateUrl: './chart-widget.component.html', -styleUrls: ['./chart-widget.component.css'] + selector: 'chart-widget', + templateUrl: './chart-widget.component.html', + styleUrls: ['./chart-widget.component.css'] }) export class ChartWidgetComponent implements OnInit, OnDestroy { - - - @ViewChild(BaseChartDirective, { static: true }) public mychart: BaseChartDirective; - @Input() widget?: ChartWidget; - - series: Series[] = []; - newSeriesList: Series[] = []; - debug = false; - - dataLocal: ChartDataLocal = new ChartDataLocal([],[],[],[]); - - data: ChartData = { - datasets: [], - labels : [], - }; - responses: Response[] = []; - - public loadingData: boolean = false; - - subscriptions: Subscription[] = []; - - labels: string[] = []; - - options: ChartConfiguration['options'] = {}; - - updatable: boolean = false; - - config = { - type : 'bar' as ChartType, - data: this.data, - options: this.options - }; - - validSeries: Series[] = []; - - intervals = Intervals.getIntervals(); - - constructor( - private widgetService: WidgetService, - private dataService: DataService, - private messageService: MessageService, - private utils: UtilsService, - private route: ActivatedRoute, - private router: Router, - private zone: NgZone) { - - } - - ngOnInit(): void { - //this.loadData(); - /** - * In questo modo. il widget analizza prima la presenza di eventuali parametri quesrystring e poi - * richiede al server remoto di ricevere i dati attraverso il comando loaddata(); - * Non e' pulito, pero' sembra funzionare. - * Il sistema funziona SOLO per parametri che abbiano un valore numerico e non stringa. - * Da perfezionare. - */ - this.route.queryParams.subscribe(params => { - this.listenForConfigurationChanged(params); - }); - this.setUpdatable(); - - } - - - - - ngOnDestroy() { - for (const subscription of this.subscriptions) - if (subscription) - subscription.unsubscribe(); - this.subscriptions = []; - } - - private setUpdatable(){ - - for (const series of this.validSeries) { - for (let param of series.datasourceConfiguration?.parameters!){ - if(param.type === "any_date_choosen_by_user" || - param.type === "any_interval_choosen_by_user") this.updatable = true; - } - } - } - - - private loadData(){ - this.loadingData = true; - - let httpposts = []; - - this.validSeries = this.widget!.seriesList!.filter(o => !!o.datasourceConfiguration); - - for (const series of this.validSeries) { - //console.log("getting data for: " + JSON.stringify(series.datasourceConfiguration!)); - httpposts.push(this.dataService.getData(series.datasourceConfiguration!)); - } - - let forkJoinResult = forkJoin(httpposts).subscribe(result => { - for (let res of result){ - let response = { - "result": res?.result || [], - "metadata": res?.metadata || {} - }; - //console.log("response: " + JSON.stringify(response)); - this.responses.push(response); - } - - this.elaborateResponses(); - this.loadingData = false; - this.chart?.update(); - }); - - - } - - updateData(){ - this.data.datasets = []; - this.data.labels = []; - this.dataLocal.datasets = []; - this.dataLocal.labels = []; - this.route.queryParams.subscribe(params => { - this.listenForConfigurationChanged(params); - }); - } - - listenForConfigurationChanged(params: Params) { - this.newSeriesList = []; - this.data.datasets = []; - this.data.labels = []; - this.responses = []; - - this.series = this.widget!.seriesList!; - for (var i in this.series){ - let serie = this.series[i]; - for (const param in params){ - for (var p in serie.datasourceConfiguration!.parameters){ - let parameter = serie.datasourceConfiguration!.parameters[p]; - if(parameter.type === "querystring" && parameter.name === param){ - parameter.value = params[param]; - } - } - } - this.newSeriesList.push(serie); - } - this.widget!.seriesList = this.newSeriesList; - this.debug ? this.createFakeData() : this.loadData(); - } - - public createFakeData (){ - this.utils.showInfoMessage("Creating fake data" ); - - this.options = { - "responsive": true, - "scales": { - "x": { - "title": { - "display": true, - "text": "" - } - }, - "y": { - "title": { - "display": true, - "text": "W | watts" - } - } - }, - onClick: (e, elements) => {this.elaborateClick(e, elements)} - }; - - /* this.data = { - labels: [ - "00:00:00", "00:15:00", "00:30:00", "00:45:00", "01:00:00", "01:15:00", "01:30:00", "01:45:00", - "02:00:00", "02:15:00", "02:30:00", "02:45:00", "03:00:00", "03:15:00", "03:30:00", "03:45:00", - "04:00:00", "04:15:00", "04:30:00", "04:45:00", "05:00:00", "05:15:00", "05:30:00", "05:45:00", - "06:00:00", "06:15:00", "06:30:00", "06:45:00", "07:00:00", "07:15:00", "07:30:00", "07:45:00", - "08:00:00", "08:15:00", "08:30:00", "08:45:00", "09:00:00", "09:15:00", "09:30:00", "09:45:00", - "10:00:00", "10:15:00", "10:30:00", "10:45:00", "11:00:00", "11:15:00", "11:30:00", "11:45:00", - "12:00:00", "12:15:00", "12:30:00", "12:45:00" - ], - datasets: [{ - type: 'bar', - label: 'Today average values in customisable intervals', - data: [ 274, 266, 217, 261, 152, 100, 156, 152, 146, 205, 247, 248, 89, 142, 143, 90, 149, 245, - 188, 191, 246, 241, 88, 144, 140, 88, 131, 221, 172, 205, 402, 1611, 328, 169, 150, 131, - 299, 338, 260, 210, 238, 155, 247, 235, 298, 1746, 262, 350, 187, 237, 263, 196 ], - borderColor: 'blue', - backgroundColor: 'blue' - }, { - type: 'line', - label: 'Line Dataset', - data: [ 196, 145, 156, 122, 177, 122, 123, 84, 116, 96, 113, 133, 102, 137, 95, 111, 104, 158, 101, - 123, 304, 151, 282, 125, 76, 253, 250, 315, 112, 150, 148, 456, 297, 273, 313, 135, 171, 141, - 127, 134, 208, 234, 223, 231, 163, 408, 176, 289, 211, 408, 335, 201 ], - fill: true, - cubicInterpolationMode: 'monotone', - borderColor: 'rgb(54, 162, 235)' - }] - }; - */ - this.data= { - "labels": [ ], - "datasets": [{ - "type": 'bar', - "label": 'Today average values in customisable intervals', - "data": [ - {"x": "00:00:00", "y": 274 }, {"x": "00:15:00", "y": 266 }, {"x": "00:30:00", "y": 217 }, {"x": "00:45:00", "y": 152 }, - {"x": "01:00:00", "y": 100 }, {"x": "01:15:00", "y": 156 }, {"x": "01:30:00", "y": 152 }, {"x": "01:45:00", "y": 146 }, - {"x": "02:00:00", "y": 205 }, {"x": "02:15:00", "y": 247 }, {"x": "02:30:00", "y": 248 }, {"x": "02:45:00", "y": 89 }, - {"x": "03:00:00", "y": 142 }, {"x": "03:15:00", "y": 143 }, {"x": "03:30:00", "y": 90 }, {"x": "03:45:00", "y": 149 }, - {"x": "04:00:00", "y": 245 }, {"x": "04:15:00", "y": 188 }, {"x": "04:30:00", "y": 191 }, {"x": "04:45:00", "y": 246 }, - {"x": "05:00:00", "y": 274 }, {"x": "05:15:00", "y": 266 }, {"x": "05:30:00", "y": 217 }, {"x": "05:45:00", "y": 152 }, - {"x": "06:00:00", "y": 100 }, {"x": "06:15:00", "y": 156 }, {"x": "06:30:00", "y": 152 }, {"x": "06:45:00", "y": 146 }, - {"x": "07:00:00", "y": 205 }, {"x": "07:15:00", "y": 247 }, {"x": "07:30:00", "y": 248 }, {"x": "07:45:00", "y": 89 }, - {"x": "08:00:00", "y": 142 }, {"x": "08:15:00", "y": 143 }, {"x": "08:30:00", "y": 90 }, {"x": "08:45:00", "y": 149 }, - {"x": "09:00:00", "y": 245 }, {"x": "09:15:00", "y": 188 }, {"x": "09:30:00", "y": 191 }, {"x": "09:45:00", "y": 246 }, - {"x": "10:00:00", "y": 274 }, {"x": "10:15:00", "y": 266 }, {"x": "10:30:00", "y": 217 }, {"x": "10:45:00", "y": 152 }, - {"x": "11:00:00", "y": 100 }, {"x": "11:15:00", "y": 156 }, {"x": "11:30:00", "y": 152 }, {"x": "11:45:00", "y": 146 }, - {"x": "12:00:00", "y": 205 }, {"x": "12:15:00", "y": 247 }, {"x": "12:30:00", "y": 248 }, {"x": "12:45:00", "y": 89 }, - {"x": "13:00:00", "y": 142 }, {"x": "13:15:00", "y": 143 }, {"x": "13:30:00", "y": 90 }, {"x": "13:45:00", "y": 149 }, - {"x": "14:00:00", "y": 245 }, {"x": "14:15:00", "y": 188 }, {"x": "14:30:00", "y": 191 }, {"x": "14:45:00", "y": 246 } - ] - , - "borderColor": 'blue', - "backgroundColor": 'blue' - }, - { - "type": 'line', - "label": 'Line Dataset', - "data": [ {"x": "06:00:00", "y": 100 }, {"x": "06:15:00", "y": 156 }, {"x": "06:30:00", "y": 152 }, {"x": "06:45:00", "y": 146 }, - {"x": "07:00:00", "y": 205 }, {"x": "07:15:00", "y": 247 }, {"x": "07:30:00", "y": 248 }, {"x": "07:45:00", "y": 89 }, - {"x": "08:00:00", "y": 142 }, {"x": "08:15:00", "y": 143 }, {"x": "08:30:00", "y": 90 }, {"x": "08:45:00", "y": 149 }, - {"x": "09:00:00", "y": 245 }, {"x": "09:15:00", "y": 188 }, {"x": "09:30:00", "y": 191 }, {"x": "09:45:00", "y": 246 }], - "cubicInterpolationMode": 'monotone', - "borderColor": 'rgb(54, 162, 235)' - }] - }; - } - - - getChartType(chartInput?: string){ - return chartInput; - } - - elaborateResponses(){ - let items =[]; - const validSeries = this.widget!.seriesList!.filter(o => !!o.datasourceConfiguration); - var response: Response = new Response(); - if(this.debug) this.utils.showInfoMessage(validSeries.length + " = " + this.responses.length); - - if (validSeries.length === this.responses.length){ - for (let i = 0; i < validSeries.length; i++) { - let response = this.responses[i]; - let series = validSeries[i]; - const chartData = this.fromObjectsList(response, series); - if(this.debug) this.utils.showInfoMessage( JSON.stringify( chartData.metadata ) ); - for (const dataset of chartData.datasets){ - this.dataLocal?.datasets.push(dataset); - this.data!.datasets.push(dataset); - - } - if(this.widget?.sortData){ - this.dataLocal.labels = this.dataLocal.labels.concat( - chartData.labels.filter(x => this.dataLocal.labels.every(y => y !== x))); - } - - this.options = chartData.configuration; - - } - }else { - - } - this.data!.labels = this.dataLocal.labels.sort(); - } - - fromObjectsList(objectsList: any, series: Series): ChartDataLocal { + @ViewChild(BaseChartDirective, {static: true}) public mychart: BaseChartDirective; + @Input() widget?: ChartWidget; + + series: Series[] = []; + newSeriesList: Series[] = []; + debug = false; + + dataLocal: ChartDataLocal = new ChartDataLocal([], [], [], []); + + data: ChartData = { + datasets: [], + labels: [], + }; + responses: Response[] = []; + + public loadingData: boolean = false; + + subscriptions: Subscription[] = []; + + labels: string[] = []; + + options: ChartConfiguration['options'] = {}; + + updatable: boolean = false; + + config = { + type: 'bar' as ChartType, + data: this.data, + options: this.options + }; + + validSeries: Series[] = []; + + intervals = Intervals.getIntervals(); + + constructor( + private widgetService: WidgetService, + private dataService: DataService, + private messageService: MessageService, + private utils: UtilsService, + private route: ActivatedRoute, + private router: Router, + private zone: NgZone) { + + } + + ngOnInit(): void { + //this.loadData(); + /** + * In questo modo. il widget analizza prima la presenza di eventuali parametri quesrystring e poi + * richiede al server remoto di ricevere i dati attraverso il comando loaddata(); + * Non e' pulito, pero' sembra funzionare. + * Il sistema funziona SOLO per parametri che abbiano un valore numerico e non stringa. + * Da perfezionare. + */ + this.route.queryParams.subscribe(params => { + this.listenForConfigurationChanged(params); + }); + this.setUpdatable(); + + } + + ngOnDestroy() { + for (const subscription of this.subscriptions) + if (subscription) + subscription.unsubscribe(); + this.subscriptions = []; + } + + private setUpdatable() { + for (const series of this.validSeries) { + for (let param of series.datasourceConfiguration?.parameters!) { + if (param.type === "any_date_choosen_by_user" || + param.type === "any_interval_choosen_by_user") this.updatable = true; + } + } + } + + private loadData() { + this.loadingData = true; + + let httpposts = []; + + this.validSeries = this.widget!.seriesList!.filter(o => !!o.datasourceConfiguration); + + for (const series of this.validSeries) { + //console.log("getting data for: " + JSON.stringify(series.datasourceConfiguration!)); + httpposts.push(this.dataService.getData(series.datasourceConfiguration!)); + } + + forkJoin(httpposts).subscribe({ + next: result => { + for (let res of result) { + let response = { + "result": res?.result || [], + "metadata": res?.metadata || {} + }; + //console.log("response: " + JSON.stringify(response)); + this.responses.push(response); + } + + this.elaborateResponses(); + this.loadingData = false; + this.chart?.update(); + }, + error: er => { + this.loadingData = false; + } + }); + } + + updateData() { + this.data.datasets = []; + this.data.labels = []; + this.dataLocal.datasets = []; + this.dataLocal.labels = []; + this.route.queryParams.subscribe(params => { + this.listenForConfigurationChanged(params); + }); + } + + listenForConfigurationChanged(params: Params) { + this.newSeriesList = []; + this.data.datasets = []; + this.data.labels = []; + this.responses = []; + + this.series = this.widget!.seriesList!; + for (var i in this.series) { + let serie = this.series[i]; + for (const param in params) { + for (var p in serie.datasourceConfiguration!.parameters) { + let parameter = serie.datasourceConfiguration!.parameters[p]; + if (parameter.type === "querystring" && parameter.name === param) { + parameter.value = params[param]; + } + } + } + this.newSeriesList.push(serie); + } + this.widget!.seriesList = this.newSeriesList; + this.debug ? this.createFakeData() : this.loadData(); + } + + public createFakeData() { + this.utils.showInfoMessage("Creating fake data"); + + this.options = { + "responsive": true, + "scales": { + "x": { + "title": { + "display": true, + "text": "" + } + }, + "y": { + "title": { + "display": true, + "text": "W | watts" + } + } + }, + onClick: (e, elements) => { + this.elaborateClick(e, elements) + } + }; + + /* this.data = { + labels: [ + "00:00:00", "00:15:00", "00:30:00", "00:45:00", "01:00:00", "01:15:00", "01:30:00", "01:45:00", + "02:00:00", "02:15:00", "02:30:00", "02:45:00", "03:00:00", "03:15:00", "03:30:00", "03:45:00", + "04:00:00", "04:15:00", "04:30:00", "04:45:00", "05:00:00", "05:15:00", "05:30:00", "05:45:00", + "06:00:00", "06:15:00", "06:30:00", "06:45:00", "07:00:00", "07:15:00", "07:30:00", "07:45:00", + "08:00:00", "08:15:00", "08:30:00", "08:45:00", "09:00:00", "09:15:00", "09:30:00", "09:45:00", + "10:00:00", "10:15:00", "10:30:00", "10:45:00", "11:00:00", "11:15:00", "11:30:00", "11:45:00", + "12:00:00", "12:15:00", "12:30:00", "12:45:00" + ], + datasets: [{ + type: 'bar', + label: 'Today average values in customisable intervals', + data: [ 274, 266, 217, 261, 152, 100, 156, 152, 146, 205, 247, 248, 89, 142, 143, 90, 149, 245, + 188, 191, 246, 241, 88, 144, 140, 88, 131, 221, 172, 205, 402, 1611, 328, 169, 150, 131, + 299, 338, 260, 210, 238, 155, 247, 235, 298, 1746, 262, 350, 187, 237, 263, 196 ], + borderColor: 'blue', + backgroundColor: 'blue' + }, { + type: 'line', + label: 'Line Dataset', + data: [ 196, 145, 156, 122, 177, 122, 123, 84, 116, 96, 113, 133, 102, 137, 95, 111, 104, 158, 101, + 123, 304, 151, 282, 125, 76, 253, 250, 315, 112, 150, 148, 456, 297, 273, 313, 135, 171, 141, + 127, 134, 208, 234, 223, 231, 163, 408, 176, 289, 211, 408, 335, 201 ], + fill: true, + cubicInterpolationMode: 'monotone', + borderColor: 'rgb(54, 162, 235)' + }] + }; + */ + this.data = { + "labels": [], + "datasets": [{ + "type": 'bar', + "label": 'Today average values in customisable intervals', + "data": [ + {"x": "00:00:00", "y": 274}, {"x": "00:15:00", "y": 266}, { + "x": "00:30:00", + "y": 217 + }, {"x": "00:45:00", "y": 152}, + {"x": "01:00:00", "y": 100}, {"x": "01:15:00", "y": 156}, { + "x": "01:30:00", + "y": 152 + }, {"x": "01:45:00", "y": 146}, + {"x": "02:00:00", "y": 205}, {"x": "02:15:00", "y": 247}, { + "x": "02:30:00", + "y": 248 + }, {"x": "02:45:00", "y": 89}, + {"x": "03:00:00", "y": 142}, {"x": "03:15:00", "y": 143}, { + "x": "03:30:00", + "y": 90 + }, {"x": "03:45:00", "y": 149}, + {"x": "04:00:00", "y": 245}, {"x": "04:15:00", "y": 188}, { + "x": "04:30:00", + "y": 191 + }, {"x": "04:45:00", "y": 246}, + {"x": "05:00:00", "y": 274}, {"x": "05:15:00", "y": 266}, { + "x": "05:30:00", + "y": 217 + }, {"x": "05:45:00", "y": 152}, + {"x": "06:00:00", "y": 100}, {"x": "06:15:00", "y": 156}, { + "x": "06:30:00", + "y": 152 + }, {"x": "06:45:00", "y": 146}, + {"x": "07:00:00", "y": 205}, {"x": "07:15:00", "y": 247}, { + "x": "07:30:00", + "y": 248 + }, {"x": "07:45:00", "y": 89}, + {"x": "08:00:00", "y": 142}, {"x": "08:15:00", "y": 143}, { + "x": "08:30:00", + "y": 90 + }, {"x": "08:45:00", "y": 149}, + {"x": "09:00:00", "y": 245}, {"x": "09:15:00", "y": 188}, { + "x": "09:30:00", + "y": 191 + }, {"x": "09:45:00", "y": 246}, + {"x": "10:00:00", "y": 274}, {"x": "10:15:00", "y": 266}, { + "x": "10:30:00", + "y": 217 + }, {"x": "10:45:00", "y": 152}, + {"x": "11:00:00", "y": 100}, {"x": "11:15:00", "y": 156}, { + "x": "11:30:00", + "y": 152 + }, {"x": "11:45:00", "y": 146}, + {"x": "12:00:00", "y": 205}, {"x": "12:15:00", "y": 247}, { + "x": "12:30:00", + "y": 248 + }, {"x": "12:45:00", "y": 89}, + {"x": "13:00:00", "y": 142}, {"x": "13:15:00", "y": 143}, { + "x": "13:30:00", + "y": 90 + }, {"x": "13:45:00", "y": 149}, + {"x": "14:00:00", "y": 245}, {"x": "14:15:00", "y": 188}, { + "x": "14:30:00", + "y": 191 + }, {"x": "14:45:00", "y": 246} + ] + , + "borderColor": 'blue', + "backgroundColor": 'blue' + }, + { + "type": 'line', + "label": 'Line Dataset', + "data": [{"x": "06:00:00", "y": 100}, {"x": "06:15:00", "y": 156}, { + "x": "06:30:00", + "y": 152 + }, {"x": "06:45:00", "y": 146}, + {"x": "07:00:00", "y": 205}, {"x": "07:15:00", "y": 247}, { + "x": "07:30:00", + "y": 248 + }, {"x": "07:45:00", "y": 89}, + {"x": "08:00:00", "y": 142}, {"x": "08:15:00", "y": 143}, { + "x": "08:30:00", + "y": 90 + }, {"x": "08:45:00", "y": 149}, + {"x": "09:00:00", "y": 245}, {"x": "09:15:00", "y": 188}, { + "x": "09:30:00", + "y": 191 + }, {"x": "09:45:00", "y": 246}], + "cubicInterpolationMode": 'monotone', + "borderColor": 'rgb(54, 162, 235)' + }] + }; + } + + + getChartType(chartInput?: string) { + return chartInput; + } + + elaborateResponses() { + let items = []; + const validSeries = this.widget!.seriesList!.filter(o => !!o.datasourceConfiguration); + var response: Response = new Response(); + if (this.debug) this.utils.showInfoMessage(validSeries.length + " = " + this.responses.length); + + if (validSeries.length === this.responses.length) { + for (let i = 0; i < validSeries.length; i++) { + let response = this.responses[i]; + let series = validSeries[i]; + const chartData = this.fromObjectsList(response, series); + if (this.debug) this.utils.showInfoMessage(JSON.stringify(chartData.metadata)); + for (const dataset of chartData.datasets) { + this.dataLocal?.datasets.push(dataset); + this.data!.datasets.push(dataset); + + } + if (this.widget?.sortData) { + this.dataLocal.labels = this.dataLocal.labels.concat( + chartData.labels.filter(x => this.dataLocal.labels.every(y => y !== x))); + } + + this.options = chartData.configuration; + + } + } else { + + } + this.data!.labels = this.dataLocal.labels.sort(); + } + + fromObjectsList(objectsList: any, series: Series): ChartDataLocal { const chartDataSets: ChartDataset[] = []; const mapDataSets: any = {}; const mapDataSets2: any = {}; - const colors: BackgroundColor[]= []; + const colors: BackgroundColor[] = []; const metadata: Metadata[] = []; var x: any = {}; var y: any = {}; var xMetadata: any = {}; var yMetadata: any = {}; - const myChart: Chart = this.mychart.chart!; + const myChart: Chart = this.mychart.chart!; const myWidget: ChartWidget = this.widget!; - + for (const column of series.selectedColumns) mapDataSets[column] = []; //Inizializza la tabella @@ -331,12 +378,12 @@ export class ChartWidgetComponent implements OnInit, OnDestroy { const labels: string[] = []; - + for (const row of objectsList.result) { - if (series.xaxisColumn){ + if (series.xaxisColumn) { labels.push(row[series.xaxisColumn]) x = row[series.xaxisColumn]; - } + } for (const column of series.selectedColumns) { mapDataSets[column].push(row[column]); y = row[column]; @@ -346,23 +393,23 @@ export class ChartWidgetComponent implements OnInit, OnDestroy { } mapDataSets2[column].push(input); } - + } - - if (series.xaxisColumn){ - xMetadata = SeriesMetadata.getFor(series.xaxisColumn, - series.datasourceConfiguration!.parametricQuery!.metadata); - metadata.push(xMetadata); + + if (series.xaxisColumn) { + xMetadata = SeriesMetadata.getFor(series.xaxisColumn, + series.datasourceConfiguration!.parametricQuery!.metadata); + metadata.push(xMetadata); } for (const column of series.selectedColumns) { const columnStyle = SeriesStyle.getFor(column, series.styles); - let labelTemp :string = ""; + let labelTemp: string = ""; labelTemp = (series.datasourceConfiguration?.label ? series.datasourceConfiguration?.label : series.datasourceConfiguration?.parametricQuery?.description) as string; - let backgroundColor = (columnStyle?.fill ? this.hexToRGB(columnStyle.color!,"0.3") : columnStyle.color) as string; - + let backgroundColor = (columnStyle?.fill ? this.hexToRGB(columnStyle.color!, "0.3") : columnStyle.color) as string; + let chartdataset: ChartDataset = { - label: labelTemp, + label: labelTemp, data: mapDataSets2[column], backgroundColor: backgroundColor, type: (columnStyle.chartType as string) as ChartType, @@ -371,150 +418,151 @@ export class ChartWidgetComponent implements OnInit, OnDestroy { pointRadius: 1, cubicInterpolationMode: columnStyle?.interpolate ? 'monotone' : 'default' } - + chartDataSets.push(chartdataset); colors.push(new BackgroundColor({ - backgroundColor: columnStyle.color - })); - - yMetadata = SeriesMetadata.getFor(column,series.datasourceConfiguration!.parametricQuery!.metadata); + backgroundColor: columnStyle.color + })); + + yMetadata = SeriesMetadata.getFor(column, series.datasourceConfiguration!.parametricQuery!.metadata); metadata.push(yMetadata); } - - let xUnit = xMetadata.unitOfMeasure ? - (xMetadata?.unitOfMeasure.includes("N/A") ? '' : xMetadata?.unitOfMeasure) : ''; - let yUnit = yMetadata.unitOfMeasure ? - (yMetadata?.unitOfMeasure.includes("N/A") ? '' : yMetadata?.unitOfMeasure) : ''; - + + let xUnit = xMetadata.unitOfMeasure ? + (xMetadata?.unitOfMeasure.includes("N/A") ? '' : xMetadata?.unitOfMeasure) : ''; + let yUnit = yMetadata.unitOfMeasure ? + (yMetadata?.unitOfMeasure.includes("N/A") ? '' : yMetadata?.unitOfMeasure) : ''; + let configuration = { - responsive: true, - plugins: { + responsive: true, + plugins: { + zoom: { + pan: { + enabled: true, + mode: 'x', + modifierKey: 'shift' + }, zoom: { - pan: { - enabled: true, - mode: 'x', - modifierKey: 'shift' - }, - zoom: { wheel: { - enabled: true + enabled: true }, pinch: { - enabled: true + enabled: true }, mode: 'x' - } } - }, - scales: { - x: { - title: { - display: true, - text: xUnit - }, - ticks: { - autoSkip: myWidget!.skipXLabel - } + } + }, + scales: { + x: { + title: { + display: true, + text: xUnit }, - y: { - title: { - display: true, - text: yUnit - } - } - }, - onClick: (e, elements) => {this.elaborateClick(e, elements)} + ticks: { + autoSkip: myWidget!.skipXLabel + } + }, + y: { + title: { + display: true, + text: yUnit + } + } + }, + onClick: (e, elements) => { + this.elaborateClick(e, elements) + } }; - + return new ChartDataLocal(labels, chartDataSets, colors, configuration); } - + transparentize(color?: string, opacity?: number) { - const _opacity = Math.round(Math.min(Math.max(opacity || 1, 0), 1) * 255); + const _opacity = Math.round(Math.min(Math.max(opacity || 1, 0), 1) * 255); return color + _opacity.toString(16).toUpperCase(); - } + } hexToRGB(hex: string, alpha?: string) { - var r = parseInt(hex.slice(1, 3), 16), - g = parseInt(hex.slice(3, 5), 16), - b = parseInt(hex.slice(5, 7), 16); - - if (alpha) { - return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"; - } else { - return "rgb(" + r + ", " + g + ", " + b + ")"; - } + var r = parseInt(hex.slice(1, 3), 16), + g = parseInt(hex.slice(3, 5), 16), + b = parseInt(hex.slice(5, 7), 16); + + if (alpha) { + return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"; + } else { + return "rgb(" + r + ", " + g + ", " + b + ")"; } + } + elaborateClick(evt: any, array: any[]) { + //console.log(evt); + //console.log(array); + const elements = this.mychart.chart!.getElementsAtEventForMode(evt, 'point', {intersect: false}, false); + + //console.log("WidgetComponent"); + if (elements && this.widget.parameterName) { + var dataIndex = elements[0].index; + var datasetIndex = elements[0].datasetIndex; + let element: Element = elements[0]; + /* + * Questo potrebbe essere migliorato perche' non utilizza le informazioni nei dati ma una bieca estrazioe del vvalore dalle proprieta' dell'oggeto + */ + const xValue = element.element.$context.raw.x; + + let queryParams: any = {}; + queryParams[this.widget.parameterName] = xValue; + if (this.widget.target) { + + //In this case, the target is decided by the administrator and I can move to the target when row is selected + let navigationExtras: NavigationExtras = { + queryParams: queryParams + }; + /* + * Questo pezzo di codice differisce da quello usato nel caso del table-widget perche', probabilmente, questa chiamata non e' fatta dalla pagina HTML + * ma da una elaborazione successiva. Questo significa che per angular, la richiesta esce dalla sua "zone". + */ + this.zone.run(() => { + this.router.navigate(["/pages/" + this.widget.target], { + relativeTo: this.route, + queryParams: queryParams, + queryParamsHandling: 'merge' + }); + }); + } else { + //in this case, only a parameter value is selcted and assigned - elaborateClick(evt: any, array: any[]){ - //console.log(evt); - //console.log(array); - const elements = this.mychart.chart!.getElementsAtEventForMode(evt, 'point', { intersect: false }, false); - - //console.log("WidgetComponent"); - if (elements && this.widget.parameterName){ - var dataIndex = elements[0].index; - var datasetIndex = elements[0].datasetIndex; - let element: Element = elements[0]; - /* - * Questo potrebbe essere migliorato perche' non utilizza le informazioni nei dati ma una bieca estrazioe del vvalore dalle proprieta' dell'oggeto - */ - const xValue = element.element.$context.raw.x; - - let queryParams: any = {}; - queryParams[this.widget.parameterName] = xValue; - if (this.widget.target) { - - //In this case, the target is decided by the administrator and I can move to the target when row is selected - let navigationExtras: NavigationExtras = { - queryParams: queryParams - }; - /* - * Questo pezzo di codice differisce da quello usato nel caso del table-widget perche', probabilmente, questa chiamata non e' fatta dalla pagina HTML - * ma da una elaborazione successiva. Questo significa che per angular, la richiesta esce dalla sua "zone". - */ - this.zone.run(() => { - this.router.navigate(["/pages/" + this.widget.target], { - relativeTo: this.route, - queryParams: queryParams, - queryParamsHandling: 'merge' - }); - }); - } - else{ - //in this case, only a parameter value is selcted and assigned - - // changes the route without moving from the current view or - // triggering a navigation event, - this.router.navigate([], { - relativeTo: this.route, - queryParams: queryParams, - queryParamsHandling: 'merge', // preserve the existing query params in the route - - }); + // changes the route without moving from the current view or + // triggering a navigation event, + this.router.navigate([], { + relativeTo: this.route, + queryParams: queryParams, + queryParamsHandling: 'merge', // preserve the existing query params in the route + + }); + } } - } -} - + } + } export class Response { - result?: any[]; + result?: any[]; metadata?: any[]; + constructor(init?: Partial) { Object.assign(this, init); } } -export class BackgroundColor{ - backgroundColor: string = "#92b3db"; - - constructor(init?: Partial) { - +export class BackgroundColor { + backgroundColor: string = "#92b3db"; + + constructor(init?: Partial) { + Object.assign(this, init); } - + } \ No newline at end of file diff --git a/lec/src/app/widgets/image/components/configuration/image-widget-configuration.component.ts b/lec/src/app/widgets/image/components/configuration/image-widget-configuration.component.ts index 0564569..cbd52e1 100644 --- a/lec/src/app/widgets/image/components/configuration/image-widget-configuration.component.ts +++ b/lec/src/app/widgets/image/components/configuration/image-widget-configuration.component.ts @@ -23,7 +23,7 @@ export class ImageWidgetConfigurationComponent implements OnInit { ngOnInit(): void { } - handleFileSelect(evt:any) { + handleFileSelect(evt: any) { evt.preventDefault() evt.stopImmediatePropagation(); const files: any[] = Array.from(evt.target.files); @@ -48,8 +48,7 @@ export class ImageWidgetConfigurationComponent implements OnInit { reader.onload = this._handleReaderLoaded.bind(this); reader.readAsBinaryString(file); - } - else { + } else { this.utilsService.showWarnMessageTrans("IMAGE_WIDGET_CONFIGURATION.FILE_UPLOAD_ERROR") } } diff --git a/lec/src/app/widgets/image/models/ImageWidget.ts b/lec/src/app/widgets/image/models/ImageWidget.ts index 288c5f4..a2661c5 100644 --- a/lec/src/app/widgets/image/models/ImageWidget.ts +++ b/lec/src/app/widgets/image/models/ImageWidget.ts @@ -10,7 +10,7 @@ export class ImageWidget extends Widget { public constructor(init?: Partial) { super(); - Object.assign(this, init); - this.type = "IMAGE"; + Object.assign(this, init); + this.type = "IMAGE"; } } \ No newline at end of file diff --git a/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.css b/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.css new file mode 100644 index 0000000..e69de29 diff --git a/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.html b/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.html new file mode 100644 index 0000000..c678fa7 --- /dev/null +++ b/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.html @@ -0,0 +1,82 @@ +
+
+
+
Marker {{ i + 1 }}
+ +
+ +
+ + + +
+
+
KPI {{ ki + 1 }}
+ +
+ + + + + +
+ +
+
+ + +
diff --git a/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.spec.ts b/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.spec.ts new file mode 100644 index 0000000..c646236 --- /dev/null +++ b/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MapWidgetConfigurationComponent } from './map-widget-configuration.component'; + +describe('MapWidgetConfigurationComponent', () => { + let component: MapWidgetConfigurationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MapWidgetConfigurationComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MapWidgetConfigurationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.ts b/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.ts new file mode 100644 index 0000000..707c1ea --- /dev/null +++ b/lec/src/app/widgets/map/components/map-widget-configuration/map-widget-configuration.component.ts @@ -0,0 +1,75 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {MapMarker, MapWidget} from "../models/map-widget"; +import {Community} from "../../../../models/Community"; +import {CommunityService} from "../../../../services/community.service"; +import {ParametricQuery} from "../../../../models/enea/ParametricQuery"; +import {DataService} from "../../../../services/data.service"; +import {DatasourceConfiguration} from "../../../Widget"; +import {TranslateService} from "@ngx-translate/core"; +import {DropdownChangeEvent} from "primeng/dropdown"; + +@Component({ + selector: 'app-map-widget-configuration', + templateUrl: './map-widget-configuration.component.html', + styleUrls: ['./map-widget-configuration.component.css'] +}) +export class MapWidgetConfigurationComponent implements OnInit { + @Input() widget?: MapWidget; + communities: Community[] = []; + kpis: ParametricQuery[] = []; + loading: boolean = false; + defaultCommunityName: string = "" + + constructor(private communityService: CommunityService, private dataService: DataService, private translateService: TranslateService) { + if (this.widget && !this.widget?.markers) + this.widget.markers = []; + } + + ngOnInit(): void { + this.translateService.get("LABEL.DEFAULT_COMMUNITY_NAME").subscribe({ + next: result => {this.defaultCommunityName = result} + }); + this.loadCommunities(); + this.loadParametricQueries(); + } + + loadParametricQueries() { + this.kpis = []; + this.dataService.getListOfFunctions().subscribe({ + next: result => this.kpis = result, + error: err => console.error(err) + }); + } + + private loadCommunities() { + this.communities = []; + this.communityService.getCommunities().subscribe({ + next: result => { + this.communities = result; + this.communities.unshift(Community.createDefaultUserCommunityEntry(this.defaultCommunityName)); + }, + error: err => console.error(err) + }); + } + + addCommunity() { + if (this.widget) { + if (!this.widget.markers) + this.widget.markers = []; + + this.widget.markers.push({ kpis: []} as MapMarker); + } + } + + removeMarker(marker: MapMarker, i: number) { + this.widget?.markers?.splice(i, 1); + } + + addKpi(marker: MapMarker) { + marker.kpis?.push(new DatasourceConfiguration()) + } + + removeKpi(marker: MapMarker, kpiIndex: number) { + marker?.kpis?.splice(kpiIndex, 1); + } +} diff --git a/lec/src/app/widgets/map/components/map-widget/map-widget.component.css b/lec/src/app/widgets/map/components/map-widget/map-widget.component.css new file mode 100644 index 0000000..1f609c1 --- /dev/null +++ b/lec/src/app/widgets/map/components/map-widget/map-widget.component.css @@ -0,0 +1,31 @@ +/*@import "leaflet/dist/leaflet.css";*/ + + +/*html, body { + height: 100%; + margin: 0; +} +.leaflet-container { + height: 400px; + width: 600px; + max-width: 100%; + max-height: 100%; +}*/ + + +/* +.map-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +}*/ + +/*#map { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +}*/ diff --git a/lec/src/app/widgets/map/components/map-widget/map-widget.component.html b/lec/src/app/widgets/map/components/map-widget/map-widget.component.html new file mode 100644 index 0000000..220de8c --- /dev/null +++ b/lec/src/app/widgets/map/components/map-widget/map-widget.component.html @@ -0,0 +1,4 @@ +
+ + + \ No newline at end of file diff --git a/lec/src/app/widgets/map/components/map-widget/map-widget.component.spec.ts b/lec/src/app/widgets/map/components/map-widget/map-widget.component.spec.ts new file mode 100644 index 0000000..9eb5180 --- /dev/null +++ b/lec/src/app/widgets/map/components/map-widget/map-widget.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MapWidgetComponent } from './map-widget.component'; + +describe('MapWidgetComponent', () => { + let component: MapWidgetComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MapWidgetComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MapWidgetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/lec/src/app/widgets/map/components/map-widget/map-widget.component.ts b/lec/src/app/widgets/map/components/map-widget/map-widget.component.ts new file mode 100644 index 0000000..5bb3751 --- /dev/null +++ b/lec/src/app/widgets/map/components/map-widget/map-widget.component.ts @@ -0,0 +1,202 @@ +import {AfterViewInit, Component, Input, OnInit, ViewChild, ViewContainerRef} from '@angular/core'; +import {MapMarker, MapWidget} from "../models/map-widget"; +import * as LF from 'leaflet'; +import {UserService} from "../../../../services/user.service"; +import {Community} from "../../../../models/Community"; +import {UtilsService} from "../../../../services/utils.service"; +import {TranslateService} from "@ngx-translate/core"; +import {LeafletMouseEvent, Marker} from "leaflet"; +import {forkJoin} from "rxjs"; +import {DataService} from "../../../../services/data.service"; +import {DatasourceConfiguration} from "../../../Widget"; + +@Component({ + selector: 'map-widget', + templateUrl: './map-widget.component.html', + styleUrls: ['./map-widget.component.css'] +}) +export class MapWidgetComponent implements OnInit, AfterViewInit { + @Input() widget?: MapWidget; + @ViewChild('popupContainer', { read: ViewContainerRef }) popupContainer?: ViewContainerRef; + private map: any; + loadingData: boolean = false; + + communities: Community [] = []; + markers: LF.Marker[] = []; + + constructor( + public userService: UserService, + private utils: UtilsService, + private dataService: DataService, + private translateService: TranslateService + ) { + } + + ngOnInit(): void {} + + ngAfterViewInit(): void { + this.initCommunities(); + } + + private initCommunities() { + this.userService.getCommunities().subscribe({ + next: result => { + this.communities = result; + this.initMap(); + this.initMarkers(); + }, + error: err => this.utils.showErrorMessage(err) + }) + } + + initMap() { + this.map = LF.map('map').setView([44.498955, 11.327591], 10); + LF.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 19, + attribution: '© OpenStreetMap' + }).addTo(this.map); + } + + private initMarkers() { + if (this.map && this.widget?.markers) { + this.widget.markers.forEach(markerConfig => { + const isUserCommunity = markerConfig && markerConfig.communityid === "my_communities"; + if (isUserCommunity) { + if (this.userService.user?.usercommunities && this.userService.user?.usercommunities.length > 0) { + this.userService.user?.usercommunities.forEach(uc => { + this.createMarkerAndAddToMap(uc, markerConfig); + }) + } + else { + this.utils.showErrorMessage("MAP_WIDGET.USER_HAS_NOT_ANY_COMMUNITY"); + } + } + else { + const community = this.communities.find(o => o.communityid == markerConfig.communityid); + if (community) { + this.createMarkerAndAddToMap(community, markerConfig); + } + else { + this.utils.showErrorMessage("MAP_WIDGET.COMMUNITY_NOT_FOUND") + } + } + }); + this.fitMarkers(); + } + } + + private createMarkerAndAddToMap(uc: Community, config: MapMarker) { + const marker = LF.marker([ + uc.georeferencex || 0, + uc.georeferencey || 0, + + ]); + + marker.bindTooltip(uc.communityName || "", { + permanent: false, // Tooltip shows on hover by default + direction: 'top', // Position the tooltip above the marker + }); + + marker.on('click', () => { + if (!this.loadingData) { + const content = this.getHtmlMessage("Caricando i dati ..."); + + if (marker.getPopup()) { + marker.setPopupContent(content); + } + else { + marker.bindPopup(content); + marker.openPopup(); + } + + this.loadData(marker, config, {"communityid": uc.communityid}); + } + }); + marker.addTo(this.map); + this.markers.push(marker); + } + + private loadData(marker: Marker, config: MapMarker, parameterValues: any) { + this.loadingData = true; + + let httpPosts = []; + + for (const kpi of config.kpis || []) { + httpPosts.push(this.dataService.getData(kpi, parameterValues)); + } + + const responses: any[] = []; + forkJoin(httpPosts).subscribe({ + next: result => { + for (let res of result) { + let response = { + "result": res?.result || [], + "metadata": res?.metadata || {} + }; + responses.push(response); + } + this.loadingData = false; + this.buildMarkerHtml(marker, responses, config); + }, + error: er => { + this.loadingData = false; + marker.setPopupContent(this.getHtmlMessage("Error during data loading!")); + } + }); + } + + private buildMarkerHtml(marker: Marker, data: any[], config: MapMarker) { + if (data) { + marker.setPopupContent(this.getHtmlMessage(JSON.stringify(data))); + let body = ""; + data.forEach((d, index) => { + const kpi = config.kpis![index]; + body += this.buildKpiHtml(d, kpi); + }); + + if (body) { + marker.setPopupContent(body); + } + else { + this.translateService.getTranslation("MAP_WIDGET.NO_DATA").subscribe(o => { + marker.setPopupContent(this.getHtmlMessage(o)); + }); + } + } + else { + this.translateService.getTranslation("MAP_WIDGET.NO_DATA").subscribe(o => { + marker.setPopupContent(this.getHtmlMessage(o)); + }); + } + } + + private buildKpiHtml(d: any, kpi: DatasourceConfiguration):string { + // d: {result: [], metadata: [{dbcolumnName: "column name"}.....]} + const header = `

${kpi.parametricQuery?.name}

` + return header + this.buildBodyHtml(d.metadata, d.result); + } + + private buildBodyHtml(metadata: any[], datas: any[]): string { + let result = "" + metadata.forEach((column, index) => { + if (column.name) { + result += `
${column?.description}
`; + datas.forEach(data => { + result += `
${data ? data[column.name] + ' ' + (column?.unitOfMeasure ? column?.unitOfMeasure : '') : '-'}
`; + }); + } + }) + return result; + } + + private getHtmlMessage(message: string) { + return `
+
${message}
+
`; + } + + private fitMarkers(): void { + const group = LF.featureGroup(this.markers); + this.map.fitBounds(group.getBounds()); + } +} diff --git a/lec/src/app/widgets/map/components/models/map-widget.ts b/lec/src/app/widgets/map/components/models/map-widget.ts new file mode 100644 index 0000000..1e8cc1a --- /dev/null +++ b/lec/src/app/widgets/map/components/models/map-widget.ts @@ -0,0 +1,26 @@ +import {DatasourceConfiguration, Widget} from "../../../Widget"; +import {Community} from "../../../../models/Community"; + +export class MapWidget extends Widget { + markers?: MapMarker[] = []; + + public constructor(init?: Partial) { + super(); + Object.assign(this, init); + this.type = "MAP"; + } +} + +export interface MapMarker { + kpis?: DatasourceConfiguration[] + communityid?: string; + showDevices?: boolean; +} + +// TODO: Use if more attributes are needed for the datasource configuration +/*export interface KpiConfiguration { + queryId?: string; + label?: string; + description?: string; + datasourceConfiguration?: DatasourceConfiguration; +}*/ diff --git a/lec/src/assets/i18n/en.json b/lec/src/assets/i18n/en.json index ff40de3..74b9fec 100644 --- a/lec/src/assets/i18n/en.json +++ b/lec/src/assets/i18n/en.json @@ -11,19 +11,21 @@ "CHART": "Chart", "LAYOUT": "Layout", "IMAGE": "Image", + "MAP": "Map", "INSERT_VALUE": "Insert value", "LOADING": "Loading", - "MY_COMMUNITIES" : "My communities", - "SELECT_A_COMMUNITY" : "Select a community", - "MY_DEVICES" : "My devices", - "SELECT_LANGUAGE" : "Select language", - "MY_PROFILE" : "My personal data", - "WHO_WE_ARE" : "About us", + "MY_COMMUNITIES": "My communities", + "SELECT_A_COMMUNITY": "Select a community", + "MY_DEVICES": "My devices", + "SELECT_LANGUAGE": "Select language", + "MY_PROFILE": "My personal data", + "WHO_WE_ARE": "About us", "CHANGEPASSWORD": "Update password", "UPDATEDATA": "Update data", "WITH": "With", "SIGN_OUT_ERROR": "Error during sign out. Please contact support!", - "SIGN_OUT_SUCCESS": "Signed out successfully" + "SIGN_OUT_SUCCESS": "Signed out successfully", + "DEFAULT_COMMUNITY_NAME": "Use user communities" }, "VALIDATION": { "NOT_EMPTY": "Can not be empty", @@ -58,7 +60,7 @@ "PAGE_NAME_REQUIRED": "Page name is required", "PAGE_SAVED_CAN_EDIT": "Page saved. You can now edit this page.", "PAGE_SAVED": "Page saved." - }, + }, "PAGES_LIST_TITLE": "Pages list", "PAGES_LIST_BUTTON": "Pages list", "VIEW_PAGE_TITLE": "View page", @@ -69,12 +71,12 @@ "VIEW_PAGE_BUTTON_SLUG": "As slug", "VIEW_PAGE_BUTTON_SLUG_NEW_PAGE": "As slug in new page", "UNTITLED_PAGE": "[Untitled page]", - "PAGE_CHANGED_PLEASE_SAVE": "Page has changed. Please save before viewing this page." - }, + "PAGE_CHANGED_PLEASE_SAVE": "Page has changed. Please save before viewing this page." + }, "PAGES_LIST": { - "NAME" : "Name", - "SLUG" : "Slug", - "LANGUAGE" : "Language", + "NAME": "Name", + "SLUG": "Slug", + "LANGUAGE": "Language", "CURRENT_PAGE_REPORT_TEMPLATE": "Showing {first} to {last} of {totalRecords} entries", "SEARCH": "Search by name or slug...", "TOOLTIP_SLUG": "Open as slug (new window)", @@ -85,17 +87,17 @@ "PAGE_EDITOR": { "NO_CONFIG": "No configuration for this page.", "STATIC_PAGE_TITLE": "Static page", - "STATIC_PAGE_SUBTITLE": "Content is available in configured path", - "PAGE_PROPERTIES": { + "STATIC_PAGE_SUBTITLE": "Content is available in configured path", + "PAGE_PROPERTIES": { "PANEL_TITLE": "Page properties" }, - "COLUMN_PROPERTIES": { + "COLUMN_PROPERTIES": { "PANEL_TITLE": "Column properties", "NO_COLUMN_SELECTED": "No column selected" }, "WIDGET_PROPERTIES": { "PANEL_TITLE": "Widget properties", - "NO_WIDGET_SELECTED": "No widget selected" + "NO_WIDGET_SELECTED": "No widget selected" }, "ADD_WIDGET": "Add widget", "REFRESH_WIDGET": "Refresh widget", @@ -105,7 +107,7 @@ "REMOVE_ROW_CONFIRM": "Are you sure that you want to proceed?", "ADD_COLUMN_TITLE": "Add column", "REMOVE_COLUMN_TITLE": "Remove column", - "REMOVE_COLUMN_CONFIRM": "Are you sure that you want to proceed?" + "REMOVE_COLUMN_CONFIRM": "Are you sure that you want to proceed?" }, "PAGE_PROPERTIES_EDITOR": { "NAME": "Page name", @@ -115,8 +117,8 @@ "DYNAMIC": "Dynamic page", "PERMISSION": "Permission to access this page", "COMMUNITY": "Community", - "SELECT_COMMUNITY" : "Choose community", - "SHOW_IN_MENU" : "Show in menu list" + "SELECT_COMMUNITY": "Choose community", + "SHOW_IN_MENU": "Show in menu list" }, "COLUMN_PROPERTIES_EDITOR": { "WIDTH": "Width" @@ -174,8 +176,8 @@ "POLARAREA": "Polar area", "RADAR": "Radar" }, - "SERIES_STYLES" : "Series styles", - "SERIES_COLOR" : "Series color" + "SERIES_STYLES": "Series styles", + "SERIES_COLOR": "Series color" }, "STATIC_PAGES": { "ABOUT_US": "About us", @@ -219,10 +221,8 @@ "BORDER_STYLE_DASHED": "Dashed", "BORDER_STYLE_NONE": "None", "PX_PLACEHOLDER": "Ez: 3px", - "BACKGROUND_COLOR": "Background color", "PADDING": "Padding", - "FONT": "Font", "FONT_SIZE": "Size", "FONT_COLOR": "Color" @@ -259,7 +259,7 @@ "ORGANIZATION": "Organization", "SUCCESS": "Update completed" }, - "CHANGEPASSWORD": { + "CHANGEPASSWORD": { "TITLE": "Update your password", "ACTION": "Update", "PASSWORD": "New Password", @@ -280,10 +280,24 @@ "ACTIVATE_ERROR": "Activation failed! Contact client service!", "CHECK": "Activation code check" }, - "PARAMETERS":{ - "startDate" : "Initial date", - "Intervallo" : "Interval", - "intervalString" : "Interval", - "endDate" : "Final date" - } + "PARAMETERS": { + "startDate": "Initial date", + "Intervallo": "Interval", + "intervalString": "Interval", + "endDate": "Final date" + }, + "MAP_WIDGET": { + "MAP_WIDGET.COMMUNITY_NOT_FOUND": "Selected community is not found. Please contact support!", + "USER_HAS_NOT_ANY_COMMUNITY": "User has not any assigned community!", + "NO_DATA": "No data to show!" + }, + "MAP_WIDGET_CONFIGURATION": { + "ADD_COMMUNITY": "Add marker", + "SELECT_A_COMMUNITY": "Select a community", + "ADD_KPI": "Add KPI", + "KPI_LABEL": "Title", + "KPI_DESCRIPTION": "Description", + "SELECT_A_KPI": "Select KPI", + "REMOVE_KPI": "Remove KPI" + } } \ No newline at end of file diff --git a/lec/src/assets/i18n/it.json b/lec/src/assets/i18n/it.json index cba44f6..f280af2 100644 --- a/lec/src/assets/i18n/it.json +++ b/lec/src/assets/i18n/it.json @@ -10,19 +10,21 @@ "TABLE": "Tabella", "CHART": "Grafico", "LAYOUT": "Layout", - "IMAGE": "Immagin e", - "LOADING": "Caricamento...", - "MY_COMMUNITIES" : "Le mie comunità", + "IMAGE": "Immagine", + "MAP": "Mappa", + "LOADING": "Caricamento...", + "MY_COMMUNITIES": "Le mie comunità", "SELECT_A_COMMUNITY": "Seleziona una comunità", - "MY_DEVICES" : "I miei dispositivi", - "SELECT_LANGUAGE" : "Scelta lingua", - "MY_PROFILE" : "Modifica info", - "WHO_WE_ARE" : "Chi siamo", + "MY_DEVICES": "I miei dispositivi", + "SELECT_LANGUAGE": "Scelta lingua", + "MY_PROFILE": "Modifica info", + "WHO_WE_ARE": "Chi siamo", "CHANGEPASSWORD": "Modifica la password", "UPDATEDATA": "Aggiorna i dati", "WITH": "CON", "SIGN_OUT_ERROR": "Errore durante log out. Contattare il supporto!", - "SIGN_OUT_SUCCESS": "Logout effetuato con successo" + "SIGN_OUT_SUCCESS": "Logout effetuato con successo", + "DEFAULT_COMMUNITY_NAME": "Usa le communità dell'utente" }, "VALIDATION": { "NOT_EMPTY": "Non può essere vuoto", @@ -117,8 +119,8 @@ "DYNAMIC": "Pagina dinamica", "PERMISSION": "Permesso per accedere alla pagina", "COMMUNITY": "Comunità", - "SELECT_COMMUNITY" : "Selezionare comunità", - "SHOW_IN_MENU" : "Mostra nei menù" + "SELECT_COMMUNITY": "Selezionare comunità", + "SHOW_IN_MENU": "Mostra nei menù" }, "COLUMN_PROPERTIES_EDITOR": { "WIDTH": "Larghezza" @@ -222,10 +224,8 @@ "BORDER_STYLE_DASHED": "Trattini", "BORDER_STYLE_NONE": "Nessuno", "PX_PLACEHOLDER": "Es: 3px", - "BACKGROUND_COLOR": "Colore sfondo", "PADDING": "Padding", - "FONT": "Caratteri", "FONT_SIZE": "Dimensioni", "FONT_COLOR": "Colore" @@ -303,10 +303,24 @@ "ACTIVATE_ERROR": "Attivazione fallita! Conttatare il servizio clienti!", "CHECK": "Controllo codice" }, - "PARAMETERS":{ - "startDate" : "Data iniziale", - "Intervallo" : "Intervallo", - "intervalString" : "Intervallo", - "endDate" : "Data finale" + "PARAMETERS": { + "startDate": "Data iniziale", + "Intervallo": "Intervallo", + "intervalString": "Intervallo", + "endDate": "Data finale" + }, + "MAP_WIDGET": { + "NO_DATA": "Nessun dato da visualizzare", + "MAP_WIDGET.COMMUNITY_NOT_FOUND": "Communità selezionatà non trovata. Contattare il supporto!", + "USER_HAS_NOT_ANY_COMMUNITY": "L'utente non ha nessuna communita associata!" + }, + "MAP_WIDGET_CONFIGURATION": { + "ADD_COMMUNITY": "Aggiungi marker", + "SELECT_A_COMMUNITY": "Seleziona una communità", + "ADD_KPI": "Aggiungi KPI", + "KPI_LABEL": "Titolo", + "KPI_DESCRIPTION": "Descrizione", + "SELECT_A_KPI": "Seleziona il KPI", + "REMOVE_KPI": "Rimuovi KPI" } } diff --git a/lecservice/src/main/java/enea/lecservice/configuration/StartUpRunner.java b/lecservice/src/main/java/enea/lecservice/configuration/StartUpRunner.java index 745fb37..0526fb8 100644 --- a/lecservice/src/main/java/enea/lecservice/configuration/StartUpRunner.java +++ b/lecservice/src/main/java/enea/lecservice/configuration/StartUpRunner.java @@ -14,6 +14,6 @@ public class StartUpRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { - log.error("Host is: {}", dbHost); + log.info("Host is: {}", dbHost); } } diff --git a/lecservice/src/main/java/enea/lecservice/models/dto/CommunityDTO.java b/lecservice/src/main/java/enea/lecservice/models/dto/CommunityDTO.java index 7f74b10..b9dd3b1 100644 --- a/lecservice/src/main/java/enea/lecservice/models/dto/CommunityDTO.java +++ b/lecservice/src/main/java/enea/lecservice/models/dto/CommunityDTO.java @@ -15,10 +15,10 @@ import lombok.ToString; //@JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME) @JsonInclude(JsonInclude.Include.NON_NULL) public class CommunityDTO { - - private Long communityid; + private String communityid; private String communityName; private String description; private String wallet; - + private Double georeferencex; + private Double georeferencey; } \ No newline at end of file diff --git a/lecservice/src/main/java/enea/lecservice/security/dto/UserInfo.java b/lecservice/src/main/java/enea/lecservice/security/dto/UserInfo.java index 92ad8e9..e09ed91 100644 --- a/lecservice/src/main/java/enea/lecservice/security/dto/UserInfo.java +++ b/lecservice/src/main/java/enea/lecservice/security/dto/UserInfo.java @@ -87,7 +87,8 @@ public class UserInfo extends BaseUserInfo { String communityName; String description; String wallet; - + Double georeferencex; + Double georeferencey; } @Data diff --git a/lecservice/src/main/java/enea/lecservice/services/RegisterService.java b/lecservice/src/main/java/enea/lecservice/services/RegisterService.java index 3f68686..0376731 100644 --- a/lecservice/src/main/java/enea/lecservice/services/RegisterService.java +++ b/lecservice/src/main/java/enea/lecservice/services/RegisterService.java @@ -26,7 +26,7 @@ public class RegisterService implements IRegisterService { @Override public String getCommunities(BaseRequest request) { - String url = baseUrl + "community/listAllCommunities"; + String url = baseUrl + "user/community/listAllCommunities"; var headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_LANGUAGE, request.getContentLanguage()); diff --git a/lecservice/src/main/java/enea/lecservice/widgets/Widget.java b/lecservice/src/main/java/enea/lecservice/widgets/Widget.java index 66c9c6f..711ceeb 100644 --- a/lecservice/src/main/java/enea/lecservice/widgets/Widget.java +++ b/lecservice/src/main/java/enea/lecservice/widgets/Widget.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import enea.lecservice.widgets.chart.models.ChartWidget; import enea.lecservice.widgets.image.models.ImageWidget; +import enea.lecservice.widgets.map.MapWidget; import enea.lecservice.widgets.table.models.TableWidget; import enea.lecservice.widgets.text.models.TextWidget; import lombok.AllArgsConstructor; @@ -23,7 +24,8 @@ import lombok.experimental.SuperBuilder; @JsonSubTypes.Type(value = TextWidget.class, name = "TEXT"), @JsonSubTypes.Type(value = TableWidget.class, name = "TABLE"), @JsonSubTypes.Type(value = ChartWidget.class, name = "CHART"), - @JsonSubTypes.Type(value = ImageWidget.class, name = "IMAGE") + @JsonSubTypes.Type(value = ImageWidget.class, name = "IMAGE"), + @JsonSubTypes.Type(value = MapWidget.class, name = "MAP") }) public abstract class Widget { protected WidgetType type; @@ -34,6 +36,7 @@ public abstract class Widget { TABLE, CHART, IMAGE, - LAYOUT + LAYOUT, + MAP } } diff --git a/lecservice/src/main/java/enea/lecservice/widgets/map/MapWidget.java b/lecservice/src/main/java/enea/lecservice/widgets/map/MapWidget.java new file mode 100644 index 0000000..acd04ba --- /dev/null +++ b/lecservice/src/main/java/enea/lecservice/widgets/map/MapWidget.java @@ -0,0 +1,30 @@ +package enea.lecservice.widgets.map; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import enea.lecservice.models.dto.CommunityDTO; +import enea.lecservice.models.dto.DatasourceConfiguration; +import enea.lecservice.widgets.Widget; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import java.util.ArrayList; +import java.util.List; + +@SuperBuilder +@Getter +@Setter +@NoArgsConstructor +@JsonTypeName("MAP") +public class MapWidget extends Widget { + protected List markers = new ArrayList<>(); + + + @Getter + @Setter + public static class MapMarker { + protected List kpis = new ArrayList<>(); + protected String communityid; + protected boolean showDevices = false; + } +} diff --git a/lecservice/src/main/resources/application.properties b/lecservice/src/main/resources/application.properties index 623aea2..0cc9574 100644 --- a/lecservice/src/main/resources/application.properties +++ b/lecservice/src/main/resources/application.properties @@ -28,4 +28,4 @@ spring.jackson.serialization.fail-on-empty-beans=false skip-context-refresh-handler=false # Data service -data.api.baseurl=http://${BACKOFFICE_API:localhost}:8004/geco +data.api.baseurl=http://${BACKOFFICE_API:localhost}:8004/geco/ -- GitLab