text:
// Generated by the protocol buffer compiler.  DO NOT EDIT!
// source: google/cloud/talent/v4beta1/common.proto

package com.google.cloud.talent.v4beta1;

/**
 *
 *
 *

 * Metadata used for long running operations returned by CTS batch APIs.
 * It's used to replace [google.longrunning.Operation.metadata][google.longrunning.Operation.metadata].
 *

 *
 * Protobuf type {@code google.cloud.talent.v4beta1.BatchOperationMetadata}
 */
public final class BatchOperationMetadata extends com.google.protobuf.GeneratedMessageV3
    implements
    // @@protoc_insertion_point(message_implements:google.cloud.talent.v4beta1.BatchOperationMetadata)
    BatchOperationMetadataOrBuilder {
  private static final long serialVersionUID = 0L;
  // Use BatchOperationMetadata.newBuilder() to construct.
  private BatchOperationMetadata(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
    super(builder);
  }

  private BatchOperationMetadata() {
    state_ = 0;
    stateDescription_ = "";
  }

  @java.lang.Override
  @SuppressWarnings({"unused"})
  protected java.lang.Object newInstance(UnusedPrivateParameter unused) {
    return new BatchOperationMetadata();
  }

  @java.lang.Override
  public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
    return this.unknownFields;
  }

  public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
    return com.google.cloud.talent.v4beta1.CommonProto
        .internal_static_google_cloud_talent_v4beta1_BatchOperationMetadata_descriptor;
  }

  @java.lang.Override
  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
      internalGetFieldAccessorTable() {
    return com.google.cloud.talent.v4beta1.CommonProto
        .internal_static_google_cloud_talent_v4beta1_BatchOperationMetadata_fieldAccessorTable
        .ensureFieldAccessorsInitialized(
            com.google.cloud.talent.v4beta1.BatchOperationMetadata.class,
            com.google.cloud.talent.v4beta1.BatchOperationMetadata.Builder.class);
  }

  /** Protobuf enum {@code google.cloud.talent.v4beta1.BatchOperationMetadata.State} */
  public enum State implements com.google.protobuf.ProtocolMessageEnum {
    /**
     *
     *
     *

     * Default value.
     *

     *
     * STATE_UNSPECIFIED = 0;
     */
    STATE_UNSPECIFIED(0),
    /**
     *
     *
     *

     * The batch operation is being prepared for processing.
     *

     *
     * INITIALIZING = 1;
     */
    INITIALIZING(1),
    /**
     *
     *
     *

     * The batch operation is actively being processed.
     *

     *
     * PROCESSING = 2;
     */
    PROCESSING(2),
    /**
     *
     *
     *

     * The batch operation is processed, and at least one item has been
     * successfully processed.
     *

     *
     * SUCCEEDED = 3;
     */
    SUCCEEDED(3),
    /**
     *
     *
     *

     * The batch operation is done and no item has been successfully processed.
     *

     *
     * FAILED = 4;
     */
    FAILED(4),
    /**
     *
     *
     *

     * The batch operation is in the process of cancelling after
     * [google.longrunning.Operations.CancelOperation][google.longrunning.Operations.CancelOperation] is called.
     *

     *
     * CANCELLING = 5;
     */
    CANCELLING(5),
    /**
     *
     *
     *

     * The batch operation is done after
     * [google.longrunning.Operations.CancelOperation][google.longrunning.Operations.CancelOperation] is called. Any items
     * processed before cancelling are returned in the response.
     *

     *
     * CANCELLED = 6;
     */
    CANCELLED(6),
    UNRECOGNIZED(-1),
    ;

    /**
     *
     *
     *

     * Default value.
     *

     *
     * STATE_UNSPECIFIED = 0;
     */
    public static final int STATE_UNSPECIFIED_VALUE = 0;
    /**
     *
     *
     *

     * The batch operation is being prepared for processing.
     *

     *
     * INITIALIZING = 1;
     */
    public static final int INITIALIZING_VALUE = 1;
    /**
     *
     *
     *

     * The batch operation is actively being processed.
     *

     *
     * PROCESSING = 2;
     */
    public static final int PROCESSING_VALUE = 2;
    /**
     *
     *
     *

     * The batch operation is processed, and at least one item has been
     * successfully processed.
     *

     *
     * SUCCEEDED = 3;
     */
    public static final int SUCCEEDED_VALUE = 3;
    /**
     *
     *
     *

     * The batch operation is done and no item has been successfully processed.
     *

     *
     * FAILED = 4;
     */
    public static final int FAILED_VALUE = 4;
    /**
     *
     *
     *

     * The batch operation is in the process of cancelling after
     * [google.longrunning.Operations.CancelOperation][google.longrunning.Operations.CancelOperation] is called.
     *

     *
     * CANCELLING = 5;
     */
    public static final int CANCELLING_VALUE = 5;
    /**
     *
     *
     *

     * The batch operation is done after
     * [google.longrunning.Operations.CancelOperation][google.longrunning.Operations.CancelOperation] is called. Any items
     * processed before cancelling are returned in the response.
     *

     *
     * CANCELLED = 6;
     */
    public static final int CANCELLED_VALUE = 6;

    public final int getNumber() {
      if (this == UNRECOGNIZED) {
        throw new java.lang.IllegalArgumentException(
            "Can't get the number of an unknown enum value.");
      }
      return value;
    }

    /**
     * @param value The numeric wire value of the corresponding enum entry.
     * @return The enum associated with the given numeric wire value.
     * @deprecated Use {@link #forNumber(int)} instead.
     */
    @java.lang.Deprecated
    public static State valueOf(int value) {
      return forNumber(value);
    }

    /**
     * @param value The numeric wire value of the corresponding enum entry.
     * @return The enum associated with the given numeric wire value.
     */
    public static State forNumber(int value) {
      switch (value) {
        case 0:
          return STATE_UNSPECIFIED;
        case 1:
          return INITIALIZING;
        case 2:
          return PROCESSING;
        case 3:
          return SUCCEEDED;
        case 4:
          return FAILED;
        case 5:
          return CANCELLING;
        case 6:
          return CANCELLED;
        default:
          return null;
      }
    }

    public static com.google.protobuf.Internal.EnumLiteMap<State> internalGetValueMap() {
      return internalValueMap;
    }

    private static final com.google.protobuf.Internal.EnumLiteMap<State> internalValueMap =
        new com.google.protobuf.Internal.EnumLiteMap<State>() {
          public State findValueByNumber(int number) {
            return State.forNumber(number);
          }
        };

    public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() {
      if (this == UNRECOGNIZED) {
        throw new java.lang.IllegalStateException(
            "Can't get the descriptor of an unrecognized enum value.");
      }
      return getDescriptor().getValues().get(ordinal());
    }

    public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() {
      return getDescriptor();
    }

    public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() {
      return com.google.cloud.talent.v4beta1.BatchOperationMetadata.getDescriptor()
          .getEnumTypes()
          .get(0);
    }

    private static final State[] VALUES = values();

    public static State valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
      if (desc.getType() != getDescriptor()) {
        throw new java.lang.IllegalArgumentException("EnumValueDescriptor is not for this type.");
      }
      if (desc.getIndex() == -1) {
        return UNRECOGNIZED;
      }
      return VALUES[desc.getIndex()];
    }

    private final int value;

    private State(int value) {
      this.value = value;
    }

    // @@protoc_insertion_point(enum_scope:google.cloud.talent.v4beta1.BatchOperationMetadata.State)
  }

  public static final int STATE_FIELD_NUMBER = 1;
  private int state_;
  /**
   *
   *
   *

   * The state of a long running operation.
   *

   *
   * .google.cloud.talent.v4beta1.BatchOperationMetadata.State state = 1;
   *
   * @return The enum numeric value on the wire for state.
   */
  @java.lang.Override
  public int getStateValue() {
    return state_;
  }
  /**
   *
   *
   *

   * The state of a long running operation.
   *

   *
   * .google.cloud.talent.v4beta1.BatchOperationMetadata.State state = 1;
   *
   * @return The state.
   */
  @java.lang.Override
  public com.google.cloud.talent.v4beta1.BatchOperationMetadata.State getState() {
    @SuppressWarnings("deprecation")
    com.google.cloud.talent.v4beta1.BatchOperationMetadata.State result =
        com.google.cloud.talent.v4beta1.BatchOperationMetadata.State.valueOf(state_);
    return result == null
        ? com.google.cloud.talent.v4beta1.BatchOperationMetadata.State.UNRECOGNIZED
        : result;
  }

  public static final int STATE_DESCRIPTION_FIELD_NUMBER = 2;
  private volatile java.lang.Object stateDescription_;
  /**
   *
   *
   *

   * More detailed information about operation state.
   *

   *
   * string state_description = 2;
   *
   * @return The stateDescription.
   */
  @java.lang.Override
  public java.lang.String getStateDescription() {
    java.lang.Object ref = stateDescription_;
    if (ref instanceof java.lang.String) {
      return (java.lang.String) ref;
    } else {
      com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
      java.lang.String s = bs.toStringUtf8();
      stateDescription_ = s;
      return s;
    }
  }
  /**
   *
   *
   *

   * More detailed information about operation state.
   *

   *
   * string state_description = 2;
   *
   * @return The bytes for stateDescription.
   */
  @java.lang.Override
  public com.google.protobuf.ByteString getStateDescriptionBytes() {
    java.lang.Object ref = stateDescription_;
    if (ref instanceof java.lang.String) {
      com.google.protobuf.ByteString b =
          com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
      stateDescription_ = b;
      return b;
    } else {
      return (com.google.protobuf.ByteString) ref;
    }
  }

  public static final int SUCCESS_COUNT_FIELD_NUMBER = 3;
  private int successCount_;
  /**
   *
   *
   *

   * Count of successful item(s) inside an operation.
   *

   *
   * int32 success_count = 3;
   *
   * @return The successCount.
   */
  @java.lang.Override
  public int getSuccessCount() {
    return successCount_;
  }

  public static final int FAILURE_COUNT_FIELD_NUMBER = 4;
  private int failureCount_;
  /**
   *
   *
   *

   * Count of failed item(s) inside an operation.
   *

   *
   * int32 failure_count = 4;
   *
   * @return The failureCount.
   */
  @java.lang.Override
  public int getFailureCount() {
    return failureCount_;
  }

  public static final int TOTAL_COUNT_FIELD_NUMBER = 5;
  private int totalCount_;
  /**
   *
   *
   *

   * Count of total item(s) inside an operation.
   *

   *
   * int32 total_count = 5;
   *
   * @return The totalCount.
   */
  @java.lang.Override
  public int getTotalCount() {
    return totalCount_;
  }

  public static final int CREATE_TIME_FIELD_NUMBER = 6;
  private com.google.protobuf.Timestamp createTime_;
  /**
   *
   *
   *

   * The time when the batch operation is created.
   *

   *
   * .google.protobuf.Timestamp create_time = 6;
   *
   * @return Whether the createTime field is set.
   */
  @java.lang.Override
  public boolean hasCreateTime() {
    return createTime_ != null;
  }
  /**
   *
   *
   *

   * The time when the batch operation is created.
   *

   *
   * .google.protobuf.Timestamp create_time = 6;
   *
   * @return The createTime.
   */
  @java.lang.Override
  public com.google.protobuf.Timestamp getCreateTime() {
    return createTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : createTime_;
  }
  /**
   *
   *
   *

   * The time when the batch operation is created.
   *

   *
   * .google.protobuf.Timestamp create_time = 6;
   */
  @java.lang.Override
  public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() {
    return getCreateTime();
  }

  public static final int UPDATE_TIME_FIELD_NUMBER = 7;
  private com.google.protobuf.Timestamp updateTime_;
  /**
   *
   *
   *

   * The time when the batch operation status is updated. The metadata and the
   * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
   * returned.
   *

   *
   * .google.protobuf.Timestamp update_time = 7;
   *
   * @return Whether the updateTime field is set.
   */
  @java.lang.Override
  public boolean hasUpdateTime() {
    return updateTime_ != null;
  }
  /**
   *
   *
   *

   * The time when the batch operation status is updated. The metadata and the
   * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
   * returned.
   *

   *
   * .google.protobuf.Timestamp update_time = 7;
   *
   * @return The updateTime.
   */
  @java.lang.Override
  public com.google.protobuf.Timestamp getUpdateTime() {
    return updateTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : updateTime_;
  }
  /**
   *
   *
   *

   * The time when the batch operation status is updated. The metadata and the
   * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
   * returned.
   *

   *
   * .google.protobuf.Timestamp update_time = 7;
   */
  @java.lang.Override
  public com.google.protobuf.TimestampOrBuilder getUpdateTimeOrBuilder() {
    return getUpdateTime();
  }

  public static final int END_TIME_FIELD_NUMBER = 8;
  private com.google.protobuf.Timestamp endTime_;
  /**
   *
   *
   *

   * The time when the batch operation is finished and
   * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
   *

   *
   * .google.protobuf.Timestamp end_time = 8;
   *
   * @return Whether the endTime field is set.
   */
  @java.lang.Override
  public boolean hasEndTime() {
    return endTime_ != null;
  }
  /**
   *
   *
   *

   * The time when the batch operation is finished and
   * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
   *

   *
   * .google.protobuf.Timestamp end_time = 8;
   *
   * @return The endTime.
   */
  @java.lang.Override
  public com.google.protobuf.Timestamp getEndTime() {
    return endTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : endTime_;
  }
  /**
   *
   *
   *

   * The time when the batch operation is finished and
   * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
   *

   *
   * .google.protobuf.Timestamp end_time = 8;
   */
  @java.lang.Override
  public com.google.protobuf.TimestampOrBuilder getEndTimeOrBuilder() {
    return getEndTime();
  }

  private byte memoizedIsInitialized = -1;

  @java.lang.Override
  public final boolean isInitialized() {
    byte isInitialized = memoizedIsInitialized;
    if (isInitialized == 1) return true;
    if (isInitialized == 0) return false;

    memoizedIsInitialized = 1;
    return true;
  }

  @java.lang.Override
  public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
    if (state_
        != com.google.cloud.talent.v4beta1.BatchOperationMetadata.State.STATE_UNSPECIFIED
            .getNumber()) {
      output.writeEnum(1, state_);
    }
    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(stateDescription_)) {
      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, stateDescription_);
    }
    if (successCount_ != 0) {
      output.writeInt32(3, successCount_);
    }
    if (failureCount_ != 0) {
      output.writeInt32(4, failureCount_);
    }
    if (totalCount_ != 0) {
      output.writeInt32(5, totalCount_);
    }
    if (createTime_ != null) {
      output.writeMessage(6, getCreateTime());
    }
    if (updateTime_ != null) {
      output.writeMessage(7, getUpdateTime());
    }
    if (endTime_ != null) {
      output.writeMessage(8, getEndTime());
    }
    getUnknownFields().writeTo(output);
  }

  @java.lang.Override
  public int getSerializedSize() {
    int size = memoizedSize;
    if (size != -1) return size;

    size = 0;
    if (state_
        != com.google.cloud.talent.v4beta1.BatchOperationMetadata.State.STATE_UNSPECIFIED
            .getNumber()) {
      size += com.google.protobuf.CodedOutputStream.computeEnumSize(1, state_);
    }
    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(stateDescription_)) {
      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, stateDescription_);
    }
    if (successCount_ != 0) {
      size += com.google.protobuf.CodedOutputStream.computeInt32Size(3, successCount_);
    }
    if (failureCount_ != 0) {
      size += com.google.protobuf.CodedOutputStream.computeInt32Size(4, failureCount_);
    }
    if (totalCount_ != 0) {
      size += com.google.protobuf.CodedOutputStream.computeInt32Size(5, totalCount_);
    }
    if (createTime_ != null) {
      size += com.google.protobuf.CodedOutputStream.computeMessageSize(6, getCreateTime());
    }
    if (updateTime_ != null) {
      size += com.google.protobuf.CodedOutputStream.computeMessageSize(7, getUpdateTime());
    }
    if (endTime_ != null) {
      size += com.google.protobuf.CodedOutputStream.computeMessageSize(8, getEndTime());
    }
    size += getUnknownFields().getSerializedSize();
    memoizedSize = size;
    return size;
  }

  @java.lang.Override
  public boolean equals(final java.lang.Object obj) {
    if (obj == this) {
      return true;
    }
    if (!(obj instanceof com.google.cloud.talent.v4beta1.BatchOperationMetadata)) {
      return super.equals(obj);
    }
    com.google.cloud.talent.v4beta1.BatchOperationMetadata other =
        (com.google.cloud.talent.v4beta1.BatchOperationMetadata) obj;

    if (state_ != other.state_) return false;
    if (!getStateDescription().equals(other.getStateDescription())) return false;
    if (getSuccessCount() != other.getSuccessCount()) return false;
    if (getFailureCount() != other.getFailureCount()) return false;
    if (getTotalCount() != other.getTotalCount()) return false;
    if (hasCreateTime() != other.hasCreateTime()) return false;
    if (hasCreateTime()) {
      if (!getCreateTime().equals(other.getCreateTime())) return false;
    }
    if (hasUpdateTime() != other.hasUpdateTime()) return false;
    if (hasUpdateTime()) {
      if (!getUpdateTime().equals(other.getUpdateTime())) return false;
    }
    if (hasEndTime() != other.hasEndTime()) return false;
    if (hasEndTime()) {
      if (!getEndTime().equals(other.getEndTime())) return false;
    }
    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
    return true;
  }

  @java.lang.Override
  public int hashCode() {
    if (memoizedHashCode != 0) {
      return memoizedHashCode;
    }
    int hash = 41;
    hash = (19 * hash) + getDescriptor().hashCode();
    hash = (37 * hash) + STATE_FIELD_NUMBER;
    hash = (53 * hash) + state_;
    hash = (37 * hash) + STATE_DESCRIPTION_FIELD_NUMBER;
    hash = (53 * hash) + getStateDescription().hashCode();
    hash = (37 * hash) + SUCCESS_COUNT_FIELD_NUMBER;
    hash = (53 * hash) + getSuccessCount();
    hash = (37 * hash) + FAILURE_COUNT_FIELD_NUMBER;
    hash = (53 * hash) + getFailureCount();
    hash = (37 * hash) + TOTAL_COUNT_FIELD_NUMBER;
    hash = (53 * hash) + getTotalCount();
    if (hasCreateTime()) {
      hash = (37 * hash) + CREATE_TIME_FIELD_NUMBER;
      hash = (53 * hash) + getCreateTime().hashCode();
    }
    if (hasUpdateTime()) {
      hash = (37 * hash) + UPDATE_TIME_FIELD_NUMBER;
      hash = (53 * hash) + getUpdateTime().hashCode();
    }
    if (hasEndTime()) {
      hash = (37 * hash) + END_TIME_FIELD_NUMBER;
      hash = (53 * hash) + getEndTime().hashCode();
    }
    hash = (29 * hash) + getUnknownFields().hashCode();
    memoizedHashCode = hash;
    return hash;
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException {
    return PARSER.parseFrom(data);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
      throws com.google.protobuf.InvalidProtocolBufferException {
    return PARSER.parseFrom(data, extensionRegistry);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      com.google.protobuf.ByteString data)
      throws com.google.protobuf.InvalidProtocolBufferException {
    return PARSER.parseFrom(data);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      com.google.protobuf.ByteString data,
      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
      throws com.google.protobuf.InvalidProtocolBufferException {
    return PARSER.parseFrom(data, extensionRegistry);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(byte[] data)
      throws com.google.protobuf.InvalidProtocolBufferException {
    return PARSER.parseFrom(data);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
      throws com.google.protobuf.InvalidProtocolBufferException {
    return PARSER.parseFrom(data, extensionRegistry);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      java.io.InputStream input) throws java.io.IOException {
    return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
      throws java.io.IOException {
    return com.google.protobuf.GeneratedMessageV3.parseWithIOException(
        PARSER, input, extensionRegistry);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseDelimitedFrom(
      java.io.InputStream input) throws java.io.IOException {
    return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseDelimitedFrom(
      java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
      throws java.io.IOException {
    return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(
        PARSER, input, extensionRegistry);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      com.google.protobuf.CodedInputStream input) throws java.io.IOException {
    return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata parseFrom(
      com.google.protobuf.CodedInputStream input,
      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
      throws java.io.IOException {
    return com.google.protobuf.GeneratedMessageV3.parseWithIOException(
        PARSER, input, extensionRegistry);
  }

  @java.lang.Override
  public Builder newBuilderForType() {
    return newBuilder();
  }

  public static Builder newBuilder() {
    return DEFAULT_INSTANCE.toBuilder();
  }

  public static Builder newBuilder(
      com.google.cloud.talent.v4beta1.BatchOperationMetadata prototype) {
    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
  }

  @java.lang.Override
  public Builder toBuilder() {
    return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
  }

  @java.lang.Override
  protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
    Builder builder = new Builder(parent);
    return builder;
  }
  /**
   *
   *
   *

   * Metadata used for long running operations returned by CTS batch APIs.
   * It's used to replace [google.longrunning.Operation.metadata][google.longrunning.Operation.metadata].
   *

   *
   * Protobuf type {@code google.cloud.talent.v4beta1.BatchOperationMetadata}
   */
  public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder<Builder>
      implements
      // @@protoc_insertion_point(builder_implements:google.cloud.talent.v4beta1.BatchOperationMetadata)
      com.google.cloud.talent.v4beta1.BatchOperationMetadataOrBuilder {
    public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
      return com.google.cloud.talent.v4beta1.CommonProto
          .internal_static_google_cloud_talent_v4beta1_BatchOperationMetadata_descriptor;
    }

    @java.lang.Override
    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
        internalGetFieldAccessorTable() {
      return com.google.cloud.talent.v4beta1.CommonProto
          .internal_static_google_cloud_talent_v4beta1_BatchOperationMetadata_fieldAccessorTable
          .ensureFieldAccessorsInitialized(
              com.google.cloud.talent.v4beta1.BatchOperationMetadata.class,
              com.google.cloud.talent.v4beta1.BatchOperationMetadata.Builder.class);
    }

    // Construct using com.google.cloud.talent.v4beta1.BatchOperationMetadata.newBuilder()
    private Builder() {}

    private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
      super(parent);
    }

    @java.lang.Override
    public Builder clear() {
      super.clear();
      state_ = 0;

      stateDescription_ = "";

      successCount_ = 0;

      failureCount_ = 0;

      totalCount_ = 0;

      if (createTimeBuilder_ == null) {
        createTime_ = null;
      } else {
        createTime_ = null;
        createTimeBuilder_ = null;
      }
      if (updateTimeBuilder_ == null) {
        updateTime_ = null;
      } else {
        updateTime_ = null;
        updateTimeBuilder_ = null;
      }
      if (endTimeBuilder_ == null) {
        endTime_ = null;
      } else {
        endTime_ = null;
        endTimeBuilder_ = null;
      }
      return this;
    }

    @java.lang.Override
    public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
      return com.google.cloud.talent.v4beta1.CommonProto
          .internal_static_google_cloud_talent_v4beta1_BatchOperationMetadata_descriptor;
    }

    @java.lang.Override
    public com.google.cloud.talent.v4beta1.BatchOperationMetadata getDefaultInstanceForType() {
      return com.google.cloud.talent.v4beta1.BatchOperationMetadata.getDefaultInstance();
    }

    @java.lang.Override
    public com.google.cloud.talent.v4beta1.BatchOperationMetadata build() {
      com.google.cloud.talent.v4beta1.BatchOperationMetadata result = buildPartial();
      if (!result.isInitialized()) {
        throw newUninitializedMessageException(result);
      }
      return result;
    }

    @java.lang.Override
    public com.google.cloud.talent.v4beta1.BatchOperationMetadata buildPartial() {
      com.google.cloud.talent.v4beta1.BatchOperationMetadata result =
          new com.google.cloud.talent.v4beta1.BatchOperationMetadata(this);
      result.state_ = state_;
      result.stateDescription_ = stateDescription_;
      result.successCount_ = successCount_;
      result.failureCount_ = failureCount_;
      result.totalCount_ = totalCount_;
      if (createTimeBuilder_ == null) {
        result.createTime_ = createTime_;
      } else {
        result.createTime_ = createTimeBuilder_.build();
      }
      if (updateTimeBuilder_ == null) {
        result.updateTime_ = updateTime_;
      } else {
        result.updateTime_ = updateTimeBuilder_.build();
      }
      if (endTimeBuilder_ == null) {
        result.endTime_ = endTime_;
      } else {
        result.endTime_ = endTimeBuilder_.build();
      }
      onBuilt();
      return result;
    }

    @java.lang.Override
    public Builder clone() {
      return super.clone();
    }

    @java.lang.Override
    public Builder setField(
        com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
      return super.setField(field, value);
    }

    @java.lang.Override
    public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
      return super.clearField(field);
    }

    @java.lang.Override
    public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
      return super.clearOneof(oneof);
    }

    @java.lang.Override
    public Builder setRepeatedField(
        com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) {
      return super.setRepeatedField(field, index, value);
    }

    @java.lang.Override
    public Builder addRepeatedField(
        com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
      return super.addRepeatedField(field, value);
    }

    @java.lang.Override
    public Builder mergeFrom(com.google.protobuf.Message other) {
      if (other instanceof com.google.cloud.talent.v4beta1.BatchOperationMetadata) {
        return mergeFrom((com.google.cloud.talent.v4beta1.BatchOperationMetadata) other);
      } else {
        super.mergeFrom(other);
        return this;
      }
    }

    public Builder mergeFrom(com.google.cloud.talent.v4beta1.BatchOperationMetadata other) {
      if (other == com.google.cloud.talent.v4beta1.BatchOperationMetadata.getDefaultInstance())
        return this;
      if (other.state_ != 0) {
        setStateValue(other.getStateValue());
      }
      if (!other.getStateDescription().isEmpty()) {
        stateDescription_ = other.stateDescription_;
        onChanged();
      }
      if (other.getSuccessCount() != 0) {
        setSuccessCount(other.getSuccessCount());
      }
      if (other.getFailureCount() != 0) {
        setFailureCount(other.getFailureCount());
      }
      if (other.getTotalCount() != 0) {
        setTotalCount(other.getTotalCount());
      }
      if (other.hasCreateTime()) {
        mergeCreateTime(other.getCreateTime());
      }
      if (other.hasUpdateTime()) {
        mergeUpdateTime(other.getUpdateTime());
      }
      if (other.hasEndTime()) {
        mergeEndTime(other.getEndTime());
      }
      this.mergeUnknownFields(other.getUnknownFields());
      onChanged();
      return this;
    }

    @java.lang.Override
    public final boolean isInitialized() {
      return true;
    }

    @java.lang.Override
    public Builder mergeFrom(
        com.google.protobuf.CodedInputStream input,
        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
        throws java.io.IOException {
      if (extensionRegistry == null) {
        throw new java.lang.NullPointerException();
      }
      try {
        boolean done = false;
        while (!done) {
          int tag = input.readTag();
          switch (tag) {
            case 0:
              done = true;
              break;
            case 8:
              {
                state_ = input.readEnum();

                break;
              } // case 8
            case 18:
              {
                stateDescription_ = input.readStringRequireUtf8();

                break;
              } // case 18
            case 24:
              {
                successCount_ = input.readInt32();

                break;
              } // case 24
            case 32:
              {
                failureCount_ = input.readInt32();

                break;
              } // case 32
            case 40:
              {
                totalCount_ = input.readInt32();

                break;
              } // case 40
            case 50:
              {
                input.readMessage(getCreateTimeFieldBuilder().getBuilder(), extensionRegistry);

                break;
              } // case 50
            case 58:
              {
                input.readMessage(getUpdateTimeFieldBuilder().getBuilder(), extensionRegistry);

                break;
              } // case 58
            case 66:
              {
                input.readMessage(getEndTimeFieldBuilder().getBuilder(), extensionRegistry);

                break;
              } // case 66
            default:
              {
                if (!super.parseUnknownField(input, extensionRegistry, tag)) {
                  done = true; // was an endgroup tag
                }
                break;
              } // default:
          } // switch (tag)
        } // while (!done)
      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
        throw e.unwrapIOException();
      } finally {
        onChanged();
      } // finally
      return this;
    }

    private int state_ = 0;
    /**
     *
     *
     *

     * The state of a long running operation.
     *

     *
     * .google.cloud.talent.v4beta1.BatchOperationMetadata.State state = 1;
     *
     * @return The enum numeric value on the wire for state.
     */
    @java.lang.Override
    public int getStateValue() {
      return state_;
    }
    /**
     *
     *
     *

     * The state of a long running operation.
     *

     *
     * .google.cloud.talent.v4beta1.BatchOperationMetadata.State state = 1;
     *
     * @param value The enum numeric value on the wire for state to set.
     * @return This builder for chaining.
     */
    public Builder setStateValue(int value) {

      state_ = value;
      onChanged();
      return this;
    }
    /**
     *
     *
     *

     * The state of a long running operation.
     *

     *
     * .google.cloud.talent.v4beta1.BatchOperationMetadata.State state = 1;
     *
     * @return The state.
     */
    @java.lang.Override
    public com.google.cloud.talent.v4beta1.BatchOperationMetadata.State getState() {
      @SuppressWarnings("deprecation")
      com.google.cloud.talent.v4beta1.BatchOperationMetadata.State result =
          com.google.cloud.talent.v4beta1.BatchOperationMetadata.State.valueOf(state_);
      return result == null
          ? com.google.cloud.talent.v4beta1.BatchOperationMetadata.State.UNRECOGNIZED
          : result;
    }
    /**
     *
     *
     *

     * The state of a long running operation.
     *

     *
     * .google.cloud.talent.v4beta1.BatchOperationMetadata.State state = 1;
     *
     * @param value The state to set.
     * @return This builder for chaining.
     */
    public Builder setState(com.google.cloud.talent.v4beta1.BatchOperationMetadata.State value) {
      if (value == null) {
        throw new NullPointerException();
      }

      state_ = value.getNumber();
      onChanged();
      return this;
    }
    /**
     *
     *
     *

     * The state of a long running operation.
     *

     *
     * .google.cloud.talent.v4beta1.BatchOperationMetadata.State state = 1;
     *
     * @return This builder for chaining.
     */
    public Builder clearState() {

      state_ = 0;
      onChanged();
      return this;
    }

    private java.lang.Object stateDescription_ = "";
    /**
     *
     *
     *

     * More detailed information about operation state.
     *

     *
     * string state_description = 2;
     *
     * @return The stateDescription.
     */
    public java.lang.String getStateDescription() {
      java.lang.Object ref = stateDescription_;
      if (!(ref instanceof java.lang.String)) {
        com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
        java.lang.String s = bs.toStringUtf8();
        stateDescription_ = s;
        return s;
      } else {
        return (java.lang.String) ref;
      }
    }
    /**
     *
     *
     *

     * More detailed information about operation state.
     *

     *
     * string state_description = 2;
     *
     * @return The bytes for stateDescription.
     */
    public com.google.protobuf.ByteString getStateDescriptionBytes() {
      java.lang.Object ref = stateDescription_;
      if (ref instanceof String) {
        com.google.protobuf.ByteString b =
            com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
        stateDescription_ = b;
        return b;
      } else {
        return (com.google.protobuf.ByteString) ref;
      }
    }
    /**
     *
     *
     *

     * More detailed information about operation state.
     *

     *
     * string state_description = 2;
     *
     * @param value The stateDescription to set.
     * @return This builder for chaining.
     */
    public Builder setStateDescription(java.lang.String value) {
      if (value == null) {
        throw new NullPointerException();
      }

      stateDescription_ = value;
      onChanged();
      return this;
    }
    /**
     *
     *
     *

     * More detailed information about operation state.
     *

     *
     * string state_description = 2;
     *
     * @return This builder for chaining.
     */
    public Builder clearStateDescription() {

      stateDescription_ = getDefaultInstance().getStateDescription();
      onChanged();
      return this;
    }
    /**
     *
     *
     *

     * More detailed information about operation state.
     *

     *
     * string state_description = 2;
     *
     * @param value The bytes for stateDescription to set.
     * @return This builder for chaining.
     */
    public Builder setStateDescriptionBytes(com.google.protobuf.ByteString value) {
      if (value == null) {
        throw new NullPointerException();
      }
      checkByteStringIsUtf8(value);

      stateDescription_ = value;
      onChanged();
      return this;
    }

    private int successCount_;
    /**
     *
     *
     *

     * Count of successful item(s) inside an operation.
     *

     *
     * int32 success_count = 3;
     *
     * @return The successCount.
     */
    @java.lang.Override
    public int getSuccessCount() {
      return successCount_;
    }
    /**
     *
     *
     *

     * Count of successful item(s) inside an operation.
     *

     *
     * int32 success_count = 3;
     *
     * @param value The successCount to set.
     * @return This builder for chaining.
     */
    public Builder setSuccessCount(int value) {

      successCount_ = value;
      onChanged();
      return this;
    }
    /**
     *
     *
     *

     * Count of successful item(s) inside an operation.
     *

     *
     * int32 success_count = 3;
     *
     * @return This builder for chaining.
     */
    public Builder clearSuccessCount() {

      successCount_ = 0;
      onChanged();
      return this;
    }

    private int failureCount_;
    /**
     *
     *
     *

     * Count of failed item(s) inside an operation.
     *

     *
     * int32 failure_count = 4;
     *
     * @return The failureCount.
     */
    @java.lang.Override
    public int getFailureCount() {
      return failureCount_;
    }
    /**
     *
     *
     *

     * Count of failed item(s) inside an operation.
     *

     *
     * int32 failure_count = 4;
     *
     * @param value The failureCount to set.
     * @return This builder for chaining.
     */
    public Builder setFailureCount(int value) {

      failureCount_ = value;
      onChanged();
      return this;
    }
    /**
     *
     *
     *

     * Count of failed item(s) inside an operation.
     *

     *
     * int32 failure_count = 4;
     *
     * @return This builder for chaining.
     */
    public Builder clearFailureCount() {

      failureCount_ = 0;
      onChanged();
      return this;
    }

    private int totalCount_;
    /**
     *
     *
     *

     * Count of total item(s) inside an operation.
     *

     *
     * int32 total_count = 5;
     *
     * @return The totalCount.
     */
    @java.lang.Override
    public int getTotalCount() {
      return totalCount_;
    }
    /**
     *
     *
     *

     * Count of total item(s) inside an operation.
     *

     *
     * int32 total_count = 5;
     *
     * @param value The totalCount to set.
     * @return This builder for chaining.
     */
    public Builder setTotalCount(int value) {

      totalCount_ = value;
      onChanged();
      return this;
    }
    /**
     *
     *
     *

     * Count of total item(s) inside an operation.
     *

     *
     * int32 total_count = 5;
     *
     * @return This builder for chaining.
     */
    public Builder clearTotalCount() {

      totalCount_ = 0;
      onChanged();
      return this;
    }

    private com.google.protobuf.Timestamp createTime_;
    private com.google.protobuf.SingleFieldBuilderV3<
            com.google.protobuf.Timestamp,
            com.google.protobuf.Timestamp.Builder,
            com.google.protobuf.TimestampOrBuilder>
        createTimeBuilder_;
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     *
     * @return Whether the createTime field is set.
     */
    public boolean hasCreateTime() {
      return createTimeBuilder_ != null || createTime_ != null;
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     *
     * @return The createTime.
     */
    public com.google.protobuf.Timestamp getCreateTime() {
      if (createTimeBuilder_ == null) {
        return createTime_ == null
            ? com.google.protobuf.Timestamp.getDefaultInstance()
            : createTime_;
      } else {
        return createTimeBuilder_.getMessage();
      }
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     */
    public Builder setCreateTime(com.google.protobuf.Timestamp value) {
      if (createTimeBuilder_ == null) {
        if (value == null) {
          throw new NullPointerException();
        }
        createTime_ = value;
        onChanged();
      } else {
        createTimeBuilder_.setMessage(value);
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     */
    public Builder setCreateTime(com.google.protobuf.Timestamp.Builder builderForValue) {
      if (createTimeBuilder_ == null) {
        createTime_ = builderForValue.build();
        onChanged();
      } else {
        createTimeBuilder_.setMessage(builderForValue.build());
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     */
    public Builder mergeCreateTime(com.google.protobuf.Timestamp value) {
      if (createTimeBuilder_ == null) {
        if (createTime_ != null) {
          createTime_ =
              com.google.protobuf.Timestamp.newBuilder(createTime_).mergeFrom(value).buildPartial();
        } else {
          createTime_ = value;
        }
        onChanged();
      } else {
        createTimeBuilder_.mergeFrom(value);
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     */
    public Builder clearCreateTime() {
      if (createTimeBuilder_ == null) {
        createTime_ = null;
        onChanged();
      } else {
        createTime_ = null;
        createTimeBuilder_ = null;
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     */
    public com.google.protobuf.Timestamp.Builder getCreateTimeBuilder() {

      onChanged();
      return getCreateTimeFieldBuilder().getBuilder();
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     */
    public com.google.protobuf.TimestampOrBuilder getCreateTimeOrBuilder() {
      if (createTimeBuilder_ != null) {
        return createTimeBuilder_.getMessageOrBuilder();
      } else {
        return createTime_ == null
            ? com.google.protobuf.Timestamp.getDefaultInstance()
            : createTime_;
      }
    }
    /**
     *
     *
     *

     * The time when the batch operation is created.
     *

     *
     * .google.protobuf.Timestamp create_time = 6;
     */
    private com.google.protobuf.SingleFieldBuilderV3<
            com.google.protobuf.Timestamp,
            com.google.protobuf.Timestamp.Builder,
            com.google.protobuf.TimestampOrBuilder>
        getCreateTimeFieldBuilder() {
      if (createTimeBuilder_ == null) {
        createTimeBuilder_ =
            new com.google.protobuf.SingleFieldBuilderV3<
                com.google.protobuf.Timestamp,
                com.google.protobuf.Timestamp.Builder,
                com.google.protobuf.TimestampOrBuilder>(
                getCreateTime(), getParentForChildren(), isClean());
        createTime_ = null;
      }
      return createTimeBuilder_;
    }

    private com.google.protobuf.Timestamp updateTime_;
    private com.google.protobuf.SingleFieldBuilderV3<
            com.google.protobuf.Timestamp,
            com.google.protobuf.Timestamp.Builder,
            com.google.protobuf.TimestampOrBuilder>
        updateTimeBuilder_;
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     *
     * @return Whether the updateTime field is set.
     */
    public boolean hasUpdateTime() {
      return updateTimeBuilder_ != null || updateTime_ != null;
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     *
     * @return The updateTime.
     */
    public com.google.protobuf.Timestamp getUpdateTime() {
      if (updateTimeBuilder_ == null) {
        return updateTime_ == null
            ? com.google.protobuf.Timestamp.getDefaultInstance()
            : updateTime_;
      } else {
        return updateTimeBuilder_.getMessage();
      }
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     */
    public Builder setUpdateTime(com.google.protobuf.Timestamp value) {
      if (updateTimeBuilder_ == null) {
        if (value == null) {
          throw new NullPointerException();
        }
        updateTime_ = value;
        onChanged();
      } else {
        updateTimeBuilder_.setMessage(value);
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     */
    public Builder setUpdateTime(com.google.protobuf.Timestamp.Builder builderForValue) {
      if (updateTimeBuilder_ == null) {
        updateTime_ = builderForValue.build();
        onChanged();
      } else {
        updateTimeBuilder_.setMessage(builderForValue.build());
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     */
    public Builder mergeUpdateTime(com.google.protobuf.Timestamp value) {
      if (updateTimeBuilder_ == null) {
        if (updateTime_ != null) {
          updateTime_ =
              com.google.protobuf.Timestamp.newBuilder(updateTime_).mergeFrom(value).buildPartial();
        } else {
          updateTime_ = value;
        }
        onChanged();
      } else {
        updateTimeBuilder_.mergeFrom(value);
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     */
    public Builder clearUpdateTime() {
      if (updateTimeBuilder_ == null) {
        updateTime_ = null;
        onChanged();
      } else {
        updateTime_ = null;
        updateTimeBuilder_ = null;
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     */
    public com.google.protobuf.Timestamp.Builder getUpdateTimeBuilder() {

      onChanged();
      return getUpdateTimeFieldBuilder().getBuilder();
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     */
    public com.google.protobuf.TimestampOrBuilder getUpdateTimeOrBuilder() {
      if (updateTimeBuilder_ != null) {
        return updateTimeBuilder_.getMessageOrBuilder();
      } else {
        return updateTime_ == null
            ? com.google.protobuf.Timestamp.getDefaultInstance()
            : updateTime_;
      }
    }
    /**
     *
     *
     *

     * The time when the batch operation status is updated. The metadata and the
     * [update_time][google.cloud.talent.v4beta1.BatchOperationMetadata.update_time] is refreshed every minute otherwise cached data is
     * returned.
     *

     *
     * .google.protobuf.Timestamp update_time = 7;
     */
    private com.google.protobuf.SingleFieldBuilderV3<
            com.google.protobuf.Timestamp,
            com.google.protobuf.Timestamp.Builder,
            com.google.protobuf.TimestampOrBuilder>
        getUpdateTimeFieldBuilder() {
      if (updateTimeBuilder_ == null) {
        updateTimeBuilder_ =
            new com.google.protobuf.SingleFieldBuilderV3<
                com.google.protobuf.Timestamp,
                com.google.protobuf.Timestamp.Builder,
                com.google.protobuf.TimestampOrBuilder>(
                getUpdateTime(), getParentForChildren(), isClean());
        updateTime_ = null;
      }
      return updateTimeBuilder_;
    }

    private com.google.protobuf.Timestamp endTime_;
    private com.google.protobuf.SingleFieldBuilderV3<
            com.google.protobuf.Timestamp,
            com.google.protobuf.Timestamp.Builder,
            com.google.protobuf.TimestampOrBuilder>
        endTimeBuilder_;
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     *
     * @return Whether the endTime field is set.
     */
    public boolean hasEndTime() {
      return endTimeBuilder_ != null || endTime_ != null;
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     *
     * @return The endTime.
     */
    public com.google.protobuf.Timestamp getEndTime() {
      if (endTimeBuilder_ == null) {
        return endTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : endTime_;
      } else {
        return endTimeBuilder_.getMessage();
      }
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     */
    public Builder setEndTime(com.google.protobuf.Timestamp value) {
      if (endTimeBuilder_ == null) {
        if (value == null) {
          throw new NullPointerException();
        }
        endTime_ = value;
        onChanged();
      } else {
        endTimeBuilder_.setMessage(value);
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     */
    public Builder setEndTime(com.google.protobuf.Timestamp.Builder builderForValue) {
      if (endTimeBuilder_ == null) {
        endTime_ = builderForValue.build();
        onChanged();
      } else {
        endTimeBuilder_.setMessage(builderForValue.build());
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     */
    public Builder mergeEndTime(com.google.protobuf.Timestamp value) {
      if (endTimeBuilder_ == null) {
        if (endTime_ != null) {
          endTime_ =
              com.google.protobuf.Timestamp.newBuilder(endTime_).mergeFrom(value).buildPartial();
        } else {
          endTime_ = value;
        }
        onChanged();
      } else {
        endTimeBuilder_.mergeFrom(value);
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     */
    public Builder clearEndTime() {
      if (endTimeBuilder_ == null) {
        endTime_ = null;
        onChanged();
      } else {
        endTime_ = null;
        endTimeBuilder_ = null;
      }

      return this;
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     */
    public com.google.protobuf.Timestamp.Builder getEndTimeBuilder() {

      onChanged();
      return getEndTimeFieldBuilder().getBuilder();
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     */
    public com.google.protobuf.TimestampOrBuilder getEndTimeOrBuilder() {
      if (endTimeBuilder_ != null) {
        return endTimeBuilder_.getMessageOrBuilder();
      } else {
        return endTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : endTime_;
      }
    }
    /**
     *
     *
     *

     * The time when the batch operation is finished and
     * [google.longrunning.Operation.done][google.longrunning.Operation.done] is set to `true`.
     *

     *
     * .google.protobuf.Timestamp end_time = 8;
     */
    private com.google.protobuf.SingleFieldBuilderV3<
            com.google.protobuf.Timestamp,
            com.google.protobuf.Timestamp.Builder,
            com.google.protobuf.TimestampOrBuilder>
        getEndTimeFieldBuilder() {
      if (endTimeBuilder_ == null) {
        endTimeBuilder_ =
            new com.google.protobuf.SingleFieldBuilderV3<
                com.google.protobuf.Timestamp,
                com.google.protobuf.Timestamp.Builder,
                com.google.protobuf.TimestampOrBuilder>(
                getEndTime(), getParentForChildren(), isClean());
        endTime_ = null;
      }
      return endTimeBuilder_;
    }

    @java.lang.Override
    public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
      return super.setUnknownFields(unknownFields);
    }

    @java.lang.Override
    public final Builder mergeUnknownFields(
        final com.google.protobuf.UnknownFieldSet unknownFields) {
      return super.mergeUnknownFields(unknownFields);
    }

    // @@protoc_insertion_point(builder_scope:google.cloud.talent.v4beta1.BatchOperationMetadata)
  }

  // @@protoc_insertion_point(class_scope:google.cloud.talent.v4beta1.BatchOperationMetadata)
  private static final com.google.cloud.talent.v4beta1.BatchOperationMetadata DEFAULT_INSTANCE;

  static {
    DEFAULT_INSTANCE = new com.google.cloud.talent.v4beta1.BatchOperationMetadata();
  }

  public static com.google.cloud.talent.v4beta1.BatchOperationMetadata getDefaultInstance() {
    return DEFAULT_INSTANCE;
  }

  private static final com.google.protobuf.Parser<BatchOperationMetadata> PARSER =
      new com.google.protobuf.AbstractParser<BatchOperationMetadata>() {
        @java.lang.Override
        public BatchOperationMetadata parsePartialFrom(
            com.google.protobuf.CodedInputStream input,
            com.google.protobuf.ExtensionRegistryLite extensionRegistry)
            throws com.google.protobuf.InvalidProtocolBufferException {
          Builder builder = newBuilder();
          try {
            builder.mergeFrom(input, extensionRegistry);
          } catch (com.google.protobuf.InvalidProtocolBufferException e) {
            throw e.setUnfinishedMessage(builder.buildPartial());
          } catch (com.google.protobuf.UninitializedMessageException e) {
            throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
          } catch (java.io.IOException e) {
            throw new com.google.protobuf.InvalidProtocolBufferException(e)
                .setUnfinishedMessage(builder.buildPartial());
          }
          return builder.buildPartial();
        }
      };

  public static com.google.protobuf.Parser<BatchOperationMetadata> parser() {
    return PARSER;
  }

  @java.lang.Override
  public com.google.protobuf.Parser<BatchOperationMetadata> getParserForType() {
    return PARSER;
  }

  @java.lang.Override
  public com.google.cloud.talent.v4beta1.BatchOperationMetadata getDefaultInstanceForType() {
    return DEFAULT_INSTANCE;
  }
}


text:
@interface OCNumberSolution : OCSolution

@end

redpajama_set_name: RedPajamaGithub


text:
# Mapping

The Elasticsearch bundle requires document mapping definitions to create the correct index schema and be able to convert data to objects and vice versa - think Doctrine.

## Document class annotations

Elasticsearch type mappings are defined using annotations within document entity classes that implement DocumentInterface:
```php
<?php
namespace AppBundle\Document;

use Sineflow\ElasticsearchBundle\Document\AbstractDocument;
use Sineflow\ElasticsearchBundle\Annotation as ES;

/**
 * @ES\Document(type="product")
 */
class Product extends AbstractDocument
{
    /**
     * @var string
     *
     * @ES\Property(name="title", type="string")
     */
    public $title;
}
```

> Make sure your document classes directly implement DocumentInterface or extend AbstractDocument.

#### Document annotation

The class representing a document must be annotated as `@ES\Document`. The following properties are supported inside that annotation:

- `type` Specifies the name of the Elasticsearch type this class represents. The parameter is optional and, if not supplied, the bundle will use the lowercased class name as such.

- `repositoryClass` Allows you to specify a specific repository class for this document. If not specified, the default repository class is used.
```
repositoryClass="AppBundle\Document\Repository\ProductRepository"
```

- `parent` Allows you to specify a parent type ([more info here](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-parent-field.html))
```
parent="AppBundle:ParentDoc"
```

- `options` Allows to specify any type option supported by Elasticsearch, such as [\_all](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-all-field.html), [dynamic_templates](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html), [dynamic_date_formats](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html#date-detection), etc.

### Property annotation

Each field within the document is specified using the `@ES\Property` annotation. The following properties are supported inside that annotation:

- `name` Specifies the name of the field (required).

- `type` Specifies the type of the field in Elasticsearch (required).

- `multilanguage` A flag that specifies whether the field will be multilanguage. For more information, see [declaring multilanguage properties](#mlproperties).
```
multilanguage=true
```

- `objectName` When the field type is `object` or `nested`, this property must be specified, as it specifies which class defines the (nested) object.
```
objectName="AppBundle:ObjAlias"
```

- `multiple` Relevant only for `object` and `nested` fields. It specifies whether the field contains a single object or multiple ones.
```
multiple=true
```

- `options` An array of literal options, sent to Elasticsearch as they are. The only exception is with multilanguage properties, where further processing is applied.
```
options={
    "analyzer":"my_special_analyzer",
    "null_value":0
}
```

### Multilanguage properties

Sometimes, you may have a field that is available in more than one language. This is declared like this:

```
    /**
     * @ES\Property(
     *  name="name",
     *  type="string",
     *  multilanguage=true,
     *  options={
     *      "analyzer":"{lang}_analyzer",
     *  }
     * )
     */
    public $name;
```
> Note the use of `{lang}` placeholder in the options.

When you have a property definition like that, there will not be a field `name` in your index, but instead there will be `name-en`, `name-fr`, `name-de`, etc. where the suffixes are taken from the available languages in your application. You may also use the special `{lang}` placeholder in the options array, as often you would need to specify different analyzers, depending on the language. For more information on how that works, see [multilanguage support](i18n.md).

## Object class annotation

Object classes are almost the same as document classes:

```php
<?php
namespace AppBundle\Document;

use Sineflow\ElasticsearchBundle\Document\ObjectInterface;
use Sineflow\ElasticsearchBundle\Annotation as ES;

/**
 * @ES\Object
 */
class ObjAlias implements ObjectInterface
{
    /**
     * @var string
     *
     * @ES\Property(name="title", type="string")
     */
    public $title;
}
```

The difference with document classes is that the class must implement `ObjectInterface` and be annotated as `@ES\Object`. The mapping of the object properties follows the same rules as the one for the document properties.

More info about mapping is in the [elasticsearch mapping documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html)


text:
package main

import (
	"golang.org/x/net/context"
	"google.golang.org/appengine/log"

	"bytes"
	"fmt"
	"math"
	"sort"
	"strconv"
	"strings"
)

var (
	invalidNumberOfMarks = fmt.Errorf("Invalid number of marks.")
	invalidRangeOfMarks  = fmt.Errorf("Invalid range of marks.")
)

type termType int

const (
	Quarter termType = iota + 1
	Semester
	EndOfYear
	EndOfYearGpa
	Midterm
	WeekS1
	WeekS2
)

var termStrings = map[termType]string{
	Quarter:      "Quarter",
	Semester:     "Semester",
	EndOfYear:    "End of Year",
	EndOfYearGpa: "End of Year (GPA)",
	Midterm:      "Midterm",
	WeekS1:       "S1 Week",
	WeekS2:       "S2 Week",
}

type Term struct {
	Typ termType
	N   int
}

var terms = []Term{
	{Quarter, 1},
	{Quarter, 2},
	{Midterm, 1},
	{Semester, 1},
	{Quarter, 3},
	{Quarter, 4},
	{Midterm, 2},
	{Semester, 2},
	{EndOfYear, 0},
}

func parseTerm(s string) (Term, error) {
	cs := strings.Split(s, "|")
	if len(cs) != 2 {
		return Term{}, fmt.Errorf("Invalid term: %s", s)
	}

	typeNumber, err := strconv.Atoi(cs[0])
	if err != nil {
		return Term{}, fmt.Errorf("Invalid term: %s", s)
	}
	typ := termType(typeNumber)
	_, ok := termStrings[typ]
	if !ok {
		return Term{}, fmt.Errorf("Invalid term: %s", s)
	}

	n, err := strconv.Atoi(cs[1])
	if err != nil {
		return Term{}, fmt.Errorf("Invalid term: %s", s)
	}

	return Term{typ, n}, nil
}

// Value is used in forms
func (t Term) Value() string {
	return fmt.Sprintf("%d|%d", t.Typ, t.N)
}

// Used in reportcards template
func (t Term) ShowBehaviorReportCard() bool {
	return t.Typ == Quarter || t.Typ == Midterm
}

func (t Term) String() string {
	s, ok := termStrings[t.Typ]
	if !ok {
		panic(fmt.Sprintf("Invalid term type: %d", t.Typ))
	}
	if t.N == 0 {
		return s
	}
	return fmt.Sprintf("%s %d", s, t.N)
}

type semesterType int

const (
	QuarterSemester semesterType = iota + 1
	MidtermSemester
)

var semesterTypes = []semesterType{
	QuarterSemester,
	MidtermSemester,
}

var semesterTypeStrings = map[semesterType]string{
	QuarterSemester: "Quarter",
	MidtermSemester: "Midterm",
}

func (st semesterType) Value() int {
	return int(st)
}

func (st semesterType) String() string {
	s, ok := semesterTypeStrings[st]
	if !ok {
		panic(fmt.Sprintf("Invalid semesterType type: %d", st))
	}
	return s
}

type colDescription struct {
	Name        string
	Max         float64
	FinalWeight float64
	Editable    bool
}

type studentMarks map[Term][]float64

type gradingSystem interface {
	description(c context.Context, sy string, term Term) []colDescription
	evaluate(c context.Context, studentID, sy string, term Term, marks studentMarks) error
	get100(term Term, marks studentMarks) float64
	getExam(term Term, marks studentMarks) float64
	ready(term Term, marks studentMarks) bool

	quarterWeight() float64
	semesterWeight() float64
	subjectInAverage() bool
	displayName() string
	inStream(stream string) bool
}

type letterType struct {
	letter      string
	description string
	minMark     float64
}

func getGradingSystem(c context.Context, sy, class, subjectname string) gradingSystem {
	if subjectname == "Behavior" {
		return behaviorGradingSystem{}
	}
	if subjectname == "Attendance" {
		return attendanceGradingSystem{}
	}
	// TODO: err
	subject, err := getSubject(c, sy, class, subjectname)
	if err != nil {
		log.Errorf(c, "Could not get subject %s %s %s: %s", sy, class, subjectname, err)
		return nil
	}

	var qWeight, sWeight float64
	found := false
	for _, classSetting := range getClassSettings(c, sy) {
		if classSetting.Class != class {
			continue
		}

		qWeight = classSetting.QuarterWeight
		if qWeight > 50 {
			qWeight = 50
		}
		if qWeight < 0 {
			qWeight = 0
		}

		sWeight = 100 - qWeight*2

		found = true
		break
	}
	if !found {
		log.Errorf(c, "Could not get class settings: %s %s", sy, class)
		return nil
	}

	if qWeight == 0 {
		sWeight = 100
	} else if sWeight == 0 {
		qWeight = 50
	}

	if sWeight+qWeight*2 != 100 {
		log.Errorf(c, "Invalid weights for class: %s %s", sy, class)
		return nil
	}

	subject.qWeight = qWeight
	subject.sWeight = sWeight

	return subject
}

type letterSystem []letterType

var ABCDF = letterSystem{
	{"A", "Excellent", 90.0},
	{"B", "Good", 80.0},
	{"C", "Satisfactory", 70.0},
	{"D", "Needs Improvement", 60.0},
	{"F", "Fail Insufficient", 0.0},
}

var OVSLU = letterSystem{
	{"O", "Outstanding", 90.0},
	{"V", "Very Good", 80.0},
	{"S", "Satisfactory", 70.0},
	{"L", "Limited Progress", 60.0},
	{"U", "Unsatisfactory", 0.0},
}

var letterSystemMap = map[string]letterSystem{
	"ABCDF": ABCDF,
	"OVSLU": OVSLU,
}

// String returns a description of the letter system
func (ls letterSystem) String() string {
	buf := new(bytes.Buffer)
	previousMin := 101.0
	for i, l := range ls {
		if i > 0 {
			fmt.Fprint(buf, " - ")
		}
		fmt.Fprintf(buf, "%s: %s (%.0f-%.0f)",
			l.letter, l.description, l.minMark, previousMin-1)
		previousMin = l.minMark
	}
	return buf.String()
}

func (ls letterSystem) getLetter(mark float64) string {
	if math.IsNaN(mark) {
		return "N/A"
	}

	for _, l := range ls {
		if mark >= l.minMark {
			return l.letter
		}
	}
	// something wrong with the letterSystem
	return "Error"
}

func getLetterSystem(c context.Context, sy, class string) letterSystem {
	for _, setting := range getClassSettings(c, sy) {
		if setting.Class != class {
			continue
		}

		if ls, ok := letterSystemMap[setting.LetterSystem]; ok {
			return ls
		} else {
			log.Errorf(c, "Invalid letter system of class %s, SY: %s, letter system: %s",
				class, sy, setting.LetterSystem)
			return ABCDF
		}
	}

	// should never happen
	log.Errorf(c, "Could not find letter system of class %s, SY: %s", class, sy)
	return ABCDF
}

func classWeights(c context.Context, sy, class string) (quarter, semester float64) {
	for _, setting := range getClassSettings(c, sy) {
		if setting.Class != class {
			continue
		}

		qw := setting.QuarterWeight
		if qw >= 0 && qw <= 50 {
			return qw, 100 - qw*2
		} else {
			log.Errorf(c, "Invalid quarter weight of class %s, SY: %s, quarter weight: %d",
				class, sy, qw)
			return 40, 20
		}
	}

	// should never happen
	log.Errorf(c, "Could not find quarter weight of class %s, SY: %s", class, sy)
	return 40, 20
}

type gradingColumnType int

const (
	noGrading gradingColumnType = iota
	directGrading
	quizGrading
	groupGrading
)

var gradingColumnTypeStrings = map[gradingColumnType]string{
	noGrading:     "Unused",
	directGrading: "Direct",
	quizGrading:   "Quizzes",
	groupGrading:  "Group",
}

type gradingColumn struct {
	Type        gradingColumnType
	Name        string
	Max         float64
	FinalWeight float64

	NumQuizzes  int
	BestQuizzes int
	GroupName   string
}

type Subject struct {
	// TODO: add SY, Class
	ShortName          string
	Description        string
	Stream             string
	CalculateInAverage bool
	S1Credits          float64
	S2Credits          float64
	SemesterType       semesterType
	MidtermWeeksS1     int
	TotalWeeksS1       int
	MidtermWeeksS2     int
	TotalWeeksS2       int

	WeeklyGradingColumns   []gradingColumn
	QuarterGradingColumns  []gradingColumn
	SemesterGradingColumns []gradingColumn

	qWeight float64 `datastore:"-"`
	sWeight float64 `datastore:"-"`
}

func (s Subject) description(c context.Context, sy string, term Term) []colDescription {
	// TODO: check total max = 100
	if term.Typ == Quarter {
		if s.SemesterType != QuarterSemester {
			return nil
		}
		var cols []colDescription
		for _, gcol := range s.QuarterGradingColumns {
			if gcol.Type == directGrading {
				cols = append(cols, colDescription{gcol.Name, gcol.Max, gcol.FinalWeight, true})
			} else if gcol.Type == quizGrading {
				cols = append(cols, quizColDescriptions(gcol, false)...)
			} else if gcol.Type == groupGrading {
				cols = append(cols, groupColDescriptions(c, sy, gcol, false, false)...)
			}
		}
		cols = append(cols, colDescription{"Quarter Mark", 100, math.NaN(), false})
		cols = append(cols, colDescription{"Quarter %", s.qWeight, math.NaN(), false})
		return cols

	} else if term.Typ == WeekS1 || term.Typ == WeekS2 {
		if s.SemesterType != MidtermSemester {
			return nil
		}
		if (term.Typ == WeekS1 && term.N > s.TotalWeeksS1) ||
			(term.Typ == WeekS2 && term.N > s.TotalWeeksS2) {
			return nil
		}
		var cols []colDescription
		for _, gcol := range s.WeeklyGradingColumns {
			if gcol.Type == directGrading {
				cols = append(cols, colDescription{gcol.Name, gcol.Max, math.NaN(), true})
			} else if gcol.Type == groupGrading {
				cols = append(cols, groupColDescriptions(c, sy, gcol, false, false)...)
			}
		}
		return cols

	} else if term.Typ == Midterm {
		if s.SemesterType != MidtermSemester {
			return nil
		}
		var cols []colDescription

		for _, gcol := range s.WeeklyGradingColumns {
			if gcol.Type == directGrading {
				cols = append(cols, colDescription{gcol.Name, 100.0, 100.0, false})
			} else if gcol.Type == groupGrading {
				cols = append(cols, groupColDescriptions(c, sy, gcol, true, true)...)
			}
		}

		for _, gcol := range s.QuarterGradingColumns {
			if gcol.Type == directGrading {
				cols = append(cols, colDescription{gcol.Name, gcol.Max, 100.0, true})
			} else if gcol.Type == quizGrading {
				cols = append(cols, quizColDescriptions(gcol, true)...)
			} else if gcol.Type == groupGrading {
				cols = append(cols, groupColDescriptions(c, sy, gcol, false, true)...)
			}
		}
		cols = append(cols, colDescription{"Midterm Mark", 100, math.NaN(), false})
		return cols

	} else if term.Typ == Semester {
		var cols []colDescription
		if s.SemesterType == MidtermSemester {
			for _, gcol := range s.WeeklyGradingColumns {
				if gcol.Type == directGrading {
					cols = append(cols, colDescription{gcol.Name, gcol.FinalWeight, gcol.FinalWeight, false})
				} else if gcol.Type == groupGrading {
					cols = append(cols, groupColDescriptions(c, sy, gcol, true, false)...)
				}
			}

			for _, gcol := range s.QuarterGradingColumns {
				if gcol.Type == directGrading {
					cols = append(cols, colDescription{gcol.Name, gcol.Max, gcol.FinalWeight, false})
				} else if gcol.Type == quizGrading {
					cols = append(cols, colDescription{gcol.Name,
						gcol.Max * float64(gcol.BestQuizzes), gcol.FinalWeight, false})
				} else if gcol.Type == groupGrading {
					cols = append(cols, groupColDescriptions(c, sy, gcol, true, false)...)
				}
			}
		}
		for _, gcol := range s.SemesterGradingColumns {
			if gcol.Type == directGrading {
				cols = append(cols, colDescription{gcol.Name, gcol.Max, gcol.FinalWeight, true})
			} else if gcol.Type == quizGrading {
				cols = append(cols, quizColDescriptions(gcol, false)...)
			} else if gcol.Type == groupGrading {
				cols = append(cols, groupColDescriptions(c, sy, gcol, false, false)...)
			}
		}
		if s.SemesterType == QuarterSemester {
			cols = append(cols, colDescription{"Semester %", s.sWeight, math.NaN(), false})
		}
		cols = append(cols, colDescription{"Semester Mark", 100, math.NaN(), false})
		return cols

	} else if term.Typ == EndOfYear {
		return []colDescription{
			{"Semester 1 %", 100, 50, false},
			{"Semester 2 %", 100, 50, false},
			{"Final mark", 100, math.NaN(), false},
		}
	} else {
		panic(fmt.Sprintf("Invalid term type: %d", term.Typ))
	}
}

func quizColDescriptions(gc gradingColumn, midterm bool) []colDescription {
	if gc.NumQuizzes < 0 || gc.BestQuizzes > gc.NumQuizzes {
		// invalid, so return empty columns
		return []colDescription{}
	}

	var cols []colDescription
	for i := 0; i < gc.NumQuizzes; i++ {
		cols = append(cols, colDescription{
			fmt.Sprintf("%s %d", gc.Name, i+1),
			gc.Max,
			math.NaN(),
			true,
		})
	}

	var weight float64
	if midterm {
		weight = 100.0
	} else {
		weight = gc.FinalWeight
	}

	cols = append(cols, colDescription{
		fmt.Sprintf("%s (Best %d)", gc.Name, gc.BestQuizzes),
		gc.Max * float64(gc.BestQuizzes),
		weight,
		false,
	})

	return cols
}

func groupColDescriptions(c context.Context, sy string, gc gradingColumn, onlyTotalColumn, midterm bool) []colDescription {
	group, err := getGradingGroup(c, sy, gc.GroupName)
	if err != nil {
		log.Errorf(c, "Could not get grading group %s %s: %s", sy, gc.GroupName, err)
		return []colDescription{}
	}

	var cols []colDescription
	totalMax := 0.0
	for _, col := range group.Columns {
		totalMax += col.Max
		if !onlyTotalColumn {
			cols = append(cols, colDescription{
				fmt.Sprintf("(%s) %s", gc.Name, col.Name),
				col.Max,
				math.NaN(),
				true,
			})
		}
	}

	var weight float64
	if midterm {
		weight = 100.0
	} else {
		weight = gc.FinalWeight
	}

	cols = append(cols, colDescription{
		gc.Name,
		totalMax,
		weight,
		false,
	})

	return cols
}

func (s Subject) evaluate(c context.Context, studentID, sy string, term Term, marks studentMarks) error {
	var err error

	m, ok := marks[term]
	if !ok && (term.Typ == WeekS1 || term.Typ == WeekS2) {
		m, err = getWeeklyStudentMarks(c, studentID, sy, s.ShortName, term, s)
		if err != nil {
			return err
		}
	}

	cols := s.description(c, sy, term)

	switch {
	case m == nil: // first time to evaluate it
		m = make([]float64, len(cols))
		for i, _ := range cols {
			m[i] = math.NaN()
		}
	case len(m) != len(cols): // sanity check
		err = invalidNumberOfMarks
		m = make([]float64, len(cols))
		for i, _ := range cols {
			m[i] = math.NaN()
		}
	}

	// more sanity checks
	for i, d := range cols {
		if m[i] < 0 || m[i] > d.Max {
			m[i] = math.NaN()
			if err == nil {
				err = invalidRangeOfMarks
			}
		}
	}

	marks[term] = m

	if term.Typ == Quarter {
		if s.SemesterType != QuarterSemester {
			return nil
		}
		total100 := 0.0
		nextMark := 0
		for _, gcol := range s.QuarterGradingColumns {
			if gcol.Type == directGrading {
				total100 += m[nextMark] * gcol.FinalWeight / gcol.Max
				nextMark++
			} else if gcol.Type == quizGrading {
				totalQuiz := quizSum(gcol.BestQuizzes, m[nextMark:nextMark+gcol.NumQuizzes])
				nextMark += gcol.NumQuizzes
				m[nextMark] = totalQuiz
				nextMark++

				total100 += totalQuiz * gcol.FinalWeight /
					(float64(gcol.BestQuizzes) * gcol.Max)
			} else if gcol.Type == groupGrading {
				group, err := getGradingGroup(c, sy, gcol.GroupName)
				if err != nil {
					// TODO: handle error
					continue
				}

				totalMax := 0.0
				for _, col := range group.Columns {
					totalMax += col.Max
				}

				totalGroup := sumMarks(m[nextMark : nextMark+len(group.Columns)]...)
				nextMark += len(group.Columns)
				m[nextMark] = totalGroup
				nextMark++

				total100 += totalGroup / totalMax * gcol.Max
			}
		}

		if len(s.QuarterGradingColumns) == 0 {
			total100 = math.NaN()
		}

		// Quarter mark
		m[nextMark] = total100
		nextMark++

		// Quarter %
		m[nextMark] = total100 * s.qWeight / 100.0

	} else if term.Typ == WeekS1 || term.Typ == WeekS2 {
		nextMark := 0
		for _, gcol := range s.WeeklyGradingColumns {
			if gcol.Type == directGrading {
				nextMark++
			} else if gcol.Type == groupGrading {
				group, err := getGradingGroup(c, sy, gcol.GroupName)
				if err != nil {
					return err
				}

				totalGroup := sumMarks(m[nextMark : nextMark+len(group.Columns)]...)
				nextMark += len(group.Columns)
				m[nextMark] = totalGroup
				nextMark++

			}
		}
	} else if term.Typ == Midterm {
		if s.SemesterType != MidtermSemester {
			return nil
		}
		totalMark := 0.0
		totalWeight := 0.0
		nextMark := 0
		if len(s.WeeklyGradingColumns) > 0 {
			var weekMarks [][]float64
			if term.N == 1 {
				for i := 1; i <= s.MidtermWeeksS1; i++ {
					wm, err := getWeeklyStudentMarks(c, studentID, sy, s.ShortName, Term{WeekS1, i}, s)
					if err != nil {
						weekMarks = append(weekMarks, nil)
						continue
					}
					weekMarks = append(weekMarks, wm)
				}
			} else if term.N == 2 {
				for i := 1; i <= s.MidtermWeeksS2; i++ {
					wm, err := getWeeklyStudentMarks(c, studentID, sy, s.ShortName, Term{WeekS2, i}, s)
					if err != nil {
						weekMarks = append(weekMarks, nil)
						continue
					}
					weekMarks = append(weekMarks, wm)
				}
			}

			nextWeekMark := 0
			for _, gcol := range s.WeeklyGradingColumns {
				var max float64
				if gcol.Type == directGrading {
					max = gcol.Max
				} else if gcol.Type == groupGrading {
					group, err := getGradingGroup(c, sy, gcol.GroupName)
					if err != nil {
						// TODO: handle error
						continue
					}

					totalMax := 0.0
					for _, col := range group.Columns {
						totalMax += col.Max
					}
					max = totalMax

					nextWeekMark += len(group.Columns)

				}

				colTotal := 0.0
				for _, wm := range weekMarks {
					colTotal += wm[nextWeekMark]
				}
				nextWeekMark++
				m[nextMark] = colTotal / float64(len(weekMarks))
				totalMark += colTotal * 100.0 / (max * float64(len(weekMarks)))
				totalWeight += 100.0
				nextMark++
			}
		}
		for _, gcol := range s.QuarterGradingColumns {
			if gcol.Type == directGrading {
				totalMark += m[nextMark] * 100.0 / gcol.Max
				totalWeight += 100.0
				nextMark++
			} else if gcol.Type == quizGrading {
				totalQuiz := quizSum(gcol.BestQuizzes, m[nextMark:nextMark+gcol.NumQuizzes])
				nextMark += gcol.NumQuizzes
				m[nextMark] = totalQuiz
				nextMark++

				totalMark += totalQuiz * 100.0 /
					(float64(gcol.BestQuizzes) * gcol.Max)
				totalWeight += 100.0
			} else if gcol.Type == groupGrading {
				group, err := getGradingGroup(c, sy, gcol.GroupName)
				if err != nil {
					// TODO: handle error
					continue
				}

				totalMax := 0.0
				for _, col := range group.Columns {
					totalMax += col.Max
				}

				totalGroup := sumMarks(m[nextMark : nextMark+len(group.Columns)]...)
				nextMark += len(group.Columns)
				m[nextMark] = totalGroup
				nextMark++

				totalMark += totalGroup / totalMax * 100.0
				totalWeight += 100.0
			}
		}

		if len(s.QuarterGradingColumns) == 0 {
			totalMark = math.NaN()
		}

		// Midterm mark
		m[nextMark] = totalMark / totalWeight * 100.0

	} else if term.Typ == Semester {
		total100 := 0.0
		nextMark := 0
		if s.SemesterType == MidtermSemester {
			if len(s.WeeklyGradingColumns) > 0 {
				var weekMarks [][]float64
				if term.N == 1 {
					for i := 1; i <= s.TotalWeeksS1; i++ {
						wm, err := getWeeklyStudentMarks(c, studentID, sy, s.ShortName, Term{WeekS1, i}, s)
						if err != nil {
							weekMarks = append(weekMarks, nil)
							continue
						}
						weekMarks = append(weekMarks, wm)
					}
				} else if term.N == 2 {
					for i := 1; i <= s.TotalWeeksS2; i++ {
						wm, err := getWeeklyStudentMarks(c, studentID, sy, s.ShortName, Term{WeekS2, i}, s)
						if err != nil {
							weekMarks = append(weekMarks, nil)
							continue
						}
						weekMarks = append(weekMarks, wm)
					}
				}
				nextWeekMark := 0
				for _, gcol := range s.WeeklyGradingColumns {
					var max float64
					if gcol.Type == directGrading {
						max = gcol.Max
					} else if gcol.Type == groupGrading {
						group, err := getGradingGroup(c, sy, gcol.GroupName)
						if err != nil {
							// TODO: handle error
							continue
						}

						totalMax := 0.0
						for _, col := range group.Columns {
							totalMax += col.Max
						}
						max = totalMax

						nextWeekMark += len(group.Columns)

					}

					colTotal := 0.0
					for _, wm := range weekMarks {
						colTotal += wm[nextWeekMark]
					}
					nextWeekMark++
					m[nextMark] = colTotal / float64(len(weekMarks))
					total100 += colTotal * gcol.FinalWeight / (max * float64(len(weekMarks)))
					nextMark++
				}
			}

			midterm := Term{Midterm, term.N}
			s.evaluate(c, studentID, sy, midterm, marks)
			midtermMarks := marks[midterm]
			midtermNextMark := 0
			midtermNextMark += len(s.WeeklyGradingColumns)
			for _, gcol := range s.QuarterGradingColumns {
				if gcol.Type == directGrading {
					m[nextMark] =
						midtermMarks[midtermNextMark]
					total100 += m[nextMark] * gcol.FinalWeight / gcol.Max
					nextMark++
					midtermNextMark++
				} else if gcol.Type == quizGrading {
					midtermNextMark += gcol.NumQuizzes
					m[nextMark] = midtermMarks[midtermNextMark]
					total100 += m[nextMark] * gcol.FinalWeight /
						(float64(gcol.BestQuizzes) * gcol.Max)

					nextMark++
					midtermNextMark++
				} else if gcol.Type == groupGrading {
					group, err := getGradingGroup(c, sy, gcol.GroupName)
					if err != nil {
						// TODO: handle error
						continue
					}

					totalMax := 0.0
					for _, col := range group.Columns {
						totalMax += col.Max
					}

					midtermNextMark += len(group.Columns)
					m[nextMark] = midtermMarks[midtermNextMark]
					total100 += m[nextMark] / totalMax * gcol.FinalWeight

					nextMark++
					midtermNextMark++

				}
			}
		}
		for _, gcol := range s.SemesterGradingColumns {
			if gcol.Type == directGrading {
				total100 += m[nextMark] * gcol.FinalWeight / gcol.Max
				nextMark++
			} else if gcol.Type == quizGrading {
				totalQuiz := quizSum(gcol.BestQuizzes, m[nextMark:nextMark+gcol.NumQuizzes])
				nextMark += gcol.NumQuizzes
				m[nextMark] = totalQuiz
				total100 += totalQuiz * gcol.FinalWeight /
					(float64(gcol.BestQuizzes) * gcol.Max)

				nextMark++
			} else if gcol.Type == groupGrading {
				group, err := getGradingGroup(c, sy, gcol.GroupName)
				if err != nil {
					// TODO: handle error
					continue
				}

				totalMax := 0.0
				for _, col := range group.Columns {
					totalMax += col.Max
				}

				totalGroup := sumMarks(m[nextMark : nextMark+len(group.Columns)]...)
				nextMark += len(group.Columns)
				m[nextMark] = totalGroup
				nextMark++

				total100 += totalGroup / totalMax * gcol.FinalWeight
			}
		}

		if s.SemesterType == QuarterSemester {
			// Semester Exam % (or just Semester % if there are columns other than exam)
			m[nextMark] = total100 * s.sWeight / 100.0
			nextMark++

			// Semester Mark
			q2 := term.N * 2
			q1 := q2 - 1

			s.evaluate(c, studentID, sy, Term{Quarter, q1}, marks)
			s.evaluate(c, studentID, sy, Term{Quarter, q2}, marks)

			if len(s.QuarterGradingColumns) == 0 {
				m[nextMark] = total100
			} else {
				q1Marks := marks[Term{Quarter, q1}]
				q1Mark := q1Marks[len(q1Marks)-1]

				q2Marks := marks[Term{Quarter, q2}]
				q2Mark := q2Marks[len(q2Marks)-1]

				m[nextMark] = sumMarks(m[nextMark-1], q1Mark, q2Mark)
			}
		} else if s.SemesterType == MidtermSemester {
			// Semester Mark
			m[nextMark] = total100
			nextMark++
		}

	} else if term.Typ == EndOfYear {
		s.evaluate(c, studentID, sy, Term{Semester, 1}, marks)
		s.evaluate(c, studentID, sy, Term{Semester, 2}, marks)
		m[0] = s.get100(Term{Semester, 1}, marks)
		m[1] = s.get100(Term{Semester, 2}, marks)

		m[2] = sumMarks(m[0], m[1]) / 2.0
	} else {
		return fmt.Errorf("Invalid term type: %d", term.Typ)
	}

	marks[term] = m
	// TODO: errors?
	return nil
}

// sumMarks sums all values, and if one of them is math.NaN(), returns math.NaN()
func sumMarks(marks ...float64) float64 {
	var total float64
	for _, v := range marks {
		if math.IsNaN(v) {
			return math.NaN()
		}
		total += v
	}
	return total
}

// quizSum adds the top `keep` marks only and ignores the rest
func quizSum(keep int, marks []float64) float64 {
	marksCopy := append([]float64(nil), marks...)
	sort.Float64s(marksCopy)
	return sumMarks(marksCopy[len(marksCopy)-keep:]...)
}

func (s Subject) get100(term Term, marks studentMarks) float64 {
	m := marks[term]
	if term.Typ == Quarter {
		if s.SemesterType != QuarterSemester {
			return math.NaN()
		}
		return m[len(m)-2]
	} else if term.Typ == WeekS2 || term.Typ == WeekS1 {
		return math.NaN()
	} else if term.Typ == Midterm {
		if s.SemesterType != MidtermSemester {
			return math.NaN()
		}
		return m[len(m)-1]
	} else if term.Typ == Semester {
		return m[len(m)-1]
	} else if term.Typ == EndOfYear {
		return m[len(m)-1]
	}
	panic(fmt.Sprintf("Invalid term type: %d", term.Typ))
}

func (s Subject) getExam(term Term, marks studentMarks) float64 {
	m := marks[term]
	if term.Typ == Quarter {
		return math.NaN()
	} else if term.Typ == WeekS2 || term.Typ == WeekS1 {
		return math.NaN()
	} else if term.Typ == Midterm {
		return math.NaN()
	} else if term.Typ == Semester {
		return m[len(m)-2]
	} else if term.Typ == EndOfYear {
		return math.NaN()
	}
	panic(fmt.Sprintf("Invalid term type: %d", term.Typ))
}

func (s Subject) ready(term Term, marks studentMarks) bool {
	if term.Typ == WeekS2 || term.Typ == WeekS1 {
		m := marks[term]
		for _, v := range m {
			if math.IsNaN(v) {
				// mark not entered
				return false
			}
		}
		return true
	}
	return !math.IsNaN(s.get100(term, marks))
}

func (s Subject) quarterWeight() float64 {
	return s.qWeight
}

func (s Subject) semesterWeight() float64 {
	return s.sWeight
}

func (s Subject) subjectInAverage() bool {
	return s.CalculateInAverage
}

func (s Subject) displayName() string {
	return s.Description
}

func (s Subject) inStream(stream string) bool {
	return s.Stream == "" || stream == s.Stream
}

// behaviorGradingSystem contains behavrior. There are no calculations to make
type behaviorGradingSystem struct {
}

var behaviorDesc = []colDescription{
	{"Follows school guidelines for safe and appropriate behaviour", 4, 4, true},
	{"Demonstrates courtesy and respect", 4, 4, true},
	{"Listens and responds", 4, 4, true},
	{"Strives for quality work", 4, 4, true},
	{"Shows initiative / is a self - starter", 4, 4, true},
	{"Participates enthusiastically in activities", 4, 4, true},
	{"Uses time efficiently and appropriately", 4, 4, true},
	{"Completes class work on time ", 4, 4, true},
	{"Contributes to discussion and group tasks", 4, 4, true},
	{"Works cooperatively with others", 4, 4, true},
	{"Works well independently", 4, 4, true},
	{"Returns complete homework", 4, 4, true},
	{"Organizes shelf, materials and belongings ", 4, 4, true},
	{"Asks questions to clarify content", 4, 4, true},
	{"Clearly communicates to teachers ", 4, 4, true},
}

func (behaviorGradingSystem) description(c context.Context, sy string, term Term) []colDescription {
	if term.Typ == Quarter {
		return behaviorDesc
	} else if term.Typ == Midterm {
		return behaviorDesc
	} else if term.Typ == Semester || term.Typ == EndOfYear ||
		term.Typ == WeekS1 || term.Typ == WeekS2 {
		// No calculations
		return nil
	}
	panic(fmt.Sprintf("Invalid term type: %d", term.Typ))
}

func (bgs behaviorGradingSystem) evaluate(c context.Context, studentID, sy string, term Term, marks studentMarks) (err error) {
	m := marks[term]
	desc := bgs.description(c, sy, term)

	switch {
	case m == nil: // first time to evaluate it
		m = make([]float64, len(desc))
		for i, _ := range desc {
			m[i] = math.NaN()
		}
	case len(m) != len(desc): // sanity check
		err = invalidNumberOfMarks
		m = make([]float64, len(desc))
		for i, _ := range desc {
			m[i] = math.NaN()
		}
	}

	// more sanity checks
	for i, d := range desc {
		if m[i] < 0 || m[i] > d.Max {
			m[i] = math.NaN()
			if err == nil {
				err = invalidRangeOfMarks
			}
		}
	}

	// no calculations

	marks[term] = m
	return
}

func (bgs behaviorGradingSystem) get100(term Term, marks studentMarks) float64 {
	if bgs.ready(term, marks) {
		return 100.0
	}
	return math.NaN()
}

func (bgs behaviorGradingSystem) getExam(term Term, marks studentMarks) float64 {
	return math.NaN()
}

func (bgs behaviorGradingSystem) ready(term Term, marks studentMarks) bool {
	m := marks[term]
	for _, v := range m {
		if math.IsNaN(v) {
			// mark not entered
			return false
		}
	}

	return true
}

func (bgs behaviorGradingSystem) quarterWeight() float64 {
	return math.NaN()
}

func (bgs behaviorGradingSystem) semesterWeight() float64 {
	return math.NaN()
}

func (bgs behaviorGradingSystem) subjectInAverage() bool {
	return false
}

func (bgs behaviorGradingSystem) displayName() string {
	return "Behavior"
}

func (bgs behaviorGradingSystem) inStream(stream string) bool {
	return true
}

type attendanceGradingSystem struct {
}

var enterAttendanceDesc = []colDescription{
	{"Excused Absence (attendance system)", 99, 99, false},
	{"Excused Absence (manual adjustment)", 99, 99, true},
	{"Unexcused Absence", 99, 99, true},
	{"Excused Tardiness (attendance system)", 99, 99, false},
	{"Excused Tardiness (manual adjustment)", 99, 99, true},
	{"Unexcused Tardiness", 99, 99, true},
}

var displayAttendanceDesc = []colDescription{
	{"Excused Absence", 99, 99, false},
	{"Unexcused Absence", 99, 99, false},
	{"Excused Tardiness", 99, 99, false},
	{"Unexcused Tardiness", 99, 99, false},
}

func semesterAttendanceDesc(q1Name, q2Name string) []colDescription {
	return []colDescription{
		{q1Name + "/Midterm Excused Absence", 99, 99, false},
		{q2Name + " Excused Absence", 99, 99, false},
		{"Semester Excused Absence (attendance system)", 99, 99, false},
		{"Semester Excused Absence (manual adjustment)", 99, 99, true},
		{"Total Excused Absence", 99, 99, false},

		{q1Name + "/Midterm  Unexcused Absence", 99, 99, false},
		{q2Name + " Unexcused Absence", 99, 99, false},
		{"Semester Unexcused Absence", 99, 99, true},
		{"Total Unexcused Absence", 99, 99, false},

		{q1Name + "/Midterm  Excused Tardiness", 99, 99, false},
		{q2Name + " Excused Tardiness", 99, 99, false},
		{"Semester Excused Tardiness (attendance system)", 99, 99, false},
		{"Semester Excused Tardiness (manual adjustment)", 99, 99, true},
		{"Total Excused Tardiness", 99, 99, false},

		{q1Name + "/Midterm  Unexcused Tardiness", 99, 99, false},
		{q2Name + " Unexcused Tardiness", 99, 99, false},
		{"Semester Unexcused Tardiness", 99, 99, true},
		{"Total Unexcused Tardiness", 99, 99, false},
	}
}

func (_ attendanceGradingSystem) description(c context.Context, sy string, term Term) []colDescription {
	if term.Typ == Quarter {
		return enterAttendanceDesc
	} else if term.Typ == Midterm {
		return enterAttendanceDesc
	} else if term.Typ == Semester {
		if term.N == 1 {
			return semesterAttendanceDesc("Q1", "Q2")
		} else if term.N == 2 {
			return semesterAttendanceDesc("Q3", "Q4")
		}
	} else if term.Typ == EndOfYear {
		var cols []colDescription
		for _, desc := range displayAttendanceDesc {
			for _, t := range []string{"S1", "S2", "Total"} {
				name := t + " " + desc.Name
				cols = append(cols, colDescription{name, 99, 99, false})
			}
		}
		return cols
	} else if term.Typ == WeekS1 || term.Typ == WeekS2 {
		return nil
	}
	panic(fmt.Sprintf("Invalid term type: %d", term.Typ))
}

func (ags attendanceGradingSystem) evaluate(c context.Context, studentID, sy string, term Term, marks studentMarks) (err error) {
	m := marks[term]
	desc := ags.description(c, sy, term)

	switch {
	case m == nil: // first time to evaluate it
		m = make([]float64, len(desc))
		for i, _ := range desc {
			m[i] = 0
		}
	case len(m) != len(desc): // sanity check
		err = invalidNumberOfMarks
		m = make([]float64, len(desc))
		for i, _ := range desc {
			m[i] = 0
		}
	}

	// more sanity checks
	for i, d := range desc {
		if m[i] < 0 || m[i] > d.Max {
			m[i] = 0
			if err == nil {
				err = invalidRangeOfMarks
			}
		}
	}

	// calculations
	if term.Typ == Quarter {
		m[0], m[3] = getApprovedAbsenceAndTardiness(c, studentID, sy, term)
	} else if term.Typ == Midterm {
		m[0], m[3] = getApprovedAbsenceAndTardiness(c, studentID, sy, term)
	} else if term.Typ == Semester {
		q2 := Term{Quarter, term.N * 2}
		q1 := Term{Quarter, q2.N - 1}
		mt := Term{Midterm, term.N}

		ags.evaluate(c, studentID, sy, q1, marks)
		ags.evaluate(c, studentID, sy, mt, marks)
		ags.evaluate(c, studentID, sy, q2, marks)

		q1M := marks[q1]
		mtM := marks[mt]
		q2M := marks[q2]

		excusedAbsence, excusedTardiness := getApprovedAbsenceAndTardiness(c, studentID, sy, term)

		// Excused absence
		m[0] = q1M[0] + q1M[1] + mtM[0] + mtM[1]
		m[1] = q2M[0] + q2M[1]
		m[2] = excusedAbsence
		// m[3]
		m[4] = sumMarks(m[0:4]...)

		// Unexcused absence
		m[5] = q1M[2] + mtM[2]
		m[6] = q2M[2]
		// m[7]
		m[8] = sumMarks(m[5:8]...)

		// Excused tardiness
		m[9] = q1M[3] + q1M[4] + mtM[3] + mtM[4]
		m[10] = q2M[3] + q2M[4]
		m[11] = excusedTardiness
		// m[12]
		m[13] = sumMarks(m[9:13]...)

		// Unexcused tardiness
		m[14] = q1M[5] + mtM[5]
		m[15] = q2M[5]
		// m[16]
		m[17] = sumMarks(m[14:17]...)

	} else if term.Typ == EndOfYear {
		s1 := Term{Semester, 1}
		s2 := Term{Semester, 2}

		ags.evaluate(c, studentID, sy, s1, marks)
		ags.evaluate(c, studentID, sy, s2, marks)

		s1M := marks[s1]
		s2M := marks[s2]

		// Excused absence
		m[0] = s1M[4]
		m[1] = s2M[4]
		m[2] = m[0] + m[1]

		// Unexcused absence
		m[3] = s1M[8]
		m[4] = s2M[8]
		m[5] = m[3] + m[4]

		// Excused tardiness
		m[6] = s1M[13]
		m[7] = s2M[13]
		m[8] = m[6] + m[7]

		// Unexcused tardiness
		m[9] = s1M[17]
		m[10] = s2M[17]
		m[11] = m[9] + m[10]

	} else if term.Typ == WeekS1 || term.Typ == WeekS2 {
	}

	marks[term] = m

	return
}

func (_ attendanceGradingSystem) get100(term Term, marks studentMarks) float64 {
	return 100
}

func (_ attendanceGradingSystem) getExam(term Term, marks studentMarks) float64 {
	return math.NaN()
}

func (_ attendanceGradingSystem) ready(term Term, marks studentMarks) bool {
	return true
}

func (_ attendanceGradingSystem) quarterWeight() float64 {
	return math.NaN()
}

func (_ attendanceGradingSystem) semesterWeight() float64 {
	return math.NaN()
}

func (_ attendanceGradingSystem) subjectInAverage() bool {
	return false
}

func (_ attendanceGradingSystem) displayName() string {
	return "Attendance"
}

func (_ attendanceGradingSystem) inStream(stream string) bool {
	return true
}

func getApprovedAbsenceAndTardiness(c context.Context, studentID string,
	sy string, term Term) (float64, float64) {
	requests, err := getApprovedLeavesLeaveTypeOnly(c, studentID, sy, term)
	if err != nil {
		log.Errorf(c, "Could not get approved leaves: %s %s %s %s", studentID, sy, term, err)
		return math.NaN(), math.NaN()
	}
	var absence, tardiness float64
	for _, request := range requests {
		if request.Type == LeaveOfAbsence {
			absence++
		} else if request.Type == EarlyDeparture ||
			request.Type == LateArrival {
			tardiness++
		} else {
			log.Errorf(c, "Unknown leaveType: %s", request.Type)
		}
	}
	return absence, tardiness
}


text:
<?php

namespace Symfony\Component\Routing\Loader;

@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ObjectRouteLoader::class, ObjectLoader::class), \E_USER_DEPRECATED);

/**
 * A route loader that calls a method on an object to load the routes.
 *
 * @author Ryan Weaver <ryan@knpuniversity.com>
 *
 * @deprecated since Symfony 4.4, use ObjectLoader instead.
 */
abstract class ObjectRouteLoader extends ObjectLoader
{
    /**
     * Returns the object that the method will be called on to load routes.
     *
     * For example, if your application uses a service container,
     * the $id may be a service id.
     *
     * @param string $id
     *
     * @return object
     */
    abstract protected function getServiceObject($id);

    /**
     * {@inheritdoc}
     */
    public function supports($resource, $type = null)
    {
        return 'service' === $type;
    }

    /**
     * {@inheritdoc}
     */
    protected function getObject(string $id)
    {
        return $this->getServiceObject($id);
    }
}