From 0fd0eaab3b8bfc154b83a3507c3caa9d9ab556c6 Mon Sep 17 00:00:00 2001 From: Jonas Smedegaard Date: Mon, 1 Apr 2024 03:22:12 +0200 Subject: use PlantUML for Gantt diagrams --- _report.yml | 2 + _themes/doubleslash/README.md | 3 + _themes/doubleslash/general.puml | 73 ++++++++++++++ _themes/doubleslash/pgantt.puml | 70 ++++++++++++++ bin/hedgedoc2quarto | 200 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 _themes/doubleslash/README.md create mode 100644 _themes/doubleslash/general.puml create mode 100644 _themes/doubleslash/pgantt.puml diff --git a/_report.yml b/_report.yml index 0117bfe..44be2ba 100644 --- a/_report.yml +++ b/_report.yml @@ -10,6 +10,8 @@ format: # links-as-notes: true colorlinks: false pdf-engine: lualatex +# permit preprocessing PlantUML SVG files with inkscape + pdf-engine-opt: --shell-escape documentclass: scrreprt classoption: - DIV=calc diff --git a/_themes/doubleslash/README.md b/_themes/doubleslash/README.md new file mode 100644 index 0000000..80d3431 --- /dev/null +++ b/_themes/doubleslash/README.md @@ -0,0 +1,3 @@ +# PlantUML Theme + +Origin: diff --git a/_themes/doubleslash/general.puml b/_themes/doubleslash/general.puml new file mode 100644 index 0000000..b8941ce --- /dev/null +++ b/_themes/doubleslash/general.puml @@ -0,0 +1,73 @@ +''Define colors in doubleSlash corporate identity +!define DS_BLUE #00A5E1 +!define DS_LIGHTBLUE #D7E9F4 +!define DS_ORANGE #FF9F00 +!define DS_LIGHTGREY #C6C6C6 +!define DS_GREY #7A7A7A +!define DS_DARKGREY #515151 + +!define SUPERLIGHTGREY #F8F8F8 +!define ALICEBLUE #F0F8FF +!define STEELBLUE25 #374656 +!define STEELBLUE40 #8A9DB3 +!define BLACK50 #7F7F7F +!define BLACK25 #404040 +!define LIGHTGREEN #D5E8D4 + +!define TIPBGCOLOR #FFF6E7 +!define TIPBORDERCOLOR #FFD999 + +!define FONTNAME "Arial" + +skinparam Shadowing false +skinparam Padding 2 +skinparam Roundcorner 16 +skinparam BackgroundColor white +skinparam DefaultFontName FONTNAME +skinparam DefaultFontColor black +skinparam DefaultTextAlignment center + +' Title +'================================================================= +skinparam TitleFontSize 28 +skinparam TitleFontName FONTNAME +skinparam TitleFontColor black +skinparam TitleFontStyle normal + +' Note +'================================================================= +'skinparam NoteBackgroundColor TIPBGCOLOR +'skinparam NoteBorderColor TIPBORDERCOLOR +'skinparam NoteBorderThickness 0.3 +'skinparam NoteShadowing true +skinparam NoteBackgroundColor transparent +skinparam NoteBorderColor transparent +skinparam NoteBorderThickness 0.0 +skinparam NoteFontColor black +skinparam NoteFontSize 11 +skinparam NoteTextAlignment left + +' Rectangle +'================================================================= +skinparam RectangleBackgroundColor SUPERLIGHTGREY +skinparam RectangleBorderColor DS_DARKGREY +skinparam RectangleBorderColor DS_GREY +skinparam RectangleBorderThickness 0.5 +skinparam RectangleFontColor black +skinparam RectangleFontSize 14 + + +' Package +'================================================================= +skinparam PackageFontColor black +skinparam PackageBackgroundColor SUPERLIGHTGREY +skinparam PackageBorderColor DS_DARKGREY +skinparam PackageFontSize 14 + +' Arrow +'================================================================= +skinparam ArrowColor DS_GREY +skinparam ArrowThickness 1.5 +skinparam ArrowFontName FONTNAME +skinparam ArrowFontColor black +skinparam ArrowFontStyle normal diff --git a/_themes/doubleslash/pgantt.puml b/_themes/doubleslash/pgantt.puml new file mode 100644 index 0000000..15a3e1d --- /dev/null +++ b/_themes/doubleslash/pgantt.puml @@ -0,0 +1,70 @@ +!include general.puml + +'saturday are closed +'sunday are closed +'language de + +skinparam Roundcorner 6 + +saturday are colored in SUPERLIGHTGREY +sunday are colored in SUPERLIGHTGREY +printscale weekly zoom 4 + + diff --git a/bin/hedgedoc2quarto b/bin/hedgedoc2quarto index c0bde3b..c81ddd5 100755 --- a/bin/hedgedoc2quarto +++ b/bin/hedgedoc2quarto @@ -26,7 +26,8 @@ and adapts embedded diagram code. Both HedgeDoc and Quarto uses Markdown, but different flavors, -and they handle different subsets of Mermaid diagrams. +and whereas both handle (different subsets of) Mermaid diagrams, +Quarto also (through plugins) handles PlantUML diagrams. =cut @@ -43,8 +44,12 @@ $content =~ s/^ (?'code'.*?\n) \k'fence' $/ - "{mermaid}\n\%\%| fig-width: 100\%\n" - . &mmd2mmd( $+{type}, $+{code} ) + +# FIXME: implement option to choose output diagram language +# "{mermaid}\n\%\%| fig-width: 100\%\n" +# . &mmd2mmd( $+{type}, $+{code} ) + "{.plantuml}\n\%\%| fig-width: 100\%\n" + . &mmd2puml( $+{type}, $+{code} ) . $+{fence} /gsmex; @@ -65,6 +70,195 @@ sub mmd2mmd ( $type, $code ) return "$type\n$code"; } +sub mmd2puml ( $type, $code ) +{ + my @newcode; + + # strip special comment marker '%%QUARTO%%' + $code =~ s/^\s*+\K%%QUARTO%%//gm; + + open my $fh, '<', \$code or die $!; + + while (<$fh>) { + + /^\s*+$/ + and push @newcode, '' + and next; + + /^(\s*+)%%PLANTUML%%\K.*/ + and push @newcode, "$1$&" + and next; + + # convert comments markers + /^(\s*+)(?:[%]{2,}(?'comment'\s*+))?+\K.*/; + my $indent = defined( $+{comment} ) ? "$1'$2" : $1; + $_ = $&; + + /^title\s/i + and push @newcode, "${indent}$_" + and next; + + /^excludes\s+weekends\b/i + and push @newcode, "${indent}saturday are closed" + and push @newcode, "${indent}sunday are closed" + and next; + /^weekday\s+\K(?:mon|tues|wednes|thurs|fri|satur|sun)day\b/i + and push @newcode, "${indent}weeks start on $&" + and next; + /^(?:date|axis)Format\s/i + and push @newcode, "${indent}'UNSUPPORTED: $_" + and next; + /^todayMarker\s+(off|on)\b/i + and push @newcode, "${indent}'UNSUPPORTED' $_" + and next; + /^section\s+\K\S+(?:\s+\S+)*/i + and push @newcode, "${indent}-- $& --" + and next; + + if (/^tickInterval\s+(?'tickAmount'\d+)(?'tickUnit'millisecond|second|minute|hour|day|week|month)\s*$/i + ) + { + push @newcode, "${indent}projectscale daily" + and next + if $+{tickAmount} eq 1 + and $+{tickUnit} eq 'day'; + push @newcode, "${indent}projectscale weekly" and next + if $+{tickAmount} eq 1 and $+{tickUnit} eq 'week' + or $+{tickAmount} eq 7 and $+{tickUnit} eq 'day'; + push @newcode, "${indent}projectscale monthly" + and next + if $+{tickAmount} eq 1 + and $+{tickUnit} eq 'month'; + push @newcode, "${indent}projectscale quarterly" + and next + if $+{tickAmount} eq 3 + and $+{tickUnit} eq 'month'; + push @newcode, "${indent}projectscale yearly" + and next + if $+{tickAmount} eq 12 + and $+{tickUnit} eq 'month'; + push @newcode, "${indent}'UNSUPPORTED' $&" + and next; + } + + /^ + (?'title'[^:\n]+) + \s*+:\s*+ + + # optional tags + (?: + (?: + (?'active'active) + | + (?'done'done) + | + (?'crit'crit) + | + (?'milestone'milestone) + )\s*+ + ,\s*+ + )?+ + + (?: + # optional tertiary item + (?: + (?'taskID'(?&id))\s*+ + ,\s*+ + (?=.*,) # several items must follow + )?+ + + # optional secondary item + (?: + (?'startDate'(?&date)) + | + after + (?'afterTaskIDs' + (?:\s+(?&id))++ + ) + )\s*+ + ,\s*+ + )?+ + + # required main item + (?: + (?'endDate'(?&date)) + | + until + (?'untilTaskIDs' + (?:\s+(?&id))++ + ) + | + (?'duration'\d+) + \s*+d + )\s*+ + (?(DEFINE) + (?'id'[^\s\d,][^\s,]*+) # assume digit as lead caracter is illegal + (?'date'\d\d\d\d(?:-\d\d(?:-\d\d)?+)?+) + ) + $/x + or defined( $+{comment} ) + and push @newcode, "${indent}$_" + and next + or die "unhandled syntax on line $.: $_"; + + defined( $+{active} ) + or defined( $+{done} ) + or defined( $+{crit} ) + and die "unhandled tag on line $.: $_"; + + my $task = "${indent}\[$+{title}]"; + my $taskref = $task; + + # optional 3rd item + if ( $+{taskID} ) { + $task .= " as [$+{taskID}]"; + $taskref = "${indent}\[$+{taskID}]"; + } + + if ( defined( $+{afterTaskIDs} ) ) { + my @reqs = split ' ', $+{afterTaskIDs}; + + if ( $+{milestone} ) { + push @newcode, "$task happens at [$_]'s end" for @reqs; + } + elsif ( $+{endDate} ) { + push @newcode, "$task ends $+{endDate}"; + push( @newcode, "$taskref starts at [$_]'s end" ) for @reqs; + } + elsif ( defined( $+{untilTaskIDs} ) ) { + my @reqsEnd = split ' ', $+{untilTaskIDs}; + push @newcode, "$task ends at [$_]'s end" for @reqsEnd; + push( @newcode, "$taskref starts at [$_]'s end" ) for @reqs; + } + else { + push @newcode, "$task requires $+{duration} days"; + push( @newcode, "$taskref starts at [$_]'s end" ) for @reqs; + } + } + else { + if ( $+{milestone} ) { + push @newcode, "$task happens $+{startDate}"; + } + elsif ( $+{endDate} ) { + push @newcode, + "$task starts $+{startDate} and ends $+{ednDate}"; + } + elsif ( defined( $+{untilTaskIDs} ) ) { + my @reqsEnd = split ' ', $+{untilTaskIDs}; + push @newcode, "$task starts $+{startDate}"; + push @newcode, "$task ends at [$_]'s end" for @reqsEnd; + } + else { + push @newcode, + "$task starts $+{startDate} and requires $+{duration} days"; + } + } + } + + $" = "\n"; + return "\@start$type\n@newcode\n\@end$type\n"; +} + =encoding UTF-8 =head1 AUTHOR -- cgit v1.2.3