Implements database, adds profiles

This commit is contained in:
2025-10-01 22:23:00 +02:00
parent b2571c191f
commit d2da811868
86 changed files with 17323 additions and 483 deletions

View File

@@ -11,10 +11,6 @@
<artifactId>fro-core</artifactId>
<properties>
<jackson.version>2.20.0</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>ch.gtache.fro</groupId>
@@ -24,11 +20,6 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>

View File

@@ -0,0 +1,36 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.BirdFamily;
import ch.gtache.fro.Translated;
import java.util.Locale;
import java.util.Map;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link BirdFamily}
*
* @param name The family name
* @param translations The translations
*/
public record BirdFamilyImpl(String name, Map<Locale, String> translations) implements BirdFamily, Translated {
/**
* Instantiates the bird family
*
* @param name The family name
* @param translations The translations
* @throws NullPointerException If any parameter is null
*/
public BirdFamilyImpl {
requireNonNull(name);
translations = Map.copyOf(translations);
}
@Override
public String translate(final Locale locale) {
final var translation = Translated.super.translate(locale);
return translation == null ? name : translation;
}
}

View File

@@ -0,0 +1,27 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.BirdFamily;
import ch.gtache.fro.BirdFamilyTranslator;
import ch.gtache.fro.Translated;
import jakarta.inject.Inject;
import java.util.Locale;
/**
* Implementation of {@link BirdFamilyTranslator}
*/
public class BirdFamilyTranslatorImpl implements BirdFamilyTranslator {
@Inject
BirdFamilyTranslatorImpl() {
}
@Override
public String translate(final BirdFamily object, final Locale locale) {
if (object instanceof final Translated t) {
return t.translate(locale);
} else {
return object.name();
}
}
}

View File

@@ -1,23 +1,45 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.BirdFamily;
import ch.gtache.fro.MigrationType;
import ch.gtache.fro.Translated;
import java.util.Objects;
import java.util.Locale;
import java.util.Map;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link Bird}
*
* @param name The bird name
* @param name The bird name
* @param family The bird family
* @param migrationType The bird migration type
* @param translations The translations
*/
public record BirdImpl(String name) implements Bird {
public record BirdImpl(String name, BirdFamily family, MigrationType migrationType,
Map<Locale, String> translations) implements Bird, Translated {
/**
* Instantiates the bird
*
* @param name The bird name
* @param name The bird name
* @param family The bird family
* @param migrationType The bird migration type
* @param translations The translations
* @throws NullPointerException If any parameter is null
*/
public BirdImpl {
Objects.requireNonNull(name);
requireNonNull(name);
requireNonNull(family);
requireNonNull(migrationType);
translations = Map.copyOf(translations);
}
@Override
public String translate(final Locale locale) {
final var translation = Translated.super.translate(locale);
return translation == null ? name : translation;
}
}

View File

@@ -0,0 +1,27 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.BirdTranslator;
import ch.gtache.fro.Translated;
import jakarta.inject.Inject;
import java.util.Locale;
/**
* Implementation of {@link BirdTranslator}
*/
public class BirdTranslatorImpl implements BirdTranslator {
@Inject
BirdTranslatorImpl() {
}
@Override
public String translate(final Bird object, final Locale locale) {
if (object instanceof final Translated t) {
return t.translate(locale);
} else {
return object.name();
}
}
}

View File

@@ -1,6 +1,8 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.BirdFamily;
import ch.gtache.fro.MigrationType;
/**
* Represents all the birds (on Vogelwarte)
@@ -434,4 +436,15 @@ public enum CommonBirds implements Bird {
VAUTOUR_PERCNOPTERE,
VENTURON_MONTAGNARD,
VERDIER_D_EUROPE,
;
@Override
public BirdFamily family() {
throw new UnsupportedOperationException();
}
@Override
public MigrationType migrationType() {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,32 +0,0 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.BirdTranslator;
import jakarta.inject.Inject;
import java.util.Locale;
/**
* Implementation of {@link BirdTranslator} for {@link CommonBirds}
*/
public class CommonBirdsTranslatorImpl extends AbstractBundleTranslator<Bird> implements BirdTranslator {
@Inject
CommonBirdsTranslatorImpl() {
super("ch.gtache.fro.impl.BirdBundle");
}
@Override
public String translate(final Bird object, final Locale locale) {
if (object instanceof CommonBirds) {
return super.translate(object, locale);
} else {
return object.name();
}
}
@Override
protected String getKey(final Bird object) {
return "bird." + object.name() + ".label";
}
}

View File

@@ -0,0 +1,31 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.practice.BirdPracticeParameters;
import ch.gtache.fro.practice.GroupedBirdPracticeParameters;
import ch.gtache.fro.practice.PracticeProfile;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* Implementation of {@link GroupedBirdPracticeParameters}
*
* @param profile The profile
* @param parameters The parameters
*/
public record GroupedBirdPracticeParametersImpl(PracticeProfile profile,
Collection<BirdPracticeParameters> parameters) implements GroupedBirdPracticeParameters {
/**
* Instantiates the parameters
*
* @param profile The profile
* @param parameters The parameters
* @throws NullPointerException If any parameter is null
*/
public GroupedBirdPracticeParametersImpl {
Objects.requireNonNull(profile);
parameters = List.copyOf(parameters);
}
}

View File

@@ -0,0 +1,36 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.MigrationType;
import ch.gtache.fro.Translated;
import java.util.Locale;
import java.util.Map;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link MigrationType}
*
* @param name The type name
* @param translations The translations
*/
public record MigrationTypeImpl(String name, Map<Locale, String> translations) implements MigrationType, Translated {
/**
* Instantiates the type
*
* @param name The type name
* @param translations The translations
* @throws NullPointerException If any parameter is null
*/
public MigrationTypeImpl {
requireNonNull(name);
translations = Map.copyOf(translations);
}
@Override
public String translate(final Locale locale) {
final var translation = Translated.super.translate(locale);
return translation == null ? name : translation;
}
}

View File

@@ -0,0 +1,27 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.MigrationType;
import ch.gtache.fro.MigrationTypeTranslator;
import ch.gtache.fro.Translated;
import jakarta.inject.Inject;
import java.util.Locale;
/**
* Implementation of {@link MigrationTypeTranslator}
*/
public class MigrationTypeTranslatorImpl implements MigrationTypeTranslator {
@Inject
MigrationTypeTranslatorImpl() {
}
@Override
public String translate(final MigrationType object, final Locale locale) {
if (object instanceof final Translated t) {
return t.translate(locale);
} else {
return object.name();
}
}
}

View File

@@ -0,0 +1,35 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.BirdFamily;
import ch.gtache.fro.MigrationType;
import static java.util.Objects.requireNonNull;
/**
* Minimal implementation of {@link Bird}
*
* @param name The bird name
*/
public record MinimalBirdImpl(String name) implements Bird {
/**
* Instantiates the bird
*
* @param name The bird name
* @throws NullPointerException If any parameter is null
*/
public MinimalBirdImpl {
requireNonNull(name);
}
@Override
public BirdFamily family() {
throw new UnsupportedOperationException("Not supported");
}
@Override
public MigrationType migrationType() {
throw new UnsupportedOperationException("Not supported");
}
}

View File

@@ -0,0 +1,25 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.Fetcher;
import ch.gtache.fro.FilledBird;
import java.util.List;
/**
* {@link Fetcher} that does nothing
*
* @param name The name of the fetcher
*/
public record NoFetcherImpl(String name) implements Fetcher {
@Override
public List<FilledBird> fetchAll() {
return List.of();
}
@Override
public FilledBird fetch(final Bird bird) {
return new FilledBirdImpl(bird, List.of(), List.of());
}
}

View File

@@ -1,213 +0,0 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.BirdProvider;
import ch.gtache.fro.Fetcher;
import ch.gtache.fro.FetcherProvider;
import ch.gtache.fro.PictureType;
import ch.gtache.fro.ProvisionException;
import ch.gtache.fro.SoundType;
import ch.gtache.fro.practice.BirdPracticeParameters;
import ch.gtache.fro.practice.PracticeConfiguration;
import ch.gtache.fro.practice.QuestionType;
import ch.gtache.fro.practice.impl.BirdPracticeParametersImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.prefs.Preferences;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link PracticeConfiguration}
*/
public class PracticeConfigurationImpl implements PracticeConfiguration {
private static final Logger logger = LogManager.getLogger(PracticeConfigurationImpl.class);
private static final int DEFAULT_GUESS_NUMBER = 1;
private static final String QUESTION_RATES_KEY = "questionRates";
private static final String BIRD_PRACTICE_PARAMETERS_KEY = "birdPracticeParameters";
private static final String GUESSES_NUMBER_KEY = "guessNumber";
private final BirdProvider birdProvider;
private final FetcherProvider fetcherProvider;
private final ObjectMapper objectMapper;
private final Preferences preferences;
private final Map<Bird, BirdPracticeParameters> birdPracticeParameters;
private final Map<QuestionType, Double> questionRates;
private int guessesNumber;
@Inject
PracticeConfigurationImpl(final BirdProvider birdProvider, final FetcherProvider fetcherProvider, final ObjectMapper objectMapper) {
this.birdProvider = requireNonNull(birdProvider);
this.fetcherProvider = requireNonNull(fetcherProvider);
this.objectMapper = requireNonNull(objectMapper);
this.preferences = Preferences.userNodeForPackage(this.getClass());
this.birdPracticeParameters = new HashMap<>();
this.questionRates = new EnumMap<>(QuestionType.class);
this.guessesNumber = DEFAULT_GUESS_NUMBER;
load();
}
private void load() {
loadBirdPracticeParameters();
loadQuestionRates();
this.guessesNumber = preferences.getInt(GUESSES_NUMBER_KEY, DEFAULT_GUESS_NUMBER);
}
private void loadQuestionRates() {
final var json = preferences.get(QUESTION_RATES_KEY, "{}");
try {
final var deserializedQuestionRates = objectMapper.readValue(json, new QuestionRatesMapTypeReference());
questionRates.putAll(deserializedQuestionRates);
} catch (final JsonProcessingException e) {
logger.error("Failed to deserialize question rates", e);
}
Arrays.stream(QuestionType.values()).forEach(qt -> {
if (!questionRates.containsKey(qt)) {
questionRates.put(qt, qt == QuestionType.PICTURE_EXACT ? 1.0 : 0.0);
}
});
}
private void loadBirdPracticeParameters() {
final var practiceParameters = preferences.get(BIRD_PRACTICE_PARAMETERS_KEY, "[]");
try {
final var deserializedPracticeParameters = deserializeBirdPracticeParameters(objectMapper.readValue(practiceParameters, new SerializedBirdPracticeParametersTypeReference()));
birdPracticeParameters.putAll(deserializedPracticeParameters);
} catch (final JsonProcessingException e) {
logger.error("Failed to deserialize bird parameters", e);
}
try {
final var allBirds = birdProvider.getAllObjects();
allBirds.forEach(b -> {
if (!birdPracticeParameters.containsKey(b)) {
birdPracticeParameters.put(b, getDefaultParameters(b));
}
});
} catch (final ProvisionException e) {
logger.error("Failed to get all birds", e);
}
}
private Map<Bird, BirdPracticeParameters> deserializeBirdPracticeParameters(final List<SerializedBirdPracticeParameters> parameters) {
return parameters.stream().collect(Collectors.toMap(p -> {
try {
return birdProvider.getObject(p.bird());
} catch (final ProvisionException e) {
logger.error("Failed to get bird {}", p.bird(), e);
return null;
}
}, this::deserializeBirdPracticeParameters));
}
private BirdPracticeParameters deserializeBirdPracticeParameters(final SerializedBirdPracticeParameters parameters) {
try {
final var bird = birdProvider.getObject(parameters.bird());
return new BirdPracticeParametersImpl(bird, parameters.enabled(), parameters.enabledFetchers(), parameters.enabledPictureTypes(), parameters.enabledSoundTypes());
} catch (final ProvisionException e) {
logger.error("Failed to get bird {}", parameters.bird(), e);
return null;
}
}
private List<SerializedBirdPracticeParameters> serializeBirdPracticeParameters() {
return birdPracticeParameters.values().stream().map(PracticeConfigurationImpl::serializeBirdPracticeParameters).toList();
}
private static SerializedBirdPracticeParameters serializeBirdPracticeParameters(final BirdPracticeParameters parameters) {
return new SerializedBirdPracticeParameters(parameters.bird().name(), parameters.enabled(), parameters.enabledFetchers(), parameters.enabledPictureTypes(), parameters.enabledSoundTypes());
}
@Override
public int guessesNumber() {
return guessesNumber;
}
@Override
public void setGuessesNumber(final int guessNumber) {
this.guessesNumber = guessNumber;
preferences.putInt(GUESSES_NUMBER_KEY, guessNumber);
}
@Override
public Map<QuestionType, Double> questionRates() {
return new EnumMap<>(questionRates);
}
@Override
public void setQuestionRates(final Map<QuestionType, Double> questionRates) {
this.questionRates.clear();
this.questionRates.putAll(questionRates);
saveQuestionRates();
}
private void saveQuestionRates() {
final String json;
try {
json = objectMapper.writeValueAsString(questionRates);
preferences.put(QUESTION_RATES_KEY, json);
} catch (final JsonProcessingException e) {
logger.error("Failed to serialize question rates", e);
}
}
@Override
public Collection<BirdPracticeParameters> birdPracticeParameters() {
return birdPracticeParameters.values();
}
@Override
public BirdPracticeParameters birdPracticeParameters(final Bird bird) {
return birdPracticeParameters.get(bird);
}
@Override
public void setBirdPracticeParameters(final Collection<? extends BirdPracticeParameters> birdPracticeParameters) {
this.birdPracticeParameters.clear();
this.birdPracticeParameters.putAll(birdPracticeParameters.stream().collect(Collectors.toMap(BirdPracticeParameters::bird, Function.identity())));
saveBirdPracticeParameters();
}
@Override
public void setBirdPracticeParameters(final BirdPracticeParameters birdPracticeParameters) {
this.birdPracticeParameters.put(birdPracticeParameters.bird(), birdPracticeParameters);
saveBirdPracticeParameters();
}
private void saveBirdPracticeParameters() {
final var parameters = serializeBirdPracticeParameters();
try {
final var json = objectMapper.writeValueAsString(parameters);
preferences.put(BIRD_PRACTICE_PARAMETERS_KEY, json);
} catch (final JsonProcessingException e) {
logger.error("Failed to serialize bird parameters", e);
}
}
private BirdPracticeParameters getDefaultParameters(final Bird bird) {
final var pictureTypes = Set.copyOf(Arrays.asList(PictureType.values()));
final var soundTypes = Set.copyOf(Arrays.asList(SoundType.values()));
try {
final var fetcherNames = fetcherProvider.getAllObjects().stream().map(Fetcher::name).collect(Collectors.toSet());
return new BirdPracticeParametersImpl(bird, true, fetcherNames, pictureTypes, soundTypes);
} catch (final ProvisionException e) {
logger.error("Failed to get fetcher names", e);
return new BirdPracticeParametersImpl(bird, true, Set.of(), pictureTypes, soundTypes);
}
}
}

View File

@@ -1,9 +0,0 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.practice.QuestionType;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.Map;
class QuestionRatesMapTypeReference extends TypeReference<Map<QuestionType, Double>> {
}

View File

@@ -1,15 +0,0 @@
package ch.gtache.fro.impl;
import ch.gtache.fro.PictureType;
import ch.gtache.fro.SoundType;
import java.util.Set;
record SerializedBirdPracticeParameters(String bird, boolean enabled, Set<String> enabledFetchers,
Set<PictureType> enabledPictureTypes,
Set<SoundType> enabledSoundTypes) {
SerializedBirdPracticeParameters(final String bird) {
this(bird, false, Set.of(), Set.of(), Set.of());
}
}

View File

@@ -1,8 +0,0 @@
package ch.gtache.fro.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.List;
class SerializedBirdPracticeParametersTypeReference extends TypeReference<List<SerializedBirdPracticeParameters>> {
}

View File

@@ -1,22 +1,26 @@
package ch.gtache.fro.modules.impl;
import ch.gtache.fro.BirdProvider;
import ch.gtache.fro.BirdFamilyTranslator;
import ch.gtache.fro.BirdTranslator;
import ch.gtache.fro.Fetcher;
import ch.gtache.fro.FetcherProvider;
import ch.gtache.fro.MigrationTypeTranslator;
import ch.gtache.fro.PictureTypeTranslator;
import ch.gtache.fro.SoundTypeTranslator;
import ch.gtache.fro.impl.CommonBirdsProvider;
import ch.gtache.fro.impl.CommonBirdsTranslatorImpl;
import ch.gtache.fro.impl.BirdFamilyTranslatorImpl;
import ch.gtache.fro.impl.BirdTranslatorImpl;
import ch.gtache.fro.impl.FetcherProviderImpl;
import ch.gtache.fro.impl.GroupedBirdPracticeParametersImpl;
import ch.gtache.fro.impl.MigrationTypeTranslatorImpl;
import ch.gtache.fro.impl.NoFetcherImpl;
import ch.gtache.fro.impl.PictureTypeTranslatorImpl;
import ch.gtache.fro.impl.PracticeConfigurationImpl;
import ch.gtache.fro.impl.SoundTypeTranslatorImpl;
import ch.gtache.fro.modules.practice.impl.CorePracticeModule;
import ch.gtache.fro.practice.PracticeConfiguration;
import com.fasterxml.jackson.databind.ObjectMapper;
import ch.gtache.fro.practice.GroupedBirdPracticeParameters;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;
/**
* Dagger module for the core package
@@ -29,16 +33,19 @@ public abstract class CoreModule {
}
@Binds
abstract BirdProvider bindsBirdProvider(final CommonBirdsProvider commonBirdsProvider);
@Binds
abstract PracticeConfiguration bindsConfiguration(final PracticeConfigurationImpl configurationImpl);
abstract GroupedBirdPracticeParameters bindsConfiguration(final GroupedBirdPracticeParametersImpl configurationImpl);
@Binds
abstract FetcherProvider bindsFetcherProvider(final FetcherProviderImpl fetcherProviderImpl);
@Binds
abstract BirdTranslator bindsBirdTranslator(final CommonBirdsTranslatorImpl commonBirdsTranslatorImpl);
abstract BirdTranslator bindsBirdTranslator(final BirdTranslatorImpl birdTranslatorImpl);
@Binds
abstract BirdFamilyTranslator bindsBirdFamilyTranslator(final BirdFamilyTranslatorImpl birdFamilyTranslatorImpl);
@Binds
abstract MigrationTypeTranslator bindsMigrationTypeTranslator(final MigrationTypeTranslatorImpl migrationTypeTranslatorImpl);
@Binds
abstract PictureTypeTranslator bindsPictureTypeTranslator(final PictureTypeTranslatorImpl pictureTypeTranslatorImpl);
@@ -47,7 +54,8 @@ public abstract class CoreModule {
abstract SoundTypeTranslator bindsSoundTypeTranslator(final SoundTypeTranslatorImpl soundTypeTranslatorImpl);
@Provides
static ObjectMapper providesObjectMapper() {
return new ObjectMapper();
@IntoSet
static Fetcher providesChantOiseauxFetcher() {
return new NoFetcherImpl("chant-oiseaux.fr");
}
}

View File

@@ -1,13 +1,11 @@
package ch.gtache.fro.practice.impl;
import ch.gtache.fro.Bird;
import ch.gtache.fro.practice.BirdPracticeParameters;
import ch.gtache.fro.practice.GroupedBirdPracticeParameters;
import ch.gtache.fro.practice.PracticeParameters;
import ch.gtache.fro.practice.QuestionType;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Objects;
/**
* Implementation of {@link PracticeParameters}
@@ -18,7 +16,7 @@ import java.util.stream.Collectors;
* @param questionsNumber The number of questions
* @param propositionsNumber The number of propositions
*/
public record PracticeParametersImpl(Map<Bird, BirdPracticeParameters> birdParameters,
public record PracticeParametersImpl(GroupedBirdPracticeParameters birdParameters,
Map<QuestionType, Double> questionTypeRates,
int guessesNumber, int questionsNumber,
int propositionsNumber) implements PracticeParameters {
@@ -35,7 +33,7 @@ public record PracticeParametersImpl(Map<Bird, BirdPracticeParameters> birdParam
* @throws IllegalArgumentException If questionsCount is less than 1
*/
public PracticeParametersImpl {
birdParameters = Map.copyOf(birdParameters);
Objects.requireNonNull(birdParameters);
questionTypeRates = Map.copyOf(questionTypeRates);
if (guessesNumber <= 0) {
throw new IllegalArgumentException("guessesNumber must be > 0");
@@ -44,22 +42,4 @@ public record PracticeParametersImpl(Map<Bird, BirdPracticeParameters> birdParam
throw new IllegalArgumentException("questionsCount must be > 0");
}
}
/**
* Instantiates the parameters
*
* @param birdParameters The bird parameters
* @param questionTypeRates The practice type rates
* @param guessesNumber The number of unsuccessful guesses before a question is considered as failed
* @param questionsNumber The number of questions
* @param propositionsNumber The number of propositions
* @throws NullPointerException If any parameter is null
* @throws IllegalArgumentException If questionsCount is less than 1
*/
public PracticeParametersImpl(final Collection<? extends BirdPracticeParameters> birdParameters,
final Map<QuestionType, Double> questionTypeRates, final int guessesNumber,
final int questionsNumber, final int propositionsNumber) {
this(birdParameters.stream().collect(Collectors.toMap(BirdPracticeParameters::bird, e -> e)),
questionTypeRates, guessesNumber, questionsNumber, propositionsNumber);
}
}

View File

@@ -0,0 +1,23 @@
package ch.gtache.fro.practice.impl;
import ch.gtache.fro.practice.PracticeProfile;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link PracticeProfile}
*
* @param name The name of the profile
*/
public record PracticeProfileImpl(String name) implements PracticeProfile {
/**
* Instantiates the profile
*
* @param name The name of the profile
* @throws NullPointerException If any parameter is null
*/
public PracticeProfileImpl {
requireNonNull(name);
}
}

View File

@@ -48,7 +48,7 @@ public class PracticeQuestionGeneratorImpl implements PracticeQuestionGenerator
@Override
public PracticeQuestion generate(final PracticeParameters parameters) {
final var enabledBirds = parameters.birdParameters().values().stream()
final var enabledBirds = parameters.birdParameters().parameters().stream()
.filter(p -> p.enabled() && !p.enabledFetchers().isEmpty()).toList();
final var questionType = getQuestionType(parameters.questionTypeRates());
return switch (questionType) {

View File

@@ -3,18 +3,14 @@
*/
module ch.gtache.fro.core {
requires transitive ch.gtache.fro.api;
requires transitive com.fasterxml.jackson.core;
requires transitive dagger;
requires org.apache.logging.log4j;
requires java.prefs;
requires jakarta.inject;
requires com.fasterxml.jackson.databind;
requires java.prefs;
exports ch.gtache.fro.impl;
exports ch.gtache.fro.practice.impl;
exports ch.gtache.fro.modules.impl;
exports ch.gtache.fro.modules.practice.impl;
opens ch.gtache.fro.impl to com.fasterxml.jackson.databind;
}