#import "template.typ": *
#import "@preview/indenta:0.0.3": fix-indent

#let author = (
  name: "Білоус А. А.",
  full_name_gen: "Білоуса Антона Андрійовича",
  group: "ПЗПІ-23-3",
  gender: "m",
)

#let mentors = (
  (
    name: "Широкопетлєва М. С.",
    gender: "f",
    degree: "Ст. викл. каф. ПІ",
  ),
  (
    name: "Черепанова Ю. Ю.",
    gender: "f",
    degree: "Ст. викл. каф. ПІ",
  ),
  (
    name: "Русакова Н. Є.",
    gender: "f",
    degree: "Доц. каф. ПІ",
  ),
)

#let task_list = (
  done_date: datetime(year: 2024, month: 12, day: 27),
  initial_data: datetime(year: 2024, month: 9, day: 15),
  source: "методичні вказівки до виконання курсової роботи, вимоги до інформаційної системи, предметна область, що пов’язана з пакунковим репозиторієм та його менеджментом.",
  content: "вступ, аналіз предметної області; постановка задачі; проектування бази даних; опис програми; висновки; перелік джерел посилання.",
  graphics: "загальна діаграма класів, ER-діаграма, UML-діаграми, DFD-діаграма, схема БД в 1НФ, 2НФ, 3НФ, копії екранів (“скриншоти”) прикладної програми, приклади звітів прикладної програми.",
)

#let calendar_plan = (
  plan_table: [
    // thanks Yehor
    #table(
      columns: 4,
      align: (center, left, center, center),
      [Номер], [Назва етапів курсової роботи], [Строк виконання етапів роботи], [Примітки],
      [1], [Аналіз предметної області], [15.09.24 – 24.09.24], [Виконано],
      [2], [Концептуальне моделювання], [24.09.24-30.09.24], [~],
      [2], [Постановка задачі], [28.09.24 – 2.10.24], [Виконано],
      [3], [Побудова ER-діаграми та схеми БД], [2.10.24 – 18.10.24], [Виконано],
      [4], [Оформлення розділів 1, 2 та 3.1, 3.2 пояснювальної записки], [10.10.24 - 18.10.24], [Виконано],
      [5], [Перша контрольна точка з курсової роботи], [20.10.24], [Виконано],
      [6], [Нормалізація бази даних], [20.10.24 - 15.11.24], [Виконано],
      [7], [Створення програми], [20.10.24 – 20.11.24], [Виконано],
      [8], [Тестування програми, наповнення бази даних], [20.11.24 - 5.12.24], [Виконано],
      [9], [Друга контрольна точка з курсової роботи], [7.12.24], [Виконано],
      [10], [Реалізація остаточної версії програми], [7.12.24-15.12.24], [Виконано],
      [11], [Оформлення інших розділів пояснювальної записки], [1.11.24 – 25.12.24], [Виконано],
      [12], [Третя контрольна точка з курсової роботи], [27.12.24], [Виконано],
    )

  ],
  approval_date: datetime(year: 2024, month: 12, day: 27),
)

#let abstract = (
  keywords: (
    "БАЗА ДАНИХ",
    "АВТОМАТИЗАЦІЯ",
    "РЕПОЗИТОРІЙ",
    "ПАКУНОК",
    "RUST",
    "MYSQL",
    "SQL",
  ),
  text: [
    Мета даної роботи -- проєктування та розробка інформаційної системи "Репозиторій пакунків. Колаборація над пакунками", яка спрямована на створення ефективного середовища для спільної розробки та управління програмними пакунками. Система забезпечує зберігання інформації про програмні пакунки, а також надає інструменти для їх вдосконалення колективною співпрацею користувачів.

    Для реалізації інформаційної системи було обрано сучасний стек технологій, а саме:
    Rust@rust -- як основна мова програмування для всих частин застосунку,
    iced@iced -- бібліотека для побудови графічних інтерфейсів з Elm архітектурою,
    SQLx@sqlx -- бібліотека для низькорівневої роботи з базою даних, що забезпечує коректність і гнучкість,
    MySQL@mysql -- як СУБД для зберігання інформації про пакунки, користувачів, та їх відносини,
    Neovim@neovim -- як сучасний редактор коду для швидкої і зручної розробки.

    Результат розробки -- комп'ютерна програма, яка дозволяє зберігати та відображати інформацію про користувачів, пакунки та їх відносини; генерувати статистику про користувача та його пакунки. Застосунок, створений з використанням мови програмування Rust є безпечним, правильним, надійним та швидким.
  ],
)

#let appendices = (
  /*(
    title: "Приклад звіту 1",
    content: [test],
  ),
  (
    title: "Приклад звіту 2",
    content: [test],
  ),
  (
    title: "Приклад звіту 3",
    content: [test],
  ),*/
)

#show: cw-template.with(
  title: [Інформаційна система "Репозиторій пакунків". Колаборація над пакунками],
  subject_shorthand: "БД",
  department_gen: "Програмної інженерії",
  edu_program_shorthand: "ПЗПІ",
  author: author,
  mentors: mentors,
  task_list: task_list,
  calendar_plan: calendar_plan,
  abstract: abstract,
  bib_path: "bibl.yml",
  appendices: appendices,
)

#show: fix-indent()

#nheading("Вступ")
У сучасному світі істують мільйони пакунків з програмним забезпеченням, програмними бібліотеками та іншою інформацією.
Інформаційні системи управління цими пакунками стали критично важливим елементом сучасної розробки програмного забезпечення. Їх значення особливо зросло з розвитком відкритого програмного забезпечення та модульного підходу до розробки, де кожен проект може залежати від десятків, сотень, або навіть тисяч сторонніх компонентів.

Відсутність ефективних систем управління пакунками призводить до численних проблем у процесі розробки. Розробники стикаються з труднощами при пошуку потрібних бібліотек, виникають конфлікти версій, ускладнюється процес оновлення залежностей, а також з'являються проблеми з безпекою через використання застарілих версій компонентів. Це суттєво сповільнює процес розробки та може призвести до значних фінансових втрат.

Метою цієї курсової роботи є розробка інформаційної системи "Репозиторій пакунків. Колаборація над пакунками", яка спрощує процес менеджменту пакунків, створення їх відносин, керування залежностями, та надання користувачам різних ролей у розвитку репозиторію. У процесі роботи над системою було проведено детальний аналіз зхожої системи AUR@aur, котра ефективно використовується для надання користувачам дистрибутиву Arch Linux@archlinux можливості публіковати свої пакунки. Було спроектовано реляційну базу даних і розроблено комп'ютерну програму, яка дозволяє взаємодіяти з репозиторієм.

Весь застосунок написано мовою програмування Rust@rust, для графічного інтерфейсу використовується бібліотека iced@iced, для взаємодії з базою даних використвуєтсья бібліотека SQLx@sqlx. Інформація зберігається у базі даних MySQL@mysql. Розробка виконувалася у текстовому редакторі Neovim@neovim.

