# `Ltix.GradeService.Score`
[🔗](https://github.com/DecoyLex/ltix/blob/main/lib/ltix/grade_service/score.ex#L1)

A score to post to the platform's gradebook.

Scores are write-only. The tool constructs them and POSTs to the
platform. Use `new/1` to build a validated score, then pass it to
`Ltix.GradeService.post_score/3`.

The `grading_progress` must be `:fully_graded` for the platform to
record the score as a final grade. Other values are considered
partial and the platform may ignore them.

## Examples

    {:ok, score} = Ltix.GradeService.Score.new(
      user_id: "12345",
      score_given: 85,
      score_maximum: 100,
      activity_progress: :completed,
      grading_progress: :fully_graded
    )

    score.score_given
    #=> 85

# `t`

```elixir
@type t() :: %Ltix.GradeService.Score{
  activity_progress:
    :initialized | :started | :in_progress | :submitted | :completed,
  comment: nil | binary(),
  extensions: nil | %{optional(binary() | atom()) =&gt; any()},
  grading_progress:
    :fully_graded | :pending | :pending_manual | :failed | :not_ready,
  score_given: nil | number(),
  score_maximum: nil | number(),
  scoring_user_id: nil | binary(),
  submission:
    nil
    | %{
        optional(:started_at) =&gt; DateTime.t(),
        optional(:submitted_at) =&gt; DateTime.t()
      },
  timestamp: nil | DateTime.t(),
  user_id: binary()
}
```

# `new`

```elixir
@spec new(keyword() | map()) :: {:ok, t()} | {:error, Exception.t()}
```

Build a validated score from a keyword list or a map.

Auto-generates a `timestamp` with microsecond precision if not provided.

## Options

* `:timestamp` (`t:DateTime.t/0`) - When the score was set. Auto-generated if not provided.

* `:comment` (`t:String.t/0`) - Plain text comment visible to both student and instructor.

* `:extensions` (`nil` | `t:map/0`) - Required. Extension properties keyed by fully qualified URLs, given as strings or atoms. The default value is `%{}`.

* `:user_id` (`t:String.t/0`) - Required. LTI user ID of the score recipient.

* `:scoring_user_id` (`t:String.t/0`) - LTI user ID of the person who provided the score.

* `:activity_progress` (`"initialized"` | `"started"` | `"in_progress"` | `"submitted"` | `"completed"`) - Required. User's progress toward completing the activity.

* `:grading_progress` (`"fully_graded"` | `"pending"` | `"pending_manual"` | `"failed"` | `"not_ready"`) - Required. Status of the grading process. Must be `:fully_graded` for final scores.

* `:score_given` (`t:number/0`) - Score value (must be >= 0). Requires `score_maximum` when present.

* `:score_maximum` (`t:number/0`) - Maximum possible score (must be > 0). Required when `score_given` is present.

* `:submission` (`t:map/0`) - Keyword list or map with optional `:started_at` and `:submitted_at` `DateTime` values. `submitted_at` must not precede `started_at`.

## Examples

    iex> {:ok, score} = Ltix.GradeService.Score.new(user_id: "u1", activity_progress: :completed, grading_progress: :fully_graded)
    iex> score.user_id
    "u1"

Strings are also accepted for `activity_progress` and `grading_progress`,
which is convenient when values come from form params.

    iex> {:ok, score} = Ltix.GradeService.Score.new(%{user_id: "u1", activity_progress: "completed", grading_progress: "fully_graded"})
    iex> score.activity_progress
    :completed

# `new!`

```elixir
@spec new!(keyword() | map()) :: t()
```

Same as `new/1` but raises on error.

# `to_map`

```elixir
@spec to_map(t()) :: map()
```

Serialize a score to a map with camelCase string keys.

## Examples

    iex> {:ok, score} = Ltix.GradeService.Score.new(user_id: "u1", activity_progress: :completed, grading_progress: :fully_graded, timestamp: ~U[2024-01-15 10:30:00.123456Z])
    iex> map = Ltix.GradeService.Score.to_map(score)
    iex> map["timestamp"]
    "2024-01-15T10:30:00.123456Z"

# `validate`

```elixir
@spec validate(t()) :: :ok | {:error, Exception.t()}
```

Validate a score the same way as `new/1`.

Useful for a score built or changed as a struct.

## Examples

    iex> score = Ltix.GradeService.Score.new!(user_id: "u1", activity_progress: :completed, grading_progress: :fully_graded)
    iex> Ltix.GradeService.Score.validate(score)
    :ok
    iex> {:error, error} = Ltix.GradeService.Score.validate(%{score | user_id: nil})
    iex> error.field
    :user_id

---

*Consult [api-reference.md](api-reference.md) for complete listing*
