8.5. Самостоятельная работа

Примечание

При выполнении заданий используйте заготовки решений: -> Репозиторий.

Подробнее: см. Цикл выполнения и защиты заданий.

8.5.1. Комплексная задача

№8.5.1

Продолжите решение задачи № 7.5.1.

Используя результаты предыдущего решения, а также заготовку и Листинги 8.5.1-8.5.3, добавьте:

  • функцию загрузки данных из JSON-файла;

  • функцию сохранения результатов в CSV-файл;

  • обработку исключений.

Листинг 8.5.1 - JSON-файл data.json с исходными данными
 1[
 2    {
 3        "name": "Иванов Иван",
 4        "birthday": "01/12/2000",
 5        "height": 170,
 6        "weight": 70.5,
 7        "car": true,
 8        "languages": ["C++", "Python"]
 9    },
10    {
11        "name": "Сергеев Сергей",
12        "birthday": "01/06/2001",
13        "height": 180,
14        "weight": 110.4,
15        "car": false,
16        "languages": ["Pascal", "Delphi"]
17    },
18    {
19        "name": "Николаева Мария",
20        "birthday": "14/07/1998",
21        "height": 180,
22        "weight": 66.9,
23        "car": true,
24        "languages": ["C#", "C++", "C"]
25    }
26]
Листинг 8.5.2 - Пример решения с файлами
  1"""Комплексная задача.
  2
  3Сложность: O(N).
  4"""
  5
  6import json
  7import csv
  8
  9# Это пример кода, он может быть произвольно усложнен
 10# в рамках ограничений темы
 11#
 12# Требования:
 13#
 14# - переделайте функцию load_db() на загрузку данных из JSON-файла;
 15# - добавьте функцию save_db(), которая бы сохраняла db в CSV-файл;
 16# - используйте функцию save_db() для сохранения результатов фильтрации;
 17# - добавьте обработку исключений.
 18
 19
 20# Замените атрибуты словаря и др. на соответствующие своему варианту
 21
 22def load_db(filename):
 23    """Загрузить данные из json-файла 'filename'.
 24
 25    Функция не обрабатывает исключения.
 26
 27    Результат: list of dict или возбуждается исключение.
 28
 29    Сложность: O(1).
 30    """
 31    fh = None
 32    try:
 33        fh = open(filename, encoding="utf-8")
 34        db = json.loads(fh.read())
 35    finally:
 36        if fh:
 37            fh.close()
 38
 39    return db
 40
 41
 42def save_db(db, filename):
 43    """Сохранить данные 'db' в csv-файл 'filename'.
 44
 45    Функция не обрабатывает исключения.
 46
 47    Сложность: O(1).
 48    """
 49    with open(filename, "w", encoding="utf-8", newline="") as fh:
 50        writer = csv.DictWriter(fh,
 51                                fieldnames=["name", "birthday", "height",
 52                                            "weight", "car", "languages"],
 53                                quoting=csv.QUOTE_ALL)
 54        writer.writeheader()
 55        for item in db:
 56            writer.writerow(item)
 57
 58
 59def print_employee(item):
 60    """Вывести объект 'item' на экран со всеми атрибутами.
 61
 62    Сложность: O(1).
 63    """
 64    print("Имя: {}".format(item["name"]))
 65
 66    ...
 67
 68
 69def employees_filter(db, lang):
 70    """Вернуть отфильтрованную базу 'db' (список сотрудников), у которых
 71    в качестве языка программирования имеется 'lang'.
 72
 73    Аргументы:
 74        - db (list of dict): база данных;
 75        - lang (str): язык программирования.
 76
 77    Результат:
 78        - (list of dict): отфильтрованная база данных.
 79
 80    Сложность: O(N).
 81    """
 82    # res - копия db, с сотрудниками, удовлетворяющими условию по языку
 83    res = []
 84    for item in db:
 85        if lang in item["languages"]:
 86            res.append(item)
 87
 88    return res
 89
 90
 91def employee_employee_avg_height(db, younger_than):
 92    """Вернуть средний рост среди сотрудников 'db', у которых
 93    год рождения не менее 'younger_than'.
 94
 95    Аргументы:
 96        - db (list of dict): база данных;
 97        - younger_than (int): минимальный год рождения.
 98
 99    Результат:
100        - (float): если такие сотрудники есть;
101          (None): если таких сотрудников нет.
102
103    Исключения:
104        - ValueError: если кол-во найденных сотрудников равно 0.
105
106    Сложность: O(N).
107    """
108
109    r_sum = 0
110    r_col = 0
111    for item in db:
112        year = int(item["birthday"][-4:])
113        if year >= younger_than:
114            r_sum += item["height"]
115            r_col += 1
116
117    if r_col == 0:
118        raise ValueError
119
120    return r_sum / r_col
121
122
123try:
124    db = load_db("data.json")
125
126    while True:
127
128        print("\n-----")
129        print("Меню")
130        print("-----")
131        print("1. Список сотрудников.")
132        print("2. Фильтр по языку программирования.")
133        print("3. Средний рост сотрудников, моложе указанного г.р.")
134        print("\nВыберите пункт меню или нажмите ENTER для выхода: ", end="")
135
136        answer = input()
137
138        if answer == "1":
139            print("Содержимое базы данных ({}):".format(len(db)))
140            for i, item in enumerate(db, start=1):
141                print("{}.".format(i))
142                print_employee(item)
143
144        elif answer == "2":
145            lang = input("Введите язык программирования: ")
146            # "Нормализация" наименования языка на случай ошибки при вводе
147            lang = lang.capitalize()
148
149            res = employees_filter(db, lang)
150
151            if len(res) > 0:
152                print("Список сотрудников со знанием "
153                      "языка программирования {} ({}):".format(lang, len(res)))
154                for i, item in enumerate(res, start=1):
155                    print("{}.".format(i))
156                    print_employee(item)
157
158                # Сохранение результата в файл
159                try:
160                    save_db(res, "filter_result.csv")
161                    print("Результат поиска сохранен в файл.")
162                except (IOError, csv.Error):
163                    print("Не удалось сохранить результат поиска в файл.")
164            else:
165                print("Таких сотрудников нет.")
166
167        elif answer == "3":
168            try:
169                younger_than = int(input("Введите год рождения сотрудника: "))
170
171                res = employee_employee_avg_height(db, younger_than)
172
173                try:
174                    print("Средний рост сотрудников, {} г.р. и моложе: "
175                          "({:.1f}) см.".
176                          format(younger_than, res))
177                except ValueError:
178                    print("Таких сотрудников нет.")
179            except ValueError:
180                print("Год рождения должен быть целым числом.")
181
182        else:
183            break
184
185except (IOError, json.decoder.JSONDecodeError) as err:
186    print("Ошибка при чтении файла с данными:", err)
187
188except Exception as err:
189    print("Произошла ошибка!")
190    print("Тип:", type(err))
191    print("Описание:", err)
192
193
194# --------------
195# Пример вывода:
196#
197# -----
198# Меню
199# -----
200# 1. Список сотрудников.
201# 2. Фильтр по языку программирования.
202# 3. Средний рост сотрудников, моложе указанного г.р.
203#
204# Выберите пункт меню или нажмите ENTER для выхода: 1
205# Содержимое базы данных (3):
206# 1.
207# Имя: Иванов Иван
208# День рождения: 01/12/2000
209# Рост (см.): 170
210# Вес (кг.): 70.5
211# Есть машина: True
212# Языки программирования: ['С++', 'Python']
213# 2.
214# Имя: Сергеев Сергей
215# День рождения: 01/06/2001
216# Рост (см.): 180
217# Вес (кг.): 110.4
218# Есть машина: False
219# Языки программирования: ['Pascal', 'Delphi']
220# 3.
221# Имя: Николаева Мария
222# День рождения: 14/07/1998
223# Рост (см.): 180
224# Вес (кг.): 66.9
225# Есть машина: True
226# Языки программирования: ['C#', 'C++', 'C']
227#
228# -----
229# Меню
230# -----
231# 1. Список сотрудников.
232# 2. Фильтр по языку программирования.
233# 3. Средний рост сотрудников, моложе указанного г.р.
234#
235# Выберите пункт меню или нажмите ENTER для выхода: 2
236# Введите язык программирования: Python
237# Список сотрудников со знанием языка программирования Python (1):
238# 1.
239# Имя: Иванов Иван
240# День рождения: 01/12/2000
241# Рост (см.): 170
242# Вес (кг.): 70.5
243# Есть машина: True
244# Языки программирования: ['С++', 'Python']
245# Результат поиска сохранен в файл.
246#
247# -----
248# Меню
249# -----
250# 1. Список сотрудников.
251# 2. Фильтр по языку программирования.
252# 3. Средний рост сотрудников, моложе указанного г.р.
253#
254# Выберите пункт меню или нажмите ENTER для выхода: 3
255# Введите год рождения сотрудника: 2000
256# Средний рост сотрудников, 2000 г.р. и моложе: (175.0) см.
Листинг 8.5.3 - CSV-файл filter_results.csv, сохранение результатов
1"name","birthday","height","weight","car","languages"
2"Иванов Иван","01/12/2000","170","70.5","True","['C++', 'Python']"
3"Николаева Мария","14/07/1998","180","66.9","True","['C#', 'C++', 'C']"