Java tutorial
/*************************************************************************************************** * Copyright 2017 Regents of the University of California. Licensed under the Educational * Community License, Version 2.0 (the license?); you may not use this file except in * compliance with the License. You may obtain a copy of the license at * * https://opensource.org/licenses/ECL-2.0 * * Unless required under applicable law or agreed to in writing, software distributed under the * License is distributed in an AS IS? BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for specific language governing permissions * and limitations under the license. **************************************************************************************************/ package tds.assessment; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; import org.joda.time.Instant; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import tds.accommodation.Accommodation; import tds.common.Algorithm; /** * Represents admin information common to all segments, regardless of the selection algorithm. */ public class Segment { private final String key; private String label; private final Algorithm selectionAlgorithm; private String segmentId; private float startAbility; private String subject; private String assessmentKey; private int position; private int minItems; private int maxItems; private int fieldTestMinItems; private int fieldTestMaxItems; private Instant fieldTestStartDate; private Instant fieldTestEndDate; private int fieldTestStartPosition; private int fieldTestEndPosition; private int refreshMinutes; private float blueprintWeight; private float itemWeight; private float abilityOffset; private int candidateSet1Size; private String candidateSet1Order; private int randomizer; private int initialRandom; private float abilityWeight; private String adaptiveVersion; private float reportingCandidateAbilityWeight; private float precisionTarget; private float precisionTargetMetWeight; private float precisionTargetNotMetWeight; private float adaptiveCut; private float tooCloseStandardErrors; private boolean terminationOverallInformation; private boolean terminationReportingCategoryInfo; private boolean terminationMinCount; private boolean terminationTooClose; private boolean terminationFlagsAnd; private float slope; private float intercept; private float startInfo; private List<ItemGroup> itemGroups = new ArrayList<>(); // Fixed-form specific fields private List<Form> forms = new ArrayList<>(); // Adaptive-specific fields private Set<Strand> strands = new HashSet<>(); private List<Item> items = new ArrayList<>(); public Segment(@JsonProperty("key") String key, @JsonProperty("algorithm") Algorithm algorithm) { this.key = key; this.selectionAlgorithm = algorithm; } public float getStartInfo() { return startInfo; } public void setStartInfo(final float startInfo) { this.startInfo = startInfo; } /** * @return key to the segment */ public String getKey() { return key; } /** * @return the key to the assessment this segment is a part of */ public String getAssessmentKey() { return assessmentKey; } public void setAssessmentKey(String assessmentKey) { this.assessmentKey = assessmentKey; } /** * @return The human-readable label of the segment */ public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } /** * @return the associated segment id */ public String getSegmentId() { return segmentId; } public void setSegmentId(String segmentId) { this.segmentId = segmentId; } /** * @return the selection algorithm key for the segment */ public Algorithm getSelectionAlgorithm() { return selectionAlgorithm; } /** * @return the start ability value for the segment */ public float getStartAbility() { return startAbility; } public void setStartAbility(float startAbility) { this.startAbility = startAbility; } /** * @return the subject name - this can be null */ public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } /** * @return the position of the segment in the {@link Assessment} */ public int getPosition() { return position; } public void setPosition(int position) { this.position = position; } /** * @return The minimum number of items for this {@link tds.assessment.Segment} */ public int getMinItems() { return minItems; } public void setMinItems(int minItems) { this.minItems = minItems; } /** * @return The maximum number of items for this {@link tds.assessment.Segment} */ public int getMaxItems() { return maxItems; } public void setMaxItems(int maxItems) { this.maxItems = maxItems; } /** * @return The lowest possible start position in the exam where this segment may contain field test items */ public int getFieldTestStartPosition() { return fieldTestStartPosition; } public void setFieldTestStartPosition(int fieldTestStartPosition) { this.fieldTestStartPosition = fieldTestStartPosition; } /** * @return The highest possible position in the exam where this segment may contain field test items */ public int getFieldTestEndPosition() { return fieldTestEndPosition; } public void setFieldTestEndPosition(int fieldTestEndPosition) { this.fieldTestEndPosition = fieldTestEndPosition; } /** * @return the minimum number of field test items in the {@link tds.assessment.Segment} */ public int getFieldTestMinItems() { return fieldTestMinItems; } public void setFieldTestMinItems(int fieldTestMinItems) { this.fieldTestMinItems = fieldTestMinItems; } /** * @return the maximum number of field test items in the {@link tds.assessment.Segment} */ public int getFieldTestMaxItems() { return fieldTestMaxItems; } public void setFieldTestMaxItems(int fieldTestMaxItems) { this.fieldTestMaxItems = fieldTestMaxItems; } /** * @return the field test start date */ public Instant getFieldTestStartDate() { return fieldTestStartDate; } public void setFieldTestStartDate(Instant fieldTestStartDate) { this.fieldTestStartDate = fieldTestStartDate; } /** * @return the field test end date */ public Instant getFieldTestEndDate() { return fieldTestEndDate; } public void setFieldTestEndDate(Instant fieldTestEndDate) { this.fieldTestEndDate = fieldTestEndDate; } /** * @return All the {@link tds.assessment.Form}s associated with this segment. * <p> * This getter/setter is only used by {@link tds.assessment.Segment}s that are configured for the fixed form * {@link tds.common.Algorithm}. Segments configured for the "adaptive2" algorithm do not have forms, in which case an empty * {@code ArrayList} is returned * </p> */ public List<Form> getForms() { if (forms == null) { forms = new ArrayList<>(); } return forms; } public void setForms(List<Form> forms) { this.forms = forms; } /** * Choose the {@link tds.assessment.Form}s for the specified language. * <p> * This method is only used by {@link tds.assessment.Segment}s that are configured for the fixed form * {@link tds.common.Algorithm}. Segments configured for the adaptive algorithm do not have forms. * </p> * * @param languageCode The student's specified language * @return The {@link tds.assessment.Form} for the specified language */ public List<Form> getForms(final String languageCode) { List<Form> formsForLanguage = new ArrayList<>(); if (forms == null) { return new ArrayList<>(); } for (Form form : this.forms) { if (form.getLanguageCode().equalsIgnoreCase(languageCode)) { formsForLanguage.add(form); } } return formsForLanguage; } /** * Choose the {@link tds.assessment.Form} for the specified language and form cohort. * <p> * This method is only used by {@link tds.assessment.Segment}s that are configured for the fixed form * {@link tds.common.Algorithm}. Segments configured for the adaptive algorithm do not have forms. * </p> * * @param languageCode The student's specified language * @param cohort The form cohort to filter by * @return The {@link tds.assessment.Form} for the specified language */ public Optional<Form> getForm(final String languageCode, final String cohort) { Optional<Form> maybeForm = Optional.absent(); for (Form form : this.forms) { if (form.getLanguageCode().equalsIgnoreCase(languageCode) && form.getCohort().equalsIgnoreCase(cohort)) { maybeForm = Optional.of(form); break; } } return maybeForm; } /** * @return the collection of {@link tds.assessment.Strand}s containing adaptive algorithm metadata for the * {@link tds.assessment.Segment} * <p> * This method is only used by {@link tds.assessment.Segment}s that are configured for the adaptive * {@link tds.common.Algorithm}. Segments configured for the fixed form algorithm do not have strands, in * which case this method will return an empty {@code HashSet}. * </p> */ public Set<Strand> getStrands() { return strands; } public void setStrands(Set<Strand> strands) { this.strands = strands; } public List<Item> getItems() { return items; } /** * Get a collection of {@link tds.assessment.Item}s based on this Segment's selection algorithm. * * @param languageCode The language code * @return A @{code List<Item>} collection for the student's language */ public List<Item> getItems(String languageCode) { // RULE: When the Segment's algorithm is set to "fixedform", the items should come from the Form that represents // the student's selected language. Otherwise, the items are returned directly (because they will have been // collected by other means). final List<Item> retItems = new ArrayList<>(); if (getSelectionAlgorithm() == Algorithm.FIXED_FORM) { for (final Form form : getForms(languageCode)) { retItems.addAll(form.getItems()); } } else { for (final Item item : items) { // An item can have many languages (e.g. English, English-Braille, Spanish). If the item has a // language property for the specified language code, return it. for (final ItemProperty itemProperty : item.getItemProperties()) { if (itemProperty.getName().equalsIgnoreCase(Accommodation.ACCOMMODATION_TYPE_LANGUAGE) && itemProperty.getValue().equalsIgnoreCase(languageCode)) { retItems.add(item); } } } } return retItems; } /** * This setter is only used by {@link tds.assessment.Segment}s that are configured for the "adaptive2" algorithm. * Segments configured for the "fixedform" algorithm rely on a {@link tds.assessment.Form} to get the correct * collection of {@link tds.assessment.Item}s. */ public void setItems(List<Item> items) { this.items = items; } public int getRefreshMinutes() { return refreshMinutes; } public void setRefreshMinutes(final int refreshMinutes) { this.refreshMinutes = refreshMinutes; } public float getBlueprintWeight() { return blueprintWeight; } public void setBlueprintWeight(final float blueprintWeight) { this.blueprintWeight = blueprintWeight; } public float getItemWeight() { return itemWeight; } public void setItemWeight(final float itemWeight) { this.itemWeight = itemWeight; } public float getAbilityOffset() { return abilityOffset; } public void setAbilityOffset(final float abilityOffset) { this.abilityOffset = abilityOffset; } public int getCandidateSet1Size() { return candidateSet1Size; } public void setCandidateSet1Size(final int candidateSet1Size) { this.candidateSet1Size = candidateSet1Size; } public String getCandidateSet1Order() { return candidateSet1Order; } public void setCandidateSet1Order(final String candidateSet1Order) { this.candidateSet1Order = candidateSet1Order; } public int getRandomizer() { return randomizer; } public void setRandomizer(final int randomizer) { this.randomizer = randomizer; } public int getInitialRandom() { return initialRandom; } public void setInitialRandom(final int initialRandom) { this.initialRandom = initialRandom; } public float getAbilityWeight() { return abilityWeight; } public void setAbilityWeight(final float abilityWeight) { this.abilityWeight = abilityWeight; } public String getAdaptiveVersion() { return adaptiveVersion; } public void setAdaptiveVersion(final String adaptiveVersion) { this.adaptiveVersion = adaptiveVersion; } public float getReportingCandidateAbilityWeight() { return reportingCandidateAbilityWeight; } public void setReportingCandidateAbilityWeight(final float reportingCandidateAbilityWeight) { this.reportingCandidateAbilityWeight = reportingCandidateAbilityWeight; } public float getPrecisionTarget() { return precisionTarget; } public void setPrecisionTarget(final float precisionTarget) { this.precisionTarget = precisionTarget; } public float getPrecisionTargetMetWeight() { return precisionTargetMetWeight; } public void setPrecisionTargetMetWeight(final float precisionTargetMetWeight) { this.precisionTargetMetWeight = precisionTargetMetWeight; } public float getPrecisionTargetNotMetWeight() { return precisionTargetNotMetWeight; } public void setPrecisionTargetNotMetWeight(final float precisionTargetNotMetWeight) { this.precisionTargetNotMetWeight = precisionTargetNotMetWeight; } public float getAdaptiveCut() { return adaptiveCut; } public void setAdaptiveCut(final float adaptiveCut) { this.adaptiveCut = adaptiveCut; } public float getTooCloseStandardErrors() { return tooCloseStandardErrors; } public void setTooCloseStandardErrors(final float tooCloseStandardErrors) { this.tooCloseStandardErrors = tooCloseStandardErrors; } public boolean isTerminationOverallInformation() { return terminationOverallInformation; } public void setTerminationOverallInformation(final boolean terminationOverallInformation) { this.terminationOverallInformation = terminationOverallInformation; } public boolean isTerminationReportingCategoryInfo() { return terminationReportingCategoryInfo; } public void setTerminationReportingCategoryInfo(final boolean terminationReportingCategoryInfo) { this.terminationReportingCategoryInfo = terminationReportingCategoryInfo; } public boolean isTerminationMinCount() { return terminationMinCount; } public void setTerminationMinCount(final boolean terminationMinCount) { this.terminationMinCount = terminationMinCount; } public boolean isTerminationTooClose() { return terminationTooClose; } public void setTerminationTooClose(final boolean terminationTooClose) { this.terminationTooClose = terminationTooClose; } public boolean isTerminationFlagsAnd() { return terminationFlagsAnd; } public void setTerminationFlagsAnd(final boolean terminationFlagsAnd) { this.terminationFlagsAnd = terminationFlagsAnd; } public float getSlope() { return slope; } public void setSlope(final float slope) { this.slope = slope; } /** * @return leveraged when choosing items during an adaptive selection algorithm */ public float getIntercept() { return intercept; } public void setIntercept(final float intercept) { this.intercept = intercept; } /** * @return The item groups that are associated with this segment. This can be empty if there are not groups. In * those instances the item will be given a hard coded group in the selection code. */ public List<ItemGroup> getItemGroups() { return itemGroups; } public void setItemGroups(final List<ItemGroup> itemGroups) { this.itemGroups = itemGroups; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Segment segment = (Segment) o; if (Float.compare(segment.startAbility, startAbility) != 0) return false; if (position != segment.position) return false; if (minItems != segment.minItems) return false; if (maxItems != segment.maxItems) return false; if (fieldTestMinItems != segment.fieldTestMinItems) return false; if (fieldTestMaxItems != segment.fieldTestMaxItems) return false; if (fieldTestStartPosition != segment.fieldTestStartPosition) return false; if (fieldTestEndPosition != segment.fieldTestEndPosition) return false; if (refreshMinutes != segment.refreshMinutes) return false; if (Float.compare(segment.blueprintWeight, blueprintWeight) != 0) return false; if (Float.compare(segment.itemWeight, itemWeight) != 0) return false; if (Float.compare(segment.abilityOffset, abilityOffset) != 0) return false; if (candidateSet1Size != segment.candidateSet1Size) return false; if (randomizer != segment.randomizer) return false; if (initialRandom != segment.initialRandom) return false; if (Float.compare(segment.abilityWeight, abilityWeight) != 0) return false; if (Float.compare(segment.reportingCandidateAbilityWeight, reportingCandidateAbilityWeight) != 0) return false; if (Float.compare(segment.precisionTarget, precisionTarget) != 0) return false; if (Float.compare(segment.precisionTargetMetWeight, precisionTargetMetWeight) != 0) return false; if (Float.compare(segment.precisionTargetNotMetWeight, precisionTargetNotMetWeight) != 0) return false; if (Float.compare(segment.adaptiveCut, adaptiveCut) != 0) return false; if (Float.compare(segment.tooCloseStandardErrors, tooCloseStandardErrors) != 0) return false; if (terminationOverallInformation != segment.terminationOverallInformation) return false; if (terminationReportingCategoryInfo != segment.terminationReportingCategoryInfo) return false; if (terminationMinCount != segment.terminationMinCount) return false; if (terminationTooClose != segment.terminationTooClose) return false; if (terminationFlagsAnd != segment.terminationFlagsAnd) return false; if (Float.compare(segment.slope, slope) != 0) return false; if (Float.compare(segment.intercept, intercept) != 0) return false; if (key != null ? !key.equals(segment.key) : segment.key != null) return false; if (label != null ? !label.equals(segment.label) : segment.label != null) return false; if (selectionAlgorithm != segment.selectionAlgorithm) return false; if (segmentId != null ? !segmentId.equals(segment.segmentId) : segment.segmentId != null) return false; if (subject != null ? !subject.equals(segment.subject) : segment.subject != null) return false; if (assessmentKey != null ? !assessmentKey.equals(segment.assessmentKey) : segment.assessmentKey != null) return false; if (fieldTestStartDate != null ? !fieldTestStartDate.equals(segment.fieldTestStartDate) : segment.fieldTestStartDate != null) return false; if (fieldTestEndDate != null ? !fieldTestEndDate.equals(segment.fieldTestEndDate) : segment.fieldTestEndDate != null) return false; if (candidateSet1Order != null ? !candidateSet1Order.equals(segment.candidateSet1Order) : segment.candidateSet1Order != null) return false; if (adaptiveVersion != null ? !adaptiveVersion.equals(segment.adaptiveVersion) : segment.adaptiveVersion != null) return false; if (forms != null ? !forms.equals(segment.forms) : segment.forms != null) return false; if (strands != null ? !strands.equals(segment.strands) : segment.strands != null) return false; return items != null ? items.equals(segment.items) : segment.items == null; } @Override public int hashCode() { int result = key != null ? key.hashCode() : 0; result = 31 * result + (label != null ? label.hashCode() : 0); result = 31 * result + (selectionAlgorithm != null ? selectionAlgorithm.hashCode() : 0); result = 31 * result + (segmentId != null ? segmentId.hashCode() : 0); result = 31 * result + (startAbility != +0.0f ? Float.floatToIntBits(startAbility) : 0); result = 31 * result + (subject != null ? subject.hashCode() : 0); result = 31 * result + (assessmentKey != null ? assessmentKey.hashCode() : 0); result = 31 * result + position; result = 31 * result + minItems; result = 31 * result + maxItems; result = 31 * result + fieldTestMinItems; result = 31 * result + fieldTestMaxItems; result = 31 * result + (fieldTestStartDate != null ? fieldTestStartDate.hashCode() : 0); result = 31 * result + (fieldTestEndDate != null ? fieldTestEndDate.hashCode() : 0); result = 31 * result + fieldTestStartPosition; result = 31 * result + fieldTestEndPosition; result = 31 * result + refreshMinutes; result = 31 * result + (blueprintWeight != +0.0f ? Float.floatToIntBits(blueprintWeight) : 0); result = 31 * result + (itemWeight != +0.0f ? Float.floatToIntBits(itemWeight) : 0); result = 31 * result + (abilityOffset != +0.0f ? Float.floatToIntBits(abilityOffset) : 0); result = 31 * result + candidateSet1Size; result = 31 * result + (candidateSet1Order != null ? candidateSet1Order.hashCode() : 0); result = 31 * result + randomizer; result = 31 * result + initialRandom; result = 31 * result + (abilityWeight != +0.0f ? Float.floatToIntBits(abilityWeight) : 0); result = 31 * result + (adaptiveVersion != null ? adaptiveVersion.hashCode() : 0); result = 31 * result + (reportingCandidateAbilityWeight != +0.0f ? Float.floatToIntBits(reportingCandidateAbilityWeight) : 0); result = 31 * result + (precisionTarget != +0.0f ? Float.floatToIntBits(precisionTarget) : 0); result = 31 * result + (precisionTargetMetWeight != +0.0f ? Float.floatToIntBits(precisionTargetMetWeight) : 0); result = 31 * result + (precisionTargetNotMetWeight != +0.0f ? Float.floatToIntBits(precisionTargetNotMetWeight) : 0); result = 31 * result + (adaptiveCut != +0.0f ? Float.floatToIntBits(adaptiveCut) : 0); result = 31 * result + (tooCloseStandardErrors != +0.0f ? Float.floatToIntBits(tooCloseStandardErrors) : 0); result = 31 * result + (terminationOverallInformation ? 1 : 0); result = 31 * result + (terminationReportingCategoryInfo ? 1 : 0); result = 31 * result + (terminationMinCount ? 1 : 0); result = 31 * result + (terminationTooClose ? 1 : 0); result = 31 * result + (terminationFlagsAnd ? 1 : 0); result = 31 * result + (slope != +0.0f ? Float.floatToIntBits(slope) : 0); result = 31 * result + (intercept != +0.0f ? Float.floatToIntBits(intercept) : 0); result = 31 * result + (forms != null ? forms.hashCode() : 0); result = 31 * result + (strands != null ? strands.hashCode() : 0); result = 31 * result + (items != null ? items.hashCode() : 0); return result; } }