# Evidence Importer

This guide explains how to use the Evidence CSV importer in Monolith, including how lookup columns are resolved and how each row is validated before import.

## What the importer does

The importer takes a CSV file, maps your CSV columns to Monolith evidence fields, resolves values (for example, user names to user IDs), validates each row, and then imports only the rows you choose.

## Import workflow (4 steps)

{% stepper %}
{% step %}

### Upload

Upload one `.csv` file (default max size: 10 MB).
{% endstep %}

{% step %}

### Map Columns

Map each Monolith field to a CSV column. Required fields must be mapped to continue.
{% endstep %}

{% step %}

### Preview

Monolith resolves and validates every row, then marks rows as:

* **Valid** (green check)
* **Warning** (yellow alert)
* **Error** (red X)
  {% endstep %}

{% step %}

### Import

Import runs in batches (default: 5 records per batch). You will see a final success/failure summary.
{% endstep %}
{% endstepper %}

## Auto-mapping behavior

When your CSV is uploaded, Monolith tries to map columns automatically in this order:

1. Exact match to the target field key
2. Exact match to the target field label
3. Exact match to any configured alias

All matching is case-insensitive and trimmed. A CSV column is only used once during auto-map.

### Auto-mapping aliases by column

These are the aliases used for built-in Evidence fields during step 3 of auto-mapping.

| Monolith field                        | Aliases checked                                                         |
| ------------------------------------- | ----------------------------------------------------------------------- |
| `case_id` (Case Number)               | `case id`, `case`, `case number`                                        |
| `evidence_number` (Evidence Number)   | `evidence_number`, `item_number`, `evidence #`, `evi #`                 |
| `item_type` (Evidence Type)           | `type`, `evidence_type`, `evi_type`, `item type`                        |
| `manufacturer` (Brand / Manufacturer) | `brand`, `make`, `item brand`, `provider`                               |
| `model_name` (Item Name)              | `name`, `item name`, `device name`, `model`                             |
| `model_number` (Model Number)         | `model_number`, `model #`, `model`                                      |
| `serial_number` (Unique Identifier)   | `serial`, `serial number`, `serial #`, `unique id`, `unique identifier` |
| `capacity` (Size)                     | `size`, `storage`, `capacity`                                           |
| `capacity_unit` (Size Unit)           | `size unit`, `unit`, `capacity unit`, `size`, `capacity`                |
| `assigned_user_id` (Assigned User)    | `assigned user`, `assigned to`, `assigned to user`                      |
| `location_id` (Location)              | `location`, `evidence location`, `current location`                     |
| `priority_id` (Priority)              | `priority`, `evidence priority`                                         |
| `description` (Description)           | `description`, `notes`, `details`                                       |

For custom evidence fields, the importer sets one alias automatically: the custom field name lowercased.

## Lookup columns: how they work

Lookup columns are fields where the importer converts user-friendly CSV text into the internal Monolith value (usually an ID or canonical option).

If a lookup value cannot be resolved, the importer sets it to `null` and adds a **warning** (not an error), unless that field is required.

### Built-in evidence lookups

* **Case Number (`case_id`)**
  * If importing from within a case, the case is fixed and injected automatically.
  * Otherwise, the value is treated as case number text and looked up in Monolith.
  * An error is shown if case number cannot be found.
* **Evidence Type (`item_type`)**
  * Matches CSV text to an existing evidence type by name (case-insensitive).
  * The type is set to null if a match is not found.
* **Assigned User (`assigned_user_id`)**
  * Accepts any of:
    * Numeric user ID
    * User email
    * Full name
  * Resolves to the user ID.
  * Assigned user is not applied if there is no user match.
* **Location (`location_id`)**
  * Matches by exact location name (case-insensitive).
  * If multiple locations share the same name, row gets an **error**: duplicate location match.
* **Priority (`priority_id`)**
  * Matches by priority name (case-insensitive) and resolves to priority ID.
* **Size + Size Unit (`capacity`, `capacity_unit`)**
  * You can provide a combined value like `256 GB`.
  * Importer extracts:
    * `capacity = 256`
    * `capacity_unit = GB`
  * Unit matching supports `KB`, `MB`, `GB`, `TB`.

### Custom field lookups

Custom evidence fields are added dynamically and use importer logic based on field type:

* **Drop-down custom fields**
  * CSV value must match one of the configured options.
  * Value is null if there is no match to options.
* **Tag-box custom fields**
  * Supports multi-value input split by:
    * comma `,`
    * semicolon `;`
    * pipe `|`
    * newline
  * Values are trimmed and deduplicated.
  * Known options are normalized to configured option values.
  * Unknown values are ignored.
* **Date-box custom fields**
  * Accepts:
    * Parseable date strings (for example `2026-03-01`, `3/1/2026`)
    * Unix timestamp values (seconds or milliseconds)
  * Normalized to `YYYY-MM-DD`.

## Row validation: what is checked

Every row is validated after mapping and value resolution.

{% stepper %}
{% step %}

### 1) Required field checks

For each required field:

* If no CSV column was mapped: error\
  `"<Field> is required but no column was mapped"`
* If mapped but row value is empty: error\
  `"<Field> is required"`

Empty means `null`, `undefined`, empty string, or empty array.
{% endstep %}

{% step %}

### 2) Type checks

* **Number fields**
  * Must resolve to a number.
  * Non-numeric text produces an error.
* **Boolean fields** (if present in a definition)
  * Accepts normalized boolean values (`true/false`, `yes/no`, `1/0` during coercion).
  * Non-boolean result is an error.
* **Select fields**
  * Value must match one of the valid options.
  * For multi-select arrays (for example tag-box), every item must be valid.
* **Date fields**
  * Must be parseable as a valid date string.
    {% endstep %}

{% step %}

### 3) Custom field-level validation

Some fields run extra custom checks. For Evidence import, notable example:

* **Location**: fails if the same location name maps to multiple location records.
  {% endstep %}

{% step %}

### 4) Resolver warning checks

If a field has a resolver, the CSV column is mapped, and the source row had data, but the resolver still returns `null`, the row gets a warning:

`"Could not resolve value for <Field>"`

This lets users proceed while seeing unresolved lookups.
{% endstep %}
{% endstepper %}

## Errors vs warnings

* **Errors**
  * Row is invalid.
  * Invalid rows are not auto-selected for import.
* **Warnings**
  * Row can still be valid/importable.
  * Warnings flag possible data quality issues (for example unresolved lookup).

## Row selection behavior in Preview

* Valid rows are auto-selected by default.
* You can:
  * **Select Valid**
  * **Select All**
  * **Deselect All**
* Import button shows how many selected rows will be imported.

## Import execution and reporting

* Import runs only for selected **valid** rows.
* Processing occurs in batches (default size: 5).
* Final screen reports:
  * successful imports
  * failed imports
  * row-level error messages (if any)

## CSV preparation tips

* Use stable, specific values for lookups:
  * unique case numbers
  * exact evidence type names
  * exact location names
  * user email/full name for assigned user
* Avoid ambiguous location names that exist more than once.
* For size columns, prefer `capacity` + `capacity_unit`, or a single value like `512 GB`.
* For tag-box custom fields, separate values with commas/semicolons/pipes/new lines.
* Validate date formats before upload when possible.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.monolithforensics.com/monolith/monolith-features/data-import/evidence-importer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
