@Override
  @Transactional(rollbackFor = {Throwable.class})
  public StatusInfo deleteTermCascaded(String termId, ContextInfo context)
      throws DoesNotExistException, InvalidParameterException, MissingParameterException,
          OperationFailedException, PermissionDeniedException {
    StatusInfo statusInfo = new StatusInfo();

    // retrieve all the sub term ids of the give term
    List<String> subTermIds =
        getRelatedAtpIdsForParentAtpIdAndRelationType(
            termId, AtpServiceConstants.ATP_ATP_RELATION_INCLUDES_TYPE_KEY, context);
    if (subTermIds != null && !subTermIds.isEmpty()) {
      for (String subTermId : subTermIds) {
        deleteTermCascaded(subTermId, context);
      }
    }

    // delete the associated keydates
    deleteKeyDatesbyTermId(termId, context);
    // delete the associated exam period
    deleteExamPeriodByTermId(termId, context);
    // delete term/subterm
    acalService.deleteTerm(termId, context);
    statusInfo.setSuccess(Boolean.TRUE);
    return statusInfo;
  }
  @Override
  @Transactional(rollbackFor = {Throwable.class})
  public StatusInfo deleteCalendarCascaded(String academicCalendarId, ContextInfo context)
      throws DoesNotExistException, InvalidParameterException, MissingParameterException,
          OperationFailedException, PermissionDeniedException {
    AcademicCalendarInfo acalInfo = acalService.getAcademicCalendar(academicCalendarId, context);
    StatusInfo statusInfo = new StatusInfo();

    // if the calendar in official state, not to delete anything
    if (StringUtils.equals(acalInfo.getStateKey(), AtpServiceConstants.ATP_OFFICIAL_STATE_KEY)) {
      throw new OperationFailedException(
          "Calendar of the state official can't be deleted - Calendar id:" + academicCalendarId);
    }
    List<String> termIds = getTermIdsForAcademicCalendar(academicCalendarId, context);
    if (termIds != null && !termIds.isEmpty()) {
      for (String termId : termIds) {
        deleteTermCascaded(termId, context);
      }
    }

    // delete calendar
    acalService.deleteAcademicCalendar(academicCalendarId, context);
    statusInfo.setSuccess(Boolean.TRUE);
    return statusInfo;
  }
  private void testDeletes(CourseOfferingInfo co, FormatOfferingInfo fo, ActivityOfferingInfo ao)
      throws InvalidParameterException, MissingParameterException, OperationFailedException,
          PermissionDeniedException, DoesNotExistException, DependentObjectsExistException {

    // delete activity offering
    StatusInfo status = this.courseOfferingService.deleteActivityOffering(ao.getId(), callContext);
    assertNotNull(status);
    assertEquals(Boolean.TRUE, status.getIsSuccess());

    try {
      courseOfferingService.getActivityOffering(ao.getId(), callContext);
      fail("should have thrown DoesNotExistException");
    } catch (DoesNotExistException ex) {
      assertNotNull(ex.getMessage());
      assertEquals(ao.getId(), ex.getMessage());
    }

    // delete fo
    status = this.courseOfferingService.deleteFormatOffering(fo.getId(), callContext);
    assertNotNull(status);
    assertEquals(Boolean.TRUE, status.getIsSuccess());

    try {
      courseOfferingService.getFormatOffering(fo.getId(), callContext);
      fail("should have thrown DoesNotExistException");
    } catch (DoesNotExistException ex) {
      assertNotNull(ex.getMessage());
      assertEquals(fo.getId(), ex.getMessage());
    }

    // delete co
    status = this.courseOfferingService.deleteCourseOffering(co.getId(), callContext);
    assertNotNull(status);
    assertEquals(Boolean.TRUE, status.getIsSuccess());

    try {
      courseOfferingService.getCourseOffering(co.getId(), callContext);
      fail("should have thrown DoesNotExistException");
    } catch (DoesNotExistException ex) {
      assertNotNull(ex.getMessage());
      assertEquals(co.getId(), ex.getMessage());
    }
  }
  @Test
  public void testLuiServiceLuiSetMethods() throws Throwable {
    System.out.println("starting tests...");

    String luiSetTypeKey = "test.lui.set.type.key.test";
    LuiSetInfo luiSetInfo = new LuiSetInfo();
    RichTextInfo descr = new RichTextInfo();
    descr.setPlain("descr");
    luiSetInfo.setDescr(descr);
    luiSetInfo.setStateKey("test.lui.set.state.key.test");
    luiSetInfo.setTypeKey(luiSetTypeKey);
    luiSetInfo.setName("name");
    luiSetInfo.setEffectiveDate(new Date());
    luiSetInfo.setExpirationDate(new Date());
    luiSetInfo.setMeta(new MetaInfo());
    luiSetInfo.setAttributes(new ArrayList<AttributeInfo>());

    // create
    LuiSetInfo created = luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext);
    assertNotNull(created);
    assertTrue(UUIDHelper.isUUID(created.getId()));

    // read
    LuiSetInfo retrieved = luiService.getLuiSet(created.getId(), callContext);
    assertNotNull(retrieved);
    assertEquals(created.getId(), retrieved.getId());

    // update
    retrieved.setName("updated_name");
    LuiSetInfo replaced = luiService.updateLuiSet(created.getId(), retrieved, callContext);
    LuiSetInfo updated = luiService.getLuiSet(created.getId(), callContext);
    assertNotNull(replaced);
    assertNotNull(updated);
    assertEquals(created.getId(), replaced.getId());
    assertEquals(created.getId(), updated.getId());
    assertEquals(created.getName(), replaced.getName());
    assertEquals(retrieved.getName(), updated.getName());

    // delete
    StatusInfo deleteStatus = luiService.deleteLuiSet(created.getId(), callContext);
    assertNotNull(deleteStatus);
    assertTrue(deleteStatus.getIsSuccess());
    try {
      luiService.getLuiSet(created.getId(), callContext);
      fail("should have thrown DoesNotExistException");
    } catch (DoesNotExistException ex) {
      assertNotNull(ex.getMessage());
      assertEquals("luiSetId not found: " + retrieved.getId(), ex.getMessage());
    }

    // bulk operation -- get a bunch of luis using a list of their ids
    List<String> createdLuiSetIds = new ArrayList<String>();
    createdLuiSetIds.add(luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext).getId());
    createdLuiSetIds.add(luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext).getId());
    createdLuiSetIds.add(luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext).getId());
    List<LuiSetInfo> bulkRetrieved_luiSets =
        luiService.getLuiSetsByIds(createdLuiSetIds, callContext);
    assertNotNull(bulkRetrieved_luiSets);
    assertTrue(bulkRetrieved_luiSets.size() == 3);
    for (LuiSetInfo r : bulkRetrieved_luiSets) {
      assertTrue(createdLuiSetIds.contains(r.getId()));
    }

    // bulk operation -- get the list of lui-ids of a lui-set
    List<String> luiIds = new ArrayList<String>();
    luiIds.add(UUIDHelper.genStringUUID());
    luiIds.add(UUIDHelper.genStringUUID());
    luiSetInfo.setLuiIds(luiIds);
    created = luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext);
    List<String> bulkRetrieved_luiIds =
        luiService.getLuiIdsFromLuiSet(created.getId(), callContext);
    assertNotNull(bulkRetrieved_luiIds);
    assertEquals(luiIds.size(), bulkRetrieved_luiIds.size());
    assertTrue(bulkRetrieved_luiIds.containsAll(luiIds));

    // bulk operation -- get a list of lui-ids that contain a specific lui-id
    String targetLuiId = UUIDHelper.genStringUUID();
    List<String> targetLuiIdsList = new ArrayList<String>();
    targetLuiIdsList.add(targetLuiId);
    luiSetInfo.setLuiIds(targetLuiIdsList);
    // create a bunch of lui-sets containing references to that lui-id
    List<LuiSetInfo> bulkCreated_luiSets = new ArrayList<LuiSetInfo>();
    bulkCreated_luiSets.add(luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext));
    bulkCreated_luiSets.add(luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext));
    bulkCreated_luiSets.add(luiService.createLuiSet(luiSetTypeKey, luiSetInfo, callContext));
    bulkRetrieved_luiSets = luiService.getLuiSetsByLui(targetLuiId, callContext);
    assertNotNull(bulkRetrieved_luiSets);
    assertEquals(3, bulkRetrieved_luiSets.size());
    for (LuiSetInfo luiSet : bulkRetrieved_luiSets) {
      assertTrue(luiSet.getLuiIds().contains(targetLuiId));
    }

    // bulk operation -- get a list of lui-set ids by lui-set type
    luiService.createLuiSet("test.alternate.lui.set.type.key.test", luiSetInfo, callContext);
    bulkRetrieved_luiIds = luiService.getLuiSetIdsByType(luiSetTypeKey, callContext);
    assertEquals(7, bulkRetrieved_luiIds.size());
    for (String id : bulkRetrieved_luiIds) {
      assertEquals(luiSetTypeKey, luiService.getLuiSet(id, callContext).getTypeKey());
    }
    assertEquals(
        1,
        luiService.getLuiSetIdsByType("test.alternate.lui.set.type.key.test", callContext).size());
  }
  @Override
  public StatusInfo makeTermOfficialCascaded(String termId, ContextInfo contextInfo)
      throws PermissionDeniedException, MissingParameterException, InvalidParameterException,
          OperationFailedException, DoesNotExistException {
    StatusInfo statusInfo = new StatusInfo();

    // KSENROLL-7251 Implement a new servies process ot change the state of the Academic Calendar
    // from draft to official
    TermInfo termInfo = acalService.getTerm(termId, contextInfo);
    if (AtpServiceConstants.ATP_OFFICIAL_STATE_KEY.equals(termInfo.getStateKey())) {
      // If official, then should have already cascaded.
      statusInfo.setSuccess(Boolean.TRUE);
      return statusInfo;
    }
    // Assumes state propagation not wired in yet.
    Map<String, TermInfo> termIdToTermInfoProcessed = new HashMap<String, TermInfo>();
    Map<String, TermInfo> termIdToTermInfoToBeProcessed = new HashMap<String, TermInfo>();
    Set<String> parentTermIds = new HashSet<String>();
    termIdToTermInfoToBeProcessed.put(termId, termInfo); // Put initial term
    while (!termIdToTermInfoToBeProcessed.keySet().isEmpty()) {
      String nextTermId = termIdToTermInfoToBeProcessed.keySet().iterator().next();
      TermInfo nextTerm = termIdToTermInfoToBeProcessed.get(nextTermId);
      if (termIdToTermInfoProcessed.keySet().contains(nextTermId)) {
        // Skip over ones we've seen
        continue;
      }
      // Change the state
      acalService.changeTermState(
          nextTermId, AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, contextInfo);
      // Change the state of the associated exam period
      changeExamPeriodStateByTermId(
          nextTermId, AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, contextInfo);
      termIdToTermInfoProcessed.put(nextTermId, nextTerm); // Add to processed
      termIdToTermInfoToBeProcessed.remove(nextTermId); // No longer needs processing, so remove
      // Now visit all parents
      List<TermInfo> terms = acalService.getContainingTerms(nextTermId, contextInfo);
      if (terms.isEmpty()) {
        // Assume only parent terms are connected to calendars
        // Given a tree like structure, there should only ever be one parentTermId in the list
        parentTermIds.add(nextTermId);
      } else {
        for (TermInfo term : terms) {
          if (!termIdToTermInfoProcessed.keySet().contains(term.getId())
              && AtpServiceConstants.ATP_DRAFT_STATE_KEY.equals(term.getStateKey())) {
            // Only add if still draft and not yet processed
            termIdToTermInfoToBeProcessed.put(term.getId(), term);
          }
        }
      }
    }
    // Access calendar
    Map<String, AcademicCalendarInfo> idToCalendar = new HashMap<String, AcademicCalendarInfo>();
    for (String parentTermId : parentTermIds) {
      List<AcademicCalendarInfo> cals =
          acalService.getAcademicCalendarsForTerm(parentTermId, contextInfo);
      for (AcademicCalendarInfo cal : cals) {
        idToCalendar.put(cal.getId(), cal);
      }
    }
    // Now iterate over all calendars and make them official
    for (Map.Entry<String, AcademicCalendarInfo> entry : idToCalendar.entrySet()) {
      if (AtpServiceConstants.ATP_DRAFT_STATE_KEY.equals(entry.getValue().getStateKey())) {
        // Only set it if it's still draft
        acalService.changeAcademicCalendarState(
            entry.getKey(), AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, contextInfo);
      }
    }
    statusInfo.setSuccess(Boolean.TRUE);
    return statusInfo;
  }