├── src ├── main │ ├── resources │ │ └── application.yml │ └── java │ │ └── com │ │ └── idealista │ │ ├── domain │ │ ├── Quality.java │ │ ├── Typology.java │ │ ├── AdRepository.java │ │ ├── Constants.java │ │ ├── Picture.java │ │ └── Ad.java │ │ ├── Main.java │ │ ├── application │ │ ├── AdsService.java │ │ └── AdsServiceImpl.java │ │ └── infrastructure │ │ ├── persistence │ │ ├── PictureVO.java │ │ ├── AdVO.java │ │ └── InMemoryPersistence.java │ │ └── api │ │ ├── AdsController.java │ │ ├── PublicAd.java │ │ └── QualityAd.java └── test │ └── java │ └── com │ └── idealista │ └── application │ └── AdsServiceImplTest.java ├── .gitignore ├── pom.xml └── README.md /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | target/ 4 | .settings/ 5 | 6 | .project 7 | .classpath -------------------------------------------------------------------------------- /src/main/java/com/idealista/domain/Quality.java: -------------------------------------------------------------------------------- 1 | package com.idealista.domain; 2 | 3 | public enum Quality { 4 | HD, 5 | SD, 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/domain/Typology.java: -------------------------------------------------------------------------------- 1 | package com.idealista.domain; 2 | 3 | public enum Typology { 4 | FLAT, 5 | CHALET, 6 | GARAGE, 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/domain/AdRepository.java: -------------------------------------------------------------------------------- 1 | package com.idealista.domain; 2 | 3 | import java.util.List; 4 | 5 | public interface AdRepository { 6 | List findAllAds(); 7 | 8 | void save(Ad ad); 9 | 10 | List findRelevantAds(); 11 | 12 | List findIrrelevantAds(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/Main.java: -------------------------------------------------------------------------------- 1 | package com.idealista; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Main { 8 | 9 | public static void main(String [] args){ 10 | SpringApplication.run(Main.class, args); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /src/main/java/com/idealista/application/AdsService.java: -------------------------------------------------------------------------------- 1 | package com.idealista.application; 2 | 3 | import com.idealista.infrastructure.api.PublicAd; 4 | import com.idealista.infrastructure.api.QualityAd; 5 | 6 | import java.util.List; 7 | 8 | public interface AdsService { 9 | 10 | List findPublicAds(); 11 | List findQualityAds(); 12 | void calculateScores(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/domain/Constants.java: -------------------------------------------------------------------------------- 1 | package com.idealista.domain; 2 | 3 | public class Constants { 4 | public static final int ZERO = 0; 5 | public static final int FIVE = 5; 6 | public static final int TEN = 10; 7 | public static final int TWENTY = 20; 8 | public static final int THIRTY = 30; 9 | public static final int FORTY = 40; 10 | public static final int FORTY_NINE = 49; 11 | public static final int FIFTY = 50; 12 | public static final int ONE_HUNDRED = 100; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/infrastructure/persistence/PictureVO.java: -------------------------------------------------------------------------------- 1 | package com.idealista.infrastructure.persistence; 2 | 3 | public class PictureVO { 4 | 5 | private Integer id; 6 | private String url; 7 | private String quality; 8 | 9 | public PictureVO() {} 10 | 11 | public PictureVO(Integer id, String url, String quality) { 12 | this.id = id; 13 | this.url = url; 14 | this.quality = quality; 15 | } 16 | 17 | public Integer getId() { 18 | return id; 19 | } 20 | 21 | public void setId(Integer id) { 22 | this.id = id; 23 | } 24 | 25 | public String getUrl() { 26 | return url; 27 | } 28 | 29 | public void setUrl(String url) { 30 | this.url = url; 31 | } 32 | 33 | public String getQuality() { 34 | return quality; 35 | } 36 | 37 | public void setQuality(String quality) { 38 | this.quality = quality; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/infrastructure/api/AdsController.java: -------------------------------------------------------------------------------- 1 | package com.idealista.infrastructure.api; 2 | 3 | import java.util.List; 4 | 5 | import com.idealista.application.AdsService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | public class AdsController { 13 | 14 | @Autowired 15 | private AdsService adsService; 16 | 17 | @GetMapping("/ads/quality") 18 | public ResponseEntity> qualityListing() { 19 | return ResponseEntity.ok(adsService.findQualityAds()); 20 | } 21 | 22 | @GetMapping("/ads/public") 23 | public ResponseEntity> publicListing() { 24 | return ResponseEntity.ok(adsService.findPublicAds()); 25 | } 26 | 27 | @GetMapping("/ads/score") 28 | public ResponseEntity calculateScore() { 29 | adsService.calculateScores(); 30 | return ResponseEntity.accepted().build(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.idealista 8 | ad-ranking-challenge 9 | 1.0 10 | jar 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.1.3.RELEASE 16 | 17 | 18 | 19 | 20 | 1.8 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.junit.jupiter 30 | junit-jupiter-engine 31 | test 32 | 33 | 34 | org.mockito 35 | mockito-junit-jupiter 36 | test 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/infrastructure/api/PublicAd.java: -------------------------------------------------------------------------------- 1 | package com.idealista.infrastructure.api; 2 | 3 | import java.util.List; 4 | 5 | public class PublicAd { 6 | 7 | private Integer id; 8 | private String typology; 9 | private String description; 10 | private List pictureUrls; 11 | private Integer houseSize; 12 | private Integer gardenSize; 13 | 14 | public Integer getId() { 15 | return id; 16 | } 17 | 18 | public void setId(Integer id) { 19 | this.id = id; 20 | } 21 | 22 | public String getTypology() { 23 | return typology; 24 | } 25 | 26 | public void setTypology(String typology) { 27 | this.typology = typology; 28 | } 29 | 30 | public String getDescription() { 31 | return description; 32 | } 33 | 34 | public void setDescription(String description) { 35 | this.description = description; 36 | } 37 | 38 | public List getPictureUrls() { 39 | return pictureUrls; 40 | } 41 | 42 | public void setPictureUrls(List pictureUrls) { 43 | this.pictureUrls = pictureUrls; 44 | } 45 | 46 | public Integer getHouseSize() { 47 | return houseSize; 48 | } 49 | 50 | public void setHouseSize(Integer houseSize) { 51 | this.houseSize = houseSize; 52 | } 53 | 54 | public Integer getGardenSize() { 55 | return gardenSize; 56 | } 57 | 58 | public void setGardenSize(Integer gardenSize) { 59 | this.gardenSize = gardenSize; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/domain/Picture.java: -------------------------------------------------------------------------------- 1 | package com.idealista.domain; 2 | 3 | import java.util.Objects; 4 | 5 | public class Picture { 6 | private Integer id; 7 | private String url; 8 | private Quality quality; 9 | 10 | public Picture(Integer id, String url, Quality quality) { 11 | this.id = id; 12 | this.url = url; 13 | this.quality = quality; 14 | } 15 | 16 | public Integer getId() { 17 | return id; 18 | } 19 | 20 | public void setId(Integer id) { 21 | this.id = id; 22 | } 23 | 24 | public String getUrl() { 25 | return url; 26 | } 27 | 28 | public void setUrl(String url) { 29 | this.url = url; 30 | } 31 | 32 | public Quality getQuality() { 33 | return quality; 34 | } 35 | 36 | public void setQuality(Quality quality) { 37 | this.quality = quality; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "Picture{" + 43 | "id=" + id + 44 | ", url='" + url + '\'' + 45 | ", quality=" + quality + 46 | '}'; 47 | } 48 | 49 | @Override 50 | public boolean equals(Object o) { 51 | if (this == o) return true; 52 | if (o == null || getClass() != o.getClass()) return false; 53 | Picture picture = (Picture) o; 54 | return Objects.equals(id, picture.id) && Objects.equals(url, picture.url) && quality == picture.quality; 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(id, url, quality); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/idealista/application/AdsServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package com.idealista.application; 2 | 3 | import com.idealista.domain.*; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.InjectMocks; 7 | import org.mockito.Mock; 8 | import org.mockito.junit.jupiter.MockitoExtension; 9 | 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | 13 | import static org.mockito.ArgumentMatchers.any; 14 | import static org.mockito.Mockito.*; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | class AdsServiceImplTest { 18 | 19 | @Mock 20 | private AdRepository adRepository; 21 | 22 | @InjectMocks 23 | private AdsServiceImpl scoreService; 24 | 25 | @Test 26 | public void calculateScoresTest() { 27 | when(adRepository.findAllAds()).thenReturn(Arrays.asList(irrelevantAd(), relevantAd())); 28 | scoreService.calculateScores(); 29 | verify(adRepository).findAllAds(); 30 | verify(adRepository, times(2)).save(any()); 31 | } 32 | 33 | private Ad relevantAd() { 34 | return new Ad(1, 35 | Typology.FLAT, 36 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras dictum felis elit, vitae cursus erat blandit vitae. Maecenas eget efficitur massa. Maecenas ut dolor eget enim consequat iaculis vitae nec elit. Maecenas eu urna nec massa feugiat pharetra. Sed eu quam imperdiet orci lobortis fermentum. Sed odio justo, congue eget iaculis.", 37 | Arrays.asList(new Picture(1, "http://urldeprueba.com/1", Quality.HD), new Picture(2, "http://urldeprueba.com/2", Quality.HD)), 38 | 50, 39 | null); 40 | } 41 | 42 | private Ad irrelevantAd() { 43 | return new Ad(1, 44 | Typology.FLAT, 45 | "", 46 | Collections.emptyList(), 47 | 100, 48 | null); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/java/com/idealista/infrastructure/api/QualityAd.java: -------------------------------------------------------------------------------- 1 | package com.idealista.infrastructure.api; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | public class QualityAd { 7 | 8 | private Integer id; 9 | private String typology; 10 | private String description; 11 | private List pictureUrls; 12 | private Integer houseSize; 13 | private Integer gardenSize; 14 | private Integer score; 15 | private Date irrelevantSince; 16 | 17 | public Integer getId() { 18 | return id; 19 | } 20 | 21 | public void setId(Integer id) { 22 | this.id = id; 23 | } 24 | 25 | public String getTypology() { 26 | return typology; 27 | } 28 | 29 | public void setTypology(String typology) { 30 | this.typology = typology; 31 | } 32 | 33 | public String getDescription() { 34 | return description; 35 | } 36 | 37 | public void setDescription(String description) { 38 | this.description = description; 39 | } 40 | 41 | public List getPictureUrls() { 42 | return pictureUrls; 43 | } 44 | 45 | public void setPictureUrls(List pictureUrls) { 46 | this.pictureUrls = pictureUrls; 47 | } 48 | 49 | public Integer getHouseSize() { 50 | return houseSize; 51 | } 52 | 53 | public void setHouseSize(Integer houseSize) { 54 | this.houseSize = houseSize; 55 | } 56 | 57 | public Integer getGardenSize() { 58 | return gardenSize; 59 | } 60 | 61 | public void setGardenSize(Integer gardenSize) { 62 | this.gardenSize = gardenSize; 63 | } 64 | 65 | public Integer getScore() { 66 | return score; 67 | } 68 | 69 | public void setScore(Integer score) { 70 | this.score = score; 71 | } 72 | 73 | public Date getIrrelevantSince() { 74 | return irrelevantSince; 75 | } 76 | 77 | public void setIrrelevantSince(Date irrelevantSince) { 78 | this.irrelevantSince = irrelevantSince; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/infrastructure/persistence/AdVO.java: -------------------------------------------------------------------------------- 1 | package com.idealista.infrastructure.persistence; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | public class AdVO { 7 | 8 | private Integer id; 9 | private String typology; 10 | private String description; 11 | private List pictures; 12 | private Integer houseSize; 13 | private Integer gardenSize; 14 | private Integer score; 15 | private Date irrelevantSince; 16 | 17 | public AdVO() {} 18 | 19 | public AdVO(Integer id, String typology, String description, List pictures, Integer houseSize, Integer gardenSize, Integer score, Date irrelevantSince) { 20 | this.id = id; 21 | this.typology = typology; 22 | this.description = description; 23 | this.pictures = pictures; 24 | this.houseSize = houseSize; 25 | this.gardenSize = gardenSize; 26 | this.score = score; 27 | this.irrelevantSince = irrelevantSince; 28 | } 29 | 30 | public Integer getId() { 31 | return id; 32 | } 33 | 34 | public void setId(Integer id) { 35 | this.id = id; 36 | } 37 | 38 | public String getTypology() { 39 | return typology; 40 | } 41 | 42 | public void setTypology(String typology) { 43 | this.typology = typology; 44 | } 45 | 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | public void setDescription(String description) { 51 | this.description = description; 52 | } 53 | 54 | public List getPictures() { 55 | return pictures; 56 | } 57 | 58 | public void setPictures(List pictures) { 59 | this.pictures = pictures; 60 | } 61 | 62 | public Integer getHouseSize() { 63 | return houseSize; 64 | } 65 | 66 | public void setHouseSize(Integer houseSize) { 67 | this.houseSize = houseSize; 68 | } 69 | 70 | public Integer getGardenSize() { 71 | return gardenSize; 72 | } 73 | 74 | public void setGardenSize(Integer gardenSize) { 75 | this.gardenSize = gardenSize; 76 | } 77 | 78 | public Integer getScore() { 79 | return score; 80 | } 81 | 82 | public void setScore(Integer score) { 83 | this.score = score; 84 | } 85 | 86 | public Date getIrrelevantSince() { 87 | return irrelevantSince; 88 | } 89 | 90 | public void setIrrelevantSince(Date irrelevantSince) { 91 | this.irrelevantSince = irrelevantSince; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reto: Servicio para gestión de calidad de los anuncios 2 | 3 | [![Build Status](https://travis-ci.org/idealista/coding-test-ranking.svg?branch=master)](https://travis-ci.org/idealista/coding-test-ranking) 4 | 5 | Este repositorio contiene el API de un servicio que se encarga de medir la calidad de los anuncios. Tu objetivo será realizar un code review sobre el código contenido en el repositorio. Para ello, te proporcionamos la descripción de la tarea que habría recibido el equipo de desarrollo. 6 | 7 | Los supuestos están basados en un hipotético *equipo de gestión de calidad de los anuncios*, que demanda una serie de verificaciones automáticas para clasificar los anuncios en base a una serie de características concretas. 8 | 9 | ## Historias de usuario 10 | 11 | * Yo, como encargado del equipo de gestión de calidad de los anuncios quiero asignar una puntuación a un anuncio para que los usuarios de idealista puedan ordenar anuncios de más completos a menos completos. La puntuación del anuncio es un valor entre 0 y 100 que se calcula teniendo en cuenta las siguientes reglas: 12 | * Si el anuncio no tiene ninguna foto se restan 10 puntos. Cada foto que tenga el anuncio proporciona 20 puntos si es una foto de alta resolución (HD) o 10 si no lo es. 13 | * Que el anuncio tenga un texto descriptivo suma 5 puntos. 14 | * El tamaño de la descripción también proporciona puntos cuando el anuncio es sobre un piso o sobre un chalet. En el caso de los pisos, la descripción aporta 10 puntos si tiene entre 20 y 49 palabras o 30 puntos si tiene 50 o más palabras. En el caso de los chalets, si tiene más de 50 palabras, añade 20 puntos. 15 | * Que las siguientes palabras aparezcan en la descripción añaden 5 puntos cada una: Luminoso, Nuevo, Céntrico, Reformado, Ático. 16 | * Que el anuncio esté completo también aporta puntos. Para considerar un anuncio completo este tiene que tener descripción, al menos una foto y los datos particulares de cada tipología, esto es, en el caso de los pisos tiene que tener también tamaño de vivienda, en el de los chalets, tamaño de vivienda y de jardín. Además, excepcionalmente, en los garajes no es necesario que el anuncio tenga descripción. Si el anuncio tiene todos los datos anteriores, proporciona otros 40 puntos. 17 | 18 | * Yo como encargado de calidad quiero que los usuarios no vean anuncios irrelevantes para que el usuario siempre vea contenido de calidad en idealista. Un anuncio se considera irrelevante si tiene una puntación inferior a 40 puntos. 19 | 20 | * Yo como encargado de calidad quiero poder ver los anuncios irrelevantes y desde que fecha lo son para medir la calidad media del contenido del portal. 21 | 22 | * Yo como usuario de idealista quiero poder ver los anuncios ordenados de mejor a peor para encontrar fácilmente mi vivienda. 23 | 24 | ## Consideraciones importantes 25 | 26 | Para nosotros, lo importante de este code review es entender como piensas. Queremos saber que consideras importante y que no, que crees que podría mejorarse y que crees que está bien como está. 27 | 28 | Si tienes dudas entre comentar algo porque es algo que en un proyecto real no comentarías, hazlo. Sabemos que en este code review falta un montón de contexto sobre consensos que tendrías con tu equipo en una situación real, por lo que cualquier comentario nos va a servir mejor para entenderte. 29 | 30 | No queremos que dediques tiempo a refactorizar el código, pero si hay algún comentario que quieres hacer y no sabes como explicarnos, nos puedes adjuntar código en cualquier lenguaje que conozcas (o pseudocódigo). 31 | 32 | Para facilitar las cosas, cuando quieras referirte a alguna línea en concreto del código utiliza como nomenclatura algo parecido a NombreDeClase#lineaDeCódigo o NombreDeClase#NombreDeMétodo. 33 | 34 | ## Criterios de aceptación 35 | 36 | Debes entregarnos un fichero de texto con todos los comentarios que harías sobre el código del repositorio. 37 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/domain/Ad.java: -------------------------------------------------------------------------------- 1 | package com.idealista.domain; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | import java.util.Objects; 6 | 7 | public class Ad { 8 | 9 | private Integer id; 10 | private Typology typology; 11 | private String description; 12 | private List pictures; 13 | private Integer houseSize; 14 | private Integer gardenSize; 15 | private Integer score; 16 | private Date irrelevantSince; 17 | 18 | public Ad(Integer id, 19 | Typology typology, 20 | String description, 21 | List pictures, 22 | Integer houseSize, 23 | Integer gardenSize, 24 | Integer score, 25 | Date irrelevantSince) { 26 | this.id = id; 27 | this.typology = typology; 28 | this.description = description; 29 | this.pictures = pictures; 30 | this.houseSize = houseSize; 31 | this.gardenSize = gardenSize; 32 | this.score = score; 33 | this.irrelevantSince = irrelevantSince; 34 | } 35 | 36 | public Ad(Integer id, 37 | Typology typology, 38 | String description, 39 | List pictures, 40 | Integer houseSize, 41 | Integer gardenSize) { 42 | this.id = id; 43 | this.typology = typology; 44 | this.description = description; 45 | this.pictures = pictures; 46 | this.houseSize = houseSize; 47 | this.gardenSize = gardenSize; 48 | } 49 | 50 | public boolean isComplete() { 51 | return (Typology.GARAGE.equals(typology) && !pictures.isEmpty()) 52 | || (Typology.FLAT.equals(typology) && !pictures.isEmpty() && description != null && !description.isEmpty() && houseSize != null) 53 | || (Typology.CHALET.equals(typology) && !pictures.isEmpty() && description != null && !description.isEmpty() && houseSize != null && gardenSize != null); 54 | } 55 | 56 | public Integer getId() { 57 | return id; 58 | } 59 | 60 | public void setId(Integer id) { 61 | this.id = id; 62 | } 63 | 64 | public Typology getTypology() { 65 | return typology; 66 | } 67 | 68 | public void setTypology(Typology typology) { 69 | this.typology = typology; 70 | } 71 | 72 | public String getDescription() { 73 | return description; 74 | } 75 | 76 | public void setDescription(String description) { 77 | this.description = description; 78 | } 79 | 80 | public List getPictures() { 81 | return pictures; 82 | } 83 | 84 | public void setPictures(List pictures) { 85 | this.pictures = pictures; 86 | } 87 | 88 | public Integer getHouseSize() { 89 | return houseSize; 90 | } 91 | 92 | public void setHouseSize(Integer houseSize) { 93 | this.houseSize = houseSize; 94 | } 95 | 96 | public Integer getGardenSize() { 97 | return gardenSize; 98 | } 99 | 100 | public void setGardenSize(Integer gardenSize) { 101 | this.gardenSize = gardenSize; 102 | } 103 | 104 | public Integer getScore() { 105 | return score; 106 | } 107 | 108 | public void setScore(Integer score) { 109 | this.score = score; 110 | } 111 | 112 | public Date getIrrelevantSince() { 113 | return irrelevantSince; 114 | } 115 | 116 | public void setIrrelevantSince(Date irrelevantSince) { 117 | this.irrelevantSince = irrelevantSince; 118 | } 119 | 120 | @Override 121 | public boolean equals(Object o) { 122 | if (this == o) return true; 123 | if (o == null || getClass() != o.getClass()) return false; 124 | Ad ad = (Ad) o; 125 | return Objects.equals(id, ad.id) && typology == ad.typology && Objects.equals(description, ad.description) && Objects.equals(pictures, ad.pictures) && Objects.equals(houseSize, ad.houseSize) && Objects.equals(gardenSize, ad.gardenSize) && Objects.equals(score, ad.score) && Objects.equals(irrelevantSince, ad.irrelevantSince); 126 | } 127 | 128 | @Override 129 | public int hashCode() { 130 | return Objects.hash(id, typology, description, pictures, houseSize, gardenSize, score, irrelevantSince); 131 | } 132 | 133 | @Override 134 | public String toString() { 135 | return "Ad{" + 136 | "id=" + id + 137 | ", typology=" + typology + 138 | ", description='" + description + '\'' + 139 | ", pictures=" + pictures + 140 | ", houseSize=" + houseSize + 141 | ", gardenSize=" + gardenSize + 142 | ", score=" + score + 143 | ", irrelevantSince=" + irrelevantSince + 144 | '}'; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/infrastructure/persistence/InMemoryPersistence.java: -------------------------------------------------------------------------------- 1 | package com.idealista.infrastructure.persistence; 2 | 3 | import com.idealista.domain.*; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | @Repository 13 | public class InMemoryPersistence implements AdRepository { 14 | 15 | private List ads; 16 | private List pictures; 17 | 18 | public InMemoryPersistence() { 19 | ads = new ArrayList(); 20 | ads.add(new AdVO(1, "CHALET", "Este piso es una ganga, compra, compra, COMPRA!!!!!", Collections.emptyList(), 300, null, null, null)); 21 | ads.add(new AdVO(2, "FLAT", "Nuevo ático céntrico recién reformado. No deje pasar la oportunidad y adquiera este ático de lujo", Arrays.asList(4), 300, null, null, null)); 22 | ads.add(new AdVO(3, "CHALET", "", Arrays.asList(2), 300, null, null, null)); 23 | ads.add(new AdVO(4, "FLAT", "Ático céntrico muy luminoso y recién reformado, parece nuevo", Arrays.asList(5), 300, null, null, null)); 24 | ads.add(new AdVO(5, "FLAT", "Pisazo,", Arrays.asList(3, 8), 300, null, null, null)); 25 | ads.add(new AdVO(6, "GARAGE", "", Arrays.asList(6), 300, null, null, null)); 26 | ads.add(new AdVO(7, "GARAGE", "Garaje en el centro de Albacete", Collections.emptyList(), 300, null, null, null)); 27 | ads.add(new AdVO(8, "CHALET", "Maravilloso chalet situado en lAs afueras de un pequeño pueblo rural. El entorno es espectacular, las vistas magníficas. ¡Cómprelo ahora!", Arrays.asList(1, 7), 300, null, null, null)); 28 | 29 | pictures = new ArrayList(); 30 | pictures.add(new PictureVO(1, "http://www.idealista.com/pictures/1", "SD")); 31 | pictures.add(new PictureVO(2, "http://www.idealista.com/pictures/2", "HD")); 32 | pictures.add(new PictureVO(3, "http://www.idealista.com/pictures/3", "SD")); 33 | pictures.add(new PictureVO(4, "http://www.idealista.com/pictures/4", "HD")); 34 | pictures.add(new PictureVO(5, "http://www.idealista.com/pictures/5", "SD")); 35 | pictures.add(new PictureVO(6, "http://www.idealista.com/pictures/6", "SD")); 36 | pictures.add(new PictureVO(7, "http://www.idealista.com/pictures/7", "SD")); 37 | pictures.add(new PictureVO(8, "http://www.idealista.com/pictures/8", "HD")); 38 | } 39 | 40 | @Override 41 | public List findAllAds() { 42 | return ads 43 | .stream() 44 | .map(this::mapToDomain) 45 | .collect(Collectors.toList()); 46 | } 47 | 48 | @Override 49 | public void save(Ad ad) { 50 | ads.removeIf(x -> x.getId().equals(ad.getId())); 51 | ads.add(mapToPersistence(ad)); 52 | 53 | ad.getPictures() 54 | .forEach(this::save); 55 | } 56 | 57 | private void save(Picture picture) { 58 | pictures.removeIf(x -> x.getId().equals(picture.getId())); 59 | pictures.add(mapToPersistence(picture)); 60 | } 61 | 62 | @Override 63 | public List findRelevantAds() { 64 | return ads 65 | .stream() 66 | .filter(x -> x.getScore() >= Constants.FORTY) 67 | .map(this::mapToDomain) 68 | .collect(Collectors.toList()); 69 | } 70 | 71 | @Override 72 | public List findIrrelevantAds() { 73 | return ads 74 | .stream() 75 | .filter(x -> x.getScore() < Constants.FORTY) 76 | .map(this::mapToDomain) 77 | .collect(Collectors.toList()); 78 | } 79 | 80 | private Ad mapToDomain(AdVO adVO) { 81 | return new Ad(adVO.getId(), 82 | Typology.valueOf(adVO.getTypology()), 83 | adVO.getDescription(), 84 | adVO.getPictures().stream().map(this::mapToDomain).collect(Collectors.toList()), 85 | adVO.getHouseSize(), 86 | adVO.getGardenSize(), 87 | adVO.getScore(), 88 | adVO.getIrrelevantSince()); 89 | } 90 | 91 | private Picture mapToDomain(Integer pictureId) { 92 | return pictures 93 | .stream() 94 | .filter(x -> x.getId().equals(pictureId)) 95 | .findFirst() 96 | .map(pictureVO -> new Picture(pictureVO.getId(), pictureVO.getUrl(), Quality.valueOf(pictureVO.getQuality()))) 97 | .orElse(null); 98 | } 99 | 100 | private AdVO mapToPersistence(Ad ad) { 101 | return new AdVO(ad.getId(), 102 | ad.getTypology().name(), 103 | ad.getDescription(), 104 | ad.getPictures().stream().map(Picture::getId).collect(Collectors.toList()), 105 | ad.getHouseSize(), 106 | ad.getGardenSize(), 107 | ad.getScore(), 108 | ad.getIrrelevantSince()); 109 | } 110 | 111 | private PictureVO mapToPersistence(Picture picture) { 112 | return new PictureVO(picture.getId(), 113 | picture.getUrl(), 114 | picture.getQuality().name()); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/idealista/application/AdsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.idealista.application; 2 | 3 | import com.idealista.domain.*; 4 | import com.idealista.infrastructure.api.PublicAd; 5 | import com.idealista.infrastructure.api.QualityAd; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.*; 10 | import java.util.stream.Collectors; 11 | 12 | @Service 13 | public class AdsServiceImpl implements AdsService { 14 | 15 | @Autowired 16 | private AdRepository adRepository; 17 | 18 | @Override 19 | public List findPublicAds() { 20 | List ads = adRepository.findRelevantAds(); 21 | ads.sort(Comparator.comparing(Ad::getScore)); 22 | 23 | List result = new ArrayList<>(); 24 | for (Ad ad: ads) { 25 | PublicAd publicAd = new PublicAd(); 26 | publicAd.setDescription(ad.getDescription()); 27 | publicAd.setGardenSize(ad.getGardenSize()); 28 | publicAd.setHouseSize(ad.getHouseSize()); 29 | publicAd.setId(ad.getId()); 30 | publicAd.setPictureUrls(ad.getPictures().stream().map(Picture::getUrl).collect(Collectors.toList())); 31 | publicAd.setTypology(ad.getTypology().name()); 32 | 33 | result.add(publicAd); 34 | } 35 | return result; 36 | } 37 | 38 | @Override 39 | public List findQualityAds() { 40 | List ads = adRepository.findIrrelevantAds(); 41 | 42 | List result = new ArrayList<>(); 43 | for (Ad ad: ads) { 44 | QualityAd qualityAd = new QualityAd(); 45 | qualityAd.setDescription(ad.getDescription()); 46 | qualityAd.setGardenSize(ad.getGardenSize()); 47 | qualityAd.setHouseSize(ad.getHouseSize()); 48 | qualityAd.setId(ad.getId()); 49 | qualityAd.setPictureUrls(ad.getPictures().stream().map(Picture::getUrl).collect(Collectors.toList())); 50 | qualityAd.setTypology(ad.getTypology().name()); 51 | qualityAd.setScore(ad.getScore()); 52 | qualityAd.setIrrelevantSince(ad.getIrrelevantSince()); 53 | 54 | result.add(qualityAd); 55 | } 56 | 57 | return result; 58 | } 59 | 60 | @Override 61 | public void calculateScores() { 62 | adRepository 63 | .findAllAds() 64 | .forEach(this::calculateScore); 65 | } 66 | 67 | private void calculateScore(Ad ad) { 68 | int score = Constants.ZERO; 69 | 70 | //Calcular puntuación por fotos 71 | if (ad.getPictures().isEmpty()) { 72 | score -= Constants.TEN; //Si no hay fotos restamos 10 puntos 73 | } else { 74 | for (Picture picture: ad.getPictures()) { 75 | if(Quality.HD.equals(picture.getQuality())) { 76 | score += Constants.TWENTY; //Cada foto en alta definición aporta 20 puntos 77 | } else { 78 | score += Constants.TEN; //Cada foto normal aporta 10 puntos 79 | } 80 | } 81 | } 82 | 83 | //Calcular puntuación por descripción 84 | Optional optDesc = Optional.ofNullable(ad.getDescription()); 85 | 86 | if (optDesc.isPresent()) { 87 | String description = optDesc.get(); 88 | 89 | if (!description.isEmpty()) { 90 | score += Constants.FIVE; 91 | } 92 | 93 | List wds = Arrays.asList(description.split(" ")); //número de palabras 94 | if (Typology.FLAT.equals(ad.getTypology())) { 95 | if (wds.size() >= Constants.TWENTY && wds.size() <= Constants.FORTY_NINE) { 96 | score += Constants.TEN; 97 | } 98 | 99 | if (wds.size() >= Constants.FIFTY) { 100 | score += Constants.THIRTY; 101 | } 102 | } 103 | 104 | if (Typology.CHALET.equals(ad.getTypology())) { 105 | if (wds.size() >= Constants.FIFTY) { 106 | score += Constants.TWENTY; 107 | } 108 | } 109 | 110 | if (wds.contains("luminoso")) score += Constants.FIVE; 111 | if (wds.contains("nuevo")) score += Constants.FIVE; 112 | if (wds.contains("céntrico")) score += Constants.FIVE; 113 | if (wds.contains("reformado")) score += Constants.FIVE; 114 | if (wds.contains("ático")) score += Constants.FIVE; 115 | } 116 | 117 | //Calcular puntuación por completitud 118 | if (ad.isComplete()) { 119 | score = Constants.FORTY; 120 | } 121 | 122 | ad.setScore(score); 123 | 124 | if (ad.getScore() < Constants.ZERO) { 125 | ad.setScore(Constants.ZERO); 126 | } 127 | 128 | if (ad.getScore() > Constants.ONE_HUNDRED) { 129 | ad.setScore(Constants.ONE_HUNDRED); 130 | } 131 | 132 | if (ad.getScore() < Constants.FORTY) { 133 | ad.setIrrelevantSince(new Date()); 134 | } else { 135 | ad.setIrrelevantSince(null); 136 | } 137 | 138 | adRepository.save(ad); 139 | } 140 | 141 | 142 | } 143 | --------------------------------------------------------------------------------