GanttEx.Import (GanttEx v0.1.0)

Copy Markdown View Source

Reads project data from other tools.

FormatFunction
DHTMLX JSONfrom_json/1
DHTMLX XMLfrom_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}

Summary

Types

Imported data.

Functions

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

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

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.

Types

data()

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

Imported data.

Functions

from_json(json)

@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(xml)

@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(xml)

@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}