Uploaded image for project: 'Flink'
  1. Flink
  2. FLINK-6356

Make times() eager and enable allowing combinations.

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 1.3.0
    • Fix Version/s: 1.3.0
    • Component/s: CEP
    • Labels:
      None

      Description

      This is the PR that addresses it https://github.com/apache/flink/pull/3761

        Issue Links

          Activity

          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user dawidwys commented on a diff in the pull request:

          https://github.com/apache/flink/pull/3761#discussion_r113377068

          — Diff: flink-libraries/flink-cep/src/main/java/org/apache/flink/cep/pattern/Quantifier.java —
          @@ -18,51 +18,83 @@
          package org.apache.flink.cep.pattern;

          import java.util.EnumSet;
          +import java.util.Objects;

          -public enum Quantifier {

          • ONE,
          • ZERO_OR_MORE_EAGER(QuantifierProperty.LOOPING, QuantifierProperty.EAGER),
          • ZERO_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING),
          • ZERO_OR_MORE_EAGER_STRICT(QuantifierProperty.EAGER, QuantifierProperty.STRICT, QuantifierProperty.LOOPING),
          • ZERO_OR_MORE_COMBINATIONS_STRICT(QuantifierProperty.STRICT, QuantifierProperty.LOOPING),
          • ONE_OR_MORE_EAGER(
          • QuantifierProperty.LOOPING,
          • QuantifierProperty.EAGER,
          • QuantifierProperty.AT_LEAST_ONE),
          • ONE_OR_MORE_EAGER_STRICT(
          • QuantifierProperty.STRICT,
          • QuantifierProperty.LOOPING,
          • QuantifierProperty.EAGER,
          • QuantifierProperty.AT_LEAST_ONE),
          • ONE_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING, QuantifierProperty.AT_LEAST_ONE),
          • ONE_OR_MORE_COMBINATIONS_STRICT(
          • QuantifierProperty.STRICT,
          • QuantifierProperty.LOOPING,
          • QuantifierProperty.AT_LEAST_ONE),
          • TIMES(QuantifierProperty.TIMES),
          • TIMES_STRICT(QuantifierProperty.TIMES, QuantifierProperty.STRICT),
          • OPTIONAL;
            +public class Quantifier {

          private final EnumSet<QuantifierProperty> properties;

          • Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) {
            + private Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) { this.properties = EnumSet.of(first, rest); }
          • Quantifier() {
          • this.properties = EnumSet.noneOf(QuantifierProperty.class);
            + public static Quantifier ONE() { + return new Quantifier(QuantifierProperty.SINGLE); + }

            +
            + public static Quantifier ONE_OR_MORE()

            { + return new Quantifier(QuantifierProperty.LOOPING, QuantifierProperty.EAGER); + }

            +
            + public static Quantifier TIMES()

            { + return new Quantifier(QuantifierProperty.TIMES, QuantifierProperty.EAGER); }

          public boolean hasProperty(QuantifierProperty property)

          { return properties.contains(property); }

          + public void combinations() {
          — End diff –

          The first `if` will be satisfied by `ONE` resulting in misleading exception. Because of that also the `else` branch of the second branch will not be reachable.

          Show
          githubbot ASF GitHub Bot added a comment - Github user dawidwys commented on a diff in the pull request: https://github.com/apache/flink/pull/3761#discussion_r113377068 — Diff: flink-libraries/flink-cep/src/main/java/org/apache/flink/cep/pattern/Quantifier.java — @@ -18,51 +18,83 @@ package org.apache.flink.cep.pattern; import java.util.EnumSet; +import java.util.Objects; -public enum Quantifier { ONE, ZERO_OR_MORE_EAGER(QuantifierProperty.LOOPING, QuantifierProperty.EAGER), ZERO_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING), ZERO_OR_MORE_EAGER_STRICT(QuantifierProperty.EAGER, QuantifierProperty.STRICT, QuantifierProperty.LOOPING), ZERO_OR_MORE_COMBINATIONS_STRICT(QuantifierProperty.STRICT, QuantifierProperty.LOOPING), ONE_OR_MORE_EAGER( QuantifierProperty.LOOPING, QuantifierProperty.EAGER, QuantifierProperty.AT_LEAST_ONE), ONE_OR_MORE_EAGER_STRICT( QuantifierProperty.STRICT, QuantifierProperty.LOOPING, QuantifierProperty.EAGER, QuantifierProperty.AT_LEAST_ONE), ONE_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING, QuantifierProperty.AT_LEAST_ONE), ONE_OR_MORE_COMBINATIONS_STRICT( QuantifierProperty.STRICT, QuantifierProperty.LOOPING, QuantifierProperty.AT_LEAST_ONE), TIMES(QuantifierProperty.TIMES), TIMES_STRICT(QuantifierProperty.TIMES, QuantifierProperty.STRICT), OPTIONAL; +public class Quantifier { private final EnumSet<QuantifierProperty> properties; Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) { + private Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) { this.properties = EnumSet.of(first, rest); } Quantifier() { this.properties = EnumSet.noneOf(QuantifierProperty.class); + public static Quantifier ONE() { + return new Quantifier(QuantifierProperty.SINGLE); + } + + public static Quantifier ONE_OR_MORE() { + return new Quantifier(QuantifierProperty.LOOPING, QuantifierProperty.EAGER); + } + + public static Quantifier TIMES() { + return new Quantifier(QuantifierProperty.TIMES, QuantifierProperty.EAGER); } public boolean hasProperty(QuantifierProperty property) { return properties.contains(property); } + public void combinations() { — End diff – The first `if` will be satisfied by `ONE` resulting in misleading exception. Because of that also the `else` branch of the second branch will not be reachable.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user dawidwys commented on a diff in the pull request:

          https://github.com/apache/flink/pull/3761#discussion_r113378455

          — Diff: flink-libraries/flink-cep/src/main/java/org/apache/flink/cep/pattern/Pattern.java —
          @@ -162,130 +186,114 @@ public int getTimes() {
          }

          /**

          • * Appends a new pattern operator to the existing one. The new pattern operator enforces strict
          • * temporal contiguity. This means that the whole pattern only matches if an event which matches
          • * this operator directly follows the preceding matching event. Thus, there cannot be any
          • * events in between two matching events.
            + * Appends a new pattern to the existing one. The new pattern enforces strict
            + * temporal contiguity. This means that the whole pattern sequence matches only
            + * if an event which matches this pattern directly follows the preceding matching
            + * event. Thus, there cannot be any events in between two matching events.
            *
          • * @param name Name of the new pattern operator
          • * @return A new pattern operator which is appended to this pattern operator
            + * @param name Name of the new pattern
            + * @return A new pattern which is appended to this one
            */
            public Pattern<T, T> next(final String name) { return new Pattern<T, T>(name, this); }

          /**

          • * Appends a new pattern operator to the existing one. The new pattern operator enforces
          • * non-strict temporal contiguity. This means that a matching event of this operator and the
            + * Appends a new pattern to the existing one. The new pattern enforces non-strict
            + * temporal contiguity. This means that a matching event of this pattern and the
          • preceding matching event might be interleaved with other events which are ignored.
            *
          • * @param name Name of the new pattern operator
          • * @return A new pattern operator which is appended to this pattern operator
            + * @param name Name of the new pattern
            + * @return A new pattern which is appended to this one
            */
            public FollowedByPattern<T, T> followedBy(final String name) { return new FollowedByPattern<T, T>(name, this); }

          /**

          • * Starts a new pattern with the initial pattern operator whose name is provided. Furthermore,
          • * the base type of the event sequence is set.
          • *
          • * @param name Name of the new pattern operator
          • * @param <X> Base type of the event pattern
          • * @return The first pattern operator of a pattern
          • */
          • public static <X> Pattern<X, X> begin(final String name) { - return new Pattern<X, X>(name, null); - }

            -

          • /**
          • * Specifies that this pattern can occur zero or more times(kleene star).
          • * This means any number of events can be matched in this state.
            + * Specifies that this pattern is optional for a final match of the pattern
            + * sequence to happen.
            *
          • * @return The same pattern with applied Kleene star operator
          • *
          • * @throws MalformedPatternException if quantifier already applied
            + * @return The same pattern as optional.
            + * @throws MalformedPatternException if the quantifier is not applicable to this pattern.
            */
          • public Pattern<T, F> zeroOrMore() {
          • return zeroOrMore(true);
            + public Pattern<T, F> optional() { + quantifier.optional(); + return this; }

          /**

          • * Specifies that this pattern can occur zero or more times(kleene star).
          • * This means any number of events can be matched in this state.
          • *
          • * If eagerness is enabled for a pattern A*B and sequence A1 A2 B will generate patterns:
          • * B, A1 B and A1 A2 B. If disabled B, A1 B, A2 B and A1 A2 B.
            + * Specifies that this pattern can occur {@code one or more}

            times.
            + * This means at least one and at most infinite number of events can
            + * be matched to this pattern.
            *

          • * @param eager if true the pattern always consumes earlier events
          • * @return The same pattern with applied Kleene star operator
            + * <p>If this quantifier is enabled for a
            + * pattern {@code A.oneOrMore().followedBy(B)}

            and a sequence of events
            + *

            {@code A1 A2 B} appears, this will generate patterns:
            + * {@code A1 B} and {@code A1 A2 B}

            . See also

            {@link #allowCombinations()}

            .
            *

          • * @throws MalformedPatternException if quantifier already applied
            + * @return The same pattern with a {@link Quantifier#ONE_OR_MORE()} quantifier applied.
            + * @throws MalformedPatternException if the quantifier is not applicable to this pattern.
            */
            - public Pattern<T, F> zeroOrMore(final boolean eager) {
            + public Pattern<T, F> oneOrMore() {
            checkIfQuantifierApplied();
            - if (eager) { - this.quantifier = Quantifier.ZERO_OR_MORE_EAGER; - } else { - this.quantifier = Quantifier.ZERO_OR_MORE_COMBINATIONS; - }
            + this.quantifier = Quantifier.ONE_OR_MORE();
            return this;
            }

            /**
            - * Specifies that this pattern can occur one or more times(kleene star).
            - * This means at least one and at most infinite number of events can be matched in this state.
            + * Specifies exact number of times that this pattern should be matched.
            *
            - * @return The same pattern with applied Kleene plus operator
            + * @param times number of times matching event must appear
            + * @return The same pattern with number of times applied
            *
            - * @throws MalformedPatternException if quantifier already applied
            + * @throws MalformedPatternException if the quantifier is not applicable to this pattern.
            */
            - public Pattern<T, F> oneOrMore() {
            - return oneOrMore(true);
            + public Pattern<T, F> times(int times) { + checkIfQuantifierApplied(); + Preconditions.checkArgument(times > 0, "You should give a positive number greater than 0."); + this.quantifier = Quantifier.TIMES(); + this.times = times; + return this; }

            /**
            - * Specifies that this pattern can occur one or more times(kleene star).
            - * This means at least one and at most infinite number of events can be matched in this state.
            + * Applicable only to {@link Quantifier#ONE_OR_MORE()}

            patterns, this option

              • End diff –

          It is also applicable to `TIMES()`

          Show
          githubbot ASF GitHub Bot added a comment - Github user dawidwys commented on a diff in the pull request: https://github.com/apache/flink/pull/3761#discussion_r113378455 — Diff: flink-libraries/flink-cep/src/main/java/org/apache/flink/cep/pattern/Pattern.java — @@ -162,130 +186,114 @@ public int getTimes() { } /** * Appends a new pattern operator to the existing one. The new pattern operator enforces strict * temporal contiguity. This means that the whole pattern only matches if an event which matches * this operator directly follows the preceding matching event. Thus, there cannot be any * events in between two matching events. + * Appends a new pattern to the existing one. The new pattern enforces strict + * temporal contiguity. This means that the whole pattern sequence matches only + * if an event which matches this pattern directly follows the preceding matching + * event. Thus, there cannot be any events in between two matching events. * * @param name Name of the new pattern operator * @return A new pattern operator which is appended to this pattern operator + * @param name Name of the new pattern + * @return A new pattern which is appended to this one */ public Pattern<T, T> next(final String name) { return new Pattern<T, T>(name, this); } /** * Appends a new pattern operator to the existing one. The new pattern operator enforces * non-strict temporal contiguity. This means that a matching event of this operator and the + * Appends a new pattern to the existing one. The new pattern enforces non-strict + * temporal contiguity. This means that a matching event of this pattern and the preceding matching event might be interleaved with other events which are ignored. * * @param name Name of the new pattern operator * @return A new pattern operator which is appended to this pattern operator + * @param name Name of the new pattern + * @return A new pattern which is appended to this one */ public FollowedByPattern<T, T> followedBy(final String name) { return new FollowedByPattern<T, T>(name, this); } /** * Starts a new pattern with the initial pattern operator whose name is provided. Furthermore, * the base type of the event sequence is set. * * @param name Name of the new pattern operator * @param <X> Base type of the event pattern * @return The first pattern operator of a pattern */ public static <X> Pattern<X, X> begin(final String name) { - return new Pattern<X, X>(name, null); - } - /** * Specifies that this pattern can occur zero or more times(kleene star). * This means any number of events can be matched in this state. + * Specifies that this pattern is optional for a final match of the pattern + * sequence to happen. * * @return The same pattern with applied Kleene star operator * * @throws MalformedPatternException if quantifier already applied + * @return The same pattern as optional. + * @throws MalformedPatternException if the quantifier is not applicable to this pattern. */ public Pattern<T, F> zeroOrMore() { return zeroOrMore(true); + public Pattern<T, F> optional() { + quantifier.optional(); + return this; } /** * Specifies that this pattern can occur zero or more times(kleene star). * This means any number of events can be matched in this state. * * If eagerness is enabled for a pattern A*B and sequence A1 A2 B will generate patterns: * B, A1 B and A1 A2 B. If disabled B, A1 B, A2 B and A1 A2 B. + * Specifies that this pattern can occur {@code one or more} times. + * This means at least one and at most infinite number of events can + * be matched to this pattern. * * @param eager if true the pattern always consumes earlier events * @return The same pattern with applied Kleene star operator + * <p>If this quantifier is enabled for a + * pattern {@code A.oneOrMore().followedBy(B)} and a sequence of events + * {@code A1 A2 B} appears, this will generate patterns: + * {@code A1 B} and {@code A1 A2 B} . See also {@link #allowCombinations()} . * * @throws MalformedPatternException if quantifier already applied + * @return The same pattern with a {@link Quantifier#ONE_OR_MORE()} quantifier applied. + * @throws MalformedPatternException if the quantifier is not applicable to this pattern. */ - public Pattern<T, F> zeroOrMore(final boolean eager) { + public Pattern<T, F> oneOrMore() { checkIfQuantifierApplied(); - if (eager) { - this.quantifier = Quantifier.ZERO_OR_MORE_EAGER; - } else { - this.quantifier = Quantifier.ZERO_OR_MORE_COMBINATIONS; - } + this.quantifier = Quantifier.ONE_OR_MORE(); return this; } /** - * Specifies that this pattern can occur one or more times(kleene star). - * This means at least one and at most infinite number of events can be matched in this state. + * Specifies exact number of times that this pattern should be matched. * - * @return The same pattern with applied Kleene plus operator + * @param times number of times matching event must appear + * @return The same pattern with number of times applied * - * @throws MalformedPatternException if quantifier already applied + * @throws MalformedPatternException if the quantifier is not applicable to this pattern. */ - public Pattern<T, F> oneOrMore() { - return oneOrMore(true); + public Pattern<T, F> times(int times) { + checkIfQuantifierApplied(); + Preconditions.checkArgument(times > 0, "You should give a positive number greater than 0."); + this.quantifier = Quantifier.TIMES(); + this.times = times; + return this; } /** - * Specifies that this pattern can occur one or more times(kleene star). - * This means at least one and at most infinite number of events can be matched in this state. + * Applicable only to {@link Quantifier#ONE_OR_MORE()} patterns, this option End diff – It is also applicable to `TIMES()`
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user dawidwys commented on a diff in the pull request:

          https://github.com/apache/flink/pull/3761#discussion_r113377152

          — Diff: flink-libraries/flink-cep/src/main/java/org/apache/flink/cep/pattern/Quantifier.java —
          @@ -18,51 +18,83 @@
          package org.apache.flink.cep.pattern;

          import java.util.EnumSet;
          +import java.util.Objects;

          -public enum Quantifier {

          • ONE,
          • ZERO_OR_MORE_EAGER(QuantifierProperty.LOOPING, QuantifierProperty.EAGER),
          • ZERO_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING),
          • ZERO_OR_MORE_EAGER_STRICT(QuantifierProperty.EAGER, QuantifierProperty.STRICT, QuantifierProperty.LOOPING),
          • ZERO_OR_MORE_COMBINATIONS_STRICT(QuantifierProperty.STRICT, QuantifierProperty.LOOPING),
          • ONE_OR_MORE_EAGER(
          • QuantifierProperty.LOOPING,
          • QuantifierProperty.EAGER,
          • QuantifierProperty.AT_LEAST_ONE),
          • ONE_OR_MORE_EAGER_STRICT(
          • QuantifierProperty.STRICT,
          • QuantifierProperty.LOOPING,
          • QuantifierProperty.EAGER,
          • QuantifierProperty.AT_LEAST_ONE),
          • ONE_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING, QuantifierProperty.AT_LEAST_ONE),
          • ONE_OR_MORE_COMBINATIONS_STRICT(
          • QuantifierProperty.STRICT,
          • QuantifierProperty.LOOPING,
          • QuantifierProperty.AT_LEAST_ONE),
          • TIMES(QuantifierProperty.TIMES),
          • TIMES_STRICT(QuantifierProperty.TIMES, QuantifierProperty.STRICT),
          • OPTIONAL;
            +public class Quantifier {

          private final EnumSet<QuantifierProperty> properties;

          • Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) {
            + private Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) { this.properties = EnumSet.of(first, rest); }
          • Quantifier() {
          • this.properties = EnumSet.noneOf(QuantifierProperty.class);
            + public static Quantifier ONE() { + return new Quantifier(QuantifierProperty.SINGLE); + }

            +
            + public static Quantifier ONE_OR_MORE()

            { + return new Quantifier(QuantifierProperty.LOOPING, QuantifierProperty.EAGER); + }

            +
            + public static Quantifier TIMES()

            { + return new Quantifier(QuantifierProperty.TIMES, QuantifierProperty.EAGER); }

          public boolean hasProperty(QuantifierProperty property)

          { return properties.contains(property); }

          + public void combinations() {
          + if (!hasProperty(Quantifier.QuantifierProperty.EAGER))

          { + throw new MalformedPatternException("Combinations already allowed!"); + }

          +
          + if (hasProperty(Quantifier.QuantifierProperty.LOOPING) || hasProperty(Quantifier.QuantifierProperty.TIMES))

          { + properties.remove(Quantifier.QuantifierProperty.EAGER); + }

          else

          { + throw new MalformedPatternException("Combinations not applicable to " + this + "!"); + }

          + }
          +
          + public void consecutive() {
          + if (hasProperty(Quantifier.QuantifierProperty.CONSECUTIVE)) {
          — End diff –

          Same here for the `ONE` quantifier.

          Show
          githubbot ASF GitHub Bot added a comment - Github user dawidwys commented on a diff in the pull request: https://github.com/apache/flink/pull/3761#discussion_r113377152 — Diff: flink-libraries/flink-cep/src/main/java/org/apache/flink/cep/pattern/Quantifier.java — @@ -18,51 +18,83 @@ package org.apache.flink.cep.pattern; import java.util.EnumSet; +import java.util.Objects; -public enum Quantifier { ONE, ZERO_OR_MORE_EAGER(QuantifierProperty.LOOPING, QuantifierProperty.EAGER), ZERO_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING), ZERO_OR_MORE_EAGER_STRICT(QuantifierProperty.EAGER, QuantifierProperty.STRICT, QuantifierProperty.LOOPING), ZERO_OR_MORE_COMBINATIONS_STRICT(QuantifierProperty.STRICT, QuantifierProperty.LOOPING), ONE_OR_MORE_EAGER( QuantifierProperty.LOOPING, QuantifierProperty.EAGER, QuantifierProperty.AT_LEAST_ONE), ONE_OR_MORE_EAGER_STRICT( QuantifierProperty.STRICT, QuantifierProperty.LOOPING, QuantifierProperty.EAGER, QuantifierProperty.AT_LEAST_ONE), ONE_OR_MORE_COMBINATIONS(QuantifierProperty.LOOPING, QuantifierProperty.AT_LEAST_ONE), ONE_OR_MORE_COMBINATIONS_STRICT( QuantifierProperty.STRICT, QuantifierProperty.LOOPING, QuantifierProperty.AT_LEAST_ONE), TIMES(QuantifierProperty.TIMES), TIMES_STRICT(QuantifierProperty.TIMES, QuantifierProperty.STRICT), OPTIONAL; +public class Quantifier { private final EnumSet<QuantifierProperty> properties; Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) { + private Quantifier(final QuantifierProperty first, final QuantifierProperty... rest) { this.properties = EnumSet.of(first, rest); } Quantifier() { this.properties = EnumSet.noneOf(QuantifierProperty.class); + public static Quantifier ONE() { + return new Quantifier(QuantifierProperty.SINGLE); + } + + public static Quantifier ONE_OR_MORE() { + return new Quantifier(QuantifierProperty.LOOPING, QuantifierProperty.EAGER); + } + + public static Quantifier TIMES() { + return new Quantifier(QuantifierProperty.TIMES, QuantifierProperty.EAGER); } public boolean hasProperty(QuantifierProperty property) { return properties.contains(property); } + public void combinations() { + if (!hasProperty(Quantifier.QuantifierProperty.EAGER)) { + throw new MalformedPatternException("Combinations already allowed!"); + } + + if (hasProperty(Quantifier.QuantifierProperty.LOOPING) || hasProperty(Quantifier.QuantifierProperty.TIMES)) { + properties.remove(Quantifier.QuantifierProperty.EAGER); + } else { + throw new MalformedPatternException("Combinations not applicable to " + this + "!"); + } + } + + public void consecutive() { + if (hasProperty(Quantifier.QuantifierProperty.CONSECUTIVE)) { — End diff – Same here for the `ONE` quantifier.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user kl0u commented on the issue:

          https://github.com/apache/flink/pull/3761

          Thanks for the review @dawidwys ! Thanks a lot for the review.
          I integrated your comments. Let me know when you are done so that I can merge this.

          Show
          githubbot ASF GitHub Bot added a comment - Github user kl0u commented on the issue: https://github.com/apache/flink/pull/3761 Thanks for the review @dawidwys ! Thanks a lot for the review. I integrated your comments. Let me know when you are done so that I can merge this.
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user dawidwys commented on the issue:

          https://github.com/apache/flink/pull/3761

          @kl0u I don't have any more comments, so for me it LGTM.

          I've also rebased the skip-till-next on top of this one, so would be nice to merge it and start reviewing #3698

          Show
          githubbot ASF GitHub Bot added a comment - Github user dawidwys commented on the issue: https://github.com/apache/flink/pull/3761 @kl0u I don't have any more comments, so for me it LGTM. I've also rebased the skip-till-next on top of this one, so would be nice to merge it and start reviewing #3698
          Hide
          githubbot ASF GitHub Bot added a comment -

          Github user asfgit closed the pull request at:

          https://github.com/apache/flink/pull/3761

          Show
          githubbot ASF GitHub Bot added a comment - Github user asfgit closed the pull request at: https://github.com/apache/flink/pull/3761
          Hide
          kkl0u Kostas Kloudas added a comment -

          Merged at 0d856b34f90c0775b7c9d5e88f5ed6cdf6090851

          Show
          kkl0u Kostas Kloudas added a comment - Merged at 0d856b34f90c0775b7c9d5e88f5ed6cdf6090851

            People

            • Assignee:
              kkl0u Kostas Kloudas
              Reporter:
              kkl0u Kostas Kloudas
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development