Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ dependencies = [
"pydantic>=2.11.7",
"semver>=3.0.4",
"fastapi[all,standard]>=0.116.1",
"pyyaml>=6.0.2",
]
dynamic = ["version",]

Expand Down
32 changes: 32 additions & 0 deletions src/experimental/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env python

# Copyright (c) 2025 Carnegie Mellon University.
# NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE
# ENGINEERING INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS.
# CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND,
# EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT
# NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR
# MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE
# OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE
# ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM
# PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
# Licensed under a MIT (SEI)-style license, please see LICENSE or contact
# permission@sei.cmu.edu for full terms.
# [DISTRIBUTION STATEMENT A] This material has been approved for
# public release and unlimited distribution. Please see Copyright notice
# for non-US Government use and distribution.
# This Software includes and/or makes use of Third-Party Software each
# subject to its own license.
# DM24-0278

"""
Provides TODO writeme
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add file docstring

"""


def main():
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove main() etc.

pass


if __name__ == "__main__":
main()
174 changes: 174 additions & 0 deletions src/experimental/gh_issue_forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/usr/bin/env python

# Copyright (c) 2025 Carnegie Mellon University.
# NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE
# ENGINEERING INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS.
# CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND,
# EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT
# NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR
# MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE
# OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE
# ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM
# PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
# Licensed under a MIT (SEI)-style license, please see LICENSE or contact
# permission@sei.cmu.edu for full terms.
# [DISTRIBUTION STATEMENT A] This material has been approved for
# public release and unlimited distribution. Please see Copyright notice
# for non-US Government use and distribution.
# This Software includes and/or makes use of Third-Party Software each
# subject to its own license.
# DM24-0278

"""
Provides TODO writeme
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add file docstring

"""
import json

import yaml
from pydantic import BaseModel

from ssvc.decision_points.base import DecisionPoint
from ssvc.decision_tables.base import DecisionTable
from ssvc.registry import get_registry


class GhFormCheckboxOption(BaseModel):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split GhForm* classes into their own separate module for maintenance

label: str
required: bool | None = None


class GhFormCheckboxes(BaseModel):
label: str
description: str | None = None
options: list[GhFormCheckboxOption]


class GhFormCheckboxesValidations(BaseModel):
required: bool | None = None


class GhFormElement(BaseModel):
type: str
id: str | None = None
attributes: GhFormCheckboxes
validations: GhFormCheckboxesValidations | None = None


class GhForm(BaseModel):
name: str
description: str
title: str | None = None
body: list[GhFormElement] | str | None = None
assignees: list[str] | None = None
labels: list[str] | None = None
type: str | None = None
projects: list[str] | None = None


def to_checkboxes(decision_point) -> GhFormCheckboxes:
"""
Given a decision point, return a github issue form compatible checkbox list
Args:
decision_point:

Returns:
GhFormCheckboxes: A github issue form compatible checkbox list
"""
description = f"{decision_point.definition.strip()}\n"
description += "\n".join(
f"- (**{option.key.strip()}**) *{option.name.strip()}*: {option.definition.strip()}"
for option in decision_point.values
)

cb = GhFormElement(
type="checkboxes",
attributes=GhFormCheckboxes(
label=decision_point.name,
description=description.strip(),
options=[
GhFormCheckboxOption(
label=f"{option.key} | {option.name}",
)
for option in decision_point.values
],
),
)

return cb


def json_to_yaml(json_str: str) -> str:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move json_to_yaml to a utils or helper module

data = json.loads(json_str)
return yaml.dump(data, sort_keys=False)


def expand_dps(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move expand_dps to a utils or helper module, this will be useful elsewhere.

Also consider making outcome exclusion optional at the top level

dt: DecisionTable, other_dts: list[DecisionTable]
) -> list[DecisionPoint]:
"""
Recursively expand a decision table to a list of decision points.
Skips the outcome decision point.

Args:
dt: DecisionTable to expand
other_dts: A list of other DecisionTables to use for expansion

Returns:
list[DecisionPoint]: A list of DecisionPoints collected after expansion
"""

dt_by_outcomes = {dt.outcome: dt for dt in other_dts}

dps = []
for dp in dt.decision_points.values():
if dp.id == dt.outcome:
# skip the outcome decision point
continue

if dp.id in dt_by_outcomes:
# we have another decision table that defines this decision point as its outcome
# so we need to expand that decision table too
dps.extend(
expand_dps(
dt_by_outcomes[dp.id],
[odt for odt in other_dts if odt.outcome != dp.id],
)
)
continue

# otherwise, just add the decision point to the list
dps.append(dp)

return dps


def main():
# load one decision point
# import registry

registry = get_registry()

from ssvc.decision_tables.ssvc.supplier_dt import LATEST as supplier_dt
from ssvc.decision_tables.ssvc.utility import LATEST as utility_dt
from ssvc.decision_tables.ssvc.public_safety_impact import (
LATEST as public_safety_dt,
)

form = GhForm(
name="SSVC Form Example",
title="SSVC Supplier Decision Table Submission",
description="Issue Form Containing SSVC Supplier Decision Table Form",
body=[],
)

for dp in expand_dps(supplier_dt, [utility_dt, public_safety_dt]):

cb = to_checkboxes(dp)
form.body.append(cb)

form_yaml = json_to_yaml(form.model_dump_json(indent=2, exclude_none=True))
print(form_yaml)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading