Вы находитесь на странице: 1из 35

МИНОБРНАУКИ РОССИИ

Федеральное государственное бюджетное образовательное учреждение


высшего образования
«МИРЭА – Российский технологический университет»
РТУ МИРЭА
Институт информационных технологий
кафедра прикладной математики

КУРСОВОЙ ПРОЕКТ (РАБОТА)

по дисциплине Системы управления данными


(наименование дисциплины)
Тема курсового проекта (работы) Разработка программного приложения
«Сервис для создания учебных карточек»

Студент группы Власов В.С. ИМБО-01-19


(Ф.И.О., учебная группа) (подпись студента)
Руководитель курсового Скляр А.Я., доцент, к.т.н.
проекта (работы) (Ф.И.О., должность, звание, учёная (подпись руководителя)
степень)
Рецензент
(при наличии)
(Ф.И.О., должность, звание, учёная (подпись рецензента)
степень)
Курсовой проект (работа)
представлен(а)
«_____»___________2022г.
к защите

Допущен(а) к защите
«_____»___________2022г.

Москва 2022
МИНОБРНАУКИ РОССИИ
Федеральное государственное бюджетное образовательное учреждение
высшего образования
«МИРЭА – Российский технологический университет»
РТУ МИРЭА

Институт информационных технологий


кафедра прикладной математики

Утверждаю

Заведующий кафедрой

(подпись) Ф.И.О.
«___»__________ 2022г.

ЗАДАНИЕ
на выполнение курсовой проекта (работы) по дисциплине

Системы управления данными


Разработка программного приложения
Тема курсового проекта (работы)
«Сервис для создания учебных карточек»

Студент Власов Виктор Сергеевич Группа ИМБО-01-19

Тема Разработка программного приложения


«Сервис для создания учебных карточек»

Исходные данные: Руководителем курсового проекта предоставлено техническое задание на


разработку программного приложения
Перечень вопросов, подлежащих разработке, и обязательного графического материала:
Изучение предметной области разрабатываемого приложения, утверждение технического
построение схемы документооборота, проектирование базы данных, построение ER-модели,
описание алгоритма разработки программного приложения, формирование архитектуры
программного приложения, описание входного контроля и методики поддержания целостности
базы данных, описание алгоритмов обработки данных внутри базы данных, описание
алгоритмов обработки данных у клиента и интерфейса, описание порядка работы с приложением.

Срок представления к защите курсового проекта (работы) до «_____»__________2022 г.

Задание на курсовой проект (работу) выдал


(подпись руководителя) (Ф.И.О. руководителя)

«___»______2022 г.
Задание на курсовой проект (работу) получил
(подпись обучающегося) (Ф.И.О. обучающегося)

3
СОДЕРЖАНИЕ

ВВЕДЕНИЕ............................................................................................................................5
1 ПОСТАНОВКА ЗАДАЧ.....................................................................................................6
2 ОПИСАНИЕ ДОКУМЕНТООБОРОТА НА ОБЪЕКТЕ..................................................8
3 СТРУКТУРА БАЗЫ ДАННЫХ СИСТЕМЫ....................................................................9
4 ОПИСАНИЕ ВХОДНОГО КОНТРОЛЯ И МЕТОДИКИ ПОДДЕРЖАНИЯ
ЛОГИЧЕСКОЙ ЦЕЛОСТНОСТИ БАЗЫ ДАННЫХ.......................................................12
5 ОПИСАНИЕ АЛГОРИТМОВ И ПРОГРАММ ОБРАБОТКИ ДАННЫХ ВНУТРИ
БАЗЫ ДАННЫХ..................................................................................................................14
6 ОПИСАНИЕ АЛГОРИТМОВ И ПРОГРАММ ОБРАБОТКИ ДАННЫХ У
КЛИЕНТА И ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА..............................................15
7 ОПИСАНИЕ ПОРЯДКА РАБОТЫ С СОЗДАННЫМ ПРОГРАММНЫМ
КОМПЛЕКСОМ...................................................................................................................21
7.1 Инструкция по запуску приложения............................................................................21
7.2 Инструкция по использованию приложения...............................................................21
СПИСОК ЛИТЕРАТУРЫ...................................................................................................24
ПРИЛОЖЕНИЯ....................................................................................................................26
Приложение А......................................................................................................................27
Приложение Б.......................................................................................................................32

4
ВВЕДЕНИЕ

В настоящее время разработка программных приложений играет


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

5
1 ПОСТАНОВКА ЗАДАЧ

Опишем задачи, которые решаются в рамках создания пользовательского


приложения:
 изучение предметной области;
 моделирование функциональности веб-сервиса;
 формирование технического задания;
 определение архитектуры разрабатываемого приложения;
 выбор алгоритмов создания базы данных;
 выбор алгоритмов обработки данных внутри базы данных;
 выбор необходимых триггеров и процедур для базы данных;
 проектирование интерфейса к веб-приложению;
 реализация frontend-части разрабатываемого приложения;
 формирование тестовых требований к готовому приложению (входной
контроль);
 реализация backend-части разрабатываемого приложения;
 тестирование программного приложения;
 сборка программного приложения.
Таким образом сформированы основные задачи, которые необходимо
выполнить в ходе разработки приложения.
Опишем пользовательский состав и их функциональные обязанности:

Таблица 1.1 – Состав пользователей и их функциональные обязанности


Роль пользователя Функциональные обязанности
Администратор Добавление пользователя в БД, вставка, редактирование, удаление
данных, ограничение пользовательского доступа к приложению
Клиент Зарегистрироваться в приложении, создать колоду, создать
карточки, удалить карточки, найти карточки, запросить карточки
для изучения
Таким образом сформированы задачи, решаемые в ходе разработки
программного приложения, а также определен состав и функциональные обязанности
пользователей проектируемого приложения.

6
2 ОПИСАНИЕ ДОКУМЕНТООБОРОТА НА ОБЪЕКТЕ

Опишем граф потока документов, осуществляемый в разрабатываемом веб-


сервисе. Граф представлен на Рисунке 2.1.

Рисунок 2.1 – Граф документооборота


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

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

3 СТРУКТУРА БАЗЫ ДАННЫХ СИСТЕМЫ

Опишем структуру базы данных, которая будет использоваться в


разрабатываемом приложении. Для этого построим диаграмму сущность-связь
(Entity-Relationship). Она показана на Рисунке 3.1.

Рисунок 3.1 – ER-диаграмма базы данных


Опишем каждую из таблиц – сопоставим поле и тип данных.
Рассмотрим таблицу «users». Сведения представлены в таблице 3.1.

8
Таблица 3.1 – Структура таблицы " users "
Название поля Тип данных
id Integer (Primary key)
username Varchar(25)
password Text
email Varchar(60)
Таким образом первичным ключом выступает поле «id», оно также является
авто-инкрементом и используется для связи с другими таблицами.
Опишем таблицу «decks». Структура таблицы представлена в Таблице 3.2.

Таблица 3.2 – Структура таблицы " decks "


Название поля Тип данных
id Integer (Primary key)
deck_name Varchar(25)
user_id Integer (Foreign key)
Таким образом первичным ключом выступает поле «id», оно также является
авто-инкрементом и используется для связи с другими таблицами, поле user_id
является внешним ключом. Связь между таблицами user и decks - один ко многим.
Опишем структуру таблицы «notes». Модель таблицы отображена в Таблице
3.3.

Таблица 3.3 – Структура таблицы " notes "


Название поля Тип данных
id Integer (Primary key)

front Varchar(200)
back Varchar(200)
deck_id Integer (Foreign key)
type_note Varchar(20)
repeat_date timestamp
Таким образом первичным ключом выступает поле «id», оно также является
авто-инкрементом, поле «deck_id» является внешним ключом. Связь между
таблицами decks и notes – один ко многим. Поле «front» хранит содержимое передней
части карточки, поле «back» – хранит содержимое задней части карточки. Поле
«type_note» предназначено для хранения типа карточки. Поле «repeat_date» хранит
дату последнего просмотра карточки.

9
Опишем структуру таблицы «history». Данная таблица хранит изменения
других таблиц и используется для логгирования. Модель таблицы отображена в
Таблице 3.4.

Таблица 3.4 – Структура таблицы "History"


Название поля Тип данных
id Integer (Primary key)

tstamp timestamp
tabname text
operation text
new_val json
old_val json
item_id bigint
Таким образом первичным ключом выступает поле «id», оно также является
авто-инкрементом.
В данной главе курсовой работы была составлена модель базы данных, а также
составлено текстовое описание каждой таблицы.

10
4 ОПИСАНИЕ ВХОДНОГО КОНТРОЛЯ И МЕТОДИКИ
ПОДДЕРЖАНИЯ ЛОГИЧЕСКОЙ ЦЕЛОСТНОСТИ БАЗЫ
ДАННЫХ

Опишем входной контроль и методику поддержания целостности базы данных.


Основная работа по контролю вводимых данных производится на уровне
бэкенда приложения и вводимых полей фреймворка flask для языка python.
username = StringField('username',
validators=[InputRequired(message="Username required"),
Length(min=4, max=25,
message="Username must be between 4 and 25 characters")])
password = PasswordField('password',
validators=[InputRequired(message="Password required"),
Length(min=4, max=25,
message="Password must be between 4 and 25 characters")])
confirm_pswd = PasswordField('confirm_pswd',
validators=[InputRequired(message="Password required"),
EqualTo('password',
message="Passwords must match")])

Рис. 4.1 Пример встроенного валидатора на flask


Данные валидаторы позволяют ещё до отправки данных на сервер, на стороне
клиента проверять правильность вводимых данных.
Для базы данных реализован триггер обеспечивающий журналирование всех
созданных таблиц. Данный триггер хранит все изменения, произведенные с
таблицами в специально созданной таблице «history». Ниже приведен код для
создания триггеров для трех таблиц. Триггер использует процедуру, которая описана
в следующем разделе.

CREATE TRIGGER users_trigger BEFORE INSERT OR UPDATE OR


DELETE ON users FOR EACH ROW EXECUTE PROCEDURE
change_trigger();
CREATE TRIGGER decks_trigger BEFORE INSERT OR UPDATE OR
DELETE ON decks FOR EACH ROW EXECUTE PROCEDURE
change_trigger();
CREATE TRIGGER notes_trigger BEFORE INSERT OR UPDATE OR

11
DELETE ON notes FOR EACH ROW EXECUTE PROCEDURE
change_trigger();

Данный триггер позволит вернуть базу данных к состоянию в определенный


момент времени и не нарушить её целостность.
Таким образом в данном разделе были описаны способы сохранения
целостности базы данных – с помощью валидаторов на уровне бэкенда flask и
триггера журналирования, а также был приведен текст триггера для журналирования.

12
5 ОПИСАНИЕ АЛГОРИТМОВ И ПРОГРАММ ОБРАБОТКИ
ДАННЫХ ВНУТРИ БАЗЫ ДАННЫХ

При создании триггера для журналирования используется процедура


change_trigger. Ниже представлен код данной процедуры.

CREATE OR REPLACE FUNCTION change_trigger() RETURNS trigger


AS $$
BEGIN
IF TG_OP = 'INSERT'
THEN
INSERT INTO history (tabname, operation, new_val,
item_id)
VALUES (TG_RELNAME, TG_OP, row_to_json(NEW), NEW.id);
RETURN NEW;
ELSIF TG_OP = 'UPDATE'
THEN
INSERT INTO history (tabname, operation, new_val,
old_val, item_id)
VALUES (TG_RELNAME, TG_OP, row_to_json(NEW),
row_to_json(OLD), NEW.id);
RETURN NEW;
ELSIF TG_OP = 'DELETE'
THEN
INSERT INTO history (tabname, operation, old_val,
item_id)
VALUES (TG_RELNAME, TG_OP, row_to_json(OLD), OLD.id);
RETURN OLD;
END IF;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;

В данном разделе курсовой работы были описаны хранимые процедуры,


используемые в ходе разработки приложения.

13
6 ОПИСАНИЕ АЛГОРИТМОВ И ПРОГРАММ ОБРАБОТКИ
ДАННЫХ У КЛИЕНТА И ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА

Опишем архитектуру программного приложения. При реализации веб-


приложения используется клиент-серверная архитектура. Приложение состоит из
нескольких модулей, в которых описана вся логика программы и базы данных, с
которой взаимодействует программа. Приложение хранится на сервере, веб-сервер
обрабатывает запросы от клиента и передает их приложению на фреймворке flask
используя WSGI. В свою очередь приложение возвращает представления данных и
веб-сервер отправляет их в виде ответа клиенту. Схема работы приложения
представлена на Рисунке 6.1.

Рисунок 6.1 – Архитектура приложения


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

Таблица 6.1 – Описание модуля приложения


Наименование Назначение Сигнатура
Точка входа в приложение Запуск и конфигурация app.py
приложения, конфигурация бд,
обработка POST и GET
запросов, менеджмент сессий.
Класс-модели Класс-модели для таблиц базы models.py
данных на основе SQLAlchemy
Поля ввода данных Описание в виде классов полей wtform_fields.py
ввода данных, реализация их
автоматической валидации
Шаблоны Шаблоны HTML, написанные с templates/
использованием технологий
Jinja2, Javascript, Ajax

14
Далее приведем скриншоты основных элементов пользовательского
интерфейса.
При запуске приложения появляется приветственное окно.

Рисунок 6.2 – Приветственная страница приложения


Прежде всего нам необходимо зарегистрироваться, нажав на кнопку «Создать
аккаунт». Переходим на страницу, где представлена стандартная форма регистрации,
необходимо заполнить имя, фамилию и указать логин с паролем.

15
Рисунок 6.3 – Страница с формой регистрации

Нажав на кнопку на «Создать», мы создаем свою учетную запись и можем


войти как авторизованный пользователь, данное изображение представлено на
Рисунке 6.2.
Нажав на кнопку «Войти» происходит переход на страницу авторизации
приложения. После авторизации пользователь переходит на главную страницу.

16
Рисунок 6.4 – Главное меню
На данной странице пользователь может создать колоду, перейти на страницу
«Добавить карточки», перейти на страницу «Учить», а также выйти из аккаунта.
Пользователь может переименовать или удалить колоду, нажав на выпадающий
список «Редактировать».

Рисунок 6.5 – Редактирование колоды

Также при клике на колоду пользователь может ознакомиться с карточками,


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

Рисунок 6.6 – Редактирование карточек

При нажатии на надпись «Добавить карточку», переходим на страницу, где


представлены поля, которые необходимо заполнить для создания учебной карточки.

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

Рисунок 6.7 – Страница добавления карточки

После создания колоды и заполнения её несколькими карточками пользователь


может перейти к странице «Учить», где представлен режим обучения путем
просмотра карточек.

Рисунок 6.8 – Страница «Учить»

После выбора колоды для обучения, пользователь нажимает на кнопку учить и


переходит в режим тренировки.

Рисунок 6.9 – Режим тренировки

Для начала тренировки пользователю нужно нажать кнопку «Начать


тренировку».

18
Рисунок 6.10 – Отображение карточки в режиме тренировки

После чего кнопка «Начать тренировку» будет скрыта и появится отображение


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

7 ОПИСАНИЕ ПОРЯДКА РАБОТЫ С СОЗДАННЫМ


ПРОГРАММНЫМ КОМПЛЕКСОМ

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


администраторских навыков сможет пользоваться разработанным приложением.

7.1 Инструкция по запуску приложения

Предварительно необходимо установить архив с приложением. После


распаковки необходимо перейти к расположению файла и кликнуть на иконку,
выбрав пункт «открыть с помощью IDE». Пример показан на Рисунке 7.1.

19
Рисунок 7.1.1 – Запуск приложения

7.2 Инструкция по использованию приложения

После клика на иконку приложения откроется IDE. Конфигурация базы данных


настроена автоматически, поэтому пользователю необходимо открыть файл app.py
(точка входа в приложение) и запустить его.

После успешного запуска ,появятся эти строчки

Рисунок 7.2.1 – Успешный запуск

И мы можем спокойно переходить по ссылке в главное меню, которое


изображено выше на Рисунке 6.3.
Для свободных пользовательских манипуляций с данными соберем в Таблицу
7.2.1 сведения о кнопках, текстовых полях и их функциональных назначениях.

Таблица 7.2.1 – Описание функционала приложения


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

20
user
Пароль Поле, заполняемое при регистрации и
авторизации, данные записываются в таблицу
user
Создать Кнопка, которая осуществляет функционал
добавления колоды или карточки в таблицы
decks и notes
Название колоды Поле, заполняемое при создании колоды,
данные записываются в таблицу decks
Колоды Кнопка, переход к одноименной странице
Добавить карточку Кнопка, переход к одноименной странице
Учить Кнопка, переход к одноименной странице
Переименовать Кнопка, при нажатии изменяет название колоды
или карточки на введенные пользователем
Удалить Кнопка, при нажатии удаляет колоду или
карточку из колоды
Выбор колоды Поле с выбором колоды
Тип карточки Поле с выбором типа карточки
Лицевая сторона Заполняемое поле, данные будут записаны в
таблицу notes
Оборотная сторона Заполняемое поле, данные будут записаны в
таблицу notes
Учить карточки Кнопка, запускающая обучающий режим
Выйти Кнопка, при нажатии пользователь выйдет из
аккаунта
Определим системные требования приложения. Они отображены в Таблице
7.2.2.

Таблица 7.2.2 – Системные требования к устройству


Операционная система
Windows XP с пакетом обновления 2+
Windows Vista Windows 7 Windows 8 Windows 10
Процессор
Intel Pentium 4 / Athlon 64 или более поздней версии с
поддержкой SSE2
Свободное место на диске
350 Мб
Оперативная память
Доступно 512 МБ
Ограничение возможности ввода некорректных значений в таблицы состоит в
том, что при попытке добавить запись, без авторизации на экране появится ошибка,
21
которая уведомит пользователя о том, что он пытается совершать действия без
авторизации.
Исходный код программы и файла базы данных приведения в приложениях А и
Б в разделе Приложения.
Таким образом в данной главе курсовой работы были определены инструкции
пользователя по запуску и использованию приложения.

СПИСОК ЛИТЕРАТУРЫ

1. Мартин Д. Организация баз данных в вычислительных системах. – М.:


Мир, 1980. – 662 с.
2. Пушников А. Ю. Введение в системы управления базами данных. Часть
1. Реляционная модель данных: Учебное пособие/Изд.-е Башкирского ун-та. – Уфа,
1999. – 108 с.
3. Дейт К. Дж. Введение в системы баз данных — 8-е изд. — М.: Вильямс,
2005. — 1328 с.
4. Грофф Джеймс Р., Вайнберг Пол Н., Опель Эндрю Дж. SQL: полное
руководство, 3-е издание - М.:«Вильямс», 2014. - 960 с.
5. Грабер Мартин. SQL. – М.: Лори, 2007. – 672 с.
6. Фиайли Крис. SQL: Руководство по изучению языка. — М.: Peachpit
Press, 2003. — 456 с.
7. Тейлор Аллен. SQL для чайников, 8-е издание. — М.: «Диалектика»,
2014. - 416 с.
8. Бондарь Александр. InterBase и Firebird. Практическое руководство для
умных пользователей и начинающих разработчиков. (Цифровая книга), 2012 - 592с.
9. Борри Хелен. Firebird. Руководство разработчика баз данных. БХВ-
Петербург - 2007 - 1104 с.
10. Ковязин А.Н., Востриков С.М. Мир InterBase: Архитектура,
администрирование и разработка приложений баз данных в InterBase/Firebird/Yaffil. -
М: КУДИЦ-Образ, 2003. – 496 с.

22
11. Скляр А.Я. Введение в InterBase. - М: Горячая линия –Телеком, 2002. -
517 с.
12. Руководство по языку SQL СУБД Firebird 3.0.
https://www.ibase.ru/files/firebird/Firebird_3_0_Language_Reference_RUS.pdf
13. Урман С. Oracle 8. Программирование на языке PL/SQL - М: Лори, 2000.
– 606 с.
14. Урман Скотт, Хардман Рон. Oracle Database 10g. Программирование на
языке PL/SQL. - М: Лори 2010 - 792 с.
15. Фейерштейн С., Прибыл Б. Oracle PL/SQL для профессионалов. - Спб:
Питер, 2011. - 800 с.
16. Хардман Рон, МакЛафлин Майкл. Oracle Database PL/SQL.
Рекомендации эксперта. -М: Лори 2014. - 450 с.
17. Дэвидсон Л. Проектирование баз данных на SQL Server 2000. - М:
Бином. Лаборатория Базовых Знаний , 2003. – 680 с.
18. Архангельский А.Я., Тагин М.А. Программирование в C++ Builder 6 и
2006. . - М: "Бином-Пресс", 2007. - 992 стр.
19. Архангельский А.Я. Программирование в С++ Builder. М.: Бином, 2007 -
1182 c.
20. Архипенков С., Голубев Д., Максименко О. ХРАНИЛИЩА ДАННЫХ.
От концепции до внедрения - М.: ДИАЛОГ-МИФИ, 2002.
21. Барсегян А., Куприянов М., Холод И., Степаненко В. Методы и модели
анализа данных: OLAP и Data Mining. – СПб.: БХВ-Петербург, 2004.
22. Спирли Эрик - “Корпоративные хранилища данных. Планирование,
разработка и реализация“. - 2001. - 400с.
23. Федоров А., Елманова Н. Введение в OLAP-технологии Microsoft. –М.:
Диалог-МИФИ, 2002. – 268 с.
24. Codd E.F., Codd S.B., and Salley C.T. "Providing OLAP (On-line Analytical
Processing) to User-Analysts: An IT Mandate". Codd & Date, Inc. 1993.
25. Скляр А.Я. Технология обработки и хранения данных (sql серверы и
хранилища данных). Учебное пособие. Электронное издание. М.; МИРЭА. 2017, 181
с.

23
26. Ян Робинсон, Джим Вебер, Эмиль Эифрем. Графовые базы данных
Новые возможности для работы со связанными данными. – М.: ДМК Пресс, 2016, 256
с.

ПРИЛОЖЕНИЯ

Приложение А – Текст создания базы данных в СУБД Postgresql


Приложение Б – Исходный код приложения
24
25
Приложение А

В данном приложении представлен текст файла базы данных.

CREATE TABLE users (


id serial PRIMARY KEY,
username varchar(25) UNIQUE NOT NULL,
password text NOT NULL
);

CREATE TABLE decks (


id serial PRIMARY KEY,
deck_name varchar(25) UNIQUE NOT NULL,
user_id integer REFERENCES users(id) ON DELETE
CASCADE NOT NULL
);

CREATE TABLE notes (


id serial PRIMARY KEY,
front varchar(200) NOT NULL,
back varchar(200) NOT NULL,
deck_id integer REFERENCES decks(id) ON DELETE
CASCADE NOT NULL,
type_note varchar(20) NOT NULL,
repeat_date timestamp
);

CREATE TABLE history (


id serial,

26
tstamp timestamp DEFAULT now(),
tabname text,
operation text,
new_val json,
old_val json,
item_id int8
);

CREATE OR REPLACE FUNCTION change_trigger() RETURNS


trigger AS $$
BEGIN
IF TG_OP = 'INSERT'
THEN
INSERT INTO history (tabname, operation, new_val,
item_id)
VALUES (TG_RELNAME, TG_OP, row_to_json(NEW),
NEW.id);
RETURN NEW;
ELSIF TG_OP = 'UPDATE'
THEN
INSERT INTO history (tabname, operation, new_val,
old_val, item_id)
VALUES (TG_RELNAME, TG_OP, row_to_json(NEW),
row_to_json(OLD), NEW.id);
RETURN NEW;
ELSIF TG_OP = 'DELETE'
THEN
INSERT INTO history (tabname, operation, old_val,
item_id)
VALUES (TG_RELNAME, TG_OP, row_to_json(OLD),
OLD.id);
RETURN OLD;
END IF;
27
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;

CREATE TRIGGER users_trigger BEFORE INSERT OR UPDATE OR


DELETE ON users FOR EACH ROW EXECUTE PROCEDURE
change_trigger();
CREATE TRIGGER decks_trigger BEFORE INSERT OR UPDATE OR
DELETE ON decks FOR EACH ROW EXECUTE PROCEDURE
change_trigger();
CREATE TRIGGER notes_trigger BEFORE INSERT OR UPDATE OR
DELETE ON notes FOR EACH ROW EXECUTE PROCEDURE
change_trigger();

Приложение Б

В данном приложении представлена часть исходного кода приложения. Сам


архив будет загружен в цдо.

from flask import Flask, redirect, url_for, request,


render_template, session, flash, json
from flask_login import LoginManager, login_user,
current_user, logout_user

from wtform_fields import *


from models import *

app = Flask(__name__)
app.secret_key = 'replace later'

# Configure database
app.config['SQLALCHEMY_DATABASE_URI']='postgresql://
lxwvizyttgkgqf:8bb45888158e3468b011f6742ea3a84c91494679be1155
bc5ff58e0aa72f5bf2@ec2-54-228-125-183.eu-west-
1.compute.amazonaws.com:5432/d7eb449u08v2c7'
28
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

# Initialize login manager


login = LoginManager(app)
login.init_app(app)

@login.user_loader
def load_user(id):
return User.query.get(int(id))

@app.route("/", methods=['GET', 'POST'])


def index():
if current_user.is_authenticated:
return redirect(url_for('decks'))

reg_form = RegistrationForm()

# Update database if validation success


if reg_form.validate_on_submit():
username = reg_form.username.data
password = reg_form.password.data
# Hash password
hashed_pswd = pbkdf2_sha256.hash(password)

# add user with hashed password to db


user_object = User(username=username,
password=hashed_pswd)
db.session.add(user_object)
db.session.commit()

flash('Registered successfully. Please login.',


'success')
return redirect(url_for('login'))

return render_template("index.html", form=reg_form)

@app.route("/login", methods=['GET', 'POST'])


def login():
if current_user.is_authenticated:
return redirect(url_for('decks'))
29
login_form = LoginForm()

# Allow login if validation success


if login_form.validate_on_submit():
user_object =
User.query.filter_by(username=login_form.username.data).first
()
login_user(user_object)
return redirect(url_for('decks'))

return render_template("login.html", form=login_form)

@app.route("/decks", methods=['GET', 'POST'])


def decks():
if not current_user.is_authenticated:
flash('Please login', 'danger')
return redirect(url_for('login'))

deck_form = DeckForm()
user_id = int(current_user.get_id())

if (request.method == "POST") and


deck_form.validate_on_submit():
deck_name = deck_form.deck_name.data

# add deck_name with user_id to db


deck_object = Deck(deck_name=deck_name,
user_id=user_id)
db.session.add(deck_object)
db.session.commit()
flash(f'Deck with name {deck_name} created.',
'success')
# return render_template("decks.html",
form=deck_form)

user_decks = [deck.deck_name for deck in


User.query.get(user_id).decks]

30
# Способ отобразить колоды
# print(User.query.get(3).decks[0].deck_name)
return render_template("decks.html", form=deck_form,
user_decks=user_decks)

@app.route("/delete", methods=['POST'])
def delete():
# Delete deck
if request.form.get('delete'):
deck_name = request.form.get('delete')
deck_object =
Deck.query.filter_by(deck_name=deck_name).first()
# print()

u = db.session.get(Deck, deck_object.id)
db.session.delete(u)
db.session.commit()
# return "test"
return redirect(url_for('decks'))

@app.route("/add", methods=['GET', 'POST'])


def add():
if not current_user.is_authenticated:
flash('Please login', 'danger')
return redirect(url_for('login'))

note_form = NoteForm()
user_id = int(current_user.get_id())

if (request.method == "POST") and


note_form.validate_on_submit():
# get data from request and fill the field
front = note_form.front.data
back = note_form.back.data
deck_name = request.form.get('deck_name')
deck_id =
Deck.query.filter_by(deck_name=deck_name).first().id
type_note = request.form.get('type_note')
date = None
# print(f"front={front}, back={back},
deck_id={deck_id}, type_note={type_note}, date={date}")
31
# add note to db
note_object = Note(front=front, back=back,
deck_id=deck_id, type_note=type_note, repeat_date=date)
db.session.add(note_object)
db.session.commit()
flash(f'Note for {deck_name} created.', 'success')
# return render_template("add.html", form=note_form)

print(User.query.get(user_id).decks)
user_decks = [deck.deck_name for deck in
User.query.get(user_id).decks]

# Способ отобразить колоды


# print(User.query.get(3).decks[0].deck_name)
# return render_template("decks.html", form=deck_form,
user_decks=user_decks)

return render_template("add.html", form=note_form,


user_decks=user_decks)

@app.route("/user/<string:name_deck>", methods=['GET',
'POST'])
def uniq_deck(name_deck):
if not current_user.is_authenticated:
flash('Please login', 'danger')
return redirect(url_for('login'))

notes_front, notes_back = get_notes_value(name_deck)


notes = []
for i in range(len(notes_front)):
notes.append([notes_front[i], notes_back[i]])

return render_template("uniq_deck.html", notes=notes,


Deck_name=name_deck)

@app.route("/learn", methods=['GET', 'POST'])


def learn():
if not current_user.is_authenticated:
flash('Please login', 'danger')
32
return redirect(url_for('login'))

if request.method == "POST":
name_deck = request.form.get('deck_name')
deck_id =
Deck.query.filter_by(deck_name=name_deck).first().id
notes = Note.query.filter_by(deck_id=deck_id).all()
notes_front = [note.front for note in notes]
notes_back = [note.back for note in notes]

return render_template("learn_deck.html",
notes_front=notes_front, notes_back=notes_back,
Deck_name=name_deck)

user_id = int(current_user.get_id())
user_decks = [deck.deck_name for deck in
User.query.get(user_id).decks]

return render_template("learn.html",
user_decks=user_decks)

@app.route("/logout", methods=['GET'])
def logout():
if not current_user.is_authenticated:
flash('Please login', 'danger')
return redirect(url_for('login'))

# Logout user
logout_user()
flash('You have logged out successfully', 'success')
return redirect(url_for('login'))

def get_notes_value(name_deck):
deck_id =
Deck.query.filter_by(deck_name=name_deck).first().id
notes = Note.query.filter_by(deck_id=deck_id).all()
notes_front = [note.front for note in notes]
notes_back = [note.back for note in notes]
return (notes_front, notes_back)

@app.route('/get_note_start', methods=['GET', 'POST'])


33
def get_note_start():
print(request.form)

name_deck = request.form.get('deck_name')
notes_front, notes_back = get_notes_value(name_deck)

return json.dumps({'notes_front': notes_front[0],


'notes_back': notes_back[0],
'notes_len': len(notes_front)})

@app.route('/get_note_next', methods=['GET', 'POST'])


def get_note_next():
name_deck = request.form.get('deck_name')
note_number_next = int(request.form.get('note_number')) +
1
notes_front, notes_back = get_notes_value(name_deck)

if note_number_next >= len(notes_front):


note_number_next = len(notes_front) - 1

return json.dumps({'notes_front':
notes_front[note_number_next],
'notes_back':
notes_back[note_number_next],
'notes_number': note_number_next})

@app.route('/get_note_prev', methods=['GET', 'POST'])


def get_note_prev():
name_deck = request.form.get('deck_name')
note_number_prev = int(request.form.get('note_number')) -
1
if note_number_prev < 0:
note_number_prev = 0
notes_front, notes_back = get_notes_value(name_deck)

return json.dumps({'notes_front':
notes_front[note_number_prev],
'notes_back':
notes_back[note_number_prev],
'notes_number': note_number_prev})
34
if __name__ == '__main__':
app.run(debug=True)

35

Вам также может понравиться