Skip to content

Commit

Permalink
Merge pull request #100 from lsst-dm/u/womullan/summaryschedule
Browse files Browse the repository at this point in the history
summary schedule:
  • Loading branch information
womullan committed Dec 11, 2022
2 parents feecdb1 + c0e0f38 commit 54a5673
Show file tree
Hide file tree
Showing 13 changed files with 829 additions and 12 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/trigger-updates.yaml
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
repo: [lsst-sitcom/sitcomtn-052, lsst-dm/dmtn-158, lsst/ldm-564, lsst/ldm-503, lsst-dm/dmtn-232]
repo: [lsst-sitcom/sitcomtn-052, lsst-dm/dmtn-158, lsst/ldm-564, lsst/ldm-503]
steps:
- uses: actions/checkout@v3
with:
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:

# auto merge for dmtn-232 is a too public so will manually check and merge it
- name: Enable Pull Request Automerge
if: steps.cpr.outputs.pull-request-operation == 'created' and matrix.repo != 'lsst-dm/dmtn-232'
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v1
# requires branch protection
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -3,3 +3,4 @@ __pycache__
venv
gantt.*
.DS_Store
cartoon.pdf
6 changes: 6 additions & 0 deletions Makefile
@@ -1,5 +1,11 @@
VENVDIR = venv

blockschedule.pdf: venv
@( \
source $(VENVDIR)/bin/activate; \
python milestones.py blockschedule --start-date -20 \
)

burndown.png: venv
( \
source $(VENVDIR)/bin/activate; \
Expand Down
4 changes: 2 additions & 2 deletions data/pmcs/202210-ME.xls
Git LFS file not shown
18 changes: 17 additions & 1 deletion milestones.py
Expand Up @@ -131,6 +131,20 @@ def parse_args():
)
graph.set_defaults(func=milestones.graph)

# RHL cartoon based on P6 "summary chart" and "celebratory milestone" entries
blockschedule = subparsers.add_parser(
"blockschedule", help="Generate the cartoon of the schedule."
)
blockschedule.add_argument("--output", help="Filename for output",
default="blockschedule.pdf")
blockschedule.add_argument("--start-date",
help="Starting date for cartoon (ISO 8601 format)")
blockschedule.add_argument("--end-date",
help="Ending date for cartoon (ISO 8601 format)")
blockschedule.add_argument("--show-weeks", help="Show week boundaries",
action='store_true', default=False)
blockschedule.set_defaults(func=milestones.blockschedule)

args = parser.parse_args()

log_levels = [logging.WARN, logging.INFO, logging.DEBUG, logging.NOTSET]
Expand All @@ -145,7 +159,9 @@ def parse_args():
if __name__ == "__main__":
args = parse_args()
print("Working with "+args.pmcs_data)
milestones = milestones.load_milestones(args.pmcs_data, args.local_data)
load_tasks = (args.func == milestones.blockschedule)
milestones = milestones.load_milestones(args.pmcs_data, args.local_data,
load_tasks)
if "months" in args and args.months > 0:
fpath = get_pmcs_path_months(args.pmcs_data, args.months)
load_f2due_pmcs_excel(fpath, milestones)
Expand Down
1 change: 1 addition & 0 deletions milestones/__init__.py
@@ -1,4 +1,5 @@
from .burndown import *
from .blockschedule import *
from .csv import *
from .celeb import *
from .delayed import *
Expand Down
167 changes: 167 additions & 0 deletions milestones/blockschedule.py
@@ -0,0 +1,167 @@
# RHL generate the block schedule diagram from the milestones with
# Summary Chart entries.

import os
import sys

import numpy as np
import matplotlib.pyplot as plt

from .cartoon import show_activities, add_legend, Activity, AdvanceRow, Milestone, Nrow, Rotation
from .cartoon_config import \
categoryNrow, categoryColors, wrappedDescrip, categoryGrouping, specials, \
nRowOfMilestones, milestoneHeight, legend_location


