package ee.esutoniagodesu.service; import com.google.common.base.Joiner; import ee.esutoniagodesu.domain.core.table.IHasCoreWord; import ee.esutoniagodesu.domain.freq.table.NresBase; import ee.esutoniagodesu.domain.jmen.table.EN_Sens; import ee.esutoniagodesu.domain.jmet.table.Sens; import ee.esutoniagodesu.domain.kanjidic2.table.Kanji; import ee.esutoniagodesu.pojo.test.compound.*; import ee.esutoniagodesu.repository.domain.heisig.HeisigCoreKwRepository; import ee.esutoniagodesu.repository.project.CoreDB; import ee.esutoniagodesu.repository.project.JMDictDB; import ee.esutoniagodesu.repository.project.JMDictEnDB; import ee.esutoniagodesu.repository.project.KanjiDB; import ee.esutoniagodesu.security.AuthoritiesConstants; import ee.esutoniagodesu.security.SecurityUtils; import ee.esutoniagodesu.util.JCString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import javax.inject.Inject; import java.io.Serializable; import java.util.*; /** * Kanji sõnade testi mudel * Salvestatud testid * Download as XLS/ODS/PDF */ @Service @Transactional public class TestCompoundService { private static final Logger log = LoggerFactory.getLogger(TestCompoundService.class); @Inject private JMDictDB jmDictDB; @Inject private JMDictEnDB jmDictEnDB; @Inject private KanjiDB kanjiDB; @Inject private CoreDB coreDB; @Inject private HeisigCoreKwRepository heisigCoreKwRepository; private static final FilterCompoundParamsDTO _params = new FilterCompoundParamsDTO(); //------------------------------ enne submitti ------------------------------ public FilterCompoundParamsDTO params() { return _params; } public FilterCompoundSubmitDTO getFormDefault(int paramId) { return FilterCompoundSubmitDefaults.getValueById(paramId).VALUE; } //------------------------------ peale submitti ------------------------------ public List<KanjiCompound> submit(FilterCompoundSubmitDTO s) { log.info("generate: s=" + s); long ms = System.currentTimeMillis(); //kanji indeksi intervall int ivfrom = s.kanjiInterval[0]; int ivto = s.kanjiInterval[1]; //kanjide nimekiri Map<Character, Kanji> kanjis = getKanjis(s.getEFilterType(), ivfrom, ivto, s.getEKanjiIntervalType(), s.radicalHintVisible); Assert.isTrue(kanjis.size() > 0); //Mitu kanjit võib olla sõnas. Intervall int compdlfrom = s.compLengthInterval[0]; int compdlto = s.compLengthInterval[1]; //leiame kandidaadid List<KanjiCompound> candidates = getCompounds(s.getEDictionary(), kanjis.values(), compdlfrom, compdlto); Assert.isTrue(candidates.size() > 0); //jagab sõnad kanjide arvu kaupa gruppidesse Map<Integer, List<KanjiCompound>> groupedByKanjiCount = divideIntoGroups(candidates, compdlfrom, compdlto); Assert.isTrue(groupedByKanjiCount.size() > 0); //1 kanjiga = 20 sõna, 2 kanjiga = 20 sõna,jne. Lineaarselt, ruutfunktsioon Map<Integer, Integer> drawCounts = getDrawCounts(s.generateCount, compdlfrom, compdlto, EDrawCountStrategy.LINEAR, groupedByKanjiCount); Assert.isTrue(drawCounts.size() > 0); //leiab igast grupist suvalised n sõna. sum(n) = testi sõnade arv List<KanjiCompound> result = drawBallots(groupedByKanjiCount, drawCounts); Assert.isTrue(result.size() > 0); //nüüd on testi minevad sõnad leitud, asume täiendava info lisamise kallale for (KanjiCompound p : result) { //eestikeelsed tähendused Sens sens = jmDictDB.getFirstSensByKanjAndRdng(p.answer, p.reading); //peab olema Jim Breeni sõnaraamatus olemas if (sens == null) { log.warn("submit: sens==null, jp={}, reading={}", p.answer, p.reading); continue; } if (p.et == null) {//ilo sõnastik määrab ET ise p.et = join(sens.getGlosses()); } //inglisekeelsed tähendused lisatakse alati kui et puudub või en pole keelatud if (p.en == null && (p.et == null || !s.noEnIfHasEt)) {//CORE sõnastik määrab en ise EN_Sens sensEn = jmDictEnDB.getFirstSensByKanjAndRdng(p.answer, p.reading); Assert.notNull(sensEn); p.en = join(sensEn.getGlosses()); } if (s.notesVisible) { String poses = join(sens.getPoses()); String flds = join(sens.getFlds()); p.notes = JCString.join(", ", poses, flds); } p.signs.stream().filter(q -> q.kanji).forEach(q -> { if (s.strokeCountHintVisible) q.strokeCountHint = kanjis.get(q.sign).getStrokeCount(); if (s.radicalHintVisible) q.radicalHint = kanjis.get(q.sign).getRadicalHint(); }); if (SecurityUtils.isUserInRole(AuthoritiesConstants.ADMIN)) { String kanji = null; for (KanjiCompound.Calligraphy sign : p.signs) { if (sign.kanji) { kanji = String.valueOf(sign.sign); break; } } heisigCoreKwRepository.findOneByKanji(kanji).ifPresent(item -> { if (item.getWord() != null && item.getWord().equals(p.answer) && item.getWordReading() != null && item.getWordReading().equals(p.reading)) { p.heisigEquals = true; } p.heisigCoreKw = item.getId() + "-" + item.getKeywordEn() + "-" + item.getWord() + "-" + item.getWordReading() + "-" + item.getWordTranslation(); }); } } log.info("generate: time=" + (System.currentTimeMillis() - ms) + ", result.size=" + result.size() + ", s=" + s); return result; } //------------------------------ kanjide leidmine ------------------------------ private Map<Character, Kanji> getKanjis(EFilterType kanjidic, int ivfrom, int ivto, EKanjiIntervalType intervalType, boolean radicalHintVisible) { long ms = System.currentTimeMillis(); List<Kanji> kanjis; switch (kanjidic) { case grade: { kanjis = kanjiDB.getGradeKanjisByLevel(ivfrom, ivto); break; } case jlpt: { kanjis = kanjiDB.getJLPTKanjisByLevel(ivfrom, ivto); break; } case jouyou: { kanjis = kanjiDB.getJouyouKanjisByGrade(ivfrom, ivto); break; } case heisig6: { kanjis = intervalType == EKanjiIntervalType.level ? kanjiDB.getHeisig6KanjisByLesson(ivfrom, ivto) : kanjiDB.getHeisig6KanjisByIndex(ivfrom, ivto); break; } case heisig4: { kanjis = intervalType == EKanjiIntervalType.level ? kanjiDB.getHeisig4KanjisByLesson(ivfrom, ivto) : kanjiDB.getHeisig4KanjisByIndex(ivfrom, ivto); break; } default: throw new RuntimeException("not implemented"); } Map<Character, Kanji> result = new LinkedHashMap<>(); for (Kanji p : kanjis) { char kanj = p.getLiteral().charAt(0); if (radicalHintVisible) { p.setRadicalHint(kanjiDB.getPrimitiveHint(kanj, _primitiveHintDelim)); } result.put(kanj, p); } log.info("getKanjis: time=" + (System.currentTimeMillis() - ms) + ", result.size=" + result.size()); return result; } //------------------------------ jp sõnade leidmine ------------------------------ private static String toJoinedKanjis(Collection<Kanji> kanjis) { StringBuilder result = new StringBuilder(); for (Kanji p : kanjis) { if (result.length() > 0) result.append(","); result.append(p.getLiteral().charAt(0)); } return result.toString(); } private List<KanjiCompound> getCompounds(EDictionary dictionary, Collection<Kanji> kanjis, int compdlfrom, int compdlto) { long ms = System.currentTimeMillis(); List<KanjiCompound> result; String joinedKanjis = toJoinedKanjis(kanjis); switch (dictionary) { case ilo_yellow_jp_et: { result = coreWordToCompoundList(coreDB.getIloWordsByKanjis(joinedKanjis, compdlfrom, compdlto)); break; } case core6k: { result = coreWordToCompoundList(coreDB.getCore6KWordsByKanjis(joinedKanjis, compdlfrom, compdlto)); break; } case core10k: { result = coreWordToCompoundList(coreDB.getCore10KWordsByKanjis(joinedKanjis, compdlfrom, compdlto)); break; } case tofu: { result = coreWordToCompoundList(coreDB.getTofuSentencesByKanjis(joinedKanjis, compdlfrom, compdlto)); break; } default: throw new RuntimeException("not implemented"); } log.info("getCompounds: result.size=" + result.size() + ", time=" + (System.currentTimeMillis() - ms)); return result; } //------------------------------ lotomasin ------------------------------ private Map<Integer, Integer> getDrawCounts(int generateCount, int compdlfrom, int compdlto, EDrawCountStrategy strategy, Map<Integer, List<KanjiCompound>> candicates) { switch (strategy) { case LINEAR: return getDrawCountsLinear(generateCount, compdlfrom, compdlto, candicates); default: throw new RuntimeException("not implemented"); } } private Map<Integer, Integer> getDrawCountsLinear(int generateCount, int compdlfrom, int compdlto, Map<Integer, List<KanjiCompound>> candicates) { int countWish = generateCount / (compdlto - compdlfrom + 1);//iga kanjide arvu kohta võiks olla nii palju sõnu Map<Integer, Integer> result = new HashMap<>(); int deficit = 0; int count; int countAll = 0; for (int p = compdlto; p >= compdlfrom; p--) { //selle kanjide arvuga sõnu kui palju on saadaval int available = candicates.get(p).size(); if (p == compdlfrom) { deficit = generateCount - countAll - countWish; } if (countWish > available) {//kui soovitakse rohkem saada kui on saadaval //siis võtame nii palju kui saada on ja määrame defitsiidi deficit += countWish - available; count = available; } else if (deficit > 0 && available > countWish) {//kui eksisteerib defitsiit ja saadaval on rohkem kui soovitud //siis võtame nii palju kui on saadaval, aga mitte rohkem kui defitsiit int even = Math.min(deficit, (available - countWish)); count = countWish + even; //kui ei saanud siit hulgast ikka piisavalt, siis kanname defitsiidi edasi deficit -= even; } else { count = countWish; } result.put(p, count); countAll += count; } if (deficit > 0) { log.warn("getDrawCountsLinear: deficit=" + deficit); } return result; } /** * @param candidates <mitme kanjiga sõna, sõnad> * @param drawCounts <selle kanjide arvuga sõnu, vali n tükki> * @param <U> sõna klass * @return lotomasinast välja valitud sõnad */ private <U> List<U> drawBallots(Map<Integer, List<U>> candidates, Map<Integer, Integer> drawCounts) { long ms = System.currentTimeMillis(); Assert.isTrue(candidates.keySet().size() == drawCounts.keySet().size()); log.debug("drawBallots: candidates.size=" + candidates.size() + ", drawCounts=" + drawCounts); List<U> result = new ArrayList<>(); BallotEngine<U> ballotEngine = new BallotEngine<>(); for (int kanjiCount : candidates.keySet()) { ballotEngine.put(candidates.get(kanjiCount)); result.addAll(ballotEngine.draw(drawCounts.get(kanjiCount))); } log.info("drawBallots: result.size=" + result.size() + ", time=" + (System.currentTimeMillis() - ms)); return result; } //------------------------------ cmpd konvertimine ------------------------------ private <U extends IHasCoreWord> List<KanjiCompound> coreWordToCompoundList(List<U> words) { List<KanjiCompound> result = new ArrayList<>(); for (IHasCoreWord p : words) { KanjiCompound compound = new KanjiCompound(); compound.answer = p.getWord(); compound.reading = p.getWordReading(); compound.et = p.getWordTranslation(); setCalligraphy(compound); result.add(compound); } return result; } public void setCalligraphy(KanjiCompound compound) { for (char q : compound.answer.toCharArray()) { KanjiCompound.Calligraphy sign = new KanjiCompound.Calligraphy(q); compound.signs.add(sign); } } /** * Saadud nimekiri sisaldab ainult kirjapilti, aga meil on vaja leida sõnastikust * hääldus, tähendused ja märgendid. */ private List<KanjiCompound> nresToCompoundList(List<NresBase> nresBases) { List<KanjiCompound> result = new ArrayList<>(); for (NresBase p : nresBases) { KanjiCompound compound = new KanjiCompound(); String jp = p.getJp(); compound.answer = jp; compound.reading = kanjiDB.getFirstReading(jp); setCalligraphy(compound); result.add(compound); } return result; } //------------------------------ abi ------------------------------ private static <U extends Serializable> String join(Collection<U> collection) { return collection != null && collection.size() > 0 ? Joiner.on(", ").join(collection) : null; } private <U extends ICountKanjis> Map<Integer, List<U>> divideIntoGroups(List<U> candicates, int compdlfrom, int compdlto) { log.debug("divideIntoGroups: candicates.size=" + candicates.size() + ", compdlfrom=" + compdlfrom + ", compdlto=" + compdlto); Map<Integer, List<U>> result = getNewCompdLenMap(compdlfrom, compdlto); for (U p : candicates) { if (p.getCountKanjis() < 1) { log.error("no kanjis {}", p); continue; } result.get(p.getCountKanjis()).add(p); } return result; } private <U> Map<Integer, List<U>> getNewCompdLenMap(int compdlfrom, int compdlto) { Map<Integer, List<U>> bresByKanjiCount = new HashMap<>(); for (int p = compdlfrom; p <= compdlto; p++) { bresByKanjiCount.put(p, new ArrayList<>()); } return bresByKanjiCount; } //------------------------------ kanjile lisateabe leidmine ------------------------------ private static final String _primitiveHintDelim = ""; }