УДК 004.415.2

Разработка программного обеспечения для мониторинга популяции попугаев

Чижов Александр Дмитриевич – студент кафедры вычислительной техники Новосибирского государственного технического университета

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

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

Сегодня в России существует проблема дефицита решений для контроля за популяцией птиц, находящихся в негосударственном владении. В Европе имеется множество клубов, позволяющих решать данную проблему.

Клуб позволяет решать биологическую проблему сохранения разнообразия редких видов птиц, в том числе: защитить животное от потери или кражи (кольцевание), предостеречь близкородственное скрещивание (генеалогическое дерево), исключить частые ошибки в содержании животных (просвещение – блог). Участниками подобных клубов могут быть не только хозяева птиц, но и заводчики, а также ученые и ветеринарные врачи, занимающиеся исследованием попугаев.

Согласно опросу Всероссийского центра изучения общественного мнения от 2020 года 3% населения России содержит декоративных птиц. То есть, около 4,5 млн человек являются обладателями попугаев. Каждый человек из указанных может стать потенциальным участником клуба.

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

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

Сравнение интернет-ресурсов производится по ряду показателей, каждый из которых оценивается от 0 до 2 баллов, где 0 – неудовлетворительный уровень показателя, 1 – удовлетворительный и 2 – высокий уровень.

Было выбрано по три отечественных (1 – Российское общество любителей волнистых попугаев [1], 2 – Московский клуб любителей певчих и экзотических птиц [2], 3 – Московский клуб «Русская канарейка» [3]) и зарубежных (4 – British Bird Council [4], 5 – International Ornithological Association [5], 6 – Federación Ornitológica Argentina [6]) ресурса. В таблицу 1 сведены результаты сравнения.

Таблица 1. Результаты сравнительного анализа.

Показатель сравнения

Номер интернет-ресурса

1

2

3

4

5

6

Информативность

1

2

2

2

2

2

Визуальная составляющая

0

1

0

2

2

2

Обратная связь

0

2

1

1

1

2

Дополнительный функционал

0

2

1

1

2

2

Общий балл

1

7

4

6

7

8

Можем заметить, что функционал только одного российского ресурса схож с зарубежными аналогами, однако уступает им в некотором областях (нет реализации интернет-магазина сопутствующей продукции для разведения птиц). Главной задачей подобных сервисов является кольцевание птиц, однако отечественные клубы не предоставляют такой возможности. Зарубежные аналоги имеют хороший уровень развития ПО, вследствие чего и пользуются большой популярностью.

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

На рисунке 1 представлена диаграмма прецедентов веб-приложения.

image001

Рисунок 1. Диаграмма прецедентов.

Иерархический список требований к проекту

Система должна содержать следующую информацию: новостную ленту; интернет-магазин с продажей сертифицированных колец; блог – небольшие статьи о птицах; страницу с автоматической расшифровкой кольца; страницу для поиска информации о птице.

Система должна предоставлять следующие возможности:

  • регистрация новых пользователей – заводчика (член клуба) и владельца. Для аккаунта с правами доступа «заводчик» доступен следующий функционал: заполнение личной информации о заводчике, регистрация новой птицы в единой базе клуба, форма, позволяющая получить правильную маркировку птицы и другое;
  • при продаже попугая заводчик или прежний владелец передает «права на владение» в анкете на птицу новому владельцу;
  • зарегистрированные пользователи получают возможность просмотра генеалогического древа птицы;
  • владелец просматривает список птиц, принадлежащих ему;
  • админ-панель необходимая для управления сайтом (интернет-магазин, блог, новости, поддержка актуальной информации и др.). Аккаунт менеджера связан только с деятельностью интернет-магазина.

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

Требования к базе данных: при проектировании реляционной базы данных необходимо помнить об эффективности структуры БД.

Стек технологий и инструментов

Для разработки WEB-приложения по мониторингу популяции попугаев был определен стек технологий и используемых инструментов. Среда разработки: WebStorm; язык программирования серверной части: Python; серверный фреймворк: Django; язык программирования клиентской части: JavaScript; клиентский фреймворк: Vue.js; СУБД: PostgreSQL; графический редактор: Figma.

Проектирование

На рисунке 2 приведена диаграмма состояний WEB-приложения.

image002

Рисунок 2. Диаграмма состояний.

Выделяются 12 сущностей: заказ, товары в заказе, корзина, пользователь, товар, адреса, птицы, статьи, новости, оценка статьи/новости/продукта. В каждой из таблиц будут храниться все данные о сущностях (описание таблиц сведено в таблицу 2).

Таблица 2. Таблицы базы данных.

Название таблиц

Описание

OrderProduct

Таблица содержит информацию о товарах в заказе

OnlineOrder

Таблица содержит информацию о деталях заказа

Cart

Таблица содержит данные о корзине пользователя

Account

Таблица содержит данные пользователя

Address

Таблица содержит адреса пользователей

Bird

Таблица содержит информацию о животном

Product

Таблица содержит информацию о товарах магазина

Article

Таблица содержит статьи

News

Таблица содержит новости

StarProduct, StarNews, StarArticle

Таблицы содержат оценки пользователей

На рисунке 3 приведена схема БД, содержащая информацию о связях таблиц, атрибутах таблиц и их типах, первичных ключах.

image003

Рисунок 3. Схема базы данных.

Серверная часть приложения

Описание ORM. В качестве примера приведем фрагмент кода модели Bird. Модель Bird хранит в себе информацию о птице.

1:                       class Bird(models.Model):

2:                           id = models.AutoField(primary_key=True)

3:                           user = models.ForeignKey(Account, on_delete=models.CASCADE)

4:                           number = models.CharField(max_length=64, null=True)

5:                           name = models.CharField(max_length=256, null=True)

6:                           date_of_birth = models.DateField(null=True)

7:                           parent1 = models.CharField(max_length=64, null=True)

8:                           parent2 = models.CharField(max_length=64, null=True)

9:                           GENDER_BIRD = (

10:                               ('Male', 'Male'),

11:                               ('Female', 'Female'),

12:                               ('None', 'None'),

13:                           )

14:                           gender = models.CharField(max_length=6, choices=GENDER_BIRD)

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

1:                       class BirdSerializer(serializers.ModelSerializer):2:                       class Meta:3:                           model = Bird4:                           fields = ('id', 'user', 'number', 'name', 'date_of_birth', 'parent1', 'parent2', 'gender')

Типовая схема HTTP запросов. Для работы с данными используется четыре типа HTTP запроса: get, post, put, delete. Все обработчики запросов для работы с данными действуют по одному сценарию.

Диспетчер URL. В корневой папке проекта находится файл urls.py, он ссылается на файлы urls.py, хранящиеся в каждом из четырех модулей. Ниже приведены фрагменты кода корневого файла urls и одного из urls модулей.

1:                       urlpatterns = [2:                           path('admin/', admin.site.urls),3:                           path('', include('Core.urls')),4:                           path('', include('Shop.urls')),5:                           path('', include('News.urls')),6:                           path('', include('Article.urls')),7:                       ]
1:                       urlpatterns = [2:                           path('news', views.newsApi),3:                           path('news/<int:section>', views.newsApi),4:                        5:                           path('news/savefile', views.SaveFile),6:                        7:                           path('news/<slug:section>', views.newsApi),8:                        9:                           path('starnews', views.starnewsApi), 10:                           path('starnews/<int:section>', views.starnewsApi),11:                       ]+static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Клиентская часть приложения

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

Маршрутизация (router). В приложении используется два типа маршрутизации статическая и динамическая. Статической являются страницы с постоянными данными, к примеру домашняя страница, страница пользователя и тд. В то время как динамические страницы необходимы для страниц с постоянно меняющимися данными, например страница открытого товара интернет-магазина или же страница статьи, новости. Ниже приведен фрагмент маршрутизации для каждого из видов.

1:                       const routes = [2:                        3:                       {4:                         path: '/',5:                         name: 'home',6:                         component: () => import('../views/Home.vue')7:                       },8:                        9:                       {10:                         path: '/open_news/:id',11:                         name: 'open_news',12:                         component: () => import('../views/Open_news.vue')13:                       },14:                       ]15:                       const router = createRouter({16:                         history: createWebHistory(process.env.BASE_URL),17:                         Routes18:                       })19:                       export default router

Хранилище состояний (VUEX). Для каждой сущности, хранимой в БД, был создан отдельный модуль в хранилище состояний VUEX. Хранилище состоит из: state – непосредственно данных, getters – функций для получения данных из state вне хранилища, mutations – изменения данных хранилища и actions – получения данных из БД. Ниже приведен пример хранилища для сущности Bird и код для подключения этого модуля к VUEX

1:                       import axios from "axios";2:                       import {variables_url} from '@/URLs'3:                        4:                       export default {5:                           state: {6:                               bird: []7:                           },8:                           getters: {9:                               ALL_BIRD: state => state.bird10:                           },11:                           mutations: {12:                               SET_BIRD: (state, bird) => state.bird = bird,13:                               NEW_BIRD: (state, bird) => state.bird.unshift(bird),14:                               UPD_BIRD: (state, new_bird) => {15:                                   const index = state.bird.findIndex(t => {return t.id === Number(new_bird.id)})16:                                   if (index !== -1){17:                                       state.bird.splice(index, 1, new_bird);18:                                   }19:                               },20:                               REMOVE_BIRD: (state, id) => {21:                                   state.bird = state.bird.filter(t => {return t.id !== id} )
        }22:                           },23:                           actions: {24:                               async GET_BIRD_ID({ commit }, bird_id) {25:                                   commit('SET_BIRD', (await axios.get(`${variables_url.API_URL}bird/${bird_id}`)).data);26:                               },27:                               async GET_BIRD({ commit, getters }, tree) {28:                                   if (getters.GET_AUTH === 4 || tree === true){29:                                       commit('SET_BIRD', (await axios.get(`${variables_url.API_URL}bird/tree`)).data);30:                                   }else{31:                                       commit('SET_BIRD', (await axios.get(`${variables_url.API_URL}bird/user/${getters.GET_ID}`)).data);
            }32:                               },33:                               async POST_BIRD({ commit }, bird) {34:                                   commit('NEW_BIRD', (await axios.post(`${variables_url.API_URL}bird`, bird)).data);35:                               },36:                               async PUT_BIRD({ commit }, bird) {37:                                   commit('UPD_BIRD', (await axios.put(`${variables_url.API_URL}bird`, bird)).data);38:                               },39:                               async DEL_BIRD({ commit }, bird) {40:                                   commit('REMOVE_BIRD', (await axios.delete(`${variables_url.API_URL}bird/${bird.id}`)).data);41:                               }42:                           },43:                       }
1:                       import { createStore } from 'vuex'2:                       import bird from "@/store/modules/bird";3:                        4:                       export default createStore({5:                         modules: {6:                           bird,7:                       }8:                       })

Дополнительный функционал. Важной особенностью приложения является построение генеалогического древа. Специальная функция (приведена ниже) на основе введённого номера птицы выдает обычный html список и с помощью css список приобретает вид дерева.

1:                       function toHTML(bird_list, number) {2:                         const result = bird_list.find(e => e.number == number);3:                         if (result) {4:                           let out = "<li>" + result.number, get_res;5:                           if (result.parent1 || result.parent2) {6:                             out += "<ul>";7:                             if (result.parent1) {8:                               get_res = toHTML(bird_list, result.parent1);9:                               if (typeof get_res != 'undefined')10:                                 out += get_res;11:                               Else12:                                 out += "<li>" + result.parent1 + "</li>";13:                             }14:                             if (result.parent2) {15:                               get_res = toHTML(bird_list, result.parent2);16:                               if (typeof get_res != 'undefined')17:                                 out += get_res;18:                               Else19:                                 out += "<li>" + result.parent2 + "</li>";20:                             }21:                             out += "</ul>";22:                           }23:                           out += "</li>";24:                           return out;25:                         }26:                       }

Для работы с оценками по пятизвездочной шкале использовался шаблон vue-star-rating. В фрагменте кода изображено подключение модуля непосредственно в html код.

1:                       <star-rating v-if="GET_AUTH === 0" :read-only="true" class="prod_text" :rating="star1" @update:rating="setRating" :star-size="20"></star-rating>2:                       <star-rating v-else class="prod_text" :rating="star1" @update:rating="setRating" :star-size="20"></star-rating>

Для оценки предусмотрены следующие свойства: если пользователь не авторизован, то он видит среднюю оценку и не может на нее влиять; если пользователь авторизован, но не ставил ранее оценку, то он может ее поставить (изначально отображается средняя величина); для авторизованного и поставившего оценку пользователя в следующий раз будет отображаться поставленная им оценка.

При загрузке страницы срабатывает следующий фрагмент для определения текущего рейтинга

1:                       star: function (){2:                         const tmp = this.ALL_STARPRODUCT.filter(t => t.user === this.GET_ID)3:                         if (tmp[0]?.user > -1)4:                           return tmp[0]?.evaluation5:                         else {6:                           let sum = 0;7:                           for(const item in this.ALL_STARPRODUCT)8:                             sum += this.ALL_STARPRODUCT[item].evaluation;9:                           if (Object.keys(this.ALL_STARPRODUCT).length === 0)10:                             return 011:                           Else12:                             return sum / Object.keys(this.ALL_STARPRODUCT).length;13:                         }14:                       },

При нажатии на шкалу оценивания срабатывает следующий фрагмент кода.

1:                       setRating(star){2:                         const tmp = this.ALL_STARPRODUCT.filter(t => t.user === this.GET_ID)3:                         if (tmp[0]?.user > -1)4:                           this.PUT_STARPRODUCT({5:                             id: tmp[0]?.id,6:                             user: this.GET_ID,7:                             product: this.$route.params.id,8:                             evaluation: star,9:                           })10:                         else{11:                           this.POST_STARPRODUCT({12:                             user: this.GET_ID,13:                             product: this.$route.params.id,14:                             evaluation: star,15:                           })16:                         }17:                       },

Для страницы смены пользователя птицы существует ряд условий. Передаваемый пользователь нажимает на кнопку сгенерировать и получает специальный код. Фрагмент кода для генерации ключа приведен ниже.

1:                       generate_code(){2:                         const bird = this.ALL_BIRD.filter(t => t.number === this.number);3:                         const exch = this.ALL_EXCHANGE.filter(t => t.bird === bird[0]?.id);4:                         if(bird[0]?.user > -1) {5:                           for(const item in exch)6:                             if (exch[item].status === 'Active') {7:                               this.get_code = exch[item].code;8:                               Return      }9:                           this.get_code = uniqid()10:                           this.POST_EXCHANGE({11:                             user_former: this.GET_ID,12:                             bird: bird[0]?.id,13:                             code: this.get_code,14:                             status: "Active",15:                           })16:                         }17:                       },

Отправитель может отменить передачу прав, нажав на специальную кнопку. Нижеприведенный фрагмент кода демонстрирует функцию, отрабатываемую при нажатии на кнопку отмены.

1:                       cancellation(){2:                         const bird = this.ALL_BIRD.filter(t => t.number === this.number);3:                         const exch = this.ALL_EXCHANGE.filter(t => t.bird === bird[0]?.id);4:                         if(bird[0]?.user > -1)5:                           for(const item in exch)6:                             if (exch[item].status === 'Active')7:                               this.PUT_EXCHANGE({8:                                 id: Number(exch[item].id),9:                                 user_former: this.GET_ID,10:                                 bird: bird[0]?.id,11:                                 code: this.get_code,12:                                 status: "Canceled",13:                               })14:                         this.get_code = "";15:                         this.number = "";16:                       },

Получатель же, вставляя этот код в специальное поле, принимает права на птицу.

1:                       add() {2:                         const exch = this.ALL_EXCHANGE.filter(t => t.code === this.set_code);3:                         console.log(exch)4:                         if (exch.length === 0 || this.set_code === "")5:                           Return6:                         const bird = this.ALL_BIRD.filter(t => t.id === exch[0]?.bird);7:                         this.POST_BIRD({8:                           user: this.GET_ID,9:                           number: bird[0]?.number,10:                           name: bird[0]?.name,11:                           date_of_birth: bird[0]?.date_of_birth,12:                           parent1: bird[0]?.parent1,13:                           parent2: bird[0]?.parent2,14:                           gender: bird[0]?.gender,15:                         })16:                         this.DEL_BIRD({17:                           id: Number(bird[0]?.id),18:                         })19:                         this.set_code = "";20:                       }

Список литературы

  1. Российское общество любителей волнистых попугаев [Электронный ресурс] – 2023. Режим доступа: http://rbs.parrots.ru/ (дата обращения: 10.03.2023).
  2. Московский клуб любителей певчих и экзотических птиц [Электронный ресурс] – 2023. Режим доступа: http://mosbirdclub.ru/ (дата обращения: 10.03.2023).
  3. Московский клуб «Русская канарейка» [Электронный ресурс] – 2023. Режим доступа: http://canaria.msk.ru/kluby/moskovskij-klub-russkaya-kanarejka.html (дата обращения: 10.03.2023).
  4. British Bird Council [Электронный ресурс]– 2023. Режим доступа: https://www.britishbirdcouncil.com/ (дата обращения: 10.03.2023).
  5. International Ornithological Association [Электронный ресурс] – 2023. Режим доступа: https://www.ioa-com-uk.org/ (дата обращения: 10.03.2023).
  6. Federación Ornitológica Argentina [Электронный ресурс] – 2023. Режим доступа: http://ornitofoa.com.ar/ (дата обращения: 10.03.2023).

Интересная статья? Поделись ей с другими: