# Duty-schedule import format

The **duty-schedule** format is used by the `/import_duty_schedule` command. Only users in `ADMIN_USERNAMES` or `ADMIN_PHONES`, or users with the **admin** role in the database (assigned via `/set_role`), can import.

## Import flow

1. **Handover time** — The bot asks for the shift handover time and optional timezone (e.g. `09:00 Europe/Moscow` or `06:00 UTC`). This is converted to UTC and used as the boundary between duty periods when creating records.
2. **JSON file** — Send a file in duty-schedule format (see below). On re-import, duties in the same date range for each user are replaced by the new data.

## Format specification

- **meta** (required) — Object with:
  - **start_date** (required) — First day of the schedule, `YYYY-MM-DD`.
  - **weeks** (optional) — Not used to limit length; the number of days is derived from the longest `duty` string (see below).

- **schedule** (required) — Array of objects. Each object:
  - **name** (required) — Full name of the person (string).
  - **duty** (required) — String of cells separated by **`;`**. Each cell corresponds to one day starting from `meta.start_date` (first cell = start_date, second = start_date + 1 day, etc.). Empty or whitespace = no event for that day.

### Cell values (single character, case-sensitive where noted)

| Value | Meaning        | Notes                    |
|-------|----------------|--------------------------|
| **в**, **В**, **б**, **Б** | Duty        | Any of these four        |
| **Н** | Unavailable | Exactly `Н`             |
| **О** | Vacation    | Exactly `О`             |
| (empty/space/other) | No event | Ignored for import       |

The number of days in the schedule is the maximum length of any `duty` string when split by `;`. If `duty` is empty or missing, it is treated as an empty list of cells.

## Example JSON

```json
{
  "meta": {
    "start_date": "2025-02-01",
    "weeks": 4
  },
  "schedule": [
    {
      "name": "Ivanov Ivan",
      "duty": ";;В;;;Н;;О;;В;;"
    },
    {
      "name": "Petrov Petr",
      "duty": ";;;В;;;;;;В;;;"
    }
  ]
}
```

- **start_date** is 2025-02-01; the longest `duty` has 14 cells (after splitting by `;`), so the schedule spans 14 days (2025-02-01 … 2025-02-14).
- First person: duty on day index 2 (В), unavailable on 6 (Н), vacation on 8 (О), duty on 11 (В). Other cells are empty.
- Second person: duty on day indices 3 and 10.

## Validation

- `meta` and `meta.start_date` must be present and valid; `start_date` must parse as `YYYY-MM-DD`.
- `schedule` must be an array; each item must be an object with string `name` (non-empty after strip) and string `duty` (if missing, treated as `""`).
- Invalid JSON or encoding raises an error; the parser reports missing or invalid fields (see `duty_teller.importers.duty_schedule.DutyScheduleParseError`).
