royalty_common
========

This library is designed to avoid using repeatable code and logic within tightly coupled Abacus services. Currently used by: 
* [lambda-royalty-accounting](https://github.com/theorchard/lambda-royalty-accounting)
* [ows-ledger](https://github.com/theorchard/ows-ledger)
* [ows-payee](https://github.com/theorchard/ows-payee)
* [ows-payment](https://github.com/theorchard/ows-payment)
* [ows-royalties](https://github.com/theorchard/ows-royalties)

Installation
------------

```bash
pip install -i https://pypi.theorchard.io/pypi/ royalty_common
```

Usage
-----

#### Marshalling

This library uses `flask_marshmallow` and provides us with following custom fields, which 
can be used in Marshmallow schemas creation:

```
Enum
FormattedDate
MoneyAmount
NonemptyString
IntegerId
Percentage  # numbers range (0, 100)
```

Example:

```python

from royalty_common.marshalling.custom_fields import ma


class BaseSalesFileSchema(ma.Schema):
    """Base sales file schema."""

    accounting_period_id = ma.IntegerId()
    name = ma.NonemptyString(required=True)
    row_count = ma.NonnegativeInteger(required=True)
    master_url = ma.NonemptyString(required=True)
    staging_url = ma.NonemptyString(required=True)
    sales_file_status = ma.Enum(options=('NEW','IN_PROGRESS','DONE'), required=True)

```

#### SQLAlchemy BaseModel

Use this model as a base class for your sqlalchemy models. It populates your model 
with following additional fields:
```
    created_at = Datetime
    last_modified = Datetime
    created_by = String
    last_modified_by = String
``` 

and has a bunch if helper methods:

###### `find_by_name`
Returns first row from the table `WHERE name=YOUR_VALUE`

###### `get_by_id`
Returns a row, found by provided primary_key value.
`None` if not found.

###### `get_by_id_or_error`
Returns a row, found by provided primary_key value.
If not found, calls `flask.abort`.

###### `delete_by_id_or_error`
The same as for `get_by_id_or_error`, but if a row
is found, deletes it.

###### `default_order`
Returns a field, that would be used as a default one 
for ordering your set of results. 

###### `build`
Constructs a new ORM object, generates autoincrement,
but doesn't populate the database

###### `create`
The same as `build` and commits into the database.

###### `current_timestamp`
Returns correct date (by default for tz.tzutc())

###### `commit_changes`
Commits the session, adding any new objects if necessary  

###### `update_attributes`
Updates your object's attributes with provided values.
Updates `last_modified_by` and `last_modified` fields
by default


#### Utils

Package with following helper modules: `dates`, `enum`
#### `dates`
Module with methods related to dates and time:

###### `safe_format_date`
Formats datetime object, uses `strftime`

###### `parse_date`
Parses a date from provided string, uses `strptime`

###### `current_timestamp`
Returns current time in provided timezone, UTC by default

##### `operating_date`
Returns provided timestamp in operating timezone.

Usage:
```python

from royalty_common.utils.dates import parse_date, operating_date


timestamp = parse_date('2020-01-01', '%Y-%m-%d')
operating_timestamp = operating_date(timestamp)  # tweaks tz to Eastern Time

```    

#### `enum`
Module with method for creating an enumerable `namedtuple`

##### `enum`
Receives a `name` and dict of `elements`. Returns a `namedtuple`.

Usage:
```python

from royalty_common.utils.enum import enum

ABACUS_DOGS = enum(
    'AbacusDogs',
    THE_BEARD='Reggie',
    THE_CHONK='Henry',
    THE_FLOOF='Geno'
)

print (ABACUS_DOGS.THE_BEARD) # output: 'Reggie'
```

#### Test Utils

Modules to use in unit/integration tests across services.

#### `factories`
Base classes for creation own factories to populate database
with test values. Base usage:


```python

from royalty_common.test_utils.factories import BaseModelFactoryWithTimestamps
import factory 

class AccountingPeriodFactory(BaseModelFactoryWithTimestamps):
    """Factory to create accounting period models for testing."""

    class Meta:
        """Meta definition for the factory."""

        model = SOME_MODEL_CLASS

    name = factory.Sequence(lambda n: f'Month 202{n}')
    status = 'SOME STATUS'
```

See detailed usage here [factory-boy](https://factoryboy.readthedocs.io/en/latest/).
