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