def blockschedule(args, milestones):
# Process Summary Chart activities/milestones and celebratory milestones

activities, celebrations = process_milestones(milestones)

blocks = create_blocks(activities, celebrations)

plt.figure(figsize=(10, 8))
show_activities(blocks, height=1, fontsize=5, show_today=True, title=os.path.split(args.pmcs_data)[1],
show_weeks=args.show_weeks, startDate=args.start_date, endDate=args.end_date)

add_legend(categoryColors, blocks, categoryGrouping, legend_location=legend_location)

plt.savefig(args.output)


def create_blocks(activities, celebrations):
#
# Convert those activities to a set of block descriptions
#
blocks = {}
row = 1
for category in activities:
blocks[category] = []

nrow = categoryNrow.get(category, 5)
nrow, rot = (nrow, None) if isinstance(nrow, int) else nrow

blocks[category] = [Nrow(nrow), Rotation(rot)]

color = categoryColors.get(category)
if color is None:
print(f"No colour is defined for category {category}", file=sys.stderr)
color = ("white", "red")
color, border = (color, None) if isinstance(color, str) else color

row += 1

for descrip in sorted(activities[category]):
start = np.min([start for (code, start, due) in activities[category][descrip]])
due = np.max([due for (code, start, due) in activities[category][descrip]]) # noqa: E221,E272

if descrip in specials:
nrow, rot, nadvance = specials[descrip]
if nrow is not None:
blocks[category].append(Nrow(nrow))
if rot is not None:
blocks[category].append(Rotation(rot))
if nadvance is not None:
blocks[category].append(AdvanceRow(nadvance))

if descrip[0] == '"' and descrip[-1] == '"':
descrip = descrip[1:-1]

blocks[category].append(Activity(descrip, start, due, color, border,
wrappedDescrip=wrappedDescrip.get(descrip)))

#
# Order/group according to categoryGrouping[]
#
if categoryGrouping is None:
grouping = [[cat] for cat in activities]
else:
grouping = categoryGrouping

_blocks = []
for cats in grouping:
sub = []
for cat in cats:
for a in blocks[cat]:
sub.append(a)

_blocks.append(sub)

blocks = _blocks
#
# Handle celebrations milestones now that we've ordered the blocks
#
Milestone.height = milestoneHeight
Milestone.rotation = 0

milestones = []
for c in celebrations:
name, start = c
milestones.append(Milestone(name, start))

milestones = sorted(milestones, key=lambda a: a.t0)
for i, ml in enumerate(milestones):
ml.drow = i % nRowOfMilestones

milestones.append(AdvanceRow((nRowOfMilestones - 1)*milestoneHeight))

blocks = [milestones] + blocks

return blocks


def process_milestones(milestones):
# Process Summary Chart activities/milestones and celebrations milestones

activities = {}
celebrations = []

for ms in milestones:
summarychart, code, start, due = ms.summarychart, ms.code, ms.start, ms.due

celebrate = False
if ms.summarychart:
pass
elif ms.celebrate:
celebrate = ms.celebrate.lower()
else:
continue
#
# handle dates, either of which may have been omitted
#
if start is None:
if due is None: # HACK
print(f"{ms} has no date field; skipping", file=sys.stderr)
continue
else:
start = due
elif due is None:
due = start
#
# Is it a celebrations milestone rather than an activity?
#
if celebrate in ("top", "y"): # it's a celebrations milestone
if celebrate == "y":
continue

celebrations.append((ms.name, due))
continue

if '.' not in summarychart:
summarychart = f"{summarychart}.{summarychart}"

category, descrip = summarychart.split(".")
category = category.replace(' ', '_')

if "," in descrip:
descrip = f'"{descrip}"'

if category not in activities:
activities[category] = {}

if descrip not in activities[category]:
activities[category][descrip] = []

activities[category][descrip].append((code, start, due))

return activities, celebrations

0 comments on commit 54a5673

Please sign in to comment.