= АНАЛІЗ ТА КОНЦЕПТУАЛЬНЕ МОДЕЛЮВАННЯ ПРЕДМЕТНОЇ ОБЛАСТІ
#v(-spacing)
== Аналіз предметної області
Дослідження предметної області є ключовим етапом у розробці інформаційної системи "Репозиторій пакунків. Колаборація над пакунками". Основною метою даного аналізу є визначення функціональних вимог та технічних особливостей системи, необхідних для ефективного управління програмними пакунками та забезпечення продуктивної співпраці розробників.

Репозиторій пакунків являє собою централізоване сховище інформації про програмне забезпечення, що забезпечує зберігання, версіонування та розповсюдження програмних компонентів. В сучасних умовах критично важливою є автоматизація таких процесів як управління залежностями, контроль версій, перевірка сумісності та забезпечення безпеки пакунків. Це дозволяє значно підвищити ефективність розробки програмного забезпечення та мінімізувати ризики, пов'язані з використанням сторонніх компонентів.

Для створення ефективної системи управління пакунками необхідно ретельно проаналізувати існуючі рішення та їх особливості. Такий аналіз допомагає виявити найбільш важливі функціональні можливості та уникнути потенційних проблем при проектуванні власної системи.

В якості системи-аналогу розглянемо Arch User Repository (AUR)@aur -- репозиторій користувацьких пакунків для дистрибутиву Arch Linux@archlinux. AUR є яскравим прикладом успішної реалізації концепції колаборативної розробки та управління пакунками. Система надає користувачам можливість самостійно створювати та поширювати пакунки, які не входять до офіційного репозиторію.

При відвідуванні головної сторінки AUR @aur_main можна побачити статиску свого акаунту, всього репозиторію а також останні оновлення пакунків. За допомогою поля пошуку можна перейти до сторінки пошуку пакунків @aur_search.

#img("img/aur/aur_main.png", "Головна сторінка AUR")

#img("img/aur/aur_search.png", "Сторінка пошуку AUR")

Зі сторінки пошуку пакунків можна перейти до сторінки інформації пакунку, або ж до сторінки користувача який супроводжує пакунку. На сторінці інформації пакунку @aur_package можна побачити що пакунок має базу пакунку, пошукові слова, ліцензії, різні ролі користувачів, інформацію про залежності, тощо. На сторінці інформації про користувача @aur_user можна побачити різні атрибути пов'язані з акаунтом.

#img("img/aur/aur_package.png", "Деталі пакунку в AUR")

#img("img/aur/aur_user.png", "Деталі користувача в AUR")

Навігація по репозиторію здебільного здійснюється за допомогою гіперпосилань на сторінку пошуку де можна обрати досить багато критерій пошуку. Наприклад, при натисканні посилання "View this user's packages" в профілі користувача lsf, можна побачити всі пакунки котрі цей користувач підтримує @aur_search_func.

#img("img/aur/aur_search_func.png", "Фунціонал пошуку AUR")

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

Аналіз інформаційної системи виявив наступні ключові об'єкти предметної області: читач, супроводжуючий, пакунок, база пакунку, залежності пакунків, відносини пакунків, роль користувача. Основним користувачем виступає читач, який потребує ефективних інструментів для отримання інформації про програмні компоненти.

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

На основі проведеного аналізу було визначено критичні вимоги до функціоналу репозиторію пакунків. Ця інформація становитиме основу для проектування системи, яка забезпечить ефективну колаборацію над програмними компонентами та автоматизує ключові процеси їх розробки та супроводу.

Особлива увага буде приділена реалізації механізмів забезпечення якості та безпеки пакунків, оскільки ці аспекти є критичними для створення надійної екосистеми програмного забезпечення. Система також передбачатиме можливості для масштабування та інтеграції з іншими інструментами розробки.

== Концептуальне моделювання предметної області
Інформаційна система передбачає взаємодію як для читачів (неавтентифікованих користувачів), так і для автентифікованих користувачів. Система автоматизує процеси пов'язні з відстеженням залежностей пакунків, прав користувачів, пошуком пакунків і генеруванням статистик.

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

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

Всі дані зберігатимуться в базі даних, яка містить пов'язані таблиці спроектовані таким чином, що б забезпечити цілістність і коректність даних в них. Розумне проектування бази даних забезпечить оптимізацію обробку, пошук та оновлення інформації. Кожному об'єкту предметної області, як от користувач, пакунок, база пакунку, залежності пакунків, відносини пакунків та роль користувача, буде відповідати певна таблиця. Для розуміння структури інформаційної системи спроектовано загаліну діаграму класів @class_diagram, яка є основою для проектування бази даних.

#img("img/class_diagram.png", "Загальна діаграма класів (рисунок виконаний самостійно)")

Система буде зберігати наступні дані:
- об'єкт користувача буде зберігати інформацію про користувачів системи. Він буде містити ім'я користувача, електронну пошту, пароль, час останнього використання обілкового запису, та його час створення з оновленням. Ця інформація необхідна для автентифікації та авторизації користувачів, а також для аудиту дій користувачів в системі;
- об'єкт бази пакунку буде зберігати базову інформацію про групи пакунків. Він дозволить об'єднувати пакунки зі спільними компонентами та користувачами. Містить ім'я та опис групи пакунків, а також час створення та останнього оновлення. Ця інформація дозволяє групувати пакунки за їх базовою функціональністю;
- об'єкт типу ролі користувача буде описувати ролі користувачів у контексті груп пакунків. Містить ідентифікатор ролі, її ім'я та опис. Приклади ролей: submitter (відправник), packager (пакувальник), maintainer (супроводжуючий), flagger (позначник). Цей об'єкт визначає дозволи користувачів на певні дії з групами пакунків;
- об'єкт ролі користувача буде прив'язувати користувачів до груп пакунків та визначати їх ролі в цих групах. Містить ідентифікатор групи пакунків, ідентифікатор користувача, ідентифікатор ролі та коментар від користувача. Ця інформація дозволяє визначити, хто, чому, і які права має в кожній групі пакунків;
- об'єкт пакунку буде зерігати інформацію про окремі пакунки. Містить ідентифікатор групи пакунків, ім'я пакунку, версію, опис, веб-покликання, опціональний час позначення, час створення та останнього оновлення. Ця інфрмація є основною для ідентифікації та опису кожного пакунку;
- об'єкт виду залежності буде визначати типи залежностей пакунків (наприклад, проста залежність, залежність для збірки пакунку, опціональна залежність). Містить ідентифікатор типу залежності та її назву. Цей об'єкт дозволить класифікувати залежності пакунків;
- об'єкт залежностей пакунку буде зберігати інформацію про залежності пакунків. Він буде містити архітектуру, умову, опис, ідентифікатор пакунку, ідентифікатор типу залежності та псевдонім назви залежності. Ця інформація дозволяє визначити, від яких інших пакунків залежить даний пакунок.
- об'єкт виду відносин буде визначати типи відносин між пакунками (наприклад: конфліктує, надає, замінює). Містить ідентифікатор типу відношення та його назву. Цей об'єкт дозволяє класифікувати відносини між пакунками;
- об'єкт відносин пакунків буде зберігати інформацію про відносини пакунків (конфлікти, надання, заміна). Містить архітектуру, вимоги, ідентифікатор пакунку, ідентифікатор типу відношення та ім'я пакунку-відношення. Ця інформація дозволяє визначити, які пакунки конфліктують з даним пакунком, які функції він надає, і які пакунки він замінює.

