project2latex/project2latex/latex.py

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,
))