113 lines
3.3 KiB
Python
113 lines
3.3 KiB
Python
"""
|
|
Render project files to pgfgantt plots.
|
|
|
|
Create a LaTeX representation of a project file.
|
|
"""
|
|
|
|
from pylatex.base_classes import command
|
|
from pylatex.base_classes import containers
|
|
from pylatex.utils import NoEscape
|
|
from project2latex import project as _project
|
|
|
|
def date_latex(date):
|
|
"""
|
|
Convert a date object to a LaTeX representation.
|
|
|
|
:param date: input date or datetime instance
|
|
:return: LaTeX representation of the date
|
|
"""
|
|
return NoEscape(date.strftime('%Y-%m-%d'))
|
|
|
|
def uid_latex(uid):
|
|
"""
|
|
Convert a task UID to a LaTeX representation.
|
|
|
|
:param uid: unique identifier to convert
|
|
:return: LaTeX representation of the UID
|
|
"""
|
|
return NoEscape("task" + str(uid))
|
|
|
|
class GanttNewLine(command.CommandBase):
|
|
pass
|
|
|
|
class GanttTask(command.CommandBase):
|
|
def __init__(self, task):
|
|
self.task = task
|
|
args = None
|
|
|
|
if task.kind == _project.TaskKind.bar:
|
|
# a normal task, lasting a given duration
|
|
self._latex_name = 'ganttbar'
|
|
args = command.Arguments(
|
|
task.name,
|
|
date_latex(task.start),
|
|
date_latex(task.finish),
|
|
)
|
|
else:
|
|
# a milestone at a fixed date with no duration
|
|
self._latex_name = 'ganttmilestone'
|
|
args = command.Arguments(
|
|
task.name,
|
|
date_latex(task.start),
|
|
)
|
|
|
|
super().__init__(
|
|
# name tasks according to their unique id
|
|
options=command.Options(
|
|
name=uid_latex(task.uid),
|
|
),
|
|
arguments=args
|
|
)
|
|
|
|
class GanttLink(command.CommandBase):
|
|
def __init__(self, from_uid, to_uid, kind):
|
|
self.from_uid = from_uid
|
|
self.to_uid = to_uid
|
|
self.kind = kind
|
|
|
|
link_type = ''
|
|
if kind == _project.PredecessorKind.start_start:
|
|
link_type = 's-s'
|
|
elif kind == _project.PredecessorKind.start_finish:
|
|
link_type = 's-f'
|
|
elif kind == _project.PredecessorKind.finish_start:
|
|
link_type = 'f-s'
|
|
elif kind == _project.PredecessorKind.finish_finish:
|
|
link_type = 'f-s'
|
|
|
|
super().__init__(
|
|
# options=NoEscape('link type=' + link_type),
|
|
arguments=command.Arguments(
|
|
uid_latex(from_uid),
|
|
uid_latex(to_uid),
|
|
)
|
|
)
|
|
|
|
class GanttChart(containers.Environment):
|
|
def __init__(self, project):
|
|
self.project = project
|
|
|
|
# initialize the chart environment with start and finish dates
|
|
super().__init__(
|
|
options='time slot format=isodate',
|
|
arguments=command.Arguments(
|
|
date_latex(project.start),
|
|
date_latex(project.finish),
|
|
)
|
|
)
|
|
|
|
# create task elements, one per line, in order
|
|
for num, task in enumerate(project.tasks):
|
|
self.append(GanttTask(task))
|
|
if num < len(project.tasks) - 1:
|
|
self.append(GanttNewLine())
|
|
|
|
# create precedence links
|
|
for task in project.tasks:
|
|
if task.predecessor_kind != _project.PredecessorKind.none:
|
|
self.append(GanttLink(
|
|
task.predecessor_uid,
|
|
task.uid,
|
|
task.predecessor_kind,
|
|
))
|