# `GanttEx.Import`
[🔗](https://github.com/rocket4ce/gantt_ex/blob/v0.1.0/lib/gantt_ex/import.ex#L1)

Reads project data from other tools.

| Format                         | Function             |
| ------------------------------ | -------------------- |
| DHTMLX JSON                    | `from_json/1`        |
| DHTMLX XML                     | `from_xml/1`         |
| MS Project XML (MSPDI)         | `from_ms_project/1`  |

Every function returns `{:ok, %{tasks: [map], links: [map]}}`, ready for
`GanttEx.Store.new/3` or for the `tasks` and `links` attributes of
`GanttEx.Live.Gantt`.

XML is parsed with the OTP SAX parser without creating atoms, and
documents declaring entities or external DTDs are rejected, so untrusted
files can be imported safely.

    iex> json = ~s({"data": [{"id": 1, "text": "A", "start_date": "06-01-2025 00:00", "duration": 2, "parent": 0}], "links": [{"id": 1, "source": 1, "target": 1, "type": "0"}]})
    iex> {:ok, %{tasks: [task], links: [link]}} = GanttEx.Import.from_json(json)
    iex> {task["text"], link["type"]}
    {"A", "0"}
    iex> {:ok, store} = GanttEx.Store.new([task])
    iex> GanttEx.Store.get_task(store, 1).end_date
    ~N[2025-01-08 00:00:00]

    iex> GanttEx.Import.from_json("[1, 2]")
    {:error, :invalid_format}

# `data`

```elixir
@type data() :: %{tasks: [map()], links: [map()]}
```

Imported data.

# `from_json`

```elixir
@spec from_json(String.t() | map()) ::
  {:ok, data()} | {:error, :invalid_json | :invalid_format}
```

Parses DHTMLX JSON (a string or an already decoded map). Tasks may be under
the `"data"` or `"tasks"` key.

# `from_ms_project`

```elixir
@spec from_ms_project(String.t()) ::
  {:ok, data()} | {:error, :invalid_xml | :invalid_format}
```

Parses Microsoft Project XML (MSPDI), as written by MS Project,
ProjectLibre, GanttProject or `GanttEx.Export.to_ms_project/2`.

The tree is rebuilt from the outline levels, summary tasks become projects
and lags are converted from MS Project units assuming 8-hour days.

    iex> store = GanttEx.Store.new!(
    ...>   [
    ...>     %{id: 1, text: "Phase", type: :project},
    ...>     %{id: 2, text: "Work", parent: 1, start_date: ~D[2025-01-06], duration: 2, progress: 0.5},
    ...>     %{id: 3, text: "Done", parent: 1, start_date: ~D[2025-01-08], type: :milestone}
    ...>   ],
    ...>   [%{id: 1, source: 2, target: 3, type: :start_to_start, lag: 1}]
    ...> )
    iex> {:ok, %{tasks: tasks, links: [link]}} = store |> GanttEx.Export.to_ms_project() |> GanttEx.Import.from_ms_project()
    iex> Enum.map(tasks, &{&1["text"], &1["type"], &1["parent"]})
    [{"Phase", "project", nil}, {"Work", "task", 1}, {"Done", "milestone", 1}]
    iex> {link["source"], link["target"], link["type"], link["lag"]}
    {2, 3, "start_to_start", 1}

# `from_xml`

```elixir
@spec from_xml(String.t()) :: {:ok, data()} | {:error, :invalid_xml}
```

Parses the DHTMLX Gantt XML format: `<task>` elements whose attributes are
the task fields (the element text is the task name) and a
`<coll_options for="links">` element with `<item>` links.

    iex> xml = """
    ...> <data>
    ...>   <task id="1" start_date="06-01-2025 00:00" duration="3" progress="0.5"><![CDATA[Design & UX]]></task>
    ...>   <task id="2" parent="1" start_date="09-01-2025 00:00" duration="2">Build</task>
    ...>   <coll_options for="links"><item id="1" source="1" target="2" type="0"/></coll_options>
    ...> </data>
    ...> """
    iex> {:ok, %{tasks: [design, build], links: [link]}} = GanttEx.Import.from_xml(xml)
    iex> {design["text"], build["parent"], link["target"]}
    {"Design & UX", "1", "2"}
    iex> GanttEx.Import.from_xml("<data><task>")
    {:error, :invalid_xml}

---

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