From dc58fde26a0e2f9b5d0cd3a1290e20685754cf49 Mon Sep 17 00:00:00 2001 From: "Goulven.Furet" Date: Mon, 23 Oct 2023 11:01:43 +0200 Subject: [PATCH] Ui --- .../AttributeRealtimeAggregationService.java | 20 ++++++--- .../services/RealtimeAggregationService.java | 7 ++-- .../config/yml/ui/AttributesConfig.java | 33 +++++++++------ .../model/product/AggregatedAttribute.java | 18 ++++++++ .../model/product/AggregatedPrice.java | 1 + .../open4goods/services/SearchService.java | 5 ++- .../services/VerticalsConfigService.java | 4 +- .../ui/controllers/ui/ProductController.java | 8 +++- .../org/open4goods/ui/helper/UiHelper.java | 39 ++++++++++++++++++ .../resources/i18n/messages_fr.properties | 7 +++- ui/src/main/resources/static/css/custom.css | 4 +- ui/src/main/resources/static/icons/ko.png | Bin 0 -> 3173 bytes ui/src/main/resources/static/icons/ok.png | Bin 0 -> 7110 bytes .../templates/inc/attribute-sourcing.html | 13 ++++++ .../inc/attributes/class_energy.html | 4 +- .../templates/inc/product-attributes.html | 21 ++++++---- .../templates/inc/product-ecoscore.html | 3 +- .../inc/product-referentiel-attributes.html | 20 ++++----- .../inc/product-unmatched-attributes.html | 14 ++++--- 19 files changed, 167 insertions(+), 54 deletions(-) create mode 100644 ui/src/main/java/org/open4goods/ui/helper/UiHelper.java create mode 100644 ui/src/main/resources/static/icons/ko.png create mode 100644 ui/src/main/resources/static/icons/ok.png create mode 100644 ui/src/main/resources/templates/inc/attribute-sourcing.html diff --git a/aggregation/src/main/java/org/open4goods/aggregation/services/aggregation/AttributeRealtimeAggregationService.java b/aggregation/src/main/java/org/open4goods/aggregation/services/aggregation/AttributeRealtimeAggregationService.java index e100e6a99..886403f2a 100644 --- a/aggregation/src/main/java/org/open4goods/aggregation/services/aggregation/AttributeRealtimeAggregationService.java +++ b/aggregation/src/main/java/org/open4goods/aggregation/services/aggregation/AttributeRealtimeAggregationService.java @@ -26,16 +26,19 @@ import org.open4goods.model.product.AggregatedFeature; import org.open4goods.model.product.Product; import org.open4goods.services.BrandService; +import org.open4goods.services.VerticalsConfigService; public class AttributeRealtimeAggregationService extends AbstractRealTimeAggregationService { - private final AttributesConfig attributesConfig; + private final BrandService brandService; - public AttributeRealtimeAggregationService(final AttributesConfig attributesConfig, BrandService brandService, final String logsFolder,boolean toConsole) { + private VerticalsConfigService verticalConfigService; + + public AttributeRealtimeAggregationService(final VerticalsConfigService verticalConfigService, BrandService brandService, final String logsFolder,boolean toConsole) { super(logsFolder,toConsole); - this.attributesConfig = attributesConfig; + this.verticalConfigService = verticalConfigService; this.brandService = brandService; } @@ -50,7 +53,13 @@ public AttributeRealtimeAggregationService(final AttributesConfig attributesConf @Override public void onDataFragment(final DataFragment dataFragment, final Product product) { + AttributesConfig attributesConfig = verticalConfigService.getConfigById(product.getVertical() == null ? "all" : product.getVertical() ).get().getAttributesConfig() ; + Set toRemoveFromUnmatched = new HashSet<>(); + // Adding the list of "to be removed" attributes + toRemoveFromUnmatched.addAll(attributesConfig.getExclusions()); + + ///////////////////////////////////////// // Converting to AggregatedAttributes for matches from config ///////////////////////////////////////// @@ -62,7 +71,6 @@ public void onDataFragment(final DataFragment dataFragment, final Product produc all.addAll(product.getAttributes().getUnmapedAttributes().stream().map(e -> new Attribute(e.getName(),e.getValue(),e.getLanguage())).toList()); - for (Attribute attr : dataFragment.getAttributes()) { // Checking if a potential AggregatedAttribute @@ -110,7 +118,7 @@ public void onDataFragment(final DataFragment dataFragment, final Product produc ///////////////////////////////////////// List matchedFeatures = dataFragment.getAttributes().stream() - .filter(this::isFeatureAttribute) + .filter(e -> isFeatureAttribute(e, attributesConfig)) .collect(Collectors.toList()); toRemoveFromUnmatched.addAll(matchedFeatures.stream().map(e->e.getName()).collect(Collectors.toSet())); @@ -337,7 +345,7 @@ private Collection aggregateFeatures(List matchedF * @param e * @return */ - private boolean isFeatureAttribute(Attribute e) { + private boolean isFeatureAttribute(Attribute e, AttributesConfig attributesConfig) { return attributesConfig.getFeaturedValues().contains(e.getRawValue().toString().trim().toUpperCase()); } diff --git a/api/src/main/java/org/open4goods/api/services/RealtimeAggregationService.java b/api/src/main/java/org/open4goods/api/services/RealtimeAggregationService.java index 3b073e3fd..6827d3c6c 100644 --- a/api/src/main/java/org/open4goods/api/services/RealtimeAggregationService.java +++ b/api/src/main/java/org/open4goods/api/services/RealtimeAggregationService.java @@ -114,7 +114,7 @@ public void shutdown() { /** * List of services in the aggregator - * + *TODO : Replace confif with verticalConfigService, to have hot vertical config onDataFragment * @param config * @return */ @@ -131,7 +131,9 @@ RealTimeAggregator getAggregator(VerticalConfig config) { services.add(new BarCodeAggregationService(apiProperties.logsFolder(), gs1prefixService,barcodeValidationService, apiProperties.isDedicatedLoggerToConsole())); - services.add(new AttributeRealtimeAggregationService(config.getAttributesConfig(), brandService, apiProperties.logsFolder(), apiProperties.isDedicatedLoggerToConsole())); + services.add(new VerticalRealTimeAggregationService( apiProperties.logsFolder(), verticalConfigService, apiProperties.isDedicatedLoggerToConsole())); + + services.add(new AttributeRealtimeAggregationService(verticalConfigService, brandService, apiProperties.logsFolder(), apiProperties.isDedicatedLoggerToConsole())); services.add(new NamesAggregationService(apiProperties.logsFolder(), apiProperties.isDedicatedLoggerToConsole())); @@ -139,7 +141,6 @@ RealTimeAggregator getAggregator(VerticalConfig config) { // services.add(new CategoryService(apiProperties.logsFolder(), taxonomyService)); - services.add(new VerticalRealTimeAggregationService( apiProperties.logsFolder(), verticalConfigService, apiProperties.isDedicatedLoggerToConsole())); services.add(new IdAggregationService( apiProperties.logsFolder(), apiProperties.isDedicatedLoggerToConsole())); diff --git a/commons/src/main/java/org/open4goods/config/yml/ui/AttributesConfig.java b/commons/src/main/java/org/open4goods/config/yml/ui/AttributesConfig.java index 031a7df6f..714b582ac 100644 --- a/commons/src/main/java/org/open4goods/config/yml/ui/AttributesConfig.java +++ b/commons/src/main/java/org/open4goods/config/yml/ui/AttributesConfig.java @@ -19,6 +19,8 @@ public class AttributesConfig { + // Local cache + private Map> cacheHashedSynonyms; /** * The specific configs configurations */ @@ -56,6 +58,9 @@ public class AttributesConfig { @JsonIgnore private Map hashedAttributesByKey; + + + public AttributesConfig(Set configs) { this.configs = configs; @@ -72,23 +77,27 @@ public AttributesConfig() { * * @return A map-ProviderKey,Synonym, Translated Key */ - @Cacheable(cacheNames = CacheConstants.FOREVER_LOCAL_CACHE_NAME) + // Spring cache ineffectiv on internal calls +// @Cacheable(cacheNames = CacheConstants.FOREVER_LOCAL_CACHE_NAME) public Map> synonyms() { - final Map> hashedSynonyms = new HashMap<>(); - - for (final AttributeConfig ac : configs) { - for (final Entry> entry : ac.getSynonyms().entrySet()) { - if (!hashedSynonyms.containsKey(entry.getKey())) { - hashedSynonyms.put(entry.getKey(), new HashMap<>()); - } - for (final String val : entry.getValue()) { - hashedSynonyms.get(entry.getKey()).put(val, ac.getKey()); + if (cacheHashedSynonyms == null) { + + final Map> hashedSynonyms = new HashMap<>(); + + for (final AttributeConfig ac : configs) { + for (final Entry> entry : ac.getSynonyms().entrySet()) { + if (!hashedSynonyms.containsKey(entry.getKey())) { + hashedSynonyms.put(entry.getKey(), new HashMap<>()); + } + for (final String val : entry.getValue()) { + hashedSynonyms.get(entry.getKey()).put(val, ac.getKey()); + } } } + cacheHashedSynonyms = hashedSynonyms; } - - return hashedSynonyms; + return cacheHashedSynonyms; } public Attribute translateAttribute(final Attribute a, final String provider) { diff --git a/commons/src/main/java/org/open4goods/model/product/AggregatedAttribute.java b/commons/src/main/java/org/open4goods/model/product/AggregatedAttribute.java index 7cafa228e..3ec77ec36 100644 --- a/commons/src/main/java/org/open4goods/model/product/AggregatedAttribute.java +++ b/commons/src/main/java/org/open4goods/model/product/AggregatedAttribute.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.open4goods.config.yml.attributes.AttributeConfig; import org.open4goods.exceptions.ValidationException; import org.open4goods.model.attribute.Attribute; @@ -63,6 +64,23 @@ public int sourcesCount() { public long distinctValues () { return sources.stream().map(e-> e.getValue()).distinct().count(); } + + /** + * For UI, a String representation of all providers names + * @return + */ + public String providersToString() { + return StringUtils.join( sources.stream().map(e-> e.getKey()).toArray(),", "); + } + + /** + * For UI, a String representation of all providers names and values + * @return + */ + public String sourcesToString() { + return StringUtils.join( sources.stream().map(e-> e.getKey() + ":"+e.getValue()).toArray(),", "); + + } public boolean hasConflicts() { diff --git a/commons/src/main/java/org/open4goods/model/product/AggregatedPrice.java b/commons/src/main/java/org/open4goods/model/product/AggregatedPrice.java index 12ee3fb26..4b01de5e7 100644 --- a/commons/src/main/java/org/open4goods/model/product/AggregatedPrice.java +++ b/commons/src/main/java/org/open4goods/model/product/AggregatedPrice.java @@ -18,6 +18,7 @@ public class AggregatedPrice extends Price { @Field(index = false, store = false, type = FieldType.Keyword) private String datasourceName; + @Field(index = false, store = false, type = FieldType.Keyword) private String offerName; @Field(index = false, store = false, type = FieldType.Keyword) diff --git a/commons/src/main/java/org/open4goods/services/SearchService.java b/commons/src/main/java/org/open4goods/services/SearchService.java index d32512bc0..a022a850f 100644 --- a/commons/src/main/java/org/open4goods/services/SearchService.java +++ b/commons/src/main/java/org/open4goods/services/SearchService.java @@ -234,8 +234,11 @@ public VerticalSearchResponse verticalSearch(VerticalConfig vertical, VerticalSe for (AttributeConfig attrConfig : customAttrFilters) { esQuery = esQuery // TODO : size from conf - .withAggregation(attrConfig.getKey(), Aggregation.of(a -> a.terms(ta -> ta.field("attributes.aggregatedAttributes."+attrConfig.getKey()+".value").missing(OTHER_BUCKET).size(100) )) ); + .withAggregation(attrConfig.getKey(), Aggregation.of(a -> a.terms(ta -> ta.field("attributes.aggregatedAttributes."+attrConfig.getKey()+".value.keyword").missing(OTHER_BUCKET).size(100) )) ); } + + + // Adding custom range aggregations for (NumericRangeFilter filter: request.getNumericFilters()) { esQuery = esQuery diff --git a/commons/src/main/java/org/open4goods/services/VerticalsConfigService.java b/commons/src/main/java/org/open4goods/services/VerticalsConfigService.java index 575cd005f..c0980688b 100644 --- a/commons/src/main/java/org/open4goods/services/VerticalsConfigService.java +++ b/commons/src/main/java/org/open4goods/services/VerticalsConfigService.java @@ -37,8 +37,8 @@ public class VerticalsConfigService { public static final Logger logger = LoggerFactory.getLogger(VerticalsConfigService.class); private static final String DEFAULT_CONFIG_FILENAME = "_default.yml"; - private static final String CLASSPATH_VERTICALS = "classpath:**verticals/*.yml"; - private static final String CLASSPATH_VERTICAL_PREFIX = "classpath:verticals/"; +// private static final String CLASSPATH_VERTICALS = "classpath:**verticals/*.yml"; +// private static final String CLASSPATH_VERTICAL_PREFIX = "classpath:verticals/"; private SerialisationService serialisationService; diff --git a/ui/src/main/java/org/open4goods/ui/controllers/ui/ProductController.java b/ui/src/main/java/org/open4goods/ui/controllers/ui/ProductController.java index 0f358d924..115e5f799 100644 --- a/ui/src/main/java/org/open4goods/ui/controllers/ui/ProductController.java +++ b/ui/src/main/java/org/open4goods/ui/controllers/ui/ProductController.java @@ -19,6 +19,7 @@ import org.open4goods.services.SerialisationService; import org.open4goods.services.VerticalsConfigService; import org.open4goods.ui.config.yml.UiConfig; +import org.open4goods.ui.helper.UiHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -187,7 +188,9 @@ private ModelAndView buildProductView(String id, String vertical, final HttpServ mv.addObject("product", data); - mv.addObject("verticalConfig", verticalConfigService.getVerticalForPath(vertical)); + + VerticalConfig verticalConfig = verticalConfigService.getVerticalForPath(vertical); + mv.addObject("verticalConfig", verticalConfig); @@ -200,6 +203,9 @@ private ModelAndView buildProductView(String id, String vertical, final HttpServ // Adding the brand informations mv.addObject("hasBrandLogo", brandService.hasLogo(data.brand())); + // Adding the UiHelper class + mv.addObject("helper", new UiHelper(request, verticalConfig)); + // Adding the images resource return mv; diff --git a/ui/src/main/java/org/open4goods/ui/helper/UiHelper.java b/ui/src/main/java/org/open4goods/ui/helper/UiHelper.java new file mode 100644 index 000000000..b4080705f --- /dev/null +++ b/ui/src/main/java/org/open4goods/ui/helper/UiHelper.java @@ -0,0 +1,39 @@ +package org.open4goods.ui.helper; + +import org.open4goods.config.yml.ui.VerticalConfig; +import org.open4goods.services.VerticalsConfigService; + +import jakarta.servlet.http.HttpServletRequest; + +public class UiHelper { + + + private HttpServletRequest request; + private VerticalConfig verticalConfig; + + public UiHelper(HttpServletRequest request, VerticalConfig verticalConfig) { + super(); + this.request = request; + this.verticalConfig = verticalConfig; + } + + + + + /** + * Return the i18n for an attribute + * @param key + * @return + */ + public String attributeName(String key) { + + return verticalConfig.getAttributesConfig().getAttributeConfigByKey(key).i18n(request.getLocale().getLanguage()); + + + } + + + + + +} diff --git a/ui/src/main/resources/i18n/messages_fr.properties b/ui/src/main/resources/i18n/messages_fr.properties index abd7944a1..004ffac1a 100644 --- a/ui/src/main/resources/i18n/messages_fr.properties +++ b/ui/src/main/resources/i18n/messages_fr.properties @@ -1,7 +1,12 @@ aria.navigation.primary = Navigation principale -attribute.sourcing = {0} source(s) de donn\u00E9e(s), {1} conflit(s) +attribute.sourcing.conflict = {1} conflits +attribute.sourcing.noconflict = Aucun conflit + +attribute.sourcing.list = {0} source(s) de donn\u00E9e(s) ({2}) + + category.meta.description = {0} produits dans la cat\u00E9gorie {1} sont \u00E9ligibles \u00E0 la compensation carbone category.meta.title = Produits de la cat\u00E9gorie {0} diff --git a/ui/src/main/resources/static/css/custom.css b/ui/src/main/resources/static/css/custom.css index c3376d3cf..09ad7373a 100644 --- a/ui/src/main/resources/static/css/custom.css +++ b/ui/src/main/resources/static/css/custom.css @@ -7,7 +7,9 @@ CUSTOM width: 90%; } - +.help-pointer:hover { + cursor : help; +} /*********************************************** Xwiki css **************************************************/ diff --git a/ui/src/main/resources/static/icons/ko.png b/ui/src/main/resources/static/icons/ko.png new file mode 100644 index 0000000000000000000000000000000000000000..b80c93b214dee69e2097a10e55136ee58e4e0699 GIT binary patch literal 3173 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 literal 0 HcmV?d00001 diff --git a/ui/src/main/resources/templates/inc/attribute-sourcing.html b/ui/src/main/resources/templates/inc/attribute-sourcing.html new file mode 100644 index 000000000..a61a5d52f --- /dev/null +++ b/ui/src/main/resources/templates/inc/attribute-sourcing.html @@ -0,0 +1,13 @@ + + + + + + + + + + 1 + + 1 + diff --git a/ui/src/main/resources/templates/inc/attributes/class_energy.html b/ui/src/main/resources/templates/inc/attributes/class_energy.html index 01e1096d7..d229f66fc 100644 --- a/ui/src/main/resources/templates/inc/attributes/class_energy.html +++ b/ui/src/main/resources/templates/inc/attributes/class_energy.html @@ -1,6 +1,6 @@ - - + + diff --git a/ui/src/main/resources/templates/inc/product-attributes.html b/ui/src/main/resources/templates/inc/product-attributes.html index 22678c658..1a9157cd1 100644 --- a/ui/src/main/resources/templates/inc/product-attributes.html +++ b/ui/src/main/resources/templates/inc/product-attributes.html @@ -1,15 +1,20 @@ -