[General] Add API docs and pydantic for Data Dict

Fix schedule for latex
This commit is contained in:
Nick Touran 2025-12-12 09:40:02 -05:00
parent e474c140ee
commit fb28c6c5c5
11 changed files with 186 additions and 57 deletions

View file

View file

@ -9,13 +9,11 @@ import logging
import os
import re
from datetime import datetime
from glob import glob
from pathlib import Path
import jpype
import jpype.imports
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist as axisartist
import pandas as pd
import yaml
from docutils import nodes
@ -29,18 +27,12 @@ logger.setLevel(logging.DEBUG)
# Start JVM with MPXJ jar
jpype.startJVM(classpath=["/home/nick/repos/mpxj/mpxj-lib/*"])
from java.io import File
from java.io import File # noqa: E402
from java.time import LocalDateTime # noqa: E402
from org.mpxj import ( # noqa: E402
Availability,
Duration,
FieldType,
ProjectFile,
Relation,
RelationType,
Resource,
TaskField,
TaskType,
TimeUnit,
)
from org.mpxj.cpm import MicrosoftScheduler, PrimaveraScheduler # noqa: E402
@ -196,7 +188,7 @@ def _preprocess_plot(project):
return df, df_deps
def plot_schedule(
def plot_schedule( # noqa: C901
input_fname: str = "scheduled.xml", project=None, output_fname: str = "schedule.svg"
):
"""Generate plot of schedule."""
@ -270,7 +262,7 @@ def plot_schedule(
plt.title("AMS High-Level Schedule")
# plt.tight_layout()
plt.savefig(output_fname)
plt.show()
# plt.show()
class ScheduleDirective(Directive):
@ -281,65 +273,69 @@ class ScheduleDirective(Directive):
def run(self): # noqa: D102
env = self.state.document.settings.env
builder = env.app.builder
schedule_data = self.arguments[0]
schedule_data_abs = os.path.join(env.srcdir, schedule_data)
schedule_data_abs = Path(env.srcdir) / schedule_data
if not os.path.exists(schedule_data_abs):
if not schedule_data_abs.exists():
logger.error(f"Schedule file not found: {schedule_data_abs}")
return []
# Image output directory
gen_dir = os.path.join(env.app.srcdir, "generated_assets")
# put image within _static so html builder knows to copy it over.
gen_dir = Path(env.app.srcdir) / "_static" / "generated_assets"
ensuredir(gen_dir)
ensuredir(os.path.join(env.app.outdir, "_downloads"))
# Name of the generated file
base = os.path.splitext(os.path.basename(schedule_data))[0]
out_image = os.path.join(gen_dir, f"{base}.svg")
out_image = gen_dir / f"{base}.svg"
start_date = datetime(2026, 1, 1)
proj = load_from_yaml(fname=schedule_data)
solve_schedule(proj, start_date)
plot_schedule(project=proj, output_fname=out_image)
writer = UniversalProjectWriter(FileFormat.MSPDI)
writer.write(proj, os.path.join("_build", "_downloads", f"{base}_mspdi.xml"))
writer.write(proj, gen_dir / f"{base}_mspdi.xml")
env.note_dependency(schedule_data_abs)
rel = str(os.path.relpath(out_image, env.app.srcdir))
# trying to mock /generated_assets/schedule.svg for the build folder
# but it ends up in _images actually.
# somewhat hacky but works in subfolders
abs_rel = os.path.join("/", rel)
image_node = nodes.image(uri=abs_rel)
uri = builder.get_relative_uri(env.docname, "_images/" + f"{base}.svg")
uri = uri.replace(".html", "")
ref_node = nodes.reference("", "", refuri=uri)
ref_node += image_node
ref_node["target"] = "_blank"
ref_node["rel"] = "noopener"
uri_dl1 = builder.get_relative_uri(
env.docname, "_downloads/" + f"{base}_mspdi.xml"
)
uri_dl1 = uri_dl1.replace(".html", "")
download1 = nodes.reference(
text="Download schedule in MS Project XML format",
refuri=uri_dl1,
classes=["download-link"],
)
uri = f"/_static/generated_assets/{base}.svg"
image_node = nodes.image(uri=uri)
paragraph = nodes.paragraph()
paragraph += ref_node
paragraph += download1
# download link only makes sense in web env, not PDF
builder_name = self.state.document.settings.env.app.builder.name
if builder_name not in ("html", "singlehtml", "dirhtml"):
paragraph += image_node
else:
# add hyperlink to image. Since this may be called from a subdir we need
# relative paths that walk up appropriately.
docname = env.docname # subdir/mydoc
relative_root_path = "../" * docname.count(os.sep)
hyperlink_uri = relative_root_path + uri[1:]
# Result when docname is 'subdir/mydoc':
# hyperlink_uri will be: ../_static/generated_assets/my_diagram.svg
ref_node = nodes.reference("", "", refuri=hyperlink_uri)
ref_node += image_node
ref_node["target"] = "_blank"
ref_node["rel"] = "noopener"
paragraph += ref_node
# and hyperlink to schedule data
hyperlink_uri = (
relative_root_path + f"_static/generated_assets/{base}_mspdi.xml"
)
download1 = nodes.reference(
text="Download schedule in MS Project XML format",
refuri=hyperlink_uri,
classes=["download-link"],
)
paragraph += download1
return [paragraph]
def setup(app):
"""Setup for sphinx extension."""
def setup(app): # noqa: D103
app.add_directive("schedule", ScheduleDirective)
return {