Packages

Toolkit for acquisition target screening: thesis briefs, a weighted composite that zeroes out group-owned targets, ranking and signal coverage.

Current section

Files

Jump to
Raw

README.md

# acquisitionuniverse for Elixir

Elixir functions for people who work through acquisition target lists. Rank companies, validate a thesis brief, print it as text and count which of the 15 common signals are actually visible. Plain maps in, plain maps out, no dependencies.

```elixir
{:acquisitionuniverse, "~> 1.0"}
```

## Rank

```elixir
companies = [
  %{id: "A", mandate_fit: 90, outreach_suitability: 80, transition_context: 50},
  %{id: "B", mandate_fit: 96, outreach_suitability: 90, transition_context: 70, group_owned: true}
]

AcquisitionUniverse.rank(companies) |> Enum.map(&{&1.id, &1.composite})
# [{"A", 84.0}, {"B", 74.2}]
```

B is the stronger fit. It is owned by a group, so it carries no outreach points and A comes first. Provide your own weights as a map with `:mandate_fit`, `:outreach_suitability` and `:transition_context`.

## Brief

```elixir
brief = %{
  name: "Precision machining, Carolinas",
  vertical: "Precision machining",
  regions: ["North Carolina", "South Carolina"],
  employees_min: 10,
  employees_max: 90,
  exclusions: ["group-owned"]
}

[] = AcquisitionUniverse.validate_brief(brief)
IO.puts(AcquisitionUniverse.brief_text(brief))
```

## Coverage

```elixir
[s0, _, s2 | _] = AcquisitionUniverse.signals()
AcquisitionUniverse.signal_coverage(%{s0 => "family associated", s2 => nil})
# %{visible: 1, checked: 2, ratio: 0.5}
```

## Functions

| Function | Result |
|---|---|
| `signals/0` | list of 15 names |
| `default_weights/0` | 0.7, 0.2, 0.1 |
| `composite/2` | float, 0 to 100 |
| `rank/2` | maps with `:composite`, best first |
| `signal_coverage/1` | map with visible, checked, ratio |
| `validate_brief/1` | list of problems |
| `brief_text/1` | string |
| `pilot_url/0` | pilot request page |

## LiveView idea

A LiveView with a form for the weights and a table for `rank/2` lets a partner drag the balance between fit and suitability and watch the list reorder. The data is just a list of maps, so `Phoenix.LiveView.stream/3` fits well.

## Process a CSV

```elixir
rows =
  "screen.csv"
  |> File.stream!()
  |> Stream.drop(1)
  |> Stream.map(&String.split(String.trim(&1), ","))
  |> Enum.map(fn [id, fit, out, trans, owned] ->
    %{id: id, mandate_fit: String.to_float(fit), outreach_suitability: String.to_float(out),
      transition_context: String.to_float(trans), group_owned: owned == "yes"}
  end)

AcquisitionUniverse.rank(rows) |> Enum.take(10)
```

## Related reading

Add-on buyers can compare notes in the [portfolio company BD](https://www.acquisitionuniverse.com/for/portfolio-company-bd.php) guide. The same team publishes [cookieless audience database](https://www.cookielessaudiences.com/industries/curation-platforms.php) material for curation platforms and a [skill normalization](https://www.resumereaderapi.com/normalization/skills-examples.php) reference for resume data.

## Questions

**Does it call a server?** No.

**Elixir version?** 1.14 or later.

**License?** MIT. info@alpha-quantum.com