Користувач системи має можливість здійснювати пошук пакунків та перегляди їх інформацію, переходити до профілей користувачів та переглядати їх пакунки. Для наочного представлення процесів та потоків даних в системі під час виконання користувачем поставленої задачі створимо DFD-діаграму @data_diagram, що відображатиме послідовність кроків під час виконання користувачем цільової бізнес-задачі перегляду інформації про пакунок та отримання звіту з цієї інформації.

#img("img/data_diagram.png", "Діаграма потоків даних (рисунок виконано самостійно)")

Для ефективного відображення інформаційних потреб користувачів необхідно побудувати діаграму варіантів використання (Use Case). Ця діаграма  @usecase_diagram  демонструє взаємодію між різними акторами системи, такими як користувачі, адміністратори та інші учасники, та функціональними можливостями системи, такими як управління пакунками, їх створення, перегляд, отримання інформації та інші операції. Діаграма варіантів використання дозволяє визначити основні сценарії взаємодії користувачів з системою, ідентифікувати ключові функції та процеси, що відбуваються під час цієї взаємодії, та забезпечити розробку системи, яка відповідає реальним потребам користувачів. Крім того, діаграма варіантів використання може бути використана для визначення пріоритетів функціональних можливостей системи, виявлення потенційних проблем та слабких місць у процесах взаємодії з користувачами, а також для оптимізації архітектури системи для забезпечення максимальної ефективності та зручності використання.

#img("img/usecase_diagram.png", "Use-Case діаграма (рисунок виконаний самостійно)")

= Постановка задачі

Метою курсової роботи є розробка інформаційної системи "Репозиторій пакунків. Колаборація над пакунками", яка забезпечить ефективне зберігання, пошук, управління та спільну розробку програмних пакунків. Після аналізу предметної області було виділено наступні вимоги щодо функціоналу інформаційної системи:

+ Підтримка основних процесів управління пакунками та їх метаданими:
  + створення пакунків, їх оновлення та видалення;
  + керування метаданими пакунків, включаючи назву, версію, веб-покликання на ресурс, ліцензію, опис та групу;
  + додання, оновлення та видалення залежностей між пакунками, а саме: обов'язкова залежність, опціональна залежність, та залежність під час збірки пакунку;
  + додання, оновлення та видалення відносин пакунків таких як: надання залежності, конфлікт з залежністю та заміна залежності.

+ Забезпечення системи користувацьких акаунтів:
  + надійний механізм реєстрації та автентифікації користувачів;
  + можливість видалення свого акаунту та передачі пакунків іншому користувачеві;
  + механізм блокування недобросовісних користувачів.

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

+ Функції пошуку та категоризації:
  + пошук та фільтрація пакунків за веб-покликанням, назвою, групою, описом пакунку, описом групи, навзвою та описом пакунку, користувачем, помітчиком, пакувальником, подавачем та супроводжуючим пакунку;
  + можливість точного та довільного пошуку;
  + можливість вибору ліміту кількості результатів пошуку;
  + сортування результатів за назвою пакунку, його версією, назвою групи, часом створення та часом останнього оновлення.

+ Надання аналітичної інформації:
  + статистика користувачів: загальна кількість користувачів, кількість активних та неактивних користувачів;
  + статистика репозиторію: загальна кількість пакунків, кількість осиротілих пакунків, кількість пакунків доданих та оновлених за останній тиждень та кількість пакунків котрі ніколи не оновлювалися;
  + інформація про залежності та відносини кожного пакунку з іншими пакунками;
  + інформація про пакунки кожного користувача та рівень прав який цей користувач має для цих пакунків.


= Проектування бази даних
#v(-spacing)
== Побудова ER-діаграми
Проведений аналіз предметної області дозволив визначити наступні сутності та їх атрибути:

- сутність "Користувач": ім'я, електронна пошта, пароль, дата останнього використання, дата створення, дата оновлення;
- сутність "Пакунок": назва, версія, опис, веб-покликання, дата позначення, дата створення, дата оновлення;
- сутність "База пакунку": назва, опис, дата створення, дата оновлення;
- сутність "Тип ролі": назва ролі, опис;
- сутність "Роль": коментар;
- сутність "Тип залежності": назва виду залежності;
- сутність "Залежність": архітектура, умова, опис, назва залежного пакунку;
- сутність "Тип відношення": назва виду відношення;
- сутність "Відношення": архітектура, умова, назва пакунку з яким є відношення.

Між цими сутностями існують наступні зв’язки:

- "Користувач" - "Роль": один до жодного або багатьох;
- "База пакунку" - "Роль": один до жодного або багатьох;
- "Тип ролі" - "Роль": один до жодного або багатьох;
- "База пакунку" - "Пакунок": один до жодного або багатьох;
- "Пакунок" - "Залежність": один до жодного або багатьох;
- "Тип залежності" - "Залежність": один до жодного або багатьох;
- "Пакунок" - "Відношення": один до жодного або багатьох;
- "Тип відношення" - "Відношення": один до жодного або багатьох.

Зобразимо визначені сутності та відповідні зв’язки у вигляді ER-діаграми @er_diagram.

#img("img/er_diagram.png", "ER-діаграма концептуальної моделі (рисунок виконаний самостійно)")

Отримана ER-діаграма буде використана для обрання та побудови логічної моделі бази даних.

== Вибір та побудова логічної моделі бази даних на базі ER-діаграми

Для створення логічної моделі бази даних репозиторію пакунків було обрано реляційну модель. Ця модель дозволяє ефективно організувати дані у вигляді взаємопов'язаних таблиць, що забезпечує чітку структуру інформації в контексті управління пакунками та колаборації між користувачами. Вона гарантує узгодженість всіх зв'язків між сутностями, такими як "Користувач", "Пакунок", "База пакунку" та іншими, а також підтримує обмеження цілісності даних.

Реляційна модель надає потужні можливості для виконання складних запитів, що є критичним для системи управління пакунками. Це дозволяє ефективно обробляти запити користувачів щодо пошуку пакунків, аналізу залежностей та відношень між ними, а також керування правами доступу через систему ролей. На основі розробленої ER-діаграми @er_diagram було спроєктовано логічну модель бази даних @logic_model.

Для забезпечення точного розуміння відповідності між ER-діаграмою та логічною схемою створено довідник атрибутів @attributes. Цей довідник встановлює однозначну відповідність між термінами, використаними в концептуальній моделі (ER-діаграмі) та їх представленням у логічній моделі бази даних. Особливу увагу приділено відображенню зв'язків між сутностями, що реалізовані через механізм зовнішніх ключів, забезпечуючи цілісність даних та правильну роботу системи контролю версій пакунків.

