✅ Решение задания

⚡ Решение в двух словах

  • Проверяем sys.argv и получаем пути.
  • Читаем файл, парсим строки, собираем список словарей.
  • Группируем через defaultdict: записи и суммы.
  • Создаём файлы категорий с сортировкой по дате.
  • Создаём monthly_report.txt с суммами и общей строкой Full.
⚠️ Рекомендация: попробуйте решить задание самостоятельно, прежде чем смотреть ответ. Решение — последняя инстанция, когда собственные попытки исчерпаны.

Полное решение

# sales_report.py
import os
import sys
from collections import defaultdict


def read_sales_data(file_path):
    """Считывает данные из .txt файла и возвращает список записей."""
    sales = []

    with open(file_path, "r", encoding="utf-8") as file:
        for line in file:
            parts = line.strip().split(",")

            # Пропускаем строки с неправильным количеством полей
            if len(parts) != 5:
                continue

            name, date, amount, category, city = parts
            year, month, day = date.split("-")

            sales.append({
                "name": name,
                "date": date,
                "amount": int(amount),
                "category": category,
                "city": city,
                "year": year,
                "month": month,
                "day": day,
            })

    return sales


def write_category_reports(output_dir, grouped_sales):
    """Создаёт файлы с отчётами по категориям в формате 'дата,продавец,сумма'."""
    for (year, month, category), records in grouped_sales.items():
        # Формируем путь к файлу категории
        category_file = os.path.join(output_dir, year, month, f"{category}.txt")

        # Создаём все промежуточные папки, если их ещё нет
        os.makedirs(os.path.dirname(category_file), exist_ok=True)

        # Сортируем записи по дате
        sorted_records = sorted(records, key=lambda x: x["date"])

        with open(category_file, "w", encoding="utf-8") as file:
            for record in sorted_records:
                file.write(f"{record['date']},{record['name']},{record['amount']}\n")


def write_monthly_reports(output_dir, category_totals):
    """Создаёт общий отчёт по каждому месяцу с суммами продаж по категориям."""
    for (year, month), category_sums in category_totals.items():
        report_file = os.path.join(output_dir, year, month, "monthly_report.txt")

        # Создаём папки перед записью файла
        os.makedirs(os.path.dirname(report_file), exist_ok=True)

        with open(report_file, "w", encoding="utf-8") as file:
            # Записываем категории в алфавитном порядке
            for category, total in sorted(category_sums.items()):
                file.write(f"{category},{total}\n")

            # Общая сумма по месяцу
            file.write(f"Full,{sum(category_sums.values())}\n")


def process_sales_data(file_path, output_dir):
    """Обрабатывает продажи и создаёт отчёты по категориям и общие отчёты по месяцам."""
    sales = read_sales_data(file_path)

    # grouped_sales хранит записи по (year, month, category)
    grouped_sales = defaultdict(list)

    # category_totals хранит суммы по категориям внутри (year, month)
    category_totals = defaultdict(lambda: defaultdict(int))

    for record in sales:
        key = (record["year"], record["month"], record["category"])
        grouped_sales[key].append(record)
        category_totals[(record["year"], record["month"])][record["category"]] += record["amount"]

    write_category_reports(output_dir, grouped_sales)
    write_monthly_reports(output_dir, category_totals)


# --- Точка входа ---
if len(sys.argv) < 3:
    print("Usage: python sales_report.py <input_file> <output_directory>")
    sys.exit(1)

input_file = sys.argv[1]
output_directory = sys.argv[2]

process_sales_data(input_file, output_directory)
print("Reports generated successfully!")

Пошаговое объяснение

1. Чтение данных

Функция read_sales_data открывает файл в кодировке utf-8 и читает его построчно. Каждая строка разбивается по запятой. Если полей не пять, строка пропускается — это защита от пустых или повреждённых строк.

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

2. Группировка

Две структуры данных:

  • grouped_sales — словарь, где ключ (year, month, category), а значение — список записей.
  • category_totals — словарь, где ключ (year, month), а значение — ещё один словарь category → total_amount.

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

3. Запись файлов по категориям

Функция write_category_reports проходит по группам, сортирует записи по дате и записывает их в файл. Путь собирается через os.path.join, а папки создаются через os.makedirs(..., exist_ok=True).

4. Запись месячных отчётов

Функция write_monthly_reports записывает суммы по категориям в алфавитном порядке и добавляет строку Full с общей суммой всех категорий.

5. Обработка аргументов

В блоке if __name__ == "__main__": (в решении используется прямая проверка в глобальной области) проверяется, что пользователь передал два аргумента. Если нет — программа выводит подсказку и завершается с кодом 1.

Как проверить

  1. Создайте файл sales_data.txt с тестовыми данными из раздела Задания.
  2. Сохраните решение в файл sales_report.py.
  3. Запустите: python sales_report.py sales_data.txt reports.
  4. Откройте папку reports/ и проверьте структуру и содержимое файлов.