π

UOMF: Requirement Analysis and Solution Deciding Template in Yankpad

Show Sidebar

This is an article from a series of blog postings. Please do read my "Using Org Mode Features" (UOMF) series page for explanations on articles of this series.

In my "How to choose a tool" article, I describe the general approach how I do think that you should make tool choices.

Well, it's not just tool choices - the process can be generalized to any choice making process, if you think about it.

In order to support this process for a few steps, I generated an Org-mode yankpad template which minimizes the manual effort to create the boilerplate.

How the template works:

Workflow after expansion:

  1. Fill in requirements (rows) in the first table — feature name, class (-2..2), weight (≥1)
  2. Add matching rows to the evaluation table, then C-c C-c on the #+TBLFM to pull feature names
  3. Add solution columns with M-S-right as needed
  4. Fill in coverage values (0/1/2)
  5. C-c C-c on the source block → breakdown + ranked results

Requirement Analysis: Example Vacation Project

This is how it looks, when applied. Notice that in that example, I omitted the optional weight feature in the "Feature" table to keep it simple. Furthermore, don't be offended by my example rating: it's just for demo purposes.

Requirements

Define features and classify them by importance.

Feature Class Weight
Good price 1 1
Good weather 2
Availability of ocean 1
Cultural possbilities 1

Solution Evaluation

Rate how strongly each solution exhibits each feature.

Feature Vienna Bahamas Stockholm
Good price 1 0 1
Good weather 1 2 0
Availability of ocean 0 2 1
Cultural possbilities 2 0 1

Results

Scoring (higher is better):

Press C-c C-c on the source block to compute results.

2026-03-22-example-results

reqs = [r for r in reqs if r is not None]
ev = [r for r in ev if r is not None]

solutions = ev[0][1:]
req_rows = reqs[1:]
ev_rows = ev[1:]

h = "| Requirement | Cl | W |"
for s in solutions:
    h += " {} cov | pts |".format(s)
lines = [h, "|-"]

totals = [0] * len(solutions)

for i, rr in enumerate(req_rows):
    feat = rr[0]
    cls = int(rr[1])
    try:
        w = int(rr[2])
    except (ValueError, TypeError):
        w = 1
    row = "| {} | {} | {} |".format(feat, cls, w)
    er = ev_rows[i]
    for j in range(len(solutions)):
        cov = int(er[1 + j])
        if cls >= 0:
            pts = cls * w * cov
        else:
            pts = abs(cls) * w * (1 - cov)
        totals[j] += pts
        row += " {} | {:+d} |".format(cov, pts)
    lines.append(row)

lines.append("|-")
total_row = "| *Total* |  |  |"
for t in totals:
    total_row += "  | *{:+d}* |".format(t)
lines.append(total_row)

lines += ["", "| Rank | Solution | Score |", "|-"]
ranked = sorted(zip(solutions, totals), key=lambda x: -x[1])
for rank, (sol, sc) in enumerate(ranked, 1):
    lines.append("| {} | {} | {:+d} |".format(rank, sol, sc))

return "\n".join(lines)	  

#+RESULTS: 2026-03-22-example-results

Requirement Cl W Vienna cov pts Bahamas cov pts Stockholm cov pts
Good price 1 1 1 +1 0 +0 1 +1
Good weather 2 1 1 +2 2 +4 0 +0
Availability of ocean 1 1 0 +0 2 +2 1 +1
Cultural possbilities 1 1 2 +2 0 +0 1 +1
Total +5 +6 +3
Rank Solution Score
1 Bahamas +6
2 Vienna +5
3 Stockholm +3

The Template

Here is the raw template. If you're new to yankpad, please do refer to the project's documentation how to use it. It's very similar to yasnippet but its definitions are managed within an Orgdown file.

`(progn (setq yas--ra-h1 (make-string (+ 1 (or (org-current-level) 0)) ?*) yas--ra-h2 (make-string (+ 2 (or (org-current-level) 0)) ?*)) "")`
`yas--ra-h1` Requirement Analysis: ${2:Project Title}

`yas--ra-h2` Requirements

Define features and classify them by importance.

- *Class* (integer, =-2= to =2=): how important is this feature?
  | -2 | must not have |
  | -1 | don't want    |
  |  0 | neutral       |
  |  1 | nice to have  |
  |  2 | must have     |
- *Weight* (integer ≥ 1): optional amplifier within a class.
  Leave at =1= if all requirements in a class are equally important.

#+NAME: ${1:`(format-time-string "%Y-%m-%d")`}-requirements
| Feature | Class | Weight |
|---------+-------+--------|
| $0      |       |      1 |

`yas--ra-h2` Solution Evaluation

Rate how strongly each solution exhibits each feature.

- *Coverage* (integer, =0= to =2=):
  | 0 | not present      |
  | 1 | somewhat present |
  | 2 | fully present    |
- To add solutions: insert columns with =M-S-<right>=.
- Feature names auto-fill: press =C-c C-c= on the =TBLFM= line.
- Ensure this table has the same number of data rows as the requirements table.

#+NAME: $1-evaluation
| Feature | ${3:Solution A} | ${4:Solution B} |
|---------+-----------------+-----------------+
|         |                 |                 |
#+TBLFM: \$1=remote($1-requirements, @@#\$1)

`yas--ra-h2` Results

Scoring (higher is better):
- Class ≥ 0: =class × weight × coverage=
- Class < 0: =|class| × weight × (1 − coverage)=

Press =C-c C-c= on the source block to compute results.

#+NAME: $1-results
#+BEGIN_SRC python :var reqs=$1-requirements ev=$1-evaluation :colnames no :results value raw
reqs = [r for r in reqs if r is not None]
ev = [r for r in ev if r is not None]

solutions = ev[0][1:]
req_rows = reqs[1:]
ev_rows = ev[1:]

h = "| Requirement | Cl | W |"
for s in solutions:
    h += " {} cov | pts |".format(s)
lines = [h, "|-"]

totals = [0] * len(solutions)

for i, rr in enumerate(req_rows):
    feat = rr[0]
    cls = int(rr[1])
    try:
        w = int(rr[2])
    except (ValueError, TypeError):
        w = 1
    row = "| {} | {} | {} |".format(feat, cls, w)
    er = ev_rows[i]
    for j in range(len(solutions)):
        cov = int(er[1 + j])
        if cls >= 0:
            pts = cls * w * cov
        else:
            pts = abs(cls) * w * (1 - cov)
        totals[j] += pts
        row += " {} | {:+d} |".format(cov, pts)
    lines.append(row)

lines.append("|-")
total_row = "| *Total* |  |  |"
for t in totals:
    total_row += "  | *{:+d}* |".format(t)
lines.append(total_row)

lines += ["", "| Rank | Solution | Score |", "|-"]
ranked = sorted(zip(solutions, totals), key=lambda x: -x[1])
for rank, (sol, sc) in enumerate(ranked, 1):
    lines.append("| {} | {} | {:+d} |".format(rank, sol, sc))

return "\n".join(lines)
#+END_SRC	  

Related articles that link to this one:

Comment via using the hashtag #20260321_UOMFRequirementAnalysisTemplate (decentralized), email (persistent) or via Disqus (ephemeral) comments below: