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
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
    {
        "name": "Иванов Иван",
        "birthday": "01/12/2000",
        "height": 170,
        "weight": 70.5,
        "car": true,
        "languages": ["C++", "Python"]
    },
    {
        "name": "Сергеев Сергей",
        "birthday": "01/06/2001",
        "height": 180,
        "weight": 110.4,
        "car": false,
        "languages": ["Pascal", "Delphi"]
    },
    {
        "name": "Николаева Мария",
        "birthday": "14/07/1998",
        "height": 180,
        "weight": 66.9,
        "car": true,
        "languages": ["C#", "C++", "C"]
    }
]
Листинг 8.5.2 - Пример решения с файлами
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
"""Комплексная задача.

Сложность: O(N).
"""

import json
import csv

# Это пример кода, он может быть произвольно усложнен
# в рамках ограничений темы
#
# Требования:
#
# - переделайте функцию load_db() на загрузку данных из JSON-файла;
# - добавьте функцию save_db(), которая бы сохраняла db в CSV-файл;
# - используйте функцию save_db() для сохранения результатов фильтрации;
# - добавьте обработку исключений.


# Замените атрибуты словаря и др. на соответствующие своему варианту

def load_db(filename):
    """Загрузить данные из json-файла 'filename'.

    Функция не обрабатывает исключения.

    Результат: list of dict или возбуждается исключение.

    Сложность: O(1).
    """
    fh = None
    try:
        fh = open(filename, encoding="utf-8")
        db = json.loads(fh.read())
    finally:
        if fh:
            fh.close()

    return db


def save_db(db, filename):
    """Сохранить данные 'db' в csv-файл 'filename'.

    Функция не обрабатывает исключения.

    Сложность: O(1).
    """
    with open(filename, "w", encoding="utf-8", newline="") as fh:
        writer = csv.DictWriter(fh,
                                fieldnames=["name", "birthday", "height",
                                            "weight", "car", "languages"],
                                quoting=csv.QUOTE_ALL)
        writer.writeheader()
        for item in db:
            writer.writerow(item)


def print_employee(item):
    """Вывести объект 'item' на экран со всеми атрибутами.

    Сложность: O(1).
    """
    print("Имя: {}".format(item["name"]))

    ...


def employees_filter(db, lang):
    """Вернуть отфильтрованную базу 'db' (список сотрудников), у которых
    в качестве языка программирования имеется 'lang'.

    Аргументы:
        - db (list of dict): база данных;
        - lang (str): язык программирования.

    Результат:
        - (list of dict): отфильтрованная база данных.

    Сложность: O(N).
    """
    # res - копия db, с сотрудниками, удовлетворяющими условию по языку
    res = []
    for item in db:
        if lang in item["languages"]:
            res.append(item)

    return res


def employee_employee_avg_height(db, younger_than):
    """Вернуть средний рост среди сотрудников 'db', у которых
    год рождения не менее 'younger_than'.

    Аргументы:
        - db (list of dict): база данных;
        - younger_than (int): минимальный год рождения.

    Результат:
        - (float): если такие сотрудники есть;
          (None): если таких сотрудников нет.

    Исключения:
        - ValueError: если кол-во найденных сотрудников равно 0.

    Сложность: O(N).
    """

    r_sum = 0
    r_col = 0
    for item in db:
        year = int(item["birthday"][-4:])
        if year >= younger_than:
            r_sum += item["height"]
            r_col += 1

    if r_col == 0:
        raise ValueError

    return r_sum / r_col


try:
    db = load_db("data.json")

    while True:

        print("\n-----")
        print("Меню")
        print("-----")
        print("1. Список сотрудников.")
        print("2. Фильтр по языку программирования.")
        print("3. Средний рост сотрудников, моложе указанного г.р.")
        print("\nВыберите пункт меню или нажмите ENTER для выхода: ", end="")

        answer = input()

        if answer == "1":
            print("Содержимое базы данных ({}):".format(len(db)))
            for i, item in enumerate(db, start=1):
                print("{}.".format(i))
                print_employee(item)

        elif answer == "2":
            lang = input("Введите язык программирования: ")
            # "Нормализация" наименования языка на случай ошибки при вводе
            lang = lang.capitalize()

            res = employees_filter(db, lang)

            if len(res) > 0:
                print("Список сотрудников со знанием "
                      "языка программирования {} ({}):".format(lang, len(res)))
                for i, item in enumerate(res, start=1):
                    print("{}.".format(i))
                    print_employee(item)

                # Сохранение результата в файл
                try:
                    save_db(res, "filter_result.csv")
                    print("Результат поиска сохранен в файл.")
                except (IOError, csv.Error):
                    print("Не удалось сохранить результат поиска в файл.")
            else:
                print("Таких сотрудников нет.")

        elif answer == "3":
            try:
                younger_than = int(input("Введите год рождения сотрудника: "))

                res = employee_employee_avg_height(db, younger_than)

                try:
                    print("Средний рост сотрудников, {} г.р. и моложе: "
                          "({:.1f}) см.".
                          format(younger_than, res))
                except ValueError:
                    print("Таких сотрудников нет.")
            except ValueError:
                print("Год рождения должен быть целым числом.")

        else:
            break

except (IOError, json.decoder.JSONDecodeError) as err:
    print("Ошибка при чтении файла с данными:", err)

except Exception as err:
    print("Произошла ошибка!")
    print("Тип:", type(err))
    print("Описание:", err)


# --------------
# Пример вывода:
#
# -----
# Меню
# -----
# 1. Список сотрудников.
# 2. Фильтр по языку программирования.
# 3. Средний рост сотрудников, моложе указанного г.р.
#
# Выберите пункт меню или нажмите ENTER для выхода: 1
# Содержимое базы данных (3):
# 1.
# Имя: Иванов Иван
# День рождения: 01/12/2000
# Рост (см.): 170
# Вес (кг.): 70.5
# Есть машина: True
# Языки программирования: ['С++', 'Python']
# 2.
# Имя: Сергеев Сергей
# День рождения: 01/06/2001
# Рост (см.): 180
# Вес (кг.): 110.4
# Есть машина: False
# Языки программирования: ['Pascal', 'Delphi']
# 3.
# Имя: Николаева Мария
# День рождения: 14/07/1998
# Рост (см.): 180
# Вес (кг.): 66.9
# Есть машина: True
# Языки программирования: ['C#', 'C++', 'C']
#
# -----
# Меню
# -----
# 1. Список сотрудников.
# 2. Фильтр по языку программирования.
# 3. Средний рост сотрудников, моложе указанного г.р.
#
# Выберите пункт меню или нажмите ENTER для выхода: 2
# Введите язык программирования: Python
# Список сотрудников со знанием языка программирования Python (1):
# 1.
# Имя: Иванов Иван
# День рождения: 01/12/2000
# Рост (см.): 170
# Вес (кг.): 70.5
# Есть машина: True
# Языки программирования: ['С++', 'Python']
# Результат поиска сохранен в файл.
#
# -----
# Меню
# -----
# 1. Список сотрудников.
# 2. Фильтр по языку программирования.
# 3. Средний рост сотрудников, моложе указанного г.р.
#
# Выберите пункт меню или нажмите ENTER для выхода: 3
# Введите год рождения сотрудника: 2000
# Средний рост сотрудников, 2000 г.р. и моложе: (175.0) см.
Листинг 8.5.3 - CSV-файл filter_results.csv, сохранение результатов
1
2
3
"name","birthday","height","weight","car","languages"
"Иванов Иван","01/12/2000","170","70.5","True","['C++', 'Python']"
"Николаева Мария","14/07/1998","180","66.9","True","['C#', 'C++', 'C']"