diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/AbsoluteMode.java b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/AbsoluteMode.java index 83164d018e..d473d53344 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/AbsoluteMode.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/AbsoluteMode.java @@ -15,7 +15,7 @@ import com.bakdata.conquery.models.common.Range; import com.bakdata.conquery.models.datasets.Dataset; import com.bakdata.conquery.models.forms.export.AbsExportGenerator; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Alignment; import com.bakdata.conquery.models.query.DateAggregationMode; import com.bakdata.conquery.models.query.QueryResolveContext; import com.bakdata.conquery.models.query.Visitable; @@ -39,7 +39,7 @@ public void visit(Consumer visitor) { } @NotNull - private DateContext.Alignment alignmentHint = DateContext.Alignment.QUARTER; + private Alignment alignmentHint = Alignment.QUARTER; @Override public Query createSpecializedQuery(DatasetRegistry datasets, User user, Dataset submittedDataset) { diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/EntityDateMode.java b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/EntityDateMode.java index f834ff7d8f..dbd16112c5 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/EntityDateMode.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/EntityDateMode.java @@ -19,7 +19,7 @@ import com.bakdata.conquery.models.common.daterange.CDateRange; import com.bakdata.conquery.models.datasets.Dataset; import com.bakdata.conquery.models.forms.managed.EntityDateQuery; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Alignment; import com.bakdata.conquery.models.query.DateAggregationMode; import com.bakdata.conquery.models.query.QueryResolveContext; import com.bakdata.conquery.models.query.Visitable; @@ -51,7 +51,7 @@ public void visit(Consumer visitor) { } @NotNull - private DateContext.Alignment alignmentHint = DateContext.Alignment.QUARTER; + private Alignment alignmentHint = Alignment.QUARTER; @Override public void resolve(QueryResolveContext context) { diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java index 1e862d4c5b..486bb45379 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java @@ -25,7 +25,8 @@ import com.bakdata.conquery.models.execution.ManagedExecution; import com.bakdata.conquery.models.forms.managed.ManagedForm; import com.bakdata.conquery.models.forms.managed.ManagedInternalForm; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Alignment; +import com.bakdata.conquery.models.forms.util.Resolution; import com.bakdata.conquery.models.i18n.I18n; import com.bakdata.conquery.models.identifiable.ids.specific.ManagedExecutionId; import com.bakdata.conquery.models.query.ManagedQuery; @@ -56,14 +57,14 @@ public class ExportForm extends Form { private Mode timeMode; @NotNull @NotEmpty - private List resolution = List.of(DateContext.Resolution.COMPLETE); + private List resolution = List.of(Resolution.COMPLETE); private boolean alsoCreateCoarserSubdivisions = true; @JsonIgnore private Query prerequisite; @JsonIgnore - private List resolvedResolutions; + private List resolvedResolutions; @Override public void visit(Consumer visitor) { @@ -118,21 +119,24 @@ public String getLocalizedTypeLabel() { /** * Maps the given resolution to a fitting alignment. It tries to use the alignment which was given as a hint. * If the alignment does not fit to a resolution (resolution is finer than the alignment), the first alignment that - * this resolution supports is chosen (see the alignment order in {@link DateContext.Resolution}) + * this resolution supports is chosen (see the alignment order in {@link Resolution}) * @param resolutions The temporal resolutions for which sub queries should be generated per entity * @param alignmentHint The preferred calendar alignment on which the sub queries of each resolution should be aligned. * Note that this alignment is chosen when a resolution is equal or coarser. * @return The given resolutions mapped to a fitting calendar alignment. */ - public static List getResolutionAlignmentMap(List resolutions, DateContext.Alignment alignmentHint) { + public static List getResolutionAlignmentMap(List resolutions, Alignment alignmentHint) { return resolutions.stream() .map(r -> ResolutionAndAlignment.of(r, getFittingAlignment(alignmentHint, r))) .collect(Collectors.toList()); } - private static DateContext.Alignment getFittingAlignment(DateContext.Alignment alignmentHint, DateContext.Resolution resolution) { - return resolution.getSupportedAlignments().contains(alignmentHint)? alignmentHint : resolution.getSupportedAlignments().iterator().next(); + private static Alignment getFittingAlignment(Alignment alignmentHint, Resolution resolution) { + if(resolution.isAlignmentSupported(alignmentHint) ) { + return alignmentHint; + } + return resolution.getDefaultAlignment(); } /** @@ -141,12 +145,12 @@ private static DateContext.Alignment getFittingAlignment(DateContext.Alignment a @RequiredArgsConstructor(access = AccessLevel.PRIVATE) @Getter public static class ResolutionAndAlignment { - private final DateContext.Resolution resolution; - private final DateContext.Alignment alignment; + private final Resolution resolution; + private final Alignment alignment; @JsonCreator - public static ResolutionAndAlignment of(DateContext.Resolution resolution, DateContext.Alignment alignment){ - if (!resolution.getSupportedAlignments().contains(alignment)) { + public static ResolutionAndAlignment of(Resolution resolution, Alignment alignment){ + if (!resolution.isAlignmentSupported(alignment)) { throw new ValidationException(String.format("The alignment %s is not supported by the resolution %s", alignment, resolution)); } diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/RelativeMode.java b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/RelativeMode.java index 8bf7d27921..8f16f6478b 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/RelativeMode.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/RelativeMode.java @@ -14,7 +14,7 @@ import com.bakdata.conquery.models.datasets.Dataset; import com.bakdata.conquery.models.forms.export.RelExportGenerator; import com.bakdata.conquery.models.forms.managed.RelativeFormQuery; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.CalendarUnit; import com.bakdata.conquery.models.query.DateAggregationMode; import com.bakdata.conquery.models.query.QueryResolveContext; import com.bakdata.conquery.models.query.Visitable; @@ -30,7 +30,7 @@ @CPSType(id="RELATIVE", base=Mode.class) public class RelativeMode extends Mode { @NotNull - private DateContext.CalendarUnit timeUnit; + private CalendarUnit timeUnit; @Min(0) private int timeCountBefore; @Min(0) diff --git a/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java b/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java index 5405a26dfb..c266cd63b0 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java +++ b/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java @@ -2,7 +2,8 @@ import com.bakdata.conquery.io.cps.CPSBase; import com.bakdata.conquery.io.cps.CPSType; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Alignment; +import com.bakdata.conquery.models.forms.util.Resolution; import com.bakdata.conquery.models.identifiable.ids.IId; import com.bakdata.conquery.models.query.entity.Entity; import com.bakdata.conquery.models.query.queryplan.QueryPlan; @@ -231,8 +232,7 @@ public static class ExecutionCreationPlanDateContextError extends ContextError { private final static String ALIGNMENT = "alignment"; private final static String RESOLUTION = "resolution"; - private final static String ALIGNMENT_SUPPORTED = "alignmentsSupported"; - private final static String TEMPLATE = "Alignment ${" + ALIGNMENT + "} and resolution ${" + RESOLUTION + "} don't fit together. The resolution only supports these alignments: ${" + ALIGNMENT_SUPPORTED + "}"; + private final static String TEMPLATE = "Alignment ${" + ALIGNMENT + "} and resolution ${" + RESOLUTION + "} are not compatible."; /** * Constructor for deserialization. @@ -242,11 +242,10 @@ private ExecutionCreationPlanDateContextError() { super(TEMPLATE); } - public ExecutionCreationPlanDateContextError(DateContext.Alignment alignment, DateContext.Resolution resolution) { + public ExecutionCreationPlanDateContextError(Alignment alignment, Resolution resolution) { this(); getContext().put(ALIGNMENT, Objects.toString(alignment)); getContext().put(RESOLUTION, Objects.toString(resolution)); - getContext().put(ALIGNMENT_SUPPORTED, Objects.toString(resolution.getSupportedAlignments())); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/externalservice/ResultType.java b/backend/src/main/java/com/bakdata/conquery/models/externalservice/ResultType.java index c69fb0897f..a3c19b8933 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/externalservice/ResultType.java +++ b/backend/src/main/java/com/bakdata/conquery/models/externalservice/ResultType.java @@ -5,7 +5,7 @@ import com.bakdata.conquery.io.cps.CPSBase; import com.bakdata.conquery.io.cps.CPSType; import com.bakdata.conquery.models.events.MajorTypeId; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Resolution; import com.bakdata.conquery.models.query.PrintSettings; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -139,13 +139,13 @@ public static class ResolutionT extends PrimitiveResultType { @Override public String print(PrintSettings cfg, Object f) { - if (f instanceof DateContext.Resolution) { - return ((DateContext.Resolution) f).toString(cfg.getLocale()); + if (f instanceof Resolution) { + return ((Resolution) f).toString(cfg.getLocale()); } try { // If the object was parsed as a simple string, try to convert it to a // DateContextMode to get Internationalization - return DateContext.Resolution.valueOf(f.toString()).toString(cfg.getLocale()); + return Resolution.valueOf(f.toString()).toString(cfg.getLocale()); } catch (Exception e) { throw new IllegalArgumentException(f + " is not a valid resolution.", e); } diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/export/RelExportGenerator.java b/backend/src/main/java/com/bakdata/conquery/models/forms/export/RelExportGenerator.java index 217fd1f287..3312efe0ed 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/forms/export/RelExportGenerator.java +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/export/RelExportGenerator.java @@ -12,7 +12,7 @@ import com.bakdata.conquery.apiv1.query.concept.specific.ResultInfoDecorator; import com.bakdata.conquery.apiv1.query.concept.specific.temporal.TemporalSampler; import com.bakdata.conquery.models.forms.managed.RelativeFormQuery; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.CalendarUnit; import com.google.common.collect.ImmutableClassToInstanceMap; import lombok.AllArgsConstructor; @@ -27,7 +27,7 @@ public static RelativeFormQuery generate(RelativeMode mode) { return generate(mode.getForm().getPrerequisite(), mode.getResolvedFeatures(), mode.getResolvedOutcomes(), mode.getIndexSelector(), mode.getIndexPlacement(), mode.getTimeCountBefore(), mode.getTimeCountAfter(), mode.getTimeUnit(), resolutionsAndAlignments); } - public static RelativeFormQuery generate(Query query, ArrayConceptQuery features, ArrayConceptQuery outcomes, TemporalSampler indexSelector, IndexPlacement indexPlacement, int timeCountBefore, int timeCountAfter, DateContext.CalendarUnit timeUnit, List resolutionsAndAlignments) { + public static RelativeFormQuery generate(Query query, ArrayConceptQuery features, ArrayConceptQuery outcomes, TemporalSampler indexSelector, IndexPlacement indexPlacement, int timeCountBefore, int timeCountAfter, CalendarUnit timeUnit, List resolutionsAndAlignments) { return new RelativeFormQuery( query, diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQuery.java b/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQuery.java index 875345559b..54e65a5ae3 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQuery.java +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQuery.java @@ -17,7 +17,7 @@ import com.bakdata.conquery.apiv1.query.concept.specific.temporal.TemporalSampler; import com.bakdata.conquery.io.cps.CPSType; import com.bakdata.conquery.models.execution.ManagedExecution; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.CalendarUnit; import com.bakdata.conquery.models.query.DateAggregationMode; import com.bakdata.conquery.models.query.QueryPlanContext; import com.bakdata.conquery.models.query.QueryResolveContext; @@ -47,7 +47,7 @@ public class RelativeFormQuery extends Query { @Min(0) private final int timeCountAfter; @NotNull - private final DateContext.CalendarUnit timeUnit; + private final CalendarUnit timeUnit; @NotNull private final List resolutionsAndAlignmentMap; diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQueryPlan.java b/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQueryPlan.java index 5bd2901173..4fb6c70567 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQueryPlan.java +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/managed/RelativeFormQueryPlan.java @@ -7,7 +7,9 @@ import com.bakdata.conquery.apiv1.forms.export_form.ExportForm; import com.bakdata.conquery.models.common.CDateSet; import com.bakdata.conquery.models.error.ConqueryError; +import com.bakdata.conquery.models.forms.util.CalendarUnit; import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Resolution; import com.bakdata.conquery.models.forms.util.ResultModifier; import com.bakdata.conquery.models.query.QueryExecutionContext; import com.bakdata.conquery.apiv1.query.concept.specific.temporal.TemporalSampler; @@ -41,7 +43,7 @@ public class RelativeFormQueryPlan implements QueryPlan { private final IndexPlacement indexPlacement; private final int timeCountBefore; private final int timeCountAfter; - private final DateContext.CalendarUnit timeUnit; + private final CalendarUnit timeUnit; private final List resolutionsAndAlignmentMap; private final transient List featureSubqueries = new ArrayList<>(); @@ -197,13 +199,13 @@ private boolean hasCompleteDateContexts(List contexts) { if (featurePlan.getAggregatorSize() > 0 && outcomePlan.getAggregatorSize() > 0) { // We have features and outcomes check if both have complete date ranges (they should be at the beginning of the list) return contexts.size()>=2 - && contexts.get(0).getSubdivisionMode().equals(DateContext.Resolution.COMPLETE) - && contexts.get(1).getSubdivisionMode().equals(DateContext.Resolution.COMPLETE) + && contexts.get(0).getSubdivisionMode().equals(Resolution.COMPLETE) + && contexts.get(1).getSubdivisionMode().equals(Resolution.COMPLETE) && !contexts.get(0).getFeatureGroup().equals(contexts.get(1).getFeatureGroup()); } // Otherwise, if only features or outcomes are given check the first date context. The empty feature/outcome query // will still return an empty result which will be merged with to a complete result. - return contexts.get(0).getSubdivisionMode().equals(DateContext.Resolution.COMPLETE); + return contexts.get(0).getSubdivisionMode().equals(Resolution.COMPLETE); } private FormQueryPlan createSubQuery(ArrayConceptQueryPlan subPlan, List contexts, FeatureGroup featureGroup) { diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/util/Alignment.java b/backend/src/main/java/com/bakdata/conquery/models/forms/util/Alignment.java new file mode 100644 index 0000000000..97ede3ad29 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/util/Alignment.java @@ -0,0 +1,70 @@ +package com.bakdata.conquery.models.forms.util; + +import com.bakdata.conquery.models.common.daterange.CDateRange; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.Map; +import java.util.OptionalInt; +import java.util.function.Function; + +/** + * Specifier for the alignment of {@link DateContext}s of a certain resolution. + * The alignment provides a method to sub divide a dateRangeMask into ranges aligned to the calendar equivalent. + * These sub divisions can then be merged to form the equally grain or coarser desired resolution for the + * {@link DateContext}s. + */ +@RequiredArgsConstructor +public enum Alignment { + NO_ALIGN(List::of){ + @Override + protected Map getAmountPerResolution() { + return Map.of(Resolution.COMPLETE, 1); + } + }, // Special case for resolution == COMPLETE + DAY(CDateRange::getCoveredDays) { + @Override + protected Map getAmountPerResolution() { + return Map.of( + Resolution.YEARS, 365, + Resolution.QUARTERS, 90, + Resolution.DAYS, 1); + } + }, + QUARTER(CDateRange::getCoveredQuarters) { + @Override + protected Map getAmountPerResolution() { + return Map.of( + Resolution.YEARS, 4, + Resolution.QUARTERS, 1); + } + }, + YEAR(CDateRange::getCoveredYears) { + @Override + protected Map getAmountPerResolution() { + return Map.of(Resolution.YEARS, 1); + } + }; + + @Getter + @JsonIgnore + private final Function> subdivider; + + @JsonIgnore + protected abstract Map getAmountPerResolution(); + + /** + * Returns the amount of calendar alignment sub date ranges that would fit in to this resolution. + */ + @JsonIgnore + public OptionalInt getAmountForResolution(Resolution resolution) { + Map amountMap = getAmountPerResolution(); + if (amountMap.containsKey(resolution)) { + return OptionalInt.of(amountMap.get(resolution)); + } + return OptionalInt.empty(); + } +} + diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/util/AlignmentReference.java b/backend/src/main/java/com/bakdata/conquery/models/forms/util/AlignmentReference.java new file mode 100644 index 0000000000..0cb22624c6 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/util/AlignmentReference.java @@ -0,0 +1,52 @@ +package com.bakdata.conquery.models.forms.util; + +import com.bakdata.conquery.models.common.daterange.CDateRange; +import com.google.common.collect.Lists; + +import java.util.List; + +/** + * Specifies whether the sub date ranges (which have the maximum length specified by the {@link Resolution}) + * are aligned with regard to beginning or the end of a date range mask. This affects the generated sub date ranges + * only if the {@link Alignment} is finer than the {@link Resolution}. + */ +public enum AlignmentReference { + START() { + @Override + public List getAlignedIterationDirection(List alignedSubDivisions) { + return alignedSubDivisions; + } + + @Override + public int getInterestingBorder(CDateRange daterange) { + return daterange.getMinValue(); + } + + @Override + public CDateRange makeMergedRange(CDateRange lastDaterange, int prioInteressingBorder) { + return CDateRange.of(prioInteressingBorder, lastDaterange.getMaxValue()); + } + }, + END() { + @Override + public List getAlignedIterationDirection(List alignedSubDivisions) { + return Lists.reverse(alignedSubDivisions); + } + + @Override + public int getInterestingBorder(CDateRange daterange) { + return daterange.getMaxValue(); + } + + @Override + public CDateRange makeMergedRange(CDateRange lastDaterange, int prioInteressingBorder) { + return CDateRange.of(lastDaterange.getMinValue(), prioInteressingBorder); + } + }; + + public abstract List getAlignedIterationDirection(List alignedSubDivisions); + + public abstract int getInterestingBorder(CDateRange daterange); + + public abstract CDateRange makeMergedRange(CDateRange lastDaterange, int prioInteressingBorder); +} diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/util/CalendarUnit.java b/backend/src/main/java/com/bakdata/conquery/models/forms/util/CalendarUnit.java new file mode 100644 index 0000000000..dea1471b28 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/util/CalendarUnit.java @@ -0,0 +1,19 @@ +package com.bakdata.conquery.models.forms.util; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * Unit that corresponds to the amounts given in a {@link com.bakdata.conquery.apiv1.forms.export_form.RelativeMode}, to + * determine the concrete feature and outcome date ranges. These are then subdivided in smaller stratification intervals + * based on provided {@link Resolution}s and {@link Alignment}s (see {@link DateContext}). + */ +@RequiredArgsConstructor +public enum CalendarUnit { + DAYS(Alignment.DAY), + QUARTERS(Alignment.QUARTER), + YEARS(Alignment.YEAR); + + @Getter + private final Alignment alignment; +} diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/util/DateContext.java b/backend/src/main/java/com/bakdata/conquery/models/forms/util/DateContext.java index 2baf9b8eae..6c12182941 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/forms/util/DateContext.java +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/util/DateContext.java @@ -7,18 +7,13 @@ import javax.annotation.Nullable; -import c10n.C10N; import com.bakdata.conquery.apiv1.forms.FeatureGroup; import com.bakdata.conquery.apiv1.forms.IndexPlacement; import com.bakdata.conquery.apiv1.forms.export_form.ExportForm; -import com.bakdata.conquery.internationalization.DateContextResolutionC10n; -import com.bakdata.conquery.internationalization.Localized; import com.bakdata.conquery.models.common.CDate; import com.bakdata.conquery.models.common.QuarterUtils; import com.bakdata.conquery.models.common.daterange.CDateRange; import com.bakdata.conquery.models.error.ConqueryError; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -34,16 +29,12 @@ public class DateContext { /** * The date range. - * - * @return The date range */ @Getter private final CDateRange dateRange; /** * Indicates to which group the context belongs. - * - * @return The groups. */ @Getter @Setter @@ -51,16 +42,12 @@ public class DateContext { /** * Indicates the relative position of the context to the event context. - * - * @return The index. */ @Getter private Integer index = null; /** * The date from which the relative context were generated. - * - * @return The event date */ @Getter @Setter private LocalDate eventDate = null; @@ -101,189 +88,6 @@ public static List generateAbsoluteContexts(CDateRange dateRangeMas return dcList; } - /** - * Specifies whether the sub date ranges (which have the maximum length specified by the {@link Resolution}) - * are aligned with regard to beginning or the end of a date range mask. This affects the generated sub date ranges - * only if the {@link Alignment} is finer than the {@link Resolution}. - */ - public static enum AlignmentReference { - START(){ - @Override - public List getAlignedIterationDirection(List alignedSubDivisions) { - return alignedSubDivisions; - } - - @Override - public int getInterestingBorder(CDateRange daterange) { - return daterange.getMinValue(); - } - - @Override - public CDateRange makeMergedRange(CDateRange lastDaterange, int prioInteressingBorder) { - return CDateRange.of(prioInteressingBorder, lastDaterange.getMaxValue()); - } - }, - END(){ - @Override - public List getAlignedIterationDirection(List alignedSubDivisions) { - return Lists.reverse(alignedSubDivisions); - } - - @Override - public int getInterestingBorder(CDateRange daterange) { - return daterange.getMaxValue(); - } - - @Override - public CDateRange makeMergedRange(CDateRange lastDaterange, int prioInteressingBorder) { - return CDateRange.of(lastDaterange.getMinValue(), prioInteressingBorder); - } - }; - - public abstract List getAlignedIterationDirection(List alignedSubDivisions); - public abstract int getInterestingBorder(CDateRange daterange); - public abstract CDateRange makeMergedRange(CDateRange lastDaterange, int prioInteressingBorder); - } - - @RequiredArgsConstructor - /** - * Defines the granularity into which a given date range mask is chunked. - * The actual size in days depends on the chosen {@link Alignment}, e.g.: - * If the resolution is YEARS and it should be aligned on the actual YEAR, the resulting contexts days might vary - * depending on if that year was a leap year. - * If the alignment is DAY, then all context will have a length of 365 day, except the dateRangeMask intersects an - * edge context. - */ - public static enum Resolution implements Localized { - /** - * For returning contexts with a single {@link CDateRange} for the entire - * {@link FeatureGroup}. - */ - COMPLETE(null, Map.of( - Alignment.NO_ALIGN, 1)) { - @Override - public String toString(Locale locale) { - - return C10N.get(DateContextResolutionC10n.class, locale).complete(); - } - }, - - /** - * The {@link CDateRange} contexts per {@link FeatureGroup} are subdivided into - * years. - */ - YEARS(COMPLETE, Map.of( - Alignment.YEAR, 1, - Alignment.QUARTER, 4, - Alignment.DAY, 365)) { - @Override - public String toString(Locale locale) { - - return C10N.get(DateContextResolutionC10n.class, locale).year(); - } - }, - - /** - * The {@link CDateRange} contexts per {@link FeatureGroup} are subdivided into - * quarters. - */ - QUARTERS(YEARS, Map.of( - Alignment.QUARTER, 1, - Alignment.DAY, 90)) { - @Override - public String toString(Locale locale) { - - return C10N.get(DateContextResolutionC10n.class, locale).quarter(); - } - }, - - /** - * The {@link CDateRange} contexts per {@link FeatureGroup} are subdivided into - * days. - */ - DAYS(QUARTERS, Map.of( - Alignment.DAY, 1)) { - @Override - public String toString(Locale locale) { - - return C10N.get(DateContextResolutionC10n.class, locale).day(); - } - }; - - - @JsonIgnore - private final Resolution coarser; - - /** - * Holds which calendar alignments are supported by this resolution and - * the amount of how many of such subdividions fill in this resolusion subdivision. - */ - @JsonIgnore - private final Map compatibleAlignmentsAndAmount; - - - private List thisAndCoarserSubdivisions; - - public abstract String toString(Locale locale); - - @JsonIgnore - public Collection getSupportedAlignments(){ - return compatibleAlignmentsAndAmount.keySet(); - } - - /** - * Returns the amount of calendar alignment sub date ranges that would fit in to this resolution. - */ - @JsonIgnore - public OptionalInt getAmountForAlignment(Alignment alignment){ - if (!this.compatibleAlignmentsAndAmount.containsKey(alignment)) { - return OptionalInt.empty(); - } - return OptionalInt.of(this.compatibleAlignmentsAndAmount.get(alignment)); - } - - @JsonIgnore - public List getThisAndCoarserSubdivisions() { - if (thisAndCoarserSubdivisions != null) { - return thisAndCoarserSubdivisions; - } - List thisAndCoarser = new ArrayList<>(); - if (coarser != null) { - thisAndCoarser.addAll(coarser.getThisAndCoarserSubdivisions()); - } - thisAndCoarser.add(this); - return thisAndCoarserSubdivisions = Collections.unmodifiableList(thisAndCoarser); - - } - } - - @RequiredArgsConstructor - /** - * Specifier for the alignment of {@link DateContext}s of a certain resolution. - * The alignment provides a method to sub divide a dateRangeMask into ranges aligned to the calendar equivalent. - * These sub divisions can then be merged to form the equally grain or coarser desired resolution for the - * {@link DateContext}s. - */ - public static enum Alignment { - NO_ALIGN(List::of), // Special case for resolution == COMPLETE - DAY(CDateRange::getCoveredDays), - QUARTER(CDateRange::getCoveredQuarters), - YEAR(CDateRange::getCoveredYears); - - @Getter @JsonIgnore - private final Function> subdivider; - } - - @RequiredArgsConstructor - public static enum CalendarUnit { - DAYS(Alignment.DAY), - QUARTERS(Alignment.QUARTER), - YEARS(Alignment.YEAR); - - @Getter - private final Alignment alignment; - } - /** * Factory function that produces a list of {@link CDateRange}s from a given dateRangeMask according to the given * {@link AlignmentReference}, {@link Resolution} and {@link Alignment}. @@ -293,9 +97,7 @@ public static Function> getDateRangeSubdivider(Align if (alignedPerResolution == 1) { // When the alignment fits the resolution we can use the the alignment subdivision directly - return (dateRange) -> { - return alignment.getSubdivider().apply(dateRange); - }; + return (dateRange) -> alignment.getSubdivider().apply(dateRange); } return (dateRange) -> { @@ -338,7 +140,7 @@ public static Function> getDateRangeSubdivider(Align * The event (a certain day) itself is expanded to a date range according to the desired alignment and the indexPlacement * determines to which group it belongs. */ - public static List generateRelativeContexts(int event, IndexPlacement indexPlacement, int featureTime, int outcomeTime, DateContext.CalendarUnit timeUnit, List resolutionAndAlignment) { + public static List generateRelativeContexts(int event, IndexPlacement indexPlacement, int featureTime, int outcomeTime, CalendarUnit timeUnit, List resolutionAndAlignment) { if (featureTime < 1 && outcomeTime < 1) { throw new IllegalArgumentException("Both relative times were smaller than 1 (featureTime: " + featureTime + "; outcomeTime: " + outcomeTime + ")"); @@ -409,7 +211,7 @@ public static List generateRelativeContexts(int event, IndexPlaceme * @param timeUnit The time unit. * @return The feature range. */ - private static CDateRange generateFeatureRange(int event, IndexPlacement indexPlacement, int featureTime, DateContext.CalendarUnit timeUnit) { + private static CDateRange generateFeatureRange(int event, IndexPlacement indexPlacement, int featureTime, CalendarUnit timeUnit) { if(featureTime <= 0){ return null; } @@ -451,7 +253,7 @@ private static CDateRange generateFeatureRange(int event, IndexPlacement indexPl * @param resolution The time unit. * @return The outcome range. */ - private static CDateRange generateOutcomeRange(int event, IndexPlacement indexPlacement, int outcomeTime, DateContext.CalendarUnit resolution) { + private static CDateRange generateOutcomeRange(int event, IndexPlacement indexPlacement, int outcomeTime, CalendarUnit resolution) { if (outcomeTime <= 0) { return null; } diff --git a/backend/src/main/java/com/bakdata/conquery/models/forms/util/Resolution.java b/backend/src/main/java/com/bakdata/conquery/models/forms/util/Resolution.java new file mode 100644 index 0000000000..8eac10178f --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/models/forms/util/Resolution.java @@ -0,0 +1,148 @@ +package com.bakdata.conquery.models.forms.util; + +import c10n.C10N; +import com.bakdata.conquery.apiv1.forms.FeatureGroup; +import com.bakdata.conquery.internationalization.DateContextResolutionC10n; +import com.bakdata.conquery.internationalization.Localized; +import com.bakdata.conquery.models.common.daterange.CDateRange; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.RequiredArgsConstructor; + +import java.util.*; + +/** + * Defines the granularity into which a given date range mask is chunked. + * The actual size in days depends on the chosen {@link Alignment}, e.g.: + * If the resolution is YEARS and it should be aligned on the actual YEAR, the resulting contexts days might vary + * depending on if that year was a leap year. + * If the alignment is DAY, then all context will have a length of 365 day, except the dateRangeMask intersects an + * edge context. + */ +@RequiredArgsConstructor +public enum Resolution implements Localized { + /** + * For returning contexts with a single {@link CDateRange} for the entire + * {@link FeatureGroup}. + */ + COMPLETE(null) { + @Override + public String toString(Locale locale) { + + return C10N.get(DateContextResolutionC10n.class, locale).complete(); + } + + @Override + protected List getCompatibleAlignments() { + return List.of(Alignment.NO_ALIGN); + } + }, + + /** + * The {@link CDateRange} contexts per {@link FeatureGroup} are subdivided into + * years. + */ + + YEARS(COMPLETE) { + @Override + public String toString(Locale locale) { + + return C10N.get(DateContextResolutionC10n.class, locale).year(); + } + + @Override + protected List getCompatibleAlignments() { + return List.of( + Alignment.YEAR, + Alignment.QUARTER, + Alignment.DAY); + } + }, + + /** + * The {@link CDateRange} contexts per {@link FeatureGroup} are subdivided into + * quarters. + */ + QUARTERS(YEARS) { + @Override + public String toString(Locale locale) { + + return C10N.get(DateContextResolutionC10n.class, locale).quarter(); + } + + @Override + protected List getCompatibleAlignments() { + return List.of( + Alignment.QUARTER, + Alignment.DAY); + } + }, + + /** + * The {@link CDateRange} contexts per {@link FeatureGroup} are subdivided into + * days. + */ + DAYS(QUARTERS) { + @Override + public String toString(Locale locale) { + + return C10N.get(DateContextResolutionC10n.class, locale).day(); + } + + @Override + protected List getCompatibleAlignments() { + return List.of(Alignment.DAY); + } + }; + + + @JsonIgnore + private final Resolution coarser; + + private List thisAndCoarserSubdivisions; + + public abstract String toString(Locale locale); + + /** + * Returns the alignments, that are compatible with this resolution. + * + * @implNote The first aligment of the returned list is considered the default (see getDefaultAlignment) + */ + @JsonIgnore + protected abstract List getCompatibleAlignments(); + + @JsonIgnore + public boolean isAlignmentSupported(Alignment alignment) { + return getCompatibleAlignments().contains(alignment); + } + + @JsonIgnore + public Alignment getDefaultAlignment() { + // The first alignment is considered the default + return getCompatibleAlignments().get(0); + } + + /** + * Returns the amount of calendar alignment sub date ranges that would fit in to this resolution. + */ + @JsonIgnore + public OptionalInt getAmountForAlignment(Alignment alignment) { + if (!isAlignmentSupported(alignment)) { + return OptionalInt.empty(); + } + return alignment.getAmountForResolution(this); + } + + @JsonIgnore + public List getThisAndCoarserSubdivisions() { + if (thisAndCoarserSubdivisions != null) { + return thisAndCoarserSubdivisions; + } + List thisAndCoarser = new ArrayList<>(); + if (coarser != null) { + thisAndCoarser.addAll(coarser.getThisAndCoarserSubdivisions()); + } + thisAndCoarser.add(this); + return thisAndCoarserSubdivisions = Collections.unmodifiableList(thisAndCoarser); + + } +} diff --git a/backend/src/test/java/com/bakdata/conquery/io/result/ResultTestUtil.java b/backend/src/test/java/com/bakdata/conquery/io/result/ResultTestUtil.java index 7d1c838fad..07c57935fc 100644 --- a/backend/src/test/java/com/bakdata/conquery/io/result/ResultTestUtil.java +++ b/backend/src/test/java/com/bakdata/conquery/io/result/ResultTestUtil.java @@ -3,7 +3,7 @@ import com.bakdata.conquery.models.datasets.concepts.select.Select; import com.bakdata.conquery.models.events.Bucket; import com.bakdata.conquery.models.externalservice.ResultType; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Resolution; import com.bakdata.conquery.models.query.queryplan.aggregators.Aggregator; import com.bakdata.conquery.models.query.queryplan.clone.CloneContext; import com.bakdata.conquery.models.query.results.EntityResult; @@ -12,7 +12,6 @@ import lombok.experimental.UtilityClass; import org.jetbrains.annotations.NotNull; -import java.time.LocalDate; import java.util.List; @UtilityClass @@ -40,7 +39,7 @@ public static List getResultTypes() { @NotNull public static List getTestEntityResults() { List results = List.of( - new SinglelineEntityResult(1, new Object[]{Boolean.TRUE, 2345634, 123423.34, "CAT1", DateContext.Resolution.DAYS.toString(), 5646, List.of(345, 534), "test_string", 4521, List.of(true, false), List.of(List.of(345, 534), List.of(1, 2)), List.of("fizz", "buzz")}), + new SinglelineEntityResult(1, new Object[]{Boolean.TRUE, 2345634, 123423.34, "CAT1", Resolution.DAYS.toString(), 5646, List.of(345, 534), "test_string", 4521, List.of(true, false), List.of(List.of(345, 534), List.of(1, 2)), List.of("fizz", "buzz")}), new SinglelineEntityResult(2, new Object[]{Boolean.FALSE, null, null, null, null, null, null, null, null, List.of(), List.of(List.of(1234, Integer.MAX_VALUE)), List.of()}), new SinglelineEntityResult(2, new Object[]{Boolean.TRUE, null, null, null, null, null, null, null, null, List.of(false, false), null, null}), new MultilineEntityResult(3, List.of( diff --git a/backend/src/test/java/com/bakdata/conquery/models/SerializationTests.java b/backend/src/test/java/com/bakdata/conquery/models/SerializationTests.java index 5b2a1b141c..b9e13df8ce 100644 --- a/backend/src/test/java/com/bakdata/conquery/models/SerializationTests.java +++ b/backend/src/test/java/com/bakdata/conquery/models/SerializationTests.java @@ -41,7 +41,8 @@ import com.bakdata.conquery.models.forms.configs.FormConfig; import com.bakdata.conquery.models.forms.frontendconfiguration.FormConfigProcessor; import com.bakdata.conquery.models.forms.managed.AbsoluteFormQuery; -import com.bakdata.conquery.models.forms.util.DateContext; +import com.bakdata.conquery.models.forms.util.Alignment; +import com.bakdata.conquery.models.forms.util.Resolution; import com.bakdata.conquery.models.identifiable.CentralRegistry; import com.bakdata.conquery.models.identifiable.IdMapSerialisationTest; import com.bakdata.conquery.models.identifiable.ids.specific.DatasetId; @@ -364,8 +365,8 @@ public void testFormQuery() throws IOException, JSONException { CDateRange.exactly(LocalDate.now()).toSimpleRange(), ArrayConceptQuery.createFromFeatures(Collections.singletonList(features)), List.of( - ExportForm.ResolutionAndAlignment.of(DateContext.Resolution.COMPLETE, DateContext.Alignment.NO_ALIGN), - ExportForm.ResolutionAndAlignment.of(DateContext.Resolution.QUARTERS, DateContext.Alignment.QUARTER) + ExportForm.ResolutionAndAlignment.of(Resolution.COMPLETE, Alignment.NO_ALIGN), + ExportForm.ResolutionAndAlignment.of(Resolution.QUARTERS, Alignment.QUARTER) ) ); diff --git a/backend/src/test/java/com/bakdata/conquery/models/externalservice/ResultTypeTest.java b/backend/src/test/java/com/bakdata/conquery/models/externalservice/ResultTypeTest.java index da645ca8f1..d883be8ade 100644 --- a/backend/src/test/java/com/bakdata/conquery/models/externalservice/ResultTypeTest.java +++ b/backend/src/test/java/com/bakdata/conquery/models/externalservice/ResultTypeTest.java @@ -5,16 +5,14 @@ import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.util.Currency; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.stream.Stream; -import com.bakdata.conquery.models.forms.util.DateContext; import com.bakdata.conquery.io.jackson.Jackson; import com.bakdata.conquery.models.config.ConqueryConfig; +import com.bakdata.conquery.models.forms.util.Resolution; import com.bakdata.conquery.models.i18n.I18n; import com.bakdata.conquery.models.query.PrintSettings; import com.google.common.collect.ImmutableMap; @@ -44,8 +42,8 @@ public static List testData() { Arguments.of(PRETTY, ResultType.BooleanT.INSTANCE, true, "Yes"), Arguments.of(PRETTY, ResultType.BooleanT.INSTANCE, false, "No"), Arguments.of(PRETTY, ResultType.CategoricalT.INSTANCE, "test", "test"), - Arguments.of(PRETTY, ResultType.ResolutionT.INSTANCE, DateContext.Resolution.COMPLETE.name(), "complete"), - Arguments.of(PRETTY_DE, ResultType.ResolutionT.INSTANCE, DateContext.Resolution.COMPLETE.name(), "Gesamt"), + Arguments.of(PRETTY, ResultType.ResolutionT.INSTANCE, Resolution.COMPLETE.name(), "complete"), + Arguments.of(PRETTY_DE, ResultType.ResolutionT.INSTANCE, Resolution.COMPLETE.name(), "Gesamt"), Arguments.of(PRETTY, ResultType.DateT.INSTANCE, LocalDate.of(2013, 7, 12).toEpochDay(), "2013-07-12"), Arguments.of(PRETTY_DE, ResultType.DateT.INSTANCE, LocalDate.of(2013, 7, 12).toEpochDay(), "12.07.2013"), Arguments.of(PRETTY, ResultType.DateRangeT.INSTANCE, List.of(Long.valueOf(LocalDate.of(2013, 7, 12).toEpochDay()).intValue(), Long.valueOf(LocalDate.of(2013, 7, 12).toEpochDay()).intValue()), "2013-07-12"), @@ -71,7 +69,7 @@ public static List testData() { Arguments.of(PLAIN, ResultType.NumericT.INSTANCE, 0.2, "0.2"), Arguments.of(PLAIN, ResultType.NumericT.INSTANCE, new BigDecimal("716283712389817246892743124.12312"), "716283712389817246892743124.12312"), Arguments.of(PLAIN, ResultType.StringT.INSTANCE, "test", "test"), - Arguments.of(PLAIN, ResultType.CategoricalT.INSTANCE, DateContext.Resolution.COMPLETE.name(), "COMPLETE"), + Arguments.of(PLAIN, ResultType.CategoricalT.INSTANCE, Resolution.COMPLETE.name(), "COMPLETE"), Arguments.of(PLAIN, ResultType.StringT.INSTANCE, ImmutableMap.of("a", 2, "c", 1), "{a=2, c=1}") ); } diff --git a/backend/src/test/java/com/bakdata/conquery/models/forms/DateContextTest.java b/backend/src/test/java/com/bakdata/conquery/models/forms/DateContextTest.java index 78e55632e7..f3d1a48a13 100644 --- a/backend/src/test/java/com/bakdata/conquery/models/forms/DateContextTest.java +++ b/backend/src/test/java/com/bakdata/conquery/models/forms/DateContextTest.java @@ -5,6 +5,7 @@ import com.bakdata.conquery.apiv1.forms.export_form.ExportForm; import com.bakdata.conquery.models.common.CDate; import com.bakdata.conquery.models.common.daterange.CDateRange; +import com.bakdata.conquery.models.forms.util.CalendarUnit; import com.bakdata.conquery.models.forms.util.DateContext; import org.junit.jupiter.api.Test; @@ -12,8 +13,8 @@ import java.util.Arrays; import java.util.List; -import static com.bakdata.conquery.models.forms.util.DateContext.Alignment.*; -import static com.bakdata.conquery.models.forms.util.DateContext.Resolution.*; +import static com.bakdata.conquery.models.forms.util.Alignment.*; +import static com.bakdata.conquery.models.forms.util.Resolution.*; import static org.assertj.core.api.Assertions.assertThat; public class DateContextTest { @@ -107,7 +108,7 @@ public void rangeAbsQuarterTestWithoutCoarse() { @Test public void rangeRelDaysBeforeTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.DAYS; + CalendarUnit timeUnit = CalendarUnit.DAYS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -128,7 +129,7 @@ public void rangeRelDaysBeforeTest() { @Test public void rangeRelDaysBeforeCompleteOnlyTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.DAYS; + CalendarUnit timeUnit = CalendarUnit.DAYS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -147,7 +148,7 @@ public void rangeRelDaysBeforeCompleteOnlyTest() { @Test public void rangeRelDaysAfterTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.DAYS; + CalendarUnit timeUnit = CalendarUnit.DAYS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -169,7 +170,7 @@ public void rangeRelDaysAfterTest() { @Test public void rangeRelDaysNeutralTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.DAYS; + CalendarUnit timeUnit = CalendarUnit.DAYS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -191,7 +192,7 @@ public void rangeRelDaysNeutralTest() { @Test public void rangeRelQuarterBeforeTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.QUARTERS; + CalendarUnit timeUnit = CalendarUnit.QUARTERS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -212,7 +213,7 @@ public void rangeRelQuarterBeforeTest() { @Test public void rangeRelQuarterAfterTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.QUARTERS; + CalendarUnit timeUnit = CalendarUnit.QUARTERS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -234,7 +235,7 @@ public void rangeRelQuarterAfterTest() { @Test public void rangeRelQuarterNeutralTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.QUARTERS; + CalendarUnit timeUnit = CalendarUnit.QUARTERS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -256,7 +257,7 @@ public void rangeRelQuarterNeutralTest() { @Test public void rangeRelYearsAlignQuarterNeutralTest() { - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.QUARTERS; + CalendarUnit timeUnit = CalendarUnit.QUARTERS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -277,9 +278,9 @@ public void rangeRelYearsAlignQuarterNeutralTest() { @Test public void rangeRelDaysAlignQuarterNeutralTest() { - // This should ignore the QUARTER alignment hint be cause it does not make sense to align a finer resolution than the alignment. + // This should ignore the QUARTER alignment hint because it does not make sense to align a finer resolution than the alignment. - DateContext.CalendarUnit timeUnit = DateContext.CalendarUnit.DAYS; + CalendarUnit timeUnit = CalendarUnit.DAYS; LocalDate eventDate = LocalDate.of(2001, 5, 23); int event = CDate.ofLocalDate(eventDate); int featureTime = 2; @@ -296,4 +297,34 @@ public void rangeRelDaysAlignQuarterNeutralTest() { new DateContext(CDateRange.of(LocalDate.of(2001, 5, 25), LocalDate.of(2001, 5, 25)), FeatureGroup.OUTCOME, 2, eventDate, DAYS) ); } + + + @Test + public void rangeRelYearsQuarterAlignYearsNeutralTest() { + // This should ignore the YEAR alignment hint for QUARTERS because the alignment is to coarse. For QUARTERS it should fallback to QUARTER. + + CalendarUnit timeUnit = CalendarUnit.QUARTERS; + LocalDate eventDate = LocalDate.of(2001, 5, 23); + int event = CDate.ofLocalDate(eventDate); + int featureTime = 3; + int outcomeTime = 3; + IndexPlacement indexPlacement = IndexPlacement.NEUTRAL; + + + List contexts = DateContext.generateRelativeContexts(event, indexPlacement, featureTime, outcomeTime, timeUnit, ExportForm.getResolutionAlignmentMap(List.of(YEARS, QUARTERS), YEAR)); + + assertThat(contexts).containsExactly ( + new DateContext(CDateRange.of(LocalDate.of(2000, 7, 1), LocalDate.of(2000, 12, 31)), FeatureGroup.FEATURE, -2, eventDate, YEARS), + new DateContext(CDateRange.of(LocalDate.of(2001, 1, 1), LocalDate.of(2001, 3, 31)), FeatureGroup.FEATURE, -1, eventDate, YEARS), + new DateContext(CDateRange.of(LocalDate.of(2001, 7, 1), LocalDate.of(2001, 12, 31)), FeatureGroup.OUTCOME, 1, eventDate, YEARS), + new DateContext(CDateRange.of(LocalDate.of(2002, 1, 1), LocalDate.of(2002, 3, 31)), FeatureGroup.OUTCOME, 2, eventDate, YEARS), + + new DateContext(CDateRange.of(LocalDate.of(2000, 7, 1), LocalDate.of(2000, 9, 30)), FeatureGroup.FEATURE, -3, eventDate, QUARTERS), + new DateContext(CDateRange.of(LocalDate.of(2000, 10, 1), LocalDate.of(2000, 12, 31)), FeatureGroup.FEATURE, -2, eventDate, QUARTERS), + new DateContext(CDateRange.of(LocalDate.of(2001, 1, 1), LocalDate.of(2001, 3, 31)), FeatureGroup.FEATURE, -1, eventDate, QUARTERS), + new DateContext(CDateRange.of(LocalDate.of(2001, 7, 1), LocalDate.of(2001, 9, 30)), FeatureGroup.OUTCOME, 1, eventDate, QUARTERS), + new DateContext(CDateRange.of(LocalDate.of(2001, 10, 1), LocalDate.of(2001, 12, 31)), FeatureGroup.OUTCOME, 2, eventDate, QUARTERS), + new DateContext(CDateRange.of(LocalDate.of(2002, 1, 1), LocalDate.of(2002, 3, 31)), FeatureGroup.OUTCOME, 3, eventDate, QUARTERS) + ); + } } diff --git a/docs/REST API JSONs.md b/docs/REST API JSONs.md index bfd9d99f9c..896b55c6b6 100644 --- a/docs/REST API JSONs.md +++ b/docs/REST API JSONs.md @@ -299,7 +299,7 @@ No fields can be set for this type.

-### EXPORT_FORM [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L44) +### EXPORT_FORM [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L45)
Details

@@ -310,10 +310,10 @@ Supported Fields: | | Field | Type | Default | Example | Description | | --- | --- | --- | --- | --- | --- | -| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L61) | alsoCreateCoarserSubdivisions | `boolean` | `true` | | | -| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L48) | queryGroupId | ID of `ManagedExecution` | ␀ | | | -| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L58) | resolution | list of one of COMPLETE, YEARS, QUARTERS, DAYS | `["COMPLETE"]` | | | -| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L55) | timeMode | `@NotNull @Valid Mode` | `null` | | | +| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L62) | alsoCreateCoarserSubdivisions | `boolean` | `true` | | | +| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L49) | queryGroupId | ID of `ManagedExecution` | ␀ | | | +| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L59) | resolution | list of one of COMPLETE, YEARS, QUARTERS, DAYS | `["COMPLETE"]` | | | +| [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/ExportForm.java#L56) | timeMode | `@NotNull @Valid Mode` | `null` | | |

### FULL_EXPORT_FORM [✎](https://github.com/bakdata/conquery/edit/develop/backend/src/main/java/com/bakdata/conquery/apiv1/forms/export_form/FullExportForm.java#L44)