Java tutorial
/* * Copyright (c) 2013 The Finnish Board of Education - Opetushallitus * * This program is free software: Licensed under the EUPL, Version 1.1 or - as * soon as they will be approved by the European Commission - subsequent versions * of the EUPL (the "Licence"); * * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * European Union Public Licence for more details. */ package fi.vm.sade.eperusteet.ylops.service.ops.impl; import com.fasterxml.jackson.databind.JsonNode; import fi.vm.sade.eperusteet.ylops.domain.KoulutusTyyppi; import fi.vm.sade.eperusteet.ylops.domain.Tila; import fi.vm.sade.eperusteet.ylops.domain.Tyyppi; import fi.vm.sade.eperusteet.ylops.domain.Vuosiluokkakokonaisuusviite; import fi.vm.sade.eperusteet.ylops.domain.cache.PerusteCache; import fi.vm.sade.eperusteet.ylops.domain.cache.PerusteCache_; import fi.vm.sade.eperusteet.ylops.domain.lukio.*; import fi.vm.sade.eperusteet.ylops.domain.ohje.Ohje; import fi.vm.sade.eperusteet.ylops.domain.oppiaine.*; import fi.vm.sade.eperusteet.ylops.domain.ops.Opetussuunnitelma; import fi.vm.sade.eperusteet.ylops.domain.ops.Opetussuunnitelma_; import fi.vm.sade.eperusteet.ylops.domain.ops.OpsOppiaine; import fi.vm.sade.eperusteet.ylops.domain.ops.OpsVuosiluokkakokonaisuus; import fi.vm.sade.eperusteet.ylops.domain.teksti.Kieli; import fi.vm.sade.eperusteet.ylops.domain.teksti.LokalisoituTeksti; import fi.vm.sade.eperusteet.ylops.domain.teksti.Omistussuhde; import fi.vm.sade.eperusteet.ylops.domain.teksti.TekstiKappaleViite; import fi.vm.sade.eperusteet.ylops.domain.vuosiluokkakokonaisuus.Vuosiluokkakokonaisuus; import fi.vm.sade.eperusteet.ylops.dto.JarjestysDto; import fi.vm.sade.eperusteet.ylops.dto.koodisto.KoodistoDto; import fi.vm.sade.eperusteet.ylops.dto.koodisto.KoodistoKoodiDto; import fi.vm.sade.eperusteet.ylops.dto.koodisto.KoodistoMetadataDto; import fi.vm.sade.eperusteet.ylops.dto.koodisto.OrganisaatioDto; import fi.vm.sade.eperusteet.ylops.dto.lukio.LukioAbstraktiOppiaineTuontiDto; import fi.vm.sade.eperusteet.ylops.dto.ops.*; import fi.vm.sade.eperusteet.ylops.dto.peruste.PerusopetuksenPerusteenSisaltoDto; import fi.vm.sade.eperusteet.ylops.dto.peruste.PerusteDto; import fi.vm.sade.eperusteet.ylops.dto.peruste.PerusteLaajaalainenosaaminenDto; import fi.vm.sade.eperusteet.ylops.dto.peruste.lukio.*; import fi.vm.sade.eperusteet.ylops.dto.teksti.LokalisoituTekstiDto; import fi.vm.sade.eperusteet.ylops.dto.teksti.TekstiKappaleDto; import fi.vm.sade.eperusteet.ylops.dto.teksti.TekstiKappaleViiteDto; import fi.vm.sade.eperusteet.ylops.repository.cache.PerusteCacheRepository; import fi.vm.sade.eperusteet.ylops.repository.ohje.OhjeRepository; import fi.vm.sade.eperusteet.ylops.repository.ops.OpetussuunnitelmaRepository; import fi.vm.sade.eperusteet.ylops.repository.ops.VuosiluokkakokonaisuusviiteRepository; import fi.vm.sade.eperusteet.ylops.repository.teksti.TekstiKappaleRepository; import fi.vm.sade.eperusteet.ylops.repository.teksti.TekstikappaleviiteRepository; import fi.vm.sade.eperusteet.ylops.service.dokumentti.DokumenttiService; import fi.vm.sade.eperusteet.ylops.service.exception.BusinessRuleViolationException; import fi.vm.sade.eperusteet.ylops.service.exception.DokumenttiException; import fi.vm.sade.eperusteet.ylops.service.exception.NotExistsException; import fi.vm.sade.eperusteet.ylops.service.external.EperusteetService; import fi.vm.sade.eperusteet.ylops.service.external.KoodistoService; import fi.vm.sade.eperusteet.ylops.service.external.OrganisaatioService; import fi.vm.sade.eperusteet.ylops.service.mapping.DtoMapper; import fi.vm.sade.eperusteet.ylops.service.ops.OpetussuunnitelmaService; import fi.vm.sade.eperusteet.ylops.service.ops.OppiaineService; import fi.vm.sade.eperusteet.ylops.service.ops.TekstiKappaleViiteService; import fi.vm.sade.eperusteet.ylops.service.ops.VuosiluokkakokonaisuusService; import fi.vm.sade.eperusteet.ylops.service.ops.lukio.LukioOpetussuunnitelmaService; import fi.vm.sade.eperusteet.ylops.service.security.PermissionEvaluator.RolePermission; import fi.vm.sade.eperusteet.ylops.service.teksti.KommenttiService; import fi.vm.sade.eperusteet.ylops.service.util.CollectionUtil; import fi.vm.sade.eperusteet.ylops.service.util.Jarjestetty; import fi.vm.sade.eperusteet.ylops.service.util.LambdaUtil.ConstructedCopier; import fi.vm.sade.eperusteet.ylops.service.util.LambdaUtil.Copier; import static fi.vm.sade.eperusteet.ylops.service.util.Nulls.assertExists; import fi.vm.sade.eperusteet.ylops.service.util.SecurityUtil; import fi.vm.sade.eperusteet.ylops.service.util.Validointi; import java.math.BigDecimal; import java.util.*; import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import java.util.function.Consumer; import java.util.stream.Collectors; import static java.util.stream.Collectors.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.criteria.*; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.security.access.method.P; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; /** * @author mikkom */ @Service @Transactional public class OpetussuunnitelmaServiceImpl implements OpetussuunnitelmaService { static private final Logger logger = LoggerFactory.getLogger(OpetussuunnitelmaServiceImpl.class); @Autowired private DtoMapper mapper; @Autowired private OpetussuunnitelmaRepository repository; @Autowired private TekstikappaleviiteRepository viiteRepository; @Autowired private TekstiKappaleRepository tekstiKappaleRepository; @Autowired private TekstiKappaleViiteService tekstiKappaleViiteService; @Autowired private OppiaineService oppiaineService; @Autowired private KoodistoService koodistoService; @Autowired private OrganisaatioService organisaatioService; @Autowired private KommenttiService kommenttiService; @Autowired private VuosiluokkakokonaisuusService vuosiluokkakokonaisuudet; @Autowired private EperusteetService eperusteetService; @Autowired private VuosiluokkakokonaisuusviiteRepository vuosiluokkakokonaisuusviiteRepository; @Autowired private OhjeRepository ohjeRepository; @Autowired private PerusteCacheRepository perusteCacheRepository; @Autowired private LukioOpetussuunnitelmaService lukioOpetussuunnitelmaService; @Autowired private DokumenttiService dokumenttiService; @PersistenceContext private EntityManager em; private List<Opetussuunnitelma> findByQuery(OpetussuunnitelmaQuery pquery) { CriteriaQuery<Opetussuunnitelma> query = getQuery(pquery); return em.createQuery(query).getResultList(); } private CriteriaQuery<Opetussuunnitelma> getQuery(OpetussuunnitelmaQuery pquery) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Opetussuunnitelma> query = builder.createQuery(Opetussuunnitelma.class); Root<Opetussuunnitelma> ops = query.from(Opetussuunnitelma.class); List<Predicate> ehdot = new ArrayList<>(); // VAIN JULKAISTUT ehdot.add(builder.equal(ops.get(Opetussuunnitelma_.tila), Tila.JULKAISTU)); // Haettu organisaatio lytyy opsilta if (pquery.getOrganisaatio() != null) { Expression<Set<String>> organisaatiot = ops.get(Opetussuunnitelma_.organisaatiot); ehdot.add(builder.and(builder.isMember(pquery.getOrganisaatio(), organisaatiot))); } // Koulutustyyppi if (pquery.getKoulutustyyppi() != null) { ehdot.add(builder.and(builder.equal(ops.get(Opetussuunnitelma_.koulutustyyppi), KoulutusTyyppi.of(pquery.getKoulutustyyppi())))); } // Perusteen tyyppi if (pquery.getTyyppi() != null) { ehdot.add(builder.and(builder.equal(ops.get(Opetussuunnitelma_.tyyppi), pquery.getTyyppi()))); } // Perusteen id if (pquery.getPerusteenId() != null) { Path<PerusteCache> cachedPeruste = ops.join(Opetussuunnitelma_.cachedPeruste); ehdot.add(builder .and(builder.equal(cachedPeruste.get(PerusteCache_.perusteId), pquery.getPerusteenId()))); } // Perusteen diaarinumero if (pquery.getPerusteenDiaarinumero() != null) { ehdot.add(builder.and(builder.equal(ops.get(Opetussuunnitelma_.perusteenDiaarinumero), pquery.getPerusteenDiaarinumero()))); } query.where(ehdot.toArray(new Predicate[ehdot.size()])); return query.select(ops); } @Override @Transactional(readOnly = true) public List<OpetussuunnitelmaJulkinenDto> getAllJulkiset(OpetussuunnitelmaQuery query) { List<Opetussuunnitelma> opetussuunnitelmat; if (query != null) { query.setTyyppi(Tyyppi.OPS); opetussuunnitelmat = findByQuery(query).stream().filter(ops -> ops.getTila() == Tila.JULKAISTU) .collect(Collectors.toList()); } else { opetussuunnitelmat = repository.findAllByTyyppiAndTilaIsJulkaistu(Tyyppi.OPS); } final List<OpetussuunnitelmaJulkinenDto> dtot = mapper.mapAsList(opetussuunnitelmat, OpetussuunnitelmaJulkinenDto.class); dtot.forEach(dto -> { for (KoodistoDto koodistoDto : dto.getKunnat()) { Map<String, String> tekstit = new HashMap<>(); KoodistoKoodiDto kunta = koodistoService.get("kunta", koodistoDto.getKoodiUri()); if (kunta != null) { for (KoodistoMetadataDto metadata : kunta.getMetadata()) { tekstit.put(metadata.getKieli(), metadata.getNimi()); } } koodistoDto.setNimi(new LokalisoituTekstiDto(tekstit)); } for (OrganisaatioDto organisaatioDto : dto.getOrganisaatiot()) { Map<String, String> tekstit = new HashMap<>(); List<String> tyypit = new ArrayList<>(); JsonNode organisaatio = organisaatioService.getOrganisaatio(organisaatioDto.getOid()); if (organisaatio != null) { JsonNode nimiNode = organisaatio.get("nimi"); if (nimiNode != null) { Iterator<Map.Entry<String, JsonNode>> it = nimiNode.fields(); while (it.hasNext()) { Map.Entry<String, JsonNode> field = it.next(); tekstit.put(field.getKey(), field.getValue().asText()); } } JsonNode tyypitNode = Optional.ofNullable(organisaatio.get("tyypit")) .orElse(organisaatio.get("organisaatiotyypit")); if (tyypitNode != null) { tyypit = StreamSupport.stream(tyypitNode.spliterator(), false).map(JsonNode::asText) .collect(Collectors.toList()); } } organisaatioDto.setNimi(new LokalisoituTekstiDto(tekstit)); organisaatioDto.setTyypit(tyypit); } }); return dtot; } @Override @Transactional(readOnly = true) public OpetussuunnitelmaJulkinenDto getOpetussuunnitelmaJulkinen(Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); if (ops.getTila() != Tila.JULKAISTU) { throw new NotExistsException("Pyydetty opetussuunnitelmaa ei ole olemassa"); } return mapper.map(ops, OpetussuunnitelmaJulkinenDto.class); } @Override @Transactional(readOnly = true) public List<OpetussuunnitelmaInfoDto> getAll(Tyyppi tyyppi, Tila tila) { Set<String> organisaatiot = SecurityUtil.getOrganizations(EnumSet.allOf(RolePermission.class)); final List<Opetussuunnitelma> opetussuunnitelmat; if (tyyppi == Tyyppi.POHJA) { opetussuunnitelmat = repository.findPohja(organisaatiot); } else { opetussuunnitelmat = repository.findAllByTyyppi(tyyppi, organisaatiot); } return mapper.mapAsList(opetussuunnitelmat, OpetussuunnitelmaInfoDto.class).stream() .filter(ops -> tila == null || ops.getTila() == tila).map(dto -> { fetchKuntaNimet(dto); fetchOrganisaatioNimet(dto); return dto; }).collect(Collectors.toList()); } @Override @Transactional(readOnly = true) public List<OpetussuunnitelmaInfoDto> getAll(Tyyppi tyyppi) { return getAll(tyyppi, null); } @Override @Cacheable("tilastot") @Transactional(readOnly = true) public OpetussuunnitelmaStatistiikkaDto getStatistiikka() { List<OpetussuunnitelmaInfoDto> opsit = mapper .mapAsList(repository.findAllByTyyppi(Tyyppi.OPS), OpetussuunnitelmaInfoDto.class).stream() .map(ops -> { try { fetchOrganisaatioNimet(ops); } catch (BusinessRuleViolationException ex) { logger.error(ex.getLocalizedMessage()); } return ops; }).collect(Collectors.toList()); OpetussuunnitelmaStatistiikkaDto result = new OpetussuunnitelmaStatistiikkaDto(); result.getTasoittain().put("seutukunnat", opsit.stream().filter(ops -> ops.getKunnat().size() > 1).count()); result.getTasoittain().put("kunnat", opsit.stream().filter(ops -> ops.getKunnat().size() == 1).count()); result.getTasoittain().put("koulujoukko", opsit.stream() .filter(ops -> ops.getOrganisaatiot().stream() .filter(org -> !ObjectUtils.isEmpty(org.getTyypit())) .filter(org -> "Oppilaitos".equals(org.getTyypit().get(0))).count() > 1) .count()); result.getTasoittain().put("koulut", opsit.stream() .filter(ops -> ops.getOrganisaatiot().stream() .filter(org -> !ObjectUtils.isEmpty(org.getTyypit())) .filter(org -> "Oppilaitos".equals(org.getTyypit().get(0))).count() == 1) .count()); result.getKielittain().put("fi", opsit.stream().filter(ops -> ops.getJulkaisukielet().contains(Kieli.FI)).count()); result.getKielittain().put("sv", opsit.stream().filter(ops -> ops.getJulkaisukielet().contains(Kieli.SV)).count()); result.getKielittain().put("se", opsit.stream().filter(ops -> ops.getJulkaisukielet().contains(Kieli.SE)).count()); result.getKielittain().put("en", opsit.stream().filter(ops -> ops.getJulkaisukielet().contains(Kieli.EN)).count()); result.getTiloittain().put("esikatseltavissa", opsit.stream().filter(OpetussuunnitelmaBaseDto::isEsikatseltavissa).count()); for (OpetussuunnitelmaInfoDto ops : opsit) { result.getKoulutustyypeittain().put(ops.getKoulutustyyppi().toString(), result.getKoulutustyypeittain().getOrDefault(ops.getKoulutustyyppi().toString(), 0L) + 1); result.getTiloittain().put(ops.getTila().toString(), result.getTiloittain().getOrDefault(ops.getTila().toString(), 0L) + 1); } return result; } @Override public List<OpetussuunnitelmaInfoDto> getAdminList() { return mapper.mapAsList(repository.findAllByTyyppi(Tyyppi.OPS), OpetussuunnitelmaInfoDto.class); } @Override @Transactional(readOnly = true) public PerusteDto getPeruste(Long opsId) { Opetussuunnitelma ops = repository.findOne(opsId); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); return eperusteetService.getPeruste(ops.getPerusteenDiaarinumero()); } @Override @Transactional(readOnly = true) public OpetussuunnitelmaKevytDto getOpetussuunnitelma(Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); OpetussuunnitelmaKevytDto dto = mapper.map(ops, OpetussuunnitelmaKevytDto.class); fetchKuntaNimet(dto); fetchOrganisaatioNimet(dto); return dto; } private void fetchLapsiOpetussuunnitelmat(Long id, Set<Opetussuunnitelma> opsit) { opsit.addAll(repository.findAllByPohjaId(id)); } @Override @Transactional(readOnly = true) public List<OpetussuunnitelmaInfoDto> getLapsiOpetussuunnitelmat(Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); Set<Opetussuunnitelma> result = new HashSet<>(); fetchLapsiOpetussuunnitelmat(id, result); return mapper.mapAsList(result, OpetussuunnitelmaInfoDto.class); } @Override @Transactional(readOnly = true) public OpetussuunnitelmaLaajaDto getOpetussuunnitelmaEnempi(Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); OpetussuunnitelmaLaajaDto dto = mapper.map(ops, OpetussuunnitelmaLaajaDto.class); fetchKuntaNimet(dto); fetchOrganisaatioNimet(dto); return dto; } @Override @Transactional(readOnly = true) public OpetussuunnitelmaDto getOpetussuunnitelmaKaikki(Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); OpetussuunnitelmaDto dto = mapper.map(ops, OpetussuunnitelmaDto.class); fetchKuntaNimet(dto); fetchOrganisaatioNimet(dto); return dto; } @Override @Transactional(readOnly = true) public Set<PerusteLaajaalainenosaaminenDto> getLaajaalaisetosaamiset(Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); return eperusteetService.getPeruste(ops.getPerusteenDiaarinumero()).getPerusopetus() .getLaajaalaisetosaamiset(); } @Override public void updateOppiainejarjestys(Long opsId, List<JarjestysDto> oppiainejarjestys) { Opetussuunnitelma ops = repository.findOne(opsId); assertExists(ops, "Pyydetty opetussuunnitelmaa ei ole olemassa"); Map<Long, OpsOppiaine> oppiaineet = new HashMap<>(); Map<Long, Oppiaine> oppimaarat = new HashMap<>(); ops.getOppiaineet().forEach(opsOppiaine -> { oppiaineet.put(opsOppiaine.getOppiaine().getId(), opsOppiaine); oppimaarat.put(opsOppiaine.getOppiaine().getId(), opsOppiaine.getOppiaine()); if (opsOppiaine.getOppiaine().getOppimaarat() != null) { opsOppiaine.getOppiaine().getOppimaarat() .forEach(oppimaara -> oppimaarat.put(oppimaara.getId(), oppimaara)); } }); for (JarjestysDto node : oppiainejarjestys) { OpsOppiaine oppiaine = oppiaineet.get(node.getOppiaineId()); if (oppiaine != null) { oppiaine.setJnro(node.getJnro()); } Oppiaine oppimaara = oppimaarat.get(node.getOppiaineId()); assertExists(oppimaara, "Pyydetty oppiainetta ei ole"); oppimaara.getVuosiluokkakokonaisuudet().forEach( oppiaineenvuosiluokkakokonaisuus -> oppiaineenvuosiluokkakokonaisuus.setJnro(node.getJnro())); } } private void fetchKuntaNimet(OpetussuunnitelmaBaseDto opetussuunnitelmaDto) { for (KoodistoDto koodistoDto : opetussuunnitelmaDto.getKunnat()) { Map<String, String> tekstit = new HashMap<>(); KoodistoKoodiDto kunta = koodistoService.get("kunta", koodistoDto.getKoodiUri()); if (kunta != null) { for (KoodistoMetadataDto metadata : kunta.getMetadata()) { tekstit.put(metadata.getKieli(), metadata.getNimi()); } } koodistoDto.setNimi(new LokalisoituTekstiDto(tekstit)); } } private void fetchOrganisaatioNimet(OpetussuunnitelmaBaseDto opetussuunnitelmaDto) { for (OrganisaatioDto organisaatioDto : opetussuunnitelmaDto.getOrganisaatiot()) { Map<String, String> tekstit = new HashMap<>(); List<String> tyypit = new ArrayList<>(); JsonNode organisaatio = organisaatioService.getOrganisaatio(organisaatioDto.getOid()); if (organisaatio != null) { JsonNode nimiNode = organisaatio.get("nimi"); if (nimiNode != null) { Iterator<Map.Entry<String, JsonNode>> it = nimiNode.fields(); while (it.hasNext()) { Map.Entry<String, JsonNode> field = it.next(); tekstit.put(field.getKey(), field.getValue().asText()); } } JsonNode tyypitNode = ofNullable(organisaatio.get("tyypit")) .orElse(organisaatio.get("organisaatiotyypit")); if (tyypitNode != null) { tyypit = StreamSupport.stream(tyypitNode.spliterator(), false).map(JsonNode::asText) .collect(Collectors.toList()); } } organisaatioDto.setNimi(new LokalisoituTekstiDto(tekstit)); organisaatioDto.setTyypit(tyypit); } } @Override public OpetussuunnitelmaDto addOpetussuunnitelma(OpetussuunnitelmaLuontiDto opetussuunnitelmaDto) { if (opetussuunnitelmaDto.getId() != null) { throw new BusinessRuleViolationException("Uudessa opetussuunnitelmassa on id"); } opetussuunnitelmaDto.setTyyppi(Tyyppi.OPS); Opetussuunnitelma ops = mapper.map(opetussuunnitelmaDto, Opetussuunnitelma.class); Set<String> userOids = SecurityUtil.getOrganizations(EnumSet.of(RolePermission.CRUD, RolePermission.ADMIN)); if (CollectionUtil.intersect(userOids, ops.getOrganisaatiot()).isEmpty()) { throw new BusinessRuleViolationException( "Kyttjll ei ole luontioikeutta " + "opetussuunnitelman organisaatioissa"); } Opetussuunnitelma pohja = ops.getPohja(); if (pohja == null) { Set<Opetussuunnitelma> pohjat = repository.findOneByTyyppiAndTilaAndKoulutustyyppi(Tyyppi.POHJA, Tila.VALMIS, opetussuunnitelmaDto.getKoulutustyyppi()); if (pohjat.isEmpty()) { throw new BusinessRuleViolationException("koulutustyypin-pohjaa-ei-ole"); } else if (pohjat.size() == 1) { pohja = pohjat.iterator().next(); } } if (pohja != null) { ops.setTekstit(new TekstiKappaleViite(Omistussuhde.OMA)); ops.getTekstit().setLapset(new ArrayList<>()); luoOpsPohjasta(pohja, ops); ops.setTila(Tila.LUONNOS); ops = repository.save(ops); if (isPohjastaTehtyPohja(pohja) && pohja.getKoulutustyyppi().isLukio()) { lisaaTeemaopinnotJosPohjassa(ops, pohja); } } else { throw new BusinessRuleViolationException("Valmista opetussuunnitelman pohjaa ei lytynyt"); } return mapper.map(ops, OpetussuunnitelmaDto.class); } private void lisaaTeemaopinnotJosPohjassa(Opetussuunnitelma ops, Opetussuunnitelma pohja) { final Long opsId = ops.getId(); pohja.getOppiaineet().stream().filter(opsOppiaine1 -> opsOppiaine1.getOppiaine().getKoodiUri() .compareTo("oppiaineetyleissivistava2_to") == 0).findFirst().ifPresent(opsOppiaine -> { LukioAbstraktiOppiaineTuontiDto dto = new LukioAbstraktiOppiaineTuontiDto(); dto.setNimi(mapper.map(opsOppiaine.getOppiaine().getNimi(), LokalisoituTekstiDto.class)); dto.setTunniste(opsOppiaine.getOppiaine().getTunniste()); lukioOpetussuunnitelmaService.addAbstraktiOppiaine(opsId, dto); }); } private void luoOpsPohjasta(Opetussuunnitelma pohja, Opetussuunnitelma ops) { ops.setPohja(pohja); if (pohja.getPerusteenDiaarinumero() == null) { throw new BusinessRuleViolationException("Pohjalta puuttuu perusteen diaarinumero"); } ops.setPerusteenDiaarinumero(pohja.getPerusteenDiaarinumero()); ops.setCachedPeruste(ops.getCachedPeruste()); if (ops.getCachedPeruste() == null) { PerusteDto peruste = eperusteetService.getPeruste(ops.getPerusteenDiaarinumero()); PerusteCache perusteCache = perusteCacheRepository.findNewestEntryForPeruste(peruste.getId()); if (perusteCache == null) { throw new BusinessRuleViolationException("Opetussuunnitelman pohjasta ei lytynyt perustetta"); } ops.setCachedPeruste(perusteCache); } boolean teeKopio = pohja.getTyyppi() == Tyyppi.POHJA; kasitteleTekstit(pohja.getTekstit(), ops.getTekstit(), teeKopio); boolean onPohjastaTehtyPohja = isPohjastaTehtyPohja(pohja); Copier<Oppiaine> oppiaineCopier = teeKopio ? Oppiaine.basicCopier() : Copier.nothing(); Map<Long, Oppiaine> newOppiaineByOld = new HashMap<>(); Copier<Oppiaine> kurssiCopier = null; if (pohja.getKoulutustyyppi().isLukio()) { luoLukiokoulutusPohjasta(pohja, ops); kurssiCopier = getLukiokurssitOppiaineCopier(pohja, ops, teeKopio); oppiaineCopier = oppiaineCopier.and(kurssiCopier).and((fromOa, toOa) -> { toOa.setAbstrakti(fromOa.getAbstrakti()); newOppiaineByOld.put(fromOa.getId(), toOa); }); } else if (teeKopio) { oppiaineCopier = oppiaineCopier.and(Oppiaine.perusopetusCopier()); } final Copier<Oppiaine> oppiainePerusCopier = oppiaineCopier; if (teeKopio && (!onPohjastaTehtyPohja || pohja.getKoulutustyyppi().isLukio())) { ConstructedCopier<Oppiaine> omConst = oppiainePerusCopier .construct(oa -> new Oppiaine(oa.getTunniste())); if (onPohjastaTehtyPohja && pohja.getKoulutustyyppi().isLukio()) { oppiaineCopier = oppiaineCopier.and(Oppiaine.oppimaaraCopier(om -> !om.isAbstraktiBool(), omConst)); } else { oppiaineCopier = oppiaineCopier.and(Oppiaine.oppimaaraCopier(omConst)); } } else if (kurssiCopier != null) { final Copier<Oppiaine> finalKurssiCopier = kurssiCopier; oppiaineCopier = oppiaineCopier.and((from, to) -> { if (from.isKoosteinen() && from.getOppiaine() == null) { from.getOppimaarat().forEach(om -> finalKurssiCopier.copy(om, om)); } }); } ConstructedCopier<OpsOppiaine> opsOppiaineCopier = OpsOppiaine.copier( oppiaineCopier.construct(existing -> teeKopio ? new Oppiaine(existing.getTunniste()) : existing), teeKopio); Stream<OpsOppiaine> oppiaineetToCopy = pohja.getKoulutustyyppi().isLukio() && pohja.getTyyppi() == Tyyppi.POHJA // ei kopioida pohjasta abstakteja yltason oppiaineita, mutta OPS:sta kyll ? pohja.getOppiaineet().stream().filter(opsOa -> !opsOa.getOppiaine().isAbstraktiBool()) : pohja.getOppiaineet().stream(); ops.setOppiaineet(oppiaineetToCopy.map(opsOppiaineCopier::copy).collect(toSet())); ops.getOppiaineJarjestykset().addAll(pohja.getOppiaineJarjestykset().stream() .map(old -> !teeKopio ? new LukioOppiaineJarjestys(ops, old.getOppiaine(), old.getJarjestys()) : (newOppiaineByOld.get(old.getId().getOppiaineId()) != null ? new LukioOppiaineJarjestys( ops, newOppiaineByOld.get(old.getId().getOppiaineId()), old.getJarjestys()) : null)) .filter(Objects::nonNull).collect(toSet())); Set<OpsVuosiluokkakokonaisuus> ovlkoot = pohja.getVuosiluokkakokonaisuudet().stream() .filter(ovlk -> ops.getVuosiluokkakokonaisuudet().stream() .anyMatch(vk -> vk.getVuosiluokkakokonaisuus().getTunniste() .equals(ovlk.getVuosiluokkakokonaisuus().getTunniste()))) .map(ovlk -> teeKopio ? new OpsVuosiluokkakokonaisuus( Vuosiluokkakokonaisuus.copyOf(ovlk.getVuosiluokkakokonaisuus()), true) : new OpsVuosiluokkakokonaisuus(ovlk.getVuosiluokkakokonaisuus(), false)) .collect(toSet()); ops.setVuosiluokkakokonaisuudet(ovlkoot); } private boolean isPohjastaTehtyPohja(Opetussuunnitelma pohja) { Opetussuunnitelma ylinpohja = pohja; while (ylinpohja.getPohja() != null) { ylinpohja = ylinpohja.getPohja(); } return ylinpohja.getId().equals(pohja.getId()); } public static Copier<Oppiaine> getLukiokurssitOppiaineCopier(Opetussuunnitelma pohja, Opetussuunnitelma ops, boolean teeKopio) { Map<UUID, Lukiokurssi> existingKurssit = teeKopio ? new HashMap<>() : pohja.getLukiokurssit().stream().map(OppiaineLukiokurssi::getKurssi) .filter(k -> k.getTunniste() != null) .collect(toMap(Kurssi::getTunniste, k -> k, (a, b) -> a)); // Tunniste ei ole unique opsin sisll Map<Long, List<OppiaineLukiokurssi>> lukiokurssitByPohjaOppiaineId = pohja.getLukiokurssit().stream() .collect(groupingBy(oaKurssi -> oaKurssi.getOppiaine().getId())); return (from, to) -> ops.getLukiokurssit() .addAll(ofNullable(lukiokurssitByPohjaOppiaineId.get(from.getId())) .map(list -> list.stream().map(oaKurssi -> { Lukiokurssi kurssi = oaKurssi.getKurssi().getTunniste() == null ? null : existingKurssit.get(oaKurssi.getKurssi().getTunniste()); if (kurssi == null) { kurssi = teeKopio ? oaKurssi.getKurssi().copy() : oaKurssi.getKurssi(); if (oaKurssi.getKurssi().getTunniste() != null) { existingKurssit.put(oaKurssi.getKurssi().getTunniste(), kurssi); } } return new OppiaineLukiokurssi(ops, to, kurssi, oaKurssi.getJarjestys(), teeKopio); }).collect(toList())).orElse(emptyList())); } private void luoLukiokoulutusPohjasta(Opetussuunnitelma from, Opetussuunnitelma to) { if (from.getAihekokonaisuudet() != null) { to.setAihekokonaisuudet(from.getAihekokonaisuudet().copy(to, from.getAihekokonaisuudet())); } if (from.getOpetuksenYleisetTavoitteet() != null) { to.setOpetuksenYleisetTavoitteet( from.getOpetuksenYleisetTavoitteet().copy(to, from.getOpetuksenYleisetTavoitteet())); } } private void kasitteleTekstit(TekstiKappaleViite vanha, TekstiKappaleViite parent, boolean teeKopio) { List<TekstiKappaleViite> vanhaLapset = vanha.getLapset(); if (vanhaLapset != null) { vanhaLapset.stream().filter(vanhaTkv -> vanhaTkv.getTekstiKappale() != null).forEach(vanhaTkv -> { TekstiKappaleViite tkv = viiteRepository.save(new TekstiKappaleViite()); tkv.setOmistussuhde(teeKopio ? Omistussuhde.OMA : Omistussuhde.LAINATTU); tkv.setLapset(new ArrayList<>()); tkv.setVanhempi(parent); tkv.setPakollinen(vanhaTkv.isPakollinen()); tkv.setTekstiKappale(teeKopio ? tekstiKappaleRepository.save(vanhaTkv.getTekstiKappale().copy()) : vanhaTkv.getTekstiKappale()); parent.getLapset().add(tkv); kasitteleTekstit(vanhaTkv, tkv, teeKopio); }); } } private Opetussuunnitelma addPohjaLisaJaEsiopetus(Opetussuunnitelma ops, PerusteDto peruste) { ops.setKoulutustyyppi(peruste.getKoulutustyyppi()); return ops; } private Opetussuunnitelma addPohjaPerusopetus(Opetussuunnitelma ops, PerusteDto peruste) { Long opsId = ops.getId(); PerusopetuksenPerusteenSisaltoDto sisalto = peruste.getPerusopetus(); if (sisalto.getVuosiluokkakokonaisuudet() != null) { sisalto.getVuosiluokkakokonaisuudet().forEach(vk -> vuosiluokkakokonaisuusviiteRepository .save(new Vuosiluokkakokonaisuusviite(vk.getTunniste(), vk.getVuosiluokat()))); if (sisalto.getOppiaineet() != null) { sisalto.getOppiaineet().stream().map(OpsDtoMapper::fromEperusteet) .forEach(oa -> oppiaineService.add(opsId, oa)); } sisalto.getVuosiluokkakokonaisuudet().stream().map(OpsDtoMapper::fromEperusteet) .forEach(vk -> vuosiluokkakokonaisuudet.add(opsId, vk)); } // Alustetaan jrjestys ePerusteista saatuun jrjestykseen Integer idx = 0; for (OpsOppiaine oa : ops.getOppiaineet()) { for (Oppiaineenvuosiluokkakokonaisuus oavlk : oa.getOppiaine().getVuosiluokkakokonaisuudet()) { oavlk.setJnro(idx); } ++idx; } return ops; } private Opetussuunnitelma addPohjaLukiokoulutus(Opetussuunnitelma ops, PerusteDto peruste) { ops.setKoulutustyyppi(peruste.getKoulutustyyppi()); LukiokoulutuksenPerusteenSisaltoDto lukioSisalto = peruste.getLukiokoulutus(); if (lukioSisalto == null) { throw new IllegalStateException("Lukiokoutuksen sislt ei lytynyt."); } if (lukioSisalto.getRakenne() != null) { importLukioRakenne(lukioSisalto.getRakenne(), ops); } if (lukioSisalto.getAihekokonaisuudet() != null) { importAihekokonaisuudet(lukioSisalto.getAihekokonaisuudet(), ops); } if (lukioSisalto.getOpetuksenYleisetTavoitteet() != null) { importYleisetTavoitteet(lukioSisalto.getOpetuksenYleisetTavoitteet(), ops); } return ops; } private void importLukioRakenne(LukioOpetussuunnitelmaRakenneDto from, Opetussuunnitelma to) { importOppiaineet(to, from.getOppiaineet(), oa -> { to.getOppiaineetReal().add(new OpsOppiaine(oa.getObj(), false)); to.getOppiaineJarjestykset().add(new LukioOppiaineJarjestys(to, oa.getObj(), oa.getJarjestys())); }, null, new HashMap<>()); } private void importOppiaineet(Opetussuunnitelma ops, Collection<LukioPerusteOppiaineDto> from, Consumer<Jarjestetty<Oppiaine>> to, Oppiaine parent, Map<UUID, Lukiokurssi> kurssit) { for (LukioPerusteOppiaineDto oppiaine : from) { Oppiaine oa = new Oppiaine(oppiaine.getTunniste()); oa.setTyyppi(OppiaineTyyppi.LUKIO); oa.setNimi(LokalisoituTeksti.of(oppiaine.getNimi().getTekstit())); oa.setOppiaine(parent); oa.setAbstrakti(oppiaine.getAbstrakti()); oa.setKoosteinen(oppiaine.isKoosteinen()); oa.setKoodiArvo(oppiaine.getKoodiArvo()); oa.setKoodiUri(oppiaine.getKoodiUri()); for (Map.Entry<LukiokurssiTyyppi, Optional<LokalisoituTekstiDto>> kv : oppiaine .getKurssiTyyppiKuvaukset().entrySet()) { kv.getKey().oppiaineKuvausSetter().set(oa, kv.getValue().map(LokalisoituTekstiDto::getTekstit) .map(LokalisoituTeksti::of).orElse(null)); } to.accept(new Jarjestetty<>(oa, oppiaine.getJarjestys())); importOppiaineet(ops, oppiaine.getOppimaarat(), child -> { oa.getOppimaaratReal().add(child.getObj()); ops.getOppiaineJarjestykset() .add(new LukioOppiaineJarjestys(ops, child.getObj(), child.getJarjestys())); }, oa, kurssit); importKurssit(ops, oppiaine.getKurssit(), oa, kurssit); } } private void importKurssit(Opetussuunnitelma ops, Set<LukiokurssiPerusteDto> from, Oppiaine to, Map<UUID, Lukiokurssi> luodut) { for (LukiokurssiPerusteDto kurssiDto : from) { ops.getLukiokurssit().add(new OppiaineLukiokurssi(ops, to, kurssiByTunniste(kurssiDto, luodut), kurssiDto.getJarjestys(), true)); } } private Lukiokurssi kurssiByTunniste(LukiokurssiPerusteDto kurssiDto, Map<UUID, Lukiokurssi> luodut) { Lukiokurssi kurssi = luodut.get(kurssiDto.getTunniste()); if (kurssi != null) { return kurssi; } kurssi = new Lukiokurssi(kurssiDto.getTunniste()); kurssi.setNimi(LokalisoituTeksti.of(kurssiDto.getNimi().getTekstit())); kurssi.setTyyppi(LukiokurssiTyyppi.ofPerusteTyyppi(kurssiDto.getTyyppi())); kurssi.setKoodiArvo(kurssiDto.getKoodiArvo()); kurssi.setKoodiUri(kurssiDto.getKoodiUri()); kurssi.setLaajuus(BigDecimal.ONE); kurssi.setLokalisoituKoodi(kurssiDto.getLokalisoituKoodi() == null ? null : LokalisoituTeksti.of(kurssiDto.getLokalisoituKoodi().getTekstit())); luodut.put(kurssi.getTunniste(), kurssi); return kurssi; } private void importAihekokonaisuudet(AihekokonaisuudetDto from, Opetussuunnitelma to) { if (to.getAihekokonaisuudet() == null) { to.setAihekokonaisuudet(new Aihekokonaisuudet(to, from.getUuidTunniste())); } Long maxJnro = 0L; Map<UUID, Aihekokonaisuus> byTunniste = to.getAihekokonaisuudet().getAihekokonaisuudet().stream() .collect(toMap(Aihekokonaisuus::getTunniste, ak -> ak)); for (AihekokonaisuusDto aihekokonaisuusDto : from.getAihekokonaisuudet()) { if (byTunniste.containsKey(aihekokonaisuusDto.getTunniste())) { continue; } Aihekokonaisuus aihekokonaisuus = new Aihekokonaisuus(to.getAihekokonaisuudet(), aihekokonaisuusDto.getTunniste()); aihekokonaisuus.setOtsikko(LokalisoituTeksti.of(aihekokonaisuusDto.getOtsikko().getTekstit())); maxJnro = Math.max(maxJnro + 1, ofNullable(aihekokonaisuus.getJnro()).orElse(0L)); aihekokonaisuus.setJnro(maxJnro); to.getAihekokonaisuudet().getAihekokonaisuudet().add(aihekokonaisuus); } } private void importYleisetTavoitteet(OpetuksenYleisetTavoitteetDto from, Opetussuunnitelma to) { if (to.getOpetuksenYleisetTavoitteet() == null) { to.setOpetuksenYleisetTavoitteet(new OpetuksenYleisetTavoitteet(to, from.getUuidTunniste())); } } @Override public void syncPohja(Long pohjaId) { Opetussuunnitelma pohja = repository.findOne(pohjaId); if (pohja.getPohja() != null) { throw new BusinessRuleViolationException("OPS ei ollut pohja"); } pohja.setOppiaineet(null); pohja.setVuosiluokkakokonaisuudet(null); pohja.getLukiokurssit().clear(); pohja.getOppiaineJarjestykset().clear(); PerusteDto peruste = eperusteetService.getPerusteUpdateCache(pohja.getPerusteenDiaarinumero()); pohja.setCachedPeruste(perusteCacheRepository.findNewestEntryForPeruste(peruste.getId())); lisaaPerusteenSisalto(pohja, peruste); } private Opetussuunnitelma lisaaPerusteenSisalto(Opetussuunnitelma ops, PerusteDto peruste) { if (peruste.getKoulutustyyppi() == null || KoulutusTyyppi.PERUSOPETUS == peruste.getKoulutustyyppi()) { return addPohjaPerusopetus(ops, peruste); } else if (KoulutusTyyppi.LISAOPETUS == peruste.getKoulutustyyppi() || KoulutusTyyppi.ESIOPETUS == peruste.getKoulutustyyppi() || KoulutusTyyppi.AIKUISTENPERUSOPETUS == peruste.getKoulutustyyppi() || KoulutusTyyppi.TPO == peruste.getKoulutustyyppi() || KoulutusTyyppi.PERUSOPETUSVALMISTAVA == peruste.getKoulutustyyppi() || KoulutusTyyppi.VARHAISKASVATUS == peruste.getKoulutustyyppi()) { return addPohjaLisaJaEsiopetus(ops, peruste); } else if (peruste.getKoulutustyyppi().isLukio()) { return addPohjaLukiokoulutus(ops, peruste); } else { throw new BusinessRuleViolationException("Ei toimintatapaa perusteen koulutustyypille"); } } @Override public OpetussuunnitelmaDto addPohja(OpetussuunnitelmaLuontiDto opetussuunnitelmaDto) { if (opetussuunnitelmaDto.getId() != null) { throw new BusinessRuleViolationException("Uudessa pohjassa on id"); } Opetussuunnitelma ops = mapper.map(opetussuunnitelmaDto, Opetussuunnitelma.class); // Jokainen pohja sislt OPH:n organisaationaan ops.getOrganisaatiot().add(SecurityUtil.OPH_OID); Set<String> userOids = SecurityUtil.getOrganizations(EnumSet.of(RolePermission.CRUD)); if (CollectionUtil.intersect(userOids, ops.getOrganisaatiot()).isEmpty()) { throw new BusinessRuleViolationException( "Kyttjll ei ole luontioikeutta " + "opetussuunnitelman pohjan organisaatioissa"); } final String diaarinumero = ops.getPerusteenDiaarinumero(); if (StringUtils.isBlank(diaarinumero)) { throw new BusinessRuleViolationException("Perusteen diaarinumeroa ei ole mritelty"); } else if (eperusteetService.findPerusteet().stream() .noneMatch(p -> diaarinumero.equals(p.getDiaarinumero()))) { throw new BusinessRuleViolationException( "Diaarinumerolla " + diaarinumero + " ei lydy voimassaolevaa perustetta"); } if (ops.getPohja() != null) { throw new BusinessRuleViolationException("Opetussuunnitelman pohjalla ei voi olla pohjaa"); } ops.setTila(Tila.LUONNOS); lisaaTekstipuunJuuri(ops); ops = repository.save(ops); lisaaTekstipuunLapset(ops); PerusteDto peruste = eperusteetService.getPeruste(ops.getPerusteenDiaarinumero()); ops.setCachedPeruste(perusteCacheRepository.findNewestEntryForPeruste(peruste.getId())); ops.setKoulutustyyppi( peruste.getKoulutustyyppi() != null ? peruste.getKoulutustyyppi() : KoulutusTyyppi.PERUSOPETUS); return mapper.map(lisaaPerusteenSisalto(ops, peruste), OpetussuunnitelmaDto.class); } private void lisaaTekstipuunJuuri(Opetussuunnitelma ops) { TekstiKappaleViite juuri = new TekstiKappaleViite(Omistussuhde.OMA); juuri = viiteRepository.saveAndFlush(juuri); ops.setTekstit(juuri); } private void lisaaTekstipuunLapset(Opetussuunnitelma ops) { LokalisoituTekstiDto nimi, teksti; nimi = new LokalisoituTekstiDto(null, Collections.singletonMap(Kieli.FI, "Opetuksen jrjestminen")); teksti = new LokalisoituTekstiDto(null, null); TekstiKappaleDto ohjeistusTeksti = new TekstiKappaleDto(nimi, teksti, Tila.LUONNOS); TekstiKappaleViiteDto.Matala ohjeistus = new TekstiKappaleViiteDto.Matala(ohjeistusTeksti); addTekstiKappale(ops.getId(), ohjeistus); nimi = new LokalisoituTekstiDto(null, Collections.singletonMap(Kieli.FI, "Opetuksen toteuttamisen lhtkohdat")); teksti = new LokalisoituTekstiDto(null, null); TekstiKappaleDto opetuksenJarjestaminenTeksti = new TekstiKappaleDto(nimi, teksti, Tila.LUONNOS); TekstiKappaleViiteDto.Matala opetuksenJarjestaminen = new TekstiKappaleViiteDto.Matala( opetuksenJarjestaminenTeksti); addTekstiKappale(ops.getId(), opetuksenJarjestaminen); } private void flattenTekstikappaleviitteet(Map<UUID, TekstiKappaleViite> viitteet, TekstiKappaleViite tov) { if (tov.getLapset() == null) { return; } for (TekstiKappaleViite lapsi : tov.getLapset()) { // Tt tarkistusta ei vlttmtt tarvitse if (viitteet.get(lapsi.getTekstiKappale().getTunniste()) != null) { continue; } viitteet.put(lapsi.getTekstiKappale().getTunniste(), lapsi); flattenTekstikappaleviitteet(viitteet, lapsi); } } @Override public void updateLapsiOpetussuunnitelmat(Long opsId) { Opetussuunnitelma ops = repository.findOne(opsId); assertExists(ops, "Pivitettv tietoa ei ole olemassa"); Set<Opetussuunnitelma> aliopsit = repository.findAllByPohjaId(opsId); for (Opetussuunnitelma aliops : aliopsit) { Map<UUID, TekstiKappaleViite> aliopsTekstit = new HashMap<>(); flattenTekstikappaleviitteet(aliopsTekstit, aliops.getTekstit()); aliops.getTekstit().getLapset().clear(); aliopsTekstit.values().forEach((teksti) -> { teksti.setVanhempi(aliops.getTekstit()); teksti.getLapset().clear(); }); } } @Override public OpetussuunnitelmaDto updateOpetussuunnitelma(OpetussuunnitelmaDto opetussuunnitelmaDto) { Opetussuunnitelma ops = repository.findOne(opetussuunnitelmaDto.getId()); assertExists(ops, "Pivitettv tietoa ei ole olemassa"); poistaKielletytMuutokset(ops, opetussuunnitelmaDto); validoiMuutokset(ops, opetussuunnitelmaDto); mapper.map(opetussuunnitelmaDto, ops); ops = repository.save(ops); return mapper.map(ops, OpetussuunnitelmaDto.class); } private void poistaKielletytMuutokset(Opetussuunnitelma ops, OpetussuunnitelmaDto opetussuunnitelmaDto) { // Ei sallita kunnan muuttamista opetussuunnitelmaDto.setKunnat( ops.getKunnat().stream().map(kunta -> mapper.map(kunta, KoodistoDto.class)).collect(toSet())); // Ei sallita organisaation muuttamista opetussuunnitelmaDto.setOrganisaatiot(ops.getOrganisaatiot().stream().map(orgOid -> { OrganisaatioDto dto = new OrganisaatioDto(); dto.setOid(orgOid); return dto; }).collect(toSet())); // Ei sallita pohjan muuttamista opetussuunnitelmaDto.setPohja(mapper.map(ops.getPohja(), OpetussuunnitelmaNimiDto.class)); // Ei sallita perusteen diaarinumeron muuttamista opetussuunnitelmaDto.setPerusteenDiaarinumero(ops.getPerusteenDiaarinumero()); // Ei sallita peruste cachen muuttamista PerusteCache cachedPeruste = ops.getCachedPeruste(); if (cachedPeruste.getPerusteId() != null) { opetussuunnitelmaDto.setPerusteenId(cachedPeruste.getPerusteId()); } else { opetussuunnitelmaDto.setPerusteenId(null); } // Tilan muuttamiseen on oma erillinen endpointtinsa opetussuunnitelmaDto.setTila(ops.getTila()); } private void validoiMuutokset(Opetussuunnitelma ops, OpetussuunnitelmaDto opetussuunnitelmaDto) { // Kyttjll ei oikeutta tulevassa organisaatiossa Set<String> userOids = SecurityUtil.getOrganizations(EnumSet.of(RolePermission.CRUD, RolePermission.ADMIN)); Set<String> organisaatiot = opetussuunnitelmaDto.getOrganisaatiot().stream().map(OrganisaatioDto::getOid) .collect(toSet()); if (CollectionUtil.intersect(userOids, organisaatiot).isEmpty()) { throw new BusinessRuleViolationException("Kyttjll ei ole oikeuksia organisaatiossa"); } if (opetussuunnitelmaDto.getTyyppi() != ops.getTyyppi()) { throw new BusinessRuleViolationException("Opetussuunnitelman tyyppi ei voi vaihtaa"); } // Ei sallita kieli ja vuoluokkakokonaisuuksien muutoksia kuin luonnostilassa if (opetussuunnitelmaDto.getTila() != Tila.LUONNOS) { if (!opetussuunnitelmaDto.getVuosiluokkakokonaisuudet().stream() .map(vlk -> vlk.getVuosiluokkakokonaisuus().getId()).collect(Collectors.toSet()) .equals(ops.getVuosiluokkakokonaisuudet().stream() .map(vlk -> vlk.getVuosiluokkakokonaisuus().getId()).collect(Collectors.toSet()))) { throw new BusinessRuleViolationException( "Opetussuunnitelman vuosiluokkakokonaisuuksia ei voi vaihtaa kuin luonnoksessa"); } if (!new HashSet<>(opetussuunnitelmaDto.getJulkaisukielet()) .equals(new HashSet<>(ops.getJulkaisukielet()))) { throw new BusinessRuleViolationException( "Opetussuunnitelman julkaisukieli ei voi vaihtaa kuin luonnoksessa"); } } { Long pohjaId = opetussuunnitelmaDto.getPohja() != null ? opetussuunnitelmaDto.getPohja().getId() : null; Long oldPohjaId = ops.getPohja() != null ? ops.getPohja().getId() : null; if (!Objects.equals(pohjaId, oldPohjaId)) { throw new BusinessRuleViolationException("Opetussuunnitelman pohjaa ei voi vaihtaa"); } } if (!Objects.equals(opetussuunnitelmaDto.getPerusteenDiaarinumero(), ops.getPerusteenDiaarinumero())) { throw new BusinessRuleViolationException("Perusteen diaarinumeroa ei voi vaihtaa"); } if (!Objects.equals(opetussuunnitelmaDto.getPerusteenId(), ops.getCachedPeruste().getPerusteId())) { throw new BusinessRuleViolationException("Opetussuunnitelman perustetta ei voi vaihtaa"); } if (opetussuunnitelmaDto.getOrganisaatiot().isEmpty()) { throw new BusinessRuleViolationException("Organisaatiolista ei voi olla tyhj"); } } private Validointi validoiOpetussuunnitelma(Opetussuunnitelma ops) { Set<Kieli> julkaisukielet = ops.getJulkaisukielet(); Validointi validointi = new Validointi(); if (ops.getPerusteenDiaarinumero().isEmpty()) { validointi.virhe("opsilla-ei-perusteen-diaarinumeroa"); } if (ops.getTekstit() != null && ops.getTekstit().getLapset() != null) { for (TekstiKappaleViite teksti : ops.getTekstit().getLapset()) { TekstiKappaleViite.validoi(validointi, teksti, julkaisukielet); } } ops.getVuosiluokkakokonaisuudet().stream().filter(OpsVuosiluokkakokonaisuus::isOma) .map(OpsVuosiluokkakokonaisuus::getVuosiluokkakokonaisuus) .forEach(vlk -> Vuosiluokkakokonaisuus.validoi(validointi, vlk, julkaisukielet)); //TODO Should we use same version of Peruste for with the Opetuusuunnitelma was based on if available? PerusteDto peruste = eperusteetService.getPeruste(ops.getPerusteenDiaarinumero()); if (peruste.getPerusopetus() != null) { ops.getOppiaineet().stream().filter(OpsOppiaine::isOma).map(OpsOppiaine::getOppiaine) .forEach(oa -> peruste.getPerusopetus().getOppiaine(oa.getTunniste()).ifPresent(poppiaine -> { Oppiaine.validoi(validointi, oa, julkaisukielet); Set<UUID> PerusteenTavoitteet = new HashSet<>(); poppiaine.getVuosiluokkakokonaisuudet().forEach(vlk -> vlk.getTavoitteet() .forEach(tavoite -> PerusteenTavoitteet.add(tavoite.getTunniste()))); Set<UUID> OpsinTavoitteet = oa.getVuosiluokkakokonaisuudet().stream() .flatMap(vlk -> vlk.getVuosiluokat().stream()) .map(Oppiaineenvuosiluokka::getTavoitteet).flatMap(Collection::stream) .map(Opetuksentavoite::getTunniste).collect(Collectors.toSet()); })); } return validointi; } private void validoiOhjeistus(TekstiKappaleViite tkv, Set<Kieli> kielet) { Validointi validointi = new Validointi(); for (TekstiKappaleViite lapsi : tkv.getLapset()) { Ohje ohje = ohjeRepository.findFirstByKohde(lapsi.getTekstiKappale().getTunniste()); if (ohje != null && (ohje.getTeksti() == null || !ohje.getTeksti().hasKielet(kielet))) { validointi.virhe("ops-pohja-ohjeistus-puuttuu", tkv.getTekstiKappale().getNimi(), tkv.getTekstiKappale().getNimi()); } else { validointi.virhe("ops-pohja-ohjeistus-puuttuu"); } validoiOhjeistus(lapsi, kielet); } validointi.tuomitse(); } private Validointi validoiPohja(Opetussuunnitelma ops) { return new Validointi(); } @Override public OpetussuunnitelmaDto updateTila(@P("id") Long id, Tila tila) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Opetussuunnitelmaa ei ole olemassa"); if (ops.getTyyppi() == Tyyppi.POHJA && tila == Tila.JULKAISTU) { tila = Tila.VALMIS; } if (ops.getTyyppi() == Tyyppi.OPS && ops.getTila() == Tila.JULKAISTU && tila == Tila.VALMIS) { ops.setTila(tila); ops = repository.save(ops); } if (tila != ops.getTila() && ops.getTila().mahdollisetSiirtymat(ops.getTyyppi() == Tyyppi.POHJA).contains(tila)) { if (ops.getTyyppi() == Tyyppi.OPS && (tila == Tila.JULKAISTU)) { Validointi validointi = validoiOpetussuunnitelma(ops); validointi.tuomitse(); for (Kieli kieli : ops.getJulkaisukielet()) { try { dokumenttiService.autogenerate(ops.getId(), kieli); } catch (DokumenttiException e) { logger.error(e.getLocalizedMessage(), e.getCause()); } } } else if (ops.getTyyppi() == Tyyppi.POHJA && tila == Tila.VALMIS) { Validointi validointi = validoiPohja(ops); validointi.tuomitse(); } if (tila == Tila.VALMIS && ops.getTyyppi() == Tyyppi.POHJA && ops.getKoulutustyyppi() != KoulutusTyyppi.TPO) { // Arkistoidaan vanhat valmiit pohjat List<Opetussuunnitelma> pohjat = repository.findAllByTyyppiAndTilaAndKoulutustyyppi(Tyyppi.POHJA, Tila.VALMIS, ops.getKoulutustyyppi()); for (Opetussuunnitelma pohja : pohjat) { pohja.setTila(Tila.POISTETTU); } } if (tila == Tila.VALMIS && ops.getTila() == Tila.LUONNOS && ops.getTyyppi() != Tyyppi.POHJA && ops.getKoulutustyyppi().isLukio()) { Validointi validointi = validoiLukioPohja(ops); validointi.tuomitse(); } ops.setTila(tila); ops = repository.save(ops); } return mapper.map(ops, OpetussuunnitelmaDto.class); } @Override public List<Validointi> validoiOpetussuunnitelma(Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Opetussuunnitelmaa ei ole olemassa"); List<Validointi> result = new ArrayList<>(); switch (ops.getTyyppi()) { case OPS: result.add(validoiOpetussuunnitelma(ops)); break; default: result.add(validoiPohja(ops)); break; } switch (ops.getKoulutustyyppi()) { case LUKIOKOULUTUS: case LUKIOVALMISTAVAKOULUTUS: result.add(validoiLukioPohja(ops)); break; default: break; } return result; } private Validointi validoiLukioPohja(Opetussuunnitelma ops) { Validointi validointi = new Validointi(); if (ops.getOppiaineet().isEmpty()) { logger.error("lukio-ei-oppiaineita"); validointi.virhe("lukio-ei-oppiaineita", ops.getNimi()); } if (ops.getAihekokonaisuudet() == null || ops.getAihekokonaisuudet().getAihekokonaisuudet().isEmpty()) { logger.error("lukio-ei-aihekokonaisuuksia"); validointi.virhe("lukio-ei-aihekokonaisuuksia", ops.getNimi()); } ops.getOppiaineet().forEach(opsOppiaine -> { if (!opsOppiaine.getOppiaine().isKoosteinen() && !oppiaineHasKurssi(opsOppiaine.getOppiaine(), ops.getLukiokurssit())) { logger.error("lukio-oppiaineessa-ei-kursseja"); validointi.virhe("lukio-oppiaineessa-ei-kursseja", opsOppiaine.getOppiaine().getNimi()); } if (opsOppiaine.getOppiaine().isKoosteinen() && opsOppiaine.getOppiaine().getOppimaarat().isEmpty()) { logger.error("lukio-oppiaineessa-ei-oppimaaria"); validointi.varoitus("lukio-oppiaineessa-ei-oppimaaria", opsOppiaine.getOppiaine().getNimi()); } }); ops.getLukiokurssit().forEach(oppiaineLukiokurssi -> { if (oppiaineLukiokurssi.getKurssi().getTyyppi().isPaikallinen()) { oppiaineLukiokurssi.getKurssi().validoiTavoitteetJaKeskeinenSisalto(validointi, ops.getJulkaisukielet()); } }); logger.error("lukio-opsin-validointi-epaonnistui", validointi.getVirheet().size()); return validointi; } private boolean oppiaineHasKurssi(Oppiaine oppiaine, Set<OppiaineLukiokurssi> lukiokurssit) { for (OppiaineLukiokurssi oppiaineLukiokurssi : lukiokurssit) { if (oppiaineLukiokurssi.getOppiaine().getTunniste().compareTo(oppiaine.getTunniste()) == 0) { return true; } } return false; } @Override public OpetussuunnitelmaDto restore(@P("id") Long id) { Opetussuunnitelma ops = repository.findOne(id); assertExists(ops, "Opetussuunnitelmaa ei ole olemassa"); ops.setTila(Tila.LUONNOS); ops = repository.save(ops); return mapper.map(ops, OpetussuunnitelmaDto.class); } @Override public void removeOpetussuunnitelma(Long id) { Opetussuunnitelma ops = repository.findOne(id); if (ops != null) { kommenttiService.getAllByOpetussuunnitelma(id).forEach(k -> kommenttiService.deleteReally(k.getId())); } repository.delete(ops); } @Override @Transactional(readOnly = true) public <T> T getTekstit(Long opsId, Class<T> t) { Opetussuunnitelma ops = repository.findOne(opsId); assertExists(ops, "Opetussuunnitelmaa ei ole olemassa"); return mapper.map(ops.getTekstit(), t); } @Override public TekstiKappaleViiteDto.Matala addTekstiKappale(Long opsId, TekstiKappaleViiteDto.Matala viite) { Opetussuunnitelma ops = repository.findOne(opsId); assertExists(ops, "Opetussuunnitelmaa ei ole olemassa"); // Listn viite juurinoden alle return tekstiKappaleViiteService.addTekstiKappaleViite(opsId, ops.getTekstit().getId(), viite); } @Override public TekstiKappaleViiteDto.Matala addTekstiKappaleLapsi(Long opsId, Long parentId, TekstiKappaleViiteDto.Matala viite) { // Listn viite parent-noden alle return tekstiKappaleViiteService.addTekstiKappaleViite(opsId, parentId, viite); } }