Автоматизируем создание отчета о расходах с помощью Python

Раньше отчеты о расходах приходилось выполнять вручную  —  в лучшем случае в электронной таблице. Затем появились компьютеры со встроенными электронными таблицами, которые облегчили составление и отслеживание финансовой отчетности. Но что если полностью автоматизировать процесс ввода данных на основе изображений чеков? Такое решение кардинально упростит создание финансовой отчетности.

Предлагаем пошаговое руководство по автоматизации отчетов о расходах с использованием Python.


Что нужно сделать

Вот 8 этапов, которые нам предстоит пройти для автоматизации финансовой отчетности.

  1. Создание аккаунта на платформе Mindee.
  2. Настройка API-ключа.
  3. Установление пакета “mindee”.
  4. Импортирование зависимостей.
  5. Написание вспомогательных функций.
  6. Загрузка, парсинг и извлечение данных из чеков.
  7. Экспортирование результатов в таблицу.
  8. Сохранение таблицы в файле .csv.

Итак, приступим!


1. Создание аккаунта на платформе Mindee

Чтобы не писать специальный код для обнаружения текста на изображениях чеков, будем использовать пакет Python под названием mindee. Он поставляется с API, позволяющим сделать все это с помощью нескольких строк кода.

Профессиональная версия этого пакета платная, однако 250 страниц в месяц предлагаются бесплатно. Для частных пользователей этого более чем достаточно, чтобы выполнять автоматизацию личных отчетов о расходах.

Создание аккаунта выполняется в два шага.

  1. Перейдите на сайт платформы mindee.
  2. Введите в форму регистрации свои учетные данные.

2. Настройка API-ключа

Чтобы установить ключ API, сделайте следующее.

  1. Нажмите на кнопку “Создать новый API”.
  2. Выберите API “Расходная квитанция”.
  3. Скопируйте свой ключ API и сохраните его.

3. Установление пакета mindee

Чтобы установить пакет mindee, запустите команду:

pip install mindee

4. Импортирование зависимостей

Для реализации этого проекта будем использовать следующие пакеты:

  • mindee;
  • pandas;
  • random;
  • glob;
  • matplotlib;
  • seaborn.

Если в вашей локальной среде нет этих пакетов, установите их с помощью pip install <package>.

После этого можно импортировать зависимости:

from mindee import Client, documents
import random
import pandas as pd
import glob
# Sanity Check using pandas and matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

5. Написание вспомогательных функций

Для автоматизации финансовой отчетности понадобятся 3 вспомогательные функции.

  • Для извлечения данных о расходах после получения ответа от API mindee.
  • Для преобразования времени суток в тип приема пищи. В используемом здесь примере финансовый отчет предполагает указание типа приема пищи, например обеда (Lunch) и ужина (Dinner), для вычисления расходов на еду.
  • Для создания итоговой таблицы со всеми данными.

Код будет выглядеть следующим образом:

# Вспомогательные функции для извлечения нужной информации
def extract_expenses_data(api_response):
"""
Extracts the data from the response returned by the
mindee API. In this case we extract, the data of the receipt,
the category (like food, transport, etc..) the time of the processing of the
receipt as well as the total amount and the filename of the image.
"""
date = api_response.date.value
category = api_response.category.value
time = api_response.time.value
amount = api_response.total_amount.value
filename = api_response.filename

return date, amount, filename, category, time

def convert_time_to_meal_type(time, hour_max = 17, hour_min=6):
"""
This function takes as input a string `time` and returns
whether that time slot corresponds to dinner or lunch given a threshold
variable separating dinner and lunch times.
"""
hour_digits = time[:2]
if int(hour_digits)<hour_max and int(hour_digits)>hour_min:
meal_type = "Lunch"
else:
meal_type = "Dinner"

return meal_type


# Код для тестирования функции `convert_time_to_meal_type()`.
hours_for_test = [str(random.randint(10,23)).zfill(2) + ":" + str(random.randint(0,59)).zfill(2) for i in range(10)]
print(hours_for_test)
for hour in hours_for_test:
print(hour, convert_time_to_meal_type(hour))


def create_table(date_list,amount_list,meal_type, time_list,category_list, filenames_list):
"""
This functions takes as input 6 lists for date, amount, meal type, time,
category and filename. It returns a pandas dataframe with the data
structured as a table.
"""
df = pd.DataFrame(
{"date":date_list, "amount":amount_list, "meal_type":meal_type,
"time":time_list, "category":category_list, "filename":filenames_list}
)
return df

# Тестирование функции `create_table`
create_table(["2021-01-01","2021-01-02"],[10,20],["Lunch","Dinner"],["10:00","20:00"],["Food","Food"],["file1","file2"])
# Вывод написанных выше тестов

['13:51', '11:49', '22:13', '19:57', '10:32', '20:47', '20:40',
'14:27', '14:41', '15:06']
13:51 Lunch
11:49 Lunch
22:13 Dinner
19:57 Dinner
10:32 Lunch
20:47 Dinner
20:40 Dinner
14:27 Lunch
14:41 Lunch
15:06 Lunch
Тестовый пример финальной таблицы. Изображение автора

6. Загрузка, парсинг и извлечение данных из чеков

Теперь остается сделать следующее.

1. Установить клиент mindee, используя полученный ключ API.

# Создание нового клиента
mindee_client = Client(api_key="Your API KEY")

2. Инициализировать несколько пустых списков, которые будут содержать извлеченные данные.

date_list = []
amount_list = []
category_list = []
time_list = []
meal_type_list = []
filenames_list = []

3. Загрузить изображение чека и передать его в API mindee.

image = "./expense_images/1669895159779.jpg"
input_doc = mindee_client.doc_from_path(image)
api_response = input_doc.parse(documents.TypeReceiptV4)
expense_data = api_response.document
expense_data

<mindee.documents.receipt.receipt_v4.ReceiptV4 at 0x7f9685b278b0>

Выводом будет объект mindee, специально предназначенный для работы с чеками (наверняка есть и другие варианты, поэтому стоит изучить их в официальной документации на платформе mindee).

4. Извлечь информацию о расходах из ответа API.

date, amount, filename, category, time = 
extract_expenses_data(expense_data)

5. Преобразовать информацию о времени суток в соответствующую информацию о типе приема пищи.

Поскольку этот пункт довольно специфичен, можно изменить данную функцию в соответствии с типами расходов, которые будут у вас. Здесь же преобразуем строку 13:30 в lunch (обед), а строку 20:30  —  в dinner (ужин).

if not time:
meal_type = "Unknown"
else:
meal_type = convert_time_to_meal_type(time)

6. Добавить извлеченную информацию в соответствующие списки.

Здесь мы извлекаем данные только из одного чека, но при работе с несколькими использование списков будет иметь куда больше смысла.

date_list.append(date)
# Здесь я заменяю . на , поскольку финальный отчет идет в
# таблицу Google, которая использует `,` вместо `.` для чисел с плавающим знаком.
amount_list.append(str(amount).replace(".", ","))
category_list.append(category)
time_list.append(time)
meal_type_list.append(meal_type)
filenames_list.append(filename)

7. Экспортирование результатов в таблицу

Теперь, когда информация упорядочена, можно создать таблицу со структурированными данными.

# Формат таблицы будет следующим: | данные | сумма | имя файла | категория |
df = create_table(date_list,amount_list,meal_type_list, time_list,category_list,
filenames_list)

# Показ таблицы для проверки
df

8. Сохранение таблицы в файле .csv

Чтобы завершить работу, сохраним таблицу со всей информацией, полученной из чеков.

df.to_csv("expense_table.csv", header=False)

Сведение воедино всех данных

Теперь, детально изучив каждый шаг, можно взглянуть на весь исходный код, запускающий этот процесс по нескольким чекам:

# Запуск основного цикла для автоматизации

# Создание нового клиента
mindee_client = Client(api_key="Your API KEY")

date_list = []
amount_list = []
category_list = []
time_list = []
meal_type_list = []
filenames_list = []
for image in glob.glob("./expense_images/*"):
input_doc = mindee_client.doc_from_path(image)
api_response = input_doc.parse(documents.TypeReceiptV4)
expense_data = api_response.document
date, amount, filename, category, time = extract_expenses_data(expense_data)
if not time:
meal_type = "Unknown"
else:
meal_type = convert_time_to_meal_type(time)

date_list.append(date)
amount_list.append(str(amount).replace(".", ","))
category_list.append(category)
time_list.append(time)
meal_type_list.append(meal_type)
filenames_list.append(filename)

# Модель финальной таблицы:

# | данные | значение | имя файла | категория |
df = create_table(date_list,amount_list,meal_type_list, time_list,category_list,
filenames_list)

# Сортировка значений в таблице по типу приема пищи (конкретно для моего случая)
df.sort_values(by="meal_type").to_csv("expense_table.csv", header=False)

# Показ таблицы для проверки
df
Итоговая таблица с данными, извлеченными из нескольких чеков. Изображение автора

Вот и все! Вы автоматизировали скучную задачу составления отчета о расходах на основе изображений чеков! В качестве последней проверки всегда полезно взглянуть на конечный результат, чтобы убедиться, что полученная информация соответствует фактическим данным в чеках.

Для этого можно с помощью matplotlib визуализировать изображения чеков рядом с текстовыми данными, полученными в результате извлечения.

# Перебор по именам файлов и визуализация изображений рядом с
# извлеченными данными
for i,filename in enumerate(df.filename):
image = plt.imread(f"./expense_images/{filename}")
plt.figure(figsize=(15,10))
plt.imshow(image)
date = df[df["filename"]==filename]["date"]
amount = df[df["filename"]==filename]["amount"]
meal_type = df[df["filename"]==filename]["meal_type"]
time = df[df["filename"]==filename]["time"]
plt.title(f"date: {date} amount: {amount} meal_type: {meal_type} time: {time}")
plt.show()
input("Continue?")
Пример изображения чека. Фото автора
Пример изображения чека. Фото автора

Кажется, результаты корректны! Такая автоматизация поможет ежемесячно экономить немало времени.


Заключение

Кем бы вы ни были  —  менеджером, разработчиком, бухгалтером или любым другим специалистом, работающим с вводом данных,  —  этот подход может оказаться очень полезным. По сути, он является наглядной демонстрацией того, что всего с помощью нескольких строк кода Python можно ускорить рутинные процессы на рабочем месте. 

Почему бы не использовать простую программу для автоматизации оформления отчетов о расходах? Автоматизируя повторяющиеся задачи, вы экономите немало времени и получаете возможность упорядочить рабочий процесс.

Исходный код можно найти здесь.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Lucas Soares: Automating Expense Reports with Python

Предыдущая статьяКогда чистый код нецелесообразен
Следующая статьяКак запустить несколько приложений React на одном порту Nginx с Docker