# @procedure

## Overview

The `@procedure` decorator is used to denote a method within a Book class as a procedure. This links a method to a specific [procedure](https://docs.kognitos.com/legacy/legacy-experience/books/custom-books/api-reference/procedures) in the Kognitos platform.

## Syntax

```python
@procedure(name: str, **kwargs)
```

### Guidelines

**1. Naming Conventions**

Names must begin with **to**. This defines the action or intent of the procedure. For example:

* `@procedure("to capitalize a (string)"`
* `@procedure("to get the (current temperature)")`
* `@procedure("to send an *SMS* message")`

**2. Output Concepts**

[Output concepts](https://docs.kognitos.com/legacy/legacy-experience/books/custom-books/api-reference/concepts) are wrapped in parentheses `()`. For example:

```python
@procedure("to capitalize a (string)")
```

**3. Proper Nouns**

Proper nouns are wrapped between asterisks `**`. For example:

```python
@procedure("to get some (users) from *office365*")
```

In this example, `office365` is considered a proper noun. The procedure is referred to as 'get some users from office365' rather than `the office365`.

## Parameters

<table><thead><tr><th width="133.78515625">Parameter</th><th width="86.625">Type</th><th width="110.99609375">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>Yes</td><td>A description that reflects the action or purpose of the procedure. See the syntax <a href="#guidelines">guidelines</a> for details.</td></tr></tbody></table>

## Keyword Arguments

<table><thead><tr><th width="231.88671875">Argument</th><th width="107.39453125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>connection_required</code></td><td><code>bool</code></td><td>A boolean that indicates whether a connection to the service is required to execute the procedure. If not specified, it defaults to <code>None</code>.</td></tr><tr><td><code>noun_phrase</code></td><td><code>str</code></td><td>A string that represents the noun phrase for the procedure.</td></tr></tbody></table>

## Examples

### 1. Capitalizing a String

This method implements a procedure that capitalizes a string with one input concept and one output concept.

```python
@procedure("to capitalize a (string)", connection_required=False)
def capitalize_string(self, string: str) -> str:
  """
  Capitalizes the input string.

  Input Concepts:
    the string: The string value you want to capitalize.

  Output Concepts:
    the string: The capitalized string.

  Example 1:
    Capitalize the string "hello"

    >>> capitalize "hello"

  """
  return string.capitalize()
```

### 2. Creating an Order in Truckmate

This method implements a procedure that creates an order in Truckmate. It has one input concept and two output concepts.

```python
@procedure("to create an order in truckmate and get the order number and the bill number")
def create_order(self, order: OrderRequestConcept) -> Tuple[int, str]:
    """
    Create a new order in Truckmate.

    Input Concepts:
        the order: The Truckmate order request (acc to openAPI spec)

    Output Concepts:
        the order number: Order ID of the created order
        the bill number: Bill Number of the created order

    Raises:
        ValueError: If the order is not created successfully.

    Example 1:
        Create the order in Truckmate
        >>> create a json
        >>> use the above as the order
        >>> set the order's id to 1234
        >>> set the order's title to "Awesome title"
        >>> set order's body to "This is the body"
        >>> create the order in truckmate and get the order number and bill number
    """
    logger.info("Creating Order in Truckmate")
    post_body = OrdersPostRequest(orders=[order])
    response = post_orders.sync_detailed(
        client=self._client,
        body=post_body,
    )
    if response.status_code == 201:
        orders_response = response.parsed.to_dict()  # type: ignore
        if orders_response.get("orders"):
            order = orders_response.get("orders")[0]  # type: ignore
            return order.get("orderId"), order.get("billNumber")

    error_response = response.parsed.to_dict()  # type: ignore
    logger.error(
        "Error while creating order in truckmate:  %s",
        error_response.get("errorText"),
    )
    raise ValueError(error_response)
```

### 3. Reading SMS messages using the Twilio API

The following method implements a procedure that reads SMS messages using the Twilio API. In this example, `SMS` is a proper noun.

```python
@procedure("to read some (*SMS* messages)")
def read_sms_messages(
    self,
    offset: Optional[int],
    limit: Optional[int],
    filter_expression: Optional[FilterExpression],
) -> List[SMSMessage]:
    """
    Read some SMS messages using the Twilio API.

    Returns:
        A list of SMS messages that matches the specified filtering criteria

    Example 1:
        Retrieve SMS messages filtered by sender and recipient numbers

        >>> read some sms messages whose sender number is "+18004445555" and whose recipient number is "+18004446666"

    Example 2:
        Retrieve SMS messages filtered by the date in which they were sent

        >>> convert "2022-03-01T15:00:00Z" to a datetime
        >>> use the above as the message date
        >>> read some sms messages whose date sent is the message date

    Example 3:
        Retrieve SMS messages that were sent in the specified time period

        >>> convert "2022-03-01T15:00:00Z" to a datetime
        >>> use the above as the start date
        >>> convert "2022-03-03T15:00:00Z" to a datetime
        >>> use the above as the end date
        >>> read some sms messages whose date sent is after the start date and whose date sent is before the end date
    """
    client = Client(self._account_sid, self._auth_token)

    filter_visitor = SMSMessageFilter()

    if filter_expression is not None:
        filter_expression.accept(filter_visitor)

    messages = client.messages.list(
        to=filter_visitor.recipient_number,
        from_=filter_visitor.sender_number,
        date_sent=filter_visitor.date_sent,
        date_sent_before=filter_visitor.date_sent_before,
        date_sent_after=filter_visitor.date_sent_after,
    )

    if offset is not None:
        messages = messages[offset:]

    if limit is not None:
        messages = messages[:limit]

    return [SMSMessage.from_message_instance(message) for message in messages]
```
