Skip to content

Commit

Permalink
simplify ranking, remove Attr Ranking, rename some ops
Browse files Browse the repository at this point in the history
  • Loading branch information
bjornregnell committed May 27, 2024
1 parent d0284a7 commit 84c4a10
Show file tree
Hide file tree
Showing 9 changed files with 32 additions and 79 deletions.
1 change: 0 additions & 1 deletion docs/concepts-GENERATED.csv
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ Product EntType ContextEnt An artifact offered to users or customers, e.g. an ap
Profit Attr IntAttr A gain or return of some entity, e.g. in monetary terms.
Prototype EntType DesignEnt A system with limited functionality used to demonstrate a design idea.
Quality EntType QualityEnt An aspect of system quality, distinguishing characteristic or degree of goodness.
Ranking Attr StrAttr A rank ordered sequence of entity ids separated by newline.
Relationship EntType DataEnt A specific way that data types are connected.
Release EntType ContextEnt A specific version of a product offered to end users at a specific time.
Req EntType GeneralEnt Something needed or wanted. An abstract term denoting any type of information relevant to the (specification of) intentions behind system development. Short for requirement.
Expand Down
2 changes: 1 addition & 1 deletion docs/langSpec-GENERATED.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ IntAttrType ::= 'Benefit' | 'Capacity' | 'Cost' | 'Damage' | 'Frequency' | 'Max'
'Probability' | 'Profit' | 'Value'
StrAttrType ::= 'Comment' | 'Constraints' | 'Deprecated' | 'Example' | 'Expectation' | 'Failure' | 'Gist' |
'Input' | 'Location' | 'Output' | 'Ranking' | 'Spec' | 'Text' | 'Title' | 'Why'
'Input' | 'Location' | 'Output' | 'Spec' | 'Text' | 'Title' | 'Why'
RelType ::= 'Binds' | 'Deprecates' | 'Excludes' | 'Has' | 'Helps' | 'Hurts' | 'Impacts' | 'Implements' |
'Inherits' | 'InteractsWith' | 'Precedes' | 'RelatesTo' | 'Requires' | 'Verifies'
Expand Down
2 changes: 1 addition & 1 deletion docs/metamodel-All-GENERATED.dot
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ digraph Metamodel {
AttrType [ label = "{AttrType[T]}" fontsize = 10]
RelType [ label = "{enum RelType|Binds, Deprecates,\lExcludes, Has,\lHelps, Hurts,\lImpacts, Implements,\lInherits, InteractsWith,\lPrecedes, RelatesTo,\lRequires, Verifies\l}" fontsize = 9]
EntType [ label = "{enum EntType|Actor, App, Barrier,\lBreakpoint, Class, Component,\lConfiguration, Data, Design,\lDomain, Epic, Event,\lFeature, Field, Function,\lGoal, Idea, Image,\lInterface, Issue, Item,\lLabel, Member, Module,\lProduct, Prototype, Quality,\lRelationship, Release, Req,\lResource, Risk, Scenario,\lScreen, Section, Service,\lStakeholder, State, Story,\lSystem, Target, Task,\lTerm, Test, UseCase,\lUser, Variant, VariationPoint,\lWorkPackage\l}" fontsize = 9]
StrAttrType [ label = "{enum StrAttrType|Comment, Constraints, Deprecated,\lExample, Expectation, Failure,\lGist, Input, Location,\lOutput, Ranking, Spec,\lText, Title, Why\l}" fontsize = 9]
StrAttrType [ label = "{enum StrAttrType|Comment, Constraints, Deprecated,\lExample, Expectation, Failure,\lGist, Input, Location,\lOutput, Spec, Text,\lTitle, Why\l}" fontsize = 9]
IntAttrType [ label = "{enum IntAttrType|Benefit, Capacity, Cost,\lDamage, Frequency, Max,\lMin, Order, Prio,\lProbability, Profit, Value\l}" fontsize = 9]


Expand Down
2 changes: 1 addition & 1 deletion docs/metamodel-ElemType-GENERATED.dot
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ digraph Metamodel {
AttrType [ label = "{AttrType[T]}" fontsize = 10]
RelType [ label = "{enum RelType|Binds, Deprecates,\lExcludes, Has,\lHelps, Hurts,\lImpacts, Implements,\lInherits, InteractsWith,\lPrecedes, RelatesTo,\lRequires, Verifies\l}" fontsize = 9]
EntType [ label = "{enum EntType|Actor, App, Barrier,\lBreakpoint, Class, Component,\lConfiguration, Data, Design,\lDomain, Epic, Event,\lFeature, Field, Function,\lGoal, Idea, Image,\lInterface, Issue, Item,\lLabel, Member, Module,\lProduct, Prototype, Quality,\lRelationship, Release, Req,\lResource, Risk, Scenario,\lScreen, Section, Service,\lStakeholder, State, Story,\lSystem, Target, Task,\lTerm, Test, UseCase,\lUser, Variant, VariationPoint,\lWorkPackage\l}" fontsize = 9]
StrAttrType [ label = "{enum StrAttrType|Comment, Constraints, Deprecated,\lExample, Expectation, Failure,\lGist, Input, Location,\lOutput, Ranking, Spec,\lText, Title, Why\l}" fontsize = 9]
StrAttrType [ label = "{enum StrAttrType|Comment, Constraints, Deprecated,\lExample, Expectation, Failure,\lGist, Input, Location,\lOutput, Spec, Text,\lTitle, Why\l}" fontsize = 9]
IntAttrType [ label = "{enum IntAttrType|Benefit, Capacity, Cost,\lDamage, Frequency, Max,\lMin, Order, Prio,\lProbability, Profit, Value\l}" fontsize = 9]


Expand Down
1 change: 0 additions & 1 deletion src/main/scala/02-meta-model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ object meta:
"Title" -> "A general or descriptive heading. One or more leading # indicate heading level.",
"Input" -> "Data consumed by an entity, ",
"Location" -> "A location of a resource such as a web address or a path to a file of persistent data.",
"Ranking" -> "A rank ordered sequence of entity ids separated by newline.",
"Output" -> "Data produced by an entity, e.g. a function or a test.",
"Spec" -> "A definition of an entity. Short for specification",
"Text" -> "An paragraph or general description.",
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/03-model-GENERATED.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ enum EntType extends NodeType:

enum StrAttrType extends AttrType[String]:
def apply(value: String): StrAttr = StrAttr(this, value)
case Comment, Constraints, Deprecated, Example, Expectation, Failure, Gist, Input, Location, Output, Ranking, Spec, Text, Title, Why
case Comment, Constraints, Deprecated, Example, Expectation, Failure, Gist, Input, Location, Output, Spec, Text, Title, Why

enum IntAttrType extends AttrType[Int]:
def apply(value: Int): IntAttr = IntAttr(this, value)
Expand Down
57 changes: 15 additions & 42 deletions src/main/scala/04-ModelMembers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,45 +128,18 @@ transparent trait ModelMembers:

def entTypesOfId(id: String): Set[EntType] = idTypeMap(id)

def entsOrderedBy(iat: IntAttrType): Vector[Ent] =
val lro: Model = leafRelsOf(iat)
lro.ents.sortBy(e => (lro / e.has / iat).headOption)

def toRankingFrom(iat: IntAttrType): Model = Model(StrAttr(Ranking, entsOrderedBy(iat).map(_.id).mkString("\n","\n","")))

def fromIdOrder(iat: IntAttrType, ids: Seq[String]): Model =
val indexOfId = ids.zipWithIndex.toMap
val pairs: Seq[(Ent, Int)] =
ids.map(entsOfId).filter(_.nonEmpty).map(_.head).map(e => e -> (indexOfId(e.id) + 1))
val rels = pairs.sortBy(_._2).map((e,i) => e.has(iat(i)))
Model(rels*)

def fromRankingTo(iat: IntAttrType) =
import meta.isEntType
val rankings: Vector[StrAttr] = nodes.collect { case a: StrAttr if a.t == Ranking => a }
val xss: Vector[Seq[String]] = rankings.map(r => r.value.words.toSeq)
val es: Vector[Ent] = xss.flatMap: xs =>
if xs.isEmpty then Vector(Req(""))
else
var i = 0
var result = Vector.empty[Ent]
while i < xs.length do
if isEntType(xs(i)) then
result :+= meta.entTypes(xs(i)).apply(xs.lift(i + 1).getOrElse(""))
i += 1
else if idMap.isDefinedAt(xs(i)) then
result = result :+ idMap(xs(i)).head
else result = result :+ Req(xs(i))
i += 1
result
val rels = es.zipWithIndex.map((e, i) => e.has(iat.apply(i + 1)))
Model(rels)
def rankBy[T](iat: IntAttrType, rt: RelType = Has): Vector[Ent] =
val lrs: Model = leafRelsOf(iat)
lrs.ents.sortBy(e => (lrs / Link(e, rt) / iat).headOption)

def withRank(iat: IntAttrType, rt: RelType = Has): Vector[Rel] =
ents.zipWithIndex.map((e, i) => Rel(e, rt, Model(iat.apply(i + 1))))

/** A new Model with distinct elems (non-recursive). **/
def distinctElems: Model = Model(elems.distinct)
def distinctTopElems: Model = Model(elems.distinct)

/** A new Model that is distinct by attribute type (non-recursive). **/
def distinctAttrType: Model =
def distinctTopAttrType: Model =
val foundAttrTypes: collection.mutable.Set[AttrType[?]] = collection.mutable.Set()
val es = elems.flatMap:
case a: Attr[?] =>
Expand Down Expand Up @@ -198,14 +171,14 @@ transparent trait ModelMembers:

/** A new Model with recursive de-duplication of its attributes by type on all levels. **/
def distinctAttrTypeDeep: Model =
val es = distinctAttrType.elems.map:
val es = distinctTopAttrType.elems.map:
case n: Node => n
case Rel(e, r, m) => Rel(e, r, m.distinctAttrTypeDeep)
Model(es)

/** Recursively sort elems alphabetically. */
// TODO: maybe better to give each Elem ordering and IntAttr special ordering???
def sorted: Model =
def sorted(using Ordering[Elem]): Model =
val es = elems.map:
case n: Node => n
case Rel(e, r, m) => Rel(e, r, m.sorted) // recur
Expand Down Expand Up @@ -236,20 +209,20 @@ transparent trait ModelMembers:
Model(ess.flatten.toVector)

/** A new model constructed by adding all elems using add one by one giving no duplicates. */
def distinct = Model() ++ self
def distinct = Model() ++ self // TODO: investigate if this is redundant to distinctElemsDeep???

/** A Model in normal form: elems are added one by one replacing same nodes and then sorted. **/
def normal: Model = distinct.sorted
def normalize: Model = distinct.sorted

def minimal: Model =
def prune: Model =
removeEmptyRelations.distinctElemsDeep.distinctAttrTypeDeep.distinctEntLinks.distinct

def atoms: Vector[Elem] = paths.flatMap(_.toModel.elems)

def maximal: Model = Model(atoms)
def expand: Model = Model(atoms)

/** True if this model is in normal form. */
def isNormal: Boolean = self == normal
def isNormal: Boolean = self == normalize

/** A Model with the nodes but not relations at the top of this Model. */
def tip: Model = cut(0)
Expand Down
43 changes: 13 additions & 30 deletions src/test/scala/TestModelOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class TestModelOps extends munit.FunSuite:
Model(Prio(42), Prio(2),Undefined(Prio))

assert:
Model(Prio(1),Req("x"),Prio(2),Undefined(Prio),Req("x")).updated(Prio(42)).distinctAttrType ==
Model(Prio(1),Req("x"),Prio(2),Undefined(Prio),Req("x")).updated(Prio(42)).distinctTopAttrType ==
Model(Prio(42),Req("x"),Req("x"))

assert:
Expand Down Expand Up @@ -105,15 +105,15 @@ class TestModelOps extends munit.FunSuite:
Req("x").has(Prio(1),Prio(2),Req("z"),Req("x"),Req("y"))
)

assert(m.normal == n)
assert(m.normalize == n)
assert(m.distinct == d)

assert:
Model(Prio(1),Prio(2),Req("x"),Req("y"),Req("x"),Prio(3)).distinctElems ==
Model(Prio(1),Prio(2),Req("x"),Req("y"),Prio(3))
Model(Prio(1),Prio(2),Req("x"),Req("y"),Req("x"),Prio(3)).elems.distinct ==
Model(Prio(1),Prio(2),Req("x"),Req("y"),Prio(3)).elems

assert:
Model(Prio(1),Prio(2),Req("x"),Req("y"),Req("x"),Prio(3)).distinctAttrType ==
Model(Prio(1),Prio(2),Req("x"),Req("y"),Req("x"),Prio(3)).distinctAttrTypeDeep ==
Model(Prio(1),Req("x"),Req("y"),Req("x"))

test("Model invariants "):
Expand All @@ -139,9 +139,9 @@ class TestModelOps extends munit.FunSuite:
assert:
m.distinct == m.paths.map(_.toModel).reduceLeft(_ ++ _)
assert:
m.maximal == m.paths.map(_.toModel).reduceLeft(_ :++ _)
m.expand == m.paths.map(_.toModel).reduceLeft(_ :++ _)
assert:
m.minimal.sorted == m.minimal.normal
m.prune.sorted == m.prune.normalize

assert:
m.paths.map(_.show).map(Path.fromString).map(_.get) == m.paths
Expand All @@ -152,10 +152,10 @@ class TestModelOps extends munit.FunSuite:
ms.forall(m => m.paths.map(_.show).map(Path.fromString).map(_.get) == m.paths)

assert:
ms.forall(m => m.minimal.maximal.normal == m.maximal.minimal.normal)
ms.forall(m => m.prune.expand.normalize == m.expand.prune.normalize)

assert:
ms.forall(m => m.minimal.maximal.normal == m.maximal.minimal.normal)
ms.forall(m => m.prune.expand.normalize == m.expand.prune.normalize)


// assert:
Expand Down Expand Up @@ -189,27 +189,10 @@ class TestModelOps extends munit.FunSuite:
),
)

assert(m.entsOrderedBy(Order) == Vector(Req("d"), Req("a"), Req("b"), Req("c"), Req("e")))

assert(m.toRankingFrom(Order) == m.toRankingFrom(Prio))
assert(m.toRankingFrom(Order).fromRankingTo(Prio) == m.toRankingFrom(Prio).fromRankingTo(Prio))

val r = Ranking:
""" Feature a Feature b
Req x, Req y
foo bar
Stakeholder y """
val mr = Model(r).fromRankingTo(Value)

assert(mr.toMarkdown ==
s"""|* Feature: a has Value: 1
|* Feature: b has Value: 2
|* Req: x has Value: 3
|* Req: y has Value: 4
|* Req: foo has Value: 5
|* Req: bar has Value: 6
|* Stakeholder: y has Value: 7
|""".stripMargin)
assert(m.rankBy(Order) == Vector(Req("d"), Req("a"), Req("b"), Req("c"), Req("e")))

assert(m.rankBy(Order) == m.rankBy(Prio))
assert(m.rankBy(Prio).toModel.withRank(Order).toModel.rankBy(Order) == m.rankBy(Order))

test("Model Examples "):
assert(examples.Prioritization.normalizedVotes().intValues.sum == 99)
Expand Down
1 change: 0 additions & 1 deletion src/test/scala/generateLang.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ object showDeprecations:
("Deleted", "Attribute", "Status") -> "use Label",
("Added", "Attribute", "Failure") -> "use together with Risk, Test",
("Added", "Attribute", "Location") -> "instead of FileName",
("Added", "Attribute", "Ranking") -> "used for ordered sequences of entities or ids",
("Deleted", "Relation", "is") -> "use inherits",
("Deleted", "Relation", "superOf") -> "use inherits in reverse direction",
("Added", "Relation", "inherits") -> "instead of is, superOf",
Expand Down

0 comments on commit 84c4a10

Please sign in to comment.