Обрана модель також враховує специфіку роботи з пакунками, їх залежностями та відношеннями, що є ключовим аспектом функціонування репозиторію пакунків. Це дозволяє ефективно відстежувати зміни в пакунках, керувати правами доступу користувачів та забезпечувати надійну основу для колаборації над пакунками.

#figure(
  table(
    columns: 2,
    table.header[ER-діаграма][Логічна модель],
    [Архітектура], [arch],
    [База пакунку], [base],
    [База пакунку], [PackageBases],
    [Веб-покликання], [url],
    [Версія], [version],
    [Відношення], [PackageRelations],
    [Дата логіну], [last_used],
    [Дата оновлення], [updated_at],
    [Дата позначення], [flagged_at],
    [Дата створення], [created_at],
    [Електронна пошта], [email],
    [Залежність], [PackageDependencies],
    [Ім'я], [name],
    [Коментар], [comment],
    [Користувач], [user],
    [Користувач], [Users],
    [Назва], [name],
    [Назва залежного пакунку], [dependency_package_name],
    [Назва пакунку з яким є відношення], [relation_package_name],
    [Опис], [description],
    [Пакунок], [Packages],
    [Пароль], [password],
    [Роль], [PackageBaseUserRoles],
    [Тип відношення], [RelationTypes],
    [Тип залежності], [DependencyTypes],
    [Тип ролі], [role],
    [Тип ролі], [PackageBaseRoles],
    [Умова], [requirement],
  ),
  caption: [довідник атрибутів (таблиця виконана самостійно)],
) <attributes>

#img("img/logic_model.png", "Логічна модель бази даних (рисунок виконаний самостійно)")

Побудована база даних відповідає умовам третьої нормальної форм, адже кожна таблиця має первинний ключ, всі атрибути є атомарними, всі не ключові атрибути знаходяться у повній функціональній залежності від відповідних первинних ключів, та у таблицях відсутні транзитивні залежності.


+ Таблиця Users:
  + від первинного ключа id залежать всі неключові атрибути, а саме: name, email, password, last_used, created_at, updated_at;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

+ Таблиця Packages:
  + від первинного ключа id залежать всі неключові атрибути, а саме: base, name, version, description, url, flagged_at, created_at, updated_at;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

+ Таблиця PackageBases:
  + від первинного ключа id залежать всі неключові атрибути, а саме: name, description, created_at, updated_at;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

+ Таблиця PackageBaseRoles:
  + від первинного ключа id залежать всі неключові атрибути, а саме: name, description;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

+ Таблиця PackageBaseUserRoles:
  + від складеного первинного ключа (base, user, role) залежать всі неключові атрибути, а саме: comment;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від складеного первинного ключа (base, user, role), відсутні транзитивні залежності.

+ Таблиця DependencyTypes:
  + від первинного ключа id залежать всі неключові атрибути, а саме: name;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

+ Таблиця PackageDependencies:
  + від первинного ключа id залежать всі неключові атрибути, а саме: arch, requirement, description, package, dependency_type, dependency_package_name;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

+ Таблиця RelationTypes:
  + від первинного ключа id залежать всі неключові атрибути, а саме: name;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

+ Таблиця PackageRelations:
  + від первинного ключа id залежать всі неключові атрибути, а саме: arch, requirement, package, relation_type, relation_package_name;
  + таблиця відповідає всім вимогам 3НФ: всі атрибути атомарні, всі неключові атрибути залежать безпосередньо від первинного ключа id, відсутні транзитивні залежності.

== Побудова логічної моделі бази даних шляхом нормалізаці

Нормалізація структури даних відіграє фундаментальну роль у процесі проектування сучасних інформаційних систем, оскільки забезпечує створення раціональної архітектури даних, що ефективно запобігає надлишковості інформації та можливим аномаліям. В контексті розробки інформаційної системи пакункового репозиторію, нормалізація набуває особливої значущості через комплексну природу зв'зків між пакунками.

Розмір ER-діаграми інформаційної системи містить понад 25 атрибутів, тому згідно з методичними вказівками з курсового проектування було обрано частину, що охоплює 5 взаємопов'язаних сутностей: "Користувач", "Пакунок", "База пакунку", "Тип ролі" та "Роль" @normal_er_frag. Така декомпозиція дозволяє детально опрацювати найбільш критичні аспекти системи, забезпечуючи при цьому можливість подальшого масштабування.

#img("img/normal/normal_er_frag.png", "Фрагмент ER-діаграми (рисунок виконаний самостійно)")

Відповідно до теорії реляційних баз даних, відношення вважається нормалізованим до першої нормальної форми (1НФ) за умови дотримання наступних критичних вимог: атомарності даних (кожне поле містить лише одне значення), унікальності ідентифікації записів через первинні ключі, відсутності порожніх значень у ключових полях, та незалежності від фізичного порядку розташування записів.

Для практичної перевірки відповідності 1НФ, створимо універсальне відношення T @normal_t, що інтегрує атрибути всіх релевантних сутностей. Це відношення формує фундаментальну структуру для подальшої нормалізації даних.

При визначенні первинного ключа особлива увага приділяється семантичному аналізу даних. Зокрема можна помітити потребу бачити яку роль має користувач для кожного пакунку. Тому що найкращим ключовими атрибутами стануть "Пакунок id" та "Роль id". Ці атрибути надають можливість унікально ідентифікувати універсальне відношення та їх сполучення охоплює всі атрибути відношення.

#img("img/normal/normal_t.png", "Універсальне відношення T (рисунок виконаний самостійно)")

Всі визначені атрибути є неподільними, а значення атомарні. Сполучений первиний ключ який складаєтсья з "Пакунок id" та "Роль id" дозволяє унікально ідентифікувати кожний кортеж. Ключові поля не мають порожніх значень, кортежі не мають фіксованого порядку, тому, універсальне вдношення Т знаходиться в першій нормальній формі.

Щоб перевірити універсальне відношення Т на відповідність другій нормальній формі (2НФ), потрібно проаналізувати його на існування часткових функціональних залежностей неключових атрибутів від частини первинного ключа.

Для перевірки чи знаходиться відношення Т в другій нормальній формі визначимо функціональні залежності атрибутів @normal_t_dep.

Можемо зробити висновок, що відношення не знаходиться в другій нормальній формі, адже деякі атрибути мають неповні функціональні залежності (залежать лише від частини ключа). Для приведення універсального відношення Т до другої нормальної форми виділимо з нього три універсальних відношення T1 та T2 виходячи з помічених раніше неповних функціональних залежностей @normal_t12.

Відношення зберігають першу нормальну форму та не містять неповних функціональних залежностей, оскільки кожне з них має лише один ключовий атрибут. Таким чином, можна зробити висновок, що ці відношення відповідають вимогам другої нормальної форми.

#img(
  "img/normal/normal_t_dep.png",
  "Універсальне відношення T із визначеними залежностями (рисунок виконаний самостійно)",
)

#img("img/normal/normal_t12.png", "Універсальні відношення T1 та Т2 (рисунок виконаний самостійно)")

Для досягнення третьої нормальної форми (3НФ) таблиці мають бути в другій нормальній формі, a також в відношеннях не має бути транзитивних залежностей, тобто кожен неключовий атрибут повинен залежати безпосередньо від первинного ключа, а не через інші неключові атрибути.

Проаналізувавши відношення Т1 та Т2 можемо побачити, що всі атрибути в них залежать від ключів "Пакунок id" та "Роль id" відповідно, але ще існують інші залежності:
- "База пакунку id" - "Назва", "Опис", "Дата створення", "Дата оновлення";
- "Тип ролі id" - "Назва", "Опис";
- "Користувач id" - "Ім'я", "Електронна пошта", "Пароль", "Дата логіну", "Дата створення", "Дата оновлення";

Винесемо з відношення Т1 два відношення "Тип ролі" та "Користувач" за допомогою ключів "Тип ролі id" та "Користувач id" відповідно. Після чого сформуємо відношення T3 в котрому залишимо тільки зовнішні ключі "Тип ролі id" та "Користувач id" @normal_t23.

#img("img/normal/normal_t23.png", [Відношення "Тип ролі", "Користувач", Т2 та Т3 (рисунок виконаний самостійно)])

Можна помітити, що відношення Т2 та Т3 мають спільне відношення "База пакунку id". Утворимо відношення "Пакунок" та "Роль" виділив з відношень Т2 та Т3 відповідно відношення "База пакунку", залишимо на його місці ключ "База пакунку id".

#img(
  "img/normal/normal.png",
  [Відношення "Користувач", "Пакунок", "База пакунку", "Тип ролі" та "Роль" (рисунок виконаний самостійно)],
)

Тепер необхідно перевірити отримані відношення на відповідність третій нормальній формі.

Всі відношення мають первинні ключі, їх атрибути є атомарними, тому відношення відповідають вимогам першої нормальної форми. Крім того, всі атрибути кожного відношення повністю функціонально залежать від первинного ключа, що у поєднанні з відповідністю першій нормальній формі дозволяє зробити висновок, що відношення відповідають другій нормальній формі. Оскільки жодне відношення не містить транзитивних залежностей, а також відповідає вимогам другої нормальної форми, можна зробити висновок, що відношення знаходяться у третій нормальній формі.

Побудуємо схему даних та позначимо зв'язки між сутностями @normal_link. Після цього порівняємо отриману схему даних з початковою ER-діаграмою для подальшого аналізу.

#img("img/normal/normal_link.png", "Побудована схема даних (рисунок виконаний самостійно)")

Порівняння отриманої схеми даних з початковою ER-діаграмою @er_diagram показує їхню повну відповідність, що свідчить про успішну нормалізацію схеми даних до третьої нормальної форми. Це значить, що всі сутності, атрибути та зв'язки між ними були правильно ідентифіковані, а всі непотрібні залежності та повторення даних були усунуті. Це забезпечить ефективність та масштабованість інформаційної системи.


= Опис програми
#v(-spacing)
== Загальні відомості
Для забеспечення простоти, ефективності та елегантності розробки інформаційної системи "Репозиторій пакунків. Колаборація над пакунками" було використано операційну систему Arch Linux@archlinux та текстовий редактор Neovim@neovim.

Для реалізації всієї комп'ютерної програми було обрано сучасну мову програмування Rust@rust, яка є надзвичайно швидкою, надійною та продуктивною. За зберігання даних відповідає база даних MySQL@mysql, вона відома своєю стабільнітю, можливостями та швидкістю. Для взаємодії з базою даних було обрано бібліотеку SQLx@sqlx, вона є дуже гарно спроектованим проектом, розрахована на асинхроні операції та підтримку багатьох баз даних на низькому рівні. Для написання інтерфейсу комп'ютерної програми було обрано бібліотеку iced@iced, ця бібліотека фокусується на простоті та безпеці програм з графічним інтерфейсом за допомогю дотримання принципів проектування Elm@elm.

== Виклик і завантаження
Користувач може взаємодіяти з ком'ютерною програмою після встановки її до своєї операційної системи та запуску бази даних.

Для зберігання даних використовується СУБД MySQL@mysql. База даних та комп'ютерна програма можуть знаходитись на різних пристроях. Перед тим як кінцевий користувач приступить до використання програми, системний адміністратор, котрий відповідає за роботу інформаційної системи, має налаштувати змінну середовища "DATABASE_URL" в котрій буде адреса до налаштованого екземпляру MySQL. Для максимальної зручності налаштування рекомендуєтсья використовувати контейнерізацю за допомогою Docker@docker, особливо рекомендується його інструмент Compose@compose.

Інформаційна система була розроблена з використанням мови Rust@rust, тому результуюча програма не потребує залежностей під час виконання і може бути використана у вигляді самодостатнього файлу виконання.

== Призначення і логічна структура
Інформаційна система полегшує колаборацію над пакунками у репозиторії для розробників. Система забеспечує інтуїтивний інтерфейс для навігації по пакункам, їх залежностям з відносинами, та користувачам. Перелік основних функцій системи:
- управління пакунками та їх метаданими;
- система користувацьких акаунтів;
- система різних рівней доступу користувачей до пакунків;
- пошу та категоризація пакунків за багатьма факторами;
- надання аналітичної інформації про репозиторій, пакунки та користувачів.

Під час розробки було використано гексагональну архітектуру@hexagonal, також відому як "архітектура портів та адаптерів". Структура проекту складаєтсья з кількох рівнів @project_structure:
+ Шар взаємодії з базою даних (тека data):
  + декларація інтерфейсу взаємодії (тека ports);
  + імплементації взаємодії (тека adapters).
+ Шар сервісів для бізенс логіки програми (тека service), cервіси мають:
  + декларацію репозиторію який бере дані з задекларованого шару бази даних (файли з назвою repository);
  + імплементацію адаптеру репозиторію який оперує отриманням даних з шару імплементації бази даних (файли з назвою adapter);
  + декларацію контракту який будується на репозиторії і описує дані з котрими буде працювати сервіс (файли з назвою contract);
  + імплеменацію сервісу котрий оперує над даними контракту та здійснює логічні операції з обчисленнями (файли з назвою service).
+ Шар графічного інтерфесу (тека src) використовує контракти з шару сервісів для валідації даних від користувача та надсилання запитів до логічної частини застосунку.
+ Головний файл проекту (тека src, файл main.rs) відповідає за конпонування всих шарів:
  + встановлення підключення до бази даних;
  + ініціалізацю адаптерів бази даних;
  + ініціалізацію адаптерів репозиторіїв за допомогою створених підключень та адаптерів бази даних;
  + запуск сервісів передаючи їм створені репозиторії;
  + відображення та менеджмент графічних частин застосунку яким передаються сервіси.

#img("img/repo/project_structure.png", "Структура проєкту")

Проєкт має всі використані під час розробки ресурси, такі як SQL скрипти та Docker Compose@compose файли, за допомогою яких можна створити тестову базу даних та наповнити її даними.

== Опис фізичної моделі бази даних
Для інформаційної системи створено базу даних яка має дев’ять таблиць. Код для її створення яких описаний нижче.

Таблиця "Users" містить інформацію про користувачів системи та має таку структуру:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- ім'я - унікальне текстове поле яке зберігає ім'я користувача (довжина до 31 символа), не може бути порожнім;
- пошта - унікальне текстове поле яке зберігає електронну пошту користувача (довжина до 255 символів), не може бути порожнім;
- пароль - текстове поле яке зберігає хеш пароля (довжина до 255 символів), не може бути порожнім;
- останній логін - зберігає дату останнього використання облікового запису, може бути порожнім;
- час створення - зберігає дату створення облікового запису не може бути порожнім;
- час оновлення - час оновлення даних в таблиці, автоматично оновлюється при зміні запису, не може бути порожнім.

```
-- Required info for an account
CREATE TABLE Users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(31)  UNIQUE NOT NULL,
    email       VARCHAR(255) UNIQUE NOT NULL,
    password    VARCHAR(255) NOT NULL,

    last_used   TIMESTAMP NULL,

    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updated_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
```

Таблиця "PackageBases" містить інформацію про бази пакунків:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- назва - унікальне текстове поле, зберігає назву базового пакунку (до 127 символів), не може бути порожнім;
- опис - текстове поле (до 510 символів), може бути порожнім, зберігає опис пакунку;
- час створення - зберігає дату створення бази пакунку, не може бути порожнім;
- час оновлення - час оновлення даних в таблиці, автоматично оновлюється при зміні запису, не може бути порожнім.

```
-- Enables multiple packages to have the same base yet different components
CREATE TABLE PackageBases (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(127) UNIQUE NOT NULL,
    description VARCHAR(510) NULL,

    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```

Таблиця "PackageBaseRoles" визначає ролі користувачів для роботи з пакунками:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- назва - унікальне текстове поле, зберігає назву ролі (наприклад: submitter, packager; довжина до 31 символу), не може бути порожнім;
- опис - текстове поле яке описує роль (до 255 символів), може бути порожнім.

```
-- User roles for working on packages: flagger, packager, submitter, maintainer, etc.
CREATE TABLE PackageBaseRoles (
    id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(31) UNIQUE NOT NULL,
    description VARCHAR(255) NULL
);

INSERT INTO PackageBaseRoles (id, name) VALUES
(1, 'submitter'),
(2, 'packager'),
(3, 'maintainer'),
(4, 'flagger');
```

Таблиця "PackageBaseUserRoles" описує ролі користувачів для баз пакунків:
- база пакунку - числове поле (ціле додатнє число), зовнішній ключ на таблицю PackageBases;
- користувач - числове поле (ціле додатнє число), зовнішній ключ на таблицю Users;
- роль - числове поле (ціле додатнє число), зовнішній ключ на таблицю PackageBaseRoles;
- коментар - текстове поле для збереження приміток (до 255 символів), може бути порожнім;
- (base, user, role) - складний (композитний) первинний ключ, необхідний для ідентифікації таблиці та забезпечення надійної роботи бази даних;

```
-- Roles that a user has for a package
CREATE TABLE PackageBaseUserRoles (
    base INT UNSIGNED,
    user INT UNSIGNED,
    role TINYINT UNSIGNED,

    comment VARCHAR(255) NULL,

    PRIMARY KEY (base, user, role), -- composite key
    FOREIGN KEY (base) REFERENCES PackageBases(id) ON DELETE CASCADE,
    FOREIGN KEY (user) REFERENCES Users(id) ON DELETE CASCADE,
    FOREIGN KEY (role) REFERENCES PackageBaseRoles(id) ON DELETE CASCADE
);
```

Таблиця "Packages" містить інформацію про окремі пакунки:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- база - зовнішній ключ (ціле додатнє число) на таблицю PackageBases;
- назва - унікальне текстове поле для збереження назви пакунку (до 127 символів), не може бути порожнім;
- версія - текстове поле для збереження версії пакунку (до 127 символів), не може бути порожнім;
- опис - текстове поле для збереження опису пакунку (до 255 символів), може бути порожнім;
- веб-покликання - текстове поле, зберігає посилання на ресурс пакунку (до 510 символів);
- час позначення - час, коли пакунок був позначений, може бути порожнім;
- час створення - зберігає дату створення пакунку, не може бути порожнім;
- час оновлення - час оновлення даних в таблиці, автоматично оновлюється при зміні запису, не може бути порожнім.

```
-- Information about the actual packages
CREATE TABLE Packages (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    base        INT UNSIGNED NOT NULL,
    name        VARCHAR(127) UNIQUE NOT NULL,
    version     VARCHAR(127) NOT NULL,
    description VARCHAR(255) NULL,
    url         VARCHAR(510) NULL,

    flagged_at TIMESTAMP NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    FOREIGN KEY (base) REFERENCES PackageBases (id) ON DELETE CASCADE
);
```

Таблиця "DependencyTypes" визначає типи залежностей:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- назва - унікальне текстове поле, зберігає назву типу залежності (наприклад: depends, makedepends; довжина до 31 символу), не може бути порожнім.

```
-- depends, makedepends, optdepends, etc.
CREATE TABLE DependencyTypes (
    id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(31) UNIQUE NOT NULL
);

INSERT INTO DependencyTypes (id, name) VALUES
(1, 'depends'),
(2, 'makedepends'),
(3, 'checkdepends'),
(4, 'optdepends');
```

Таблиця "PackageDependencies" відображає залежності пакунків:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- архітектура - текстове поле, зберігає цільову архітектуру залежності (до 63 символів), може бути порожнім;
- умова - текстове поле, яке зберігає умову залежності (до 255 символів), може бути порожнім;
- опис - текстове поле, зберігає опис залежності (до 127 символів), може бути порожнім;
- пакунок - зовнішній ключ на таблицю Packages;
- тип залежності - зовнішній ключ на таблицю DependencyTypes;
- назва залежного пакунку - текстове поле яке зберігає назва залежного пакунку (до 127 символів), не може бути порожнім.

```
-- Track which dependencies a package has
CREATE TABLE PackageDependencies (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    arch        VARCHAR(63)  NULL,
    requirement VARCHAR(255) NULL,
    description VARCHAR(127) NULL,

    package INT UNSIGNED NOT NULL,
    dependency_type TINYINT UNSIGNED NOT NULL,
    dependency_package_name VARCHAR(127) NOT NULL, -- Not an actual package, but an an alias. Allows for package substitution.

    FOREIGN KEY (package) REFERENCES Packages (id) ON DELETE CASCADE,
    FOREIGN KEY (dependency_type) REFERENCES DependencyTypes (id)
);
```

Таблиця "RelationTypes" визначає типи зв'язків між пакунками:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- назва - унікальне текстове поле яке зберігає назву зв'яку (наприклад: conflicts, provides; довжина до 31 символу), не може бути порожнім.

```
-- conflicts, provides, replaces, etc.
CREATE TABLE RelationTypes (
    id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(31) UNIQUE NOT NULL
);

INSERT INTO RelationTypes (id, name) VALUES
(1, 'conflicts'),
(2, 'provides'),
(3, 'replaces');
```

Таблиця "PackageRelations" описує зв'язки між пакунками:
- id - службове додатнє число, необхідне для ідентифікації таблиці та забезпечення надійної роботи бази даних;
- архітектура - текстове поле, зберігає цільову архітектуру зв'яку (до 63 символів), може бути порожнім;
- умова - текстове поле, яке зберігає умову зв'яку (до 255 символів), може бути порожнім;
- пакунок - зовнішній ключ на таблицю Packages;
- тип зв'язку - зовнішній ключ на таблицю RelationTypes;
- тип зв'язку з пакунком - текстове поле, зберігає назву пакунку, з яким є зв'язок (до 127 символів), не може бути порожнім.

```
-- Track which conflicts, provides and replaces a package has
CREATE TABLE PackageRelations (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    arch        VARCHAR(63) NULL,
    requirement VARCHAR(255) NULL,

    package INT UNSIGNED NOT NULL,
    relation_type TINYINT UNSIGNED NOT NULL,
    relation_package_name VARCHAR(127) NOT NULL,

    FOREIGN KEY (package) REFERENCES Packages (id) ON DELETE CASCADE,
    FOREIGN KEY (relation_type) REFERENCES RelationTypes (id)
);
```

== Опис програмної реалізації

При запуску комп'ютерної програми стартовим екраном є сторінка логіну @repo_login. Користувачі з існуючими акаунтами можуть увійти у свій акаунт за допомогою пошти або юзернема та свого паролю який надійно та безпечно зберігається в базі даних у зашифрованому вигляді.

#img("img/repo/repo_login.png", "Сторінка логіну")

Якщо у користувача немає акаунту, то він може натиснути на кнопку реєстраці для переходу на сторікну реєстарції @repo_register. Щоб створити новий акаунт. Користувач має надати ім'я користувача, електрону пошту та пароль.

Форми логіну та реєстрації перевіряють дані на валідність та не будуть робити зайвих запитів, якщо надана інформація не відповідає правилам інформаційної системи. У випадку перевірок. які не можуть бути зроблені локально, система надішле запит до бази даних і відобразить результат у графічному інтерфейсі програми.

#img("img/repo/repo_register.png", "Сторінка реєстрації")

Після успішного логіну або реєстрації програма перейде на сторінку пошуку @repo_search, яка надає можливість шукати пакунки з багатьма способами фільтрування та сортування результатів. При наведені курсору миші на елементи пошуку можна побачити стисле пояснення їх функціоналу.

Назва пакунку, його бази та його веб-покликання на ресурс є інтерактивними. Якщо натиснути на назву пакунку, то відкриється вікно з переглядом інформації та статистики про пакунок. Якщо натиснути на назву бази пакунку, то відкриється відкно де буде інформація про базу пакунку. При натисканні на веб-покликання, воно відкриєтсья в веб-браузері, котрий стоїть за замовчуванням в операціній системі користувача.

#img("img/repo/repo_search.png", "Сторінка пошуку")

Розглянемо на прикладі сторінки пошуку обробку запиту на основі даних з графічного інтерфейсу котрі ввів користувач.

Коли користувач натискає кнопку "Go", або натискає клавішу "Enter" у текстовому полі пошуку, генерується внутрішнє повідомлення "Search". Обробник повідомлень сторінки пошуку отримує це повідомлення, та починає валідацію даних текстового поля та створення структури даних з параметрами пошуку. Після чого, якщо на момент запиту не виконується інших пошукових запитів, дані з параметрами пошуку передаються до сервісу пошуку. Сервіс передає дані до адаптеру репозиторію пошуку. Адаптер репозиторію пошуку встановлює з'єднання до бази даних, і робить запит до адаптеру репозиторію пошуку бази даних з встановленим з'єднанням, після чого він закриє з'єднання до бази даних. Адаптер репозиторію пошуку бази даних використовує передане йому підключення для виконання комплексного SQL запиту який будується на основі переданих йому параметрів пошуку. Після отримання результату з бази даних, він конвертує його у набір записів інформації про пакунок. Цей набір записів буде переданий назад до адаптеру репозиторію пошуку, потім до сервісу, й у кінці передається у повідомленні "RequestResult" до сторінки пошуку, яка зможе відобразити кожен запис як рядок у таблиці.

Якщо на будь-якому рівні абстракції виникне помилка, то вона буде передана до графічного інтерфейсу сторінки пошуку і користувач буде сповіщений про виникнення помилки.

Код обробника повідомлення "Search" з валідацією даних:
```
let search_data = Data {
    mode: self.mode.into(),
    order: self.order.into(),
    search: match self.input.submit() {
        Ok(x) => x,
        Err(t) => return Some(t.into()),
    },
    limit: self.limit.into(),
    exact: self.exact,
    ascending: self.ascending,
};

self.state = State::Searching;
let arc = self.service.clone();

return Some(
    Task::perform(
        async move {
            let Some(service) = arc.try_lock() else {
                return Err("other search request is being performed".into());
            };
            service.search(search_data).await
        },
        |r| Message::RequestResult(Arc::new(r)),
    )
        .into(),
);
```

Код функції пошуку сервісу пошуку:
```
async fn search(&self, data: Data) -> Result<Vec<search::Entry>> {
    self.repository.search(data.into()).await
}
```

Код функції пошуку адаптеру репозиторію пошуку:

```
async fn search(&self, data: Data) -> Result<Vec<Entry>> {
    let c = self.driver.open_connection().await?;
    let result = SR::search(&c, data).await?;
    D::close_connection(c).await?;

    Ok(result)
}
```

Код функції пошуку адаптеру репозиторію пошуку бази даних:
```
async fn search(connection: &E, data: Data) -> Result<Vec<Entry>> {
    let mut builder = QueryBuilder::new(
        "SELECT \
            p.id, p.name, p.version, p.url, p.description, \
            p.updated_at, p.created_at, \
            pb.id AS base_id, pb.name AS base_name, \
        ( \
            SELECT COUNT(DISTINCT pbur.user) \
            FROM PackageBaseUserRoles pbur \
            WHERE pbur.base = pb.id AND pbur.role = 3 \
        ) AS maintainers_num \
        FROM \
            Packages p \
        JOIN \
            PackageBases pb ON p.base = pb.id ",
    );

    let mut push_search = |cond, param| {
        builder.push(format_args!(
            " {cond} {param} {} ",
            if data.exact { "=" } else { "LIKE" }
        ));
        builder.push_bind(if data.exact {
            data.search.to_string()
        } else {
            format!("%{}%", data.search.as_str())
        });
    };

    let join_user = " JOIN PackageBaseUserRoles pbur ON pb.id = pbur.base \
            JOIN Users u ON pbur.user = u.id WHERE ";

    match data.mode {
        Mode::Url => push_search("WHERE", "p.url"),
        Mode::Name => push_search("WHERE", "p.name"),
        Mode::PackageBase => push_search("WHERE", "pb.name"),
        Mode::Description => push_search("WHERE", "p.description"),
        Mode::BaseDescription => push_search("WHERE", "pb.description"),
        Mode::NameAndDescription => {
            // WHERE (p.name LIKE '%search_term%' OR p.description LIKE '%search_term%')
            builder.push(" WHERE p.name LIKE ");
            builder.push_bind(format!("%{}%", data.search.as_str()));
            builder.push(" OR p.description LIKE ");
            builder.push_bind(format!("%{}%", data.search.as_str()));
        }
        Mode::User => {
            push_search(
                "WHERE EXISTS ( \
                SELECT 1 \
                FROM PackageBaseUserRoles pbur \
                JOIN Users u ON pbur.user = u.id \
                WHERE pbur.base = pb.id AND",
                "u.name",
            );
            builder.push(" ) ");
        }
        Mode::Flagger => {
            push_search(join_user, "u.name");
            builder.push(" AND pbur.role = 4 ");
        } // 4
        Mode::Packager => {
            push_search(join_user, "u.name");
            builder.push(" AND pbur.role = 2 ");
        } // 2
        Mode::Submitter => {
            push_search(join_user, "u.name");
            builder.push(" AND pbur.role = 1 ");
        } // 1
        Mode::Maintainer => {
            push_search(join_user, "u.name");
            builder.push(" AND pbur.role = 3 ");
        } // 3
    }

    builder.push(format_args!(
        " ORDER BY {} {} LIMIT {};",
        match data.order {
            Order::Name => "p.name",
            Order::Version => "p.version",
            Order::BaseName => "pb.name",
            Order::UpdatedAt => "p.updated_at",
            Order::CreatedAt => "p.created_at",
        },
        if data.ascending { "ASC" } else { "DESC" },
        data.limit
    ));

    let mut entries = Vec::new();

    let mut rows = builder.build().fetch(connection);
    while let Some(row) = rows.try_next().await? {
        entries.push(Entry {
            id: row.try_get("id")?,
            name: row.try_get("name")?,
            version: row.try_get("version")?,
            base_id: row.try_get("base_id")?,
            base_name: row.try_get("base_name")?,
            url: row.try_get("url")?,
            description: row.try_get("description")?,
            // submitter_id: row.try_get("submitter_id")?,
            // submitter_name: row.try_get("submitter_name")?,
            updated_at: row.try_get("updated_at")?,
            created_at: row.try_get("created_at")?,
        });
    }

    Ok(entries)
}
```

Код обробки для повідомлення "RequestResult":
```
Message::RequestResult(r) => match &*r {
    Ok(v) => self.state = State::Table(Table(v.clone())),
    Err(e) => self.state = State::Error(e.to_string()),
},
```

Код відображення таблиці результату пошуку:
```
pub fn view(&self) -> Element<'static, Message> {
    let mut table: Vec<_> = [
        "Package",      // 0
        "Version",      // 1
        "Base",         // 2
        "URL",          // 3
        "Description",  // 4
        "Last Updated", // 5
        "Created",      // 6
    ]
    .into_iter()
    .map(|s| {
        let mut v = Vec::with_capacity(self.0.len());
        v.push(s.into());
        v.push("".into());
        v
    })
    .collect();

    for entry in &self.0 {
        table[0].push(url(&entry.name, Message::PackagePressed(entry.id)));
        table[1].push(text(entry.version.to_string()).into());
        table[2].push(url(&entry.base_name, Message::BasePressed(entry.base_id)));
        table[3].push(
            entry
                .url
                .as_ref()
                .map_or("-".into(), |s| 

                tip(
                    url(&"link", Message::URLPressed(s.clone())),
                    s.clone(),
                    tip::Position::Bottom,
                ),

                ),
        );
        table[4].push(text(entry.description.to_string()).into());
        table[5].push(text(entry.updated_at.to_string()).into());
        table[6].push(text(entry.created_at.to_string()).into());
        // table[5].push(Element::from(column( entry .maintainers .iter() .map(|(id, s)| url(s, Message::UserPressed(*id))),)));
    }

    scroll(
        row(table
            .into_iter()
            .map(|v| Column::from_vec(v).spacing(5).into()))
        .spacing(20)
        .padding(30),
    )
}
```

Подібну реалізацію мають всі частини програми. Представлений програмний код демонструє ефективну реалізацію принципів гексагональної архітектури@hexagonal. Структура коду чітко відображає розділення на рівні, кожен з яких відповідає за визначену функціональну роль, що є ключовою характеристикою даної архітектурної парадигми. Цей підхід надає чітке розмежування відповідальності де кожен компонент системи, від інтерфейсу користувача до адаптерів баз даних, має чітко визначений набір обов'язків. Це полегшує процес розробки та спрощує підтримку програмного забезпечення у довгостроковій перспективі. Незалежність бізнес-логіки від інфраструктурних рішень дозволяє спростити процес міграції до простого доповнення арсеналу адаптерів. Сервісний шар, що містить основну бізнес-логіку пошуку, не залежить від конкретних технологій зберігання даних або реалізації графічного інтерфейсу, тому його можна буде використати в інших проектах. Крім того, модульна архітектура дозволяє проводити ізольоване тестування кожного компонента, за допомогою використання макетів (mock objects) для залежностей що забезпечить глибоке покриття коду тестами.

#nheading("Висновки")

В результаті виконання курсової роботи було розроблено інформаційну систему "Репозиторій пакунків" для організації ефективної колаборації над програмними пакунками. В процесі розробки було проведено ґрунтовний аналіз предметної області, визначено ключові вимоги до системи та спроектовано оптимальну структуру бази даних для зберігання інформації про пакунки, їх версії, залежності, користувачів та їхні ролі.

Створена система забезпечує зручний інтерфейс для пошуку пакунків, управління пакунками та колаборації між розробниками. Реалізовано функціонал створення та оновлення пакунків, відстеження залежностей та взаємозв'язків між пакунками, а також систему ролей для контролю доступу.

База даних спроектована на основі реляційної моделі та нормалізована до третьої нормальної форми, що забезпечує оптимальну структуру даних та відсутність їх надлишковості. Під час розробки було використано мову програмування Rust@rust, систему управління базами даних MySQL@mysql та графічну бібліотеку iced@iced. Крім того використовуєтсья контейнеризація за допомогою Docker@docker та Docker Compose@compose що забезпечує надійність роботи системи, її масштабованість та простоту розгортання в різних середовищах.

Розроблена інформаційна система значно спрощує процес управління та отримання інформації про програмні пакунки. Вона відповідає поставленим завданням та має потенціал подальшої реалізації завдяки використанню дуже модульної архітектури моделювання програмного забеспечення, відомої як Гексагональна архітектура@hexagonal.
