# AcronymMaker in Python

[![pipeline status](https://gitlab.com/acronym-maker/python-implementation/badges/master/pipeline.svg)](https://gitlab.com/acronym-maker/python-implementation/commits/master)
[![coverage report](https://gitlab.com/acronym-maker/python-implementation/badges/master/coverage.svg)](https://gitlab.com/acronym-maker/python-implementation/-/commits/master)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=acronym-maker_python-implementation&metric=alert_status)](https://sonarcloud.io/dashboard?id=acronym-maker_python-implementation)

## Description

*AcronymMaker* creates awesome acronyms for your projects.
Let us briefly describe how it works with some vocabulary.

A *token* is a set of words from which one word must appear in the acronym
built by *AcronymMaker*.
Said differently, there must be a letter in common between a word from the
set and the built acronym.
This letter may be either the first letter of a word in the token or any
letter, depending on the *letter selection strategy* that is given to
*AcronymMaker*.

Additionally, tokens may be *optional*.
In this case, *AcronymMaker* will try to match a letter from the words in
the optional token to a letter in the acronym, but the acronym will still
be accepted if it fails to do so.

To find an acronym for a given sequence of tokens, *AcronymMaker* uses a
*dictionary*, i.e., a set of known words, in which it looks for acronyms.
A word in the dictionary is said to be *explained* (as an acronym) by the
sequence of tokens if there is a letter in the word for each word in each
(non-optional) token.
In this case, we say that the letter is *explained* by the corresponding
word.

Moreover, there are two ways to explain a word as an acronym: either
by following the order of the tokens in the specified sequence, or
without considering this order.
*AcronymMaker* supports both of them (independently).

Finally, note that there may be unexplained letters in the acronym.
Their number may be limited, by limiting both the number of consecutive
unused letters and the number of overall unused letters in a word.
If one of these limits is exceeded, then the word will not be considered
as explained.

## Requirements

This project provides a Python implementation of *AcronymMaker*, you
thus need [Python 3](https://www.python.org/downloads/) on your computer
to run it.

You may install *AcronymMaker* on your computer along with all its
[dependencies](requirements.txt) thanks to `pip` with the following command
line:

```bash
python3 -m pip install acronymmaker
```

## How to use *AcronymMaker*

There are two ways to use the Python implementation of *AcronymMaker*.
This section describes both of them.

### Command-Line Interface

*AcronymMaker* comes with a command-line interface that has the following
usage:

```
acronymmaker [-l {all,first}] [-m {ordered,ordered-greedy,unordered}] [-c <nb>] [-u <nb>] -d <dict> [<dict> ...]
```

Let us now describe the parameters of the command line above.

+ The parameter `-l` (`--select-letters`) allows specifying whether only the
  `first` letter or `all` the letters of a word from a token may be used to
  explain a letter of the acronym.

+ The parameter `-m` (`--matching-strategy`) allows specifying whether the
  tokens must be considered `ordered` or `unordered`.
  The strategy `ordered-greedy` also considers the tokens in order, but using
  a more efficient algorithm that may however miss matching acronyms which
  would have been found by the `ordered` strategy.

+ The parameters `-c` (`--max-consecutive-unused`) and `-u`
  (`--max-total-unused`) allow specifying the maximum numbers of unused
  letters in the acronym, by limiting the number of consecutive and overall
  unexplained letters, respectively.

+ The parameter `-d` (`--dictionary`) allows specifying the path to the
  dictionary file(s) from which *AcronymMaker* will look for acronyms.
  You may find such dictionaries [here](../dictionaries).
  This is the only required parameter.

Once the command-line application has started, a prompt asks you to enter your
tokens, separated by blank spaces.
Each token defines a set of words separated with slashes (`/`), and may end
with a question mark (`?`) to specify that the token is optional.
When you press `Enter`, the matching acronyms are displayed in the console.
You may then enter new sequences of tokens if you wish to find other acronyms,
or you may exit the application with either `Ctrl-C` or `Ctrl-D`.

### Python API

You may also want to directly interact with the Python API of *AcronymMaker*.
The `acronymmaker` package provides the function and classes to
programmatically set up an instance of `AcronymMaker` similarly to what is
proposed for the command-line interface.

First, there are two functions corresponding to the letter selection
strategies.
Both of them are in the `acronymmaker.selection` module.

```python
from acronymmaker.selection import select_all_letters, select_first_letter
```

There are also three classes corresponding to the matching strategies.
They are defined in the `acronymmaker.matching` module.
 
```python
from acronymmaker.matching import GreedyOrderedMatchingStrategy
from acronymmaker.matching import RegexBasedOrderedMatchingStrategy
from acronymmaker.matching import UnorderedMatchingStrategy
```

All these strategies define a constructor that takes as parameters the
maximum number of consecutive unused letters and the maximum number of
overall unused letters, as this can be seen in the example below.

```python
matching_strategy = UnorderedMatchingStrategy(max_consecutive_unused=3, max_total_unused=5)
```

It is now possible to instantiate an `AcronymMaker` to create your acronyms.
First, you need to import `AcronymMaker`.

```python
from acronymmaker.maker import AcronymMaker
```

Then, you may instantiate an `AcronymMaker` as follows.

```python
my_acronyms = []
maker = AcronymMaker(select_all_letters, matching_strategy, my_acronyms.append)
```

The `maker` initialized above will append to `my_acronyms` all the acronyms
it will identify.
You may of course provide any callback function as third parameter to the
constructor of `AcronymMaker`.
The only requirement for this function is that it must take as parameter an
instance of `Acronym` (from the `acronymmaker.matching` module).
You can for instance print it, display it on a GUI, etc.

Then, you need to tell to the instance of `AcronymMaker` what are the
words that are authorized as acronyms (a.k.a. the "dictionary").
We provide a set of dictionary as [text files](../dictionaries), but you can
of course use your own set of words.

To add new words, you can either add them one at a time, or all of them
at once.

```python
maker.add_known_word('foo')
maker.add_known_words(['bar', 'baz'])
```

Then, you need to provide the list of the tokens for which to find an acronym.
To this end, the `TokenBuilder`, defined in `acronymmaker.token`, makes
easier the creation of a `Token`.
 
```python
builder = TokenBuilder(select_all_letters)
builder.add_word('foo')
builder.add_word('bar')
builder.set_optional(True)
token = builder.build()
```

Once you have built all your tokens, put them in a list, say `tokens`.
Finally, pass this list as parameter to the `find_acronym` method of `maker`.
It will try to explain each word of the dictionary, and will invoke the
callback function specified when creating the instance of `AcronymMaker`
each time it successfully explains a word with the corresponding `Acronym`
instance.

```python
maker.find_acronyms(tokens)
```

To deal with the instances of `Acronym` that are produced by this method
and stored in the list `my_acronyms` (in this example), you may be
particularly interested in the following methods:

+ `get_word()` gives the word that is explained as an acronym, with
  each explained letter upper-cased.
 
+ `get_explanations()` gives the list of explanations of the acronym,
  i.e., all the possible combinations of words in the tokens that explain
  the word as an acronym.
  Moreover, each letter corresponding to an explained letter of the acronym
  are upper-cased.
