480 lines
18 KiB
Plaintext
480 lines
18 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n",
|
||
"<div class=\"alert alert-block alert-info\">\n",
|
||
"<h1> Przygotowanie do projektu badawczo-rozwojowego</h1>\n",
|
||
"<h2> 7. <i>Specyfikacja projektu informatycznego</i>[wykład]</h2> \n",
|
||
"<h3>Krzysztof Jassem (2021)</h3>\n",
|
||
"</div>\n",
|
||
"\n",
|
||
"![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": []
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<div class=\"alert alert-block alert-success\">\n",
|
||
" \n",
|
||
"<h3>Zakres systemu informatycznego</h3>\n",
|
||
" \n",
|
||
"Zakres systemu to obszar tego, co projektujemy – precyzyjnie odgraniczony od tego, co leży poza projektem (np. jest zadaniem projektowym kogoś innego).\n",
|
||
"\n",
|
||
"Zakres <b> funkcjonalny </b>– zestaw usług oferowanych przez system.\n",
|
||
"\n",
|
||
"Zakres <b> projektowy </b>– zasięg przestrzenny systemu – obejmuje zbiór sprzętu i oprogramowania, które zlecono nam wykonać.\n",
|
||
"</div>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Jak zdefiniować zakres projektu?\n",
|
||
"\n",
|
||
"Zakres projektu jest stopniowo uściślany za pomocą następującyh działań:\n",
|
||
"\n",
|
||
"1. Określenie ogólnego zakresu projektu, a w nim:\n",
|
||
"\n",
|
||
" * Określenie koncepcji projektu, np. w postaci prezentacji lub diagramu\n",
|
||
" * Charakterystyka wykonawców\n",
|
||
" * Lista „wykonawca-cel”\n",
|
||
" * Lista „in-out”\n",
|
||
"\n",
|
||
"2. Specyfikacja wymagań\n",
|
||
" * Wymagania funkcjonalne\n",
|
||
" * Wymaganie niefunkcjonalne\n",
|
||
" \n",
|
||
"3. Opis przypadków użycia"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# 1. Określenie ogólnego zakresu projektu"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Uczestnicy i wykonawcy"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<div class=\"alert alert-block alert-success\">\n",
|
||
"<h3>Uczestnik</h3>\n",
|
||
" \n",
|
||
"Uczestnik – ktoś lub coś, mający interes związany z zachowaniem systemu.\n",
|
||
"\n",
|
||
"\n",
|
||
"Uczestnik może być osobą, firmą lub przedsiębiorstwem, programem komputerowym lub systemem komputerowym.\n",
|
||
"</div>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<div class=\"alert alert-block alert-success\">\n",
|
||
"<h3>Wykonawca</h3>\n",
|
||
" \n",
|
||
"<b> Wykonawca </b> to Uczestnik, który kontaktuje się z systemem.\n",
|
||
"\n",
|
||
"<b> Wykonawca główny </b> to Wykonawca, który kontaktuje się z systemem w celu uzyskania jednej z jego usług. Wykonawca główny najczęściej rozpoczyna przypadek użycia. \n",
|
||
"\n",
|
||
"<b> Wykonawca pomocniczy </b> to Wykonawca z zewnątrz wykonujący usługi dla systemu (drukarka, usługa WWW, osoby, które mają dostarczyć pewne badania).\n",
|
||
"\n",
|
||
"</div>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<div class=\"alert alert-info alert-success\">\n",
|
||
" \n",
|
||
"Uczestnik ma interesy. \n",
|
||
"Wykonawca ma zachowania.\n",
|
||
"</div>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Charakterystyka wykonawców\n",
|
||
"Charakterystykę wykonawców można opisać za pomocą tabeli. \n",
|
||
"\n",
|
||
"Przykład tabeli dla systemu Cybertest2:\n",
|
||
"\n",
|
||
"<img src=\"obrazy/wykonawcy.png\" alt=\"Lista Wykonawców\" width=600px>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Lista Wykonawca-Cel\n",
|
||
"Na podstawie charakterystyki wykonawców oraz zakresu systemu tworzy się listę **Wykonawca - Cel**, w której dla każdego typu wykonawców określa się cele, jakie wykonawca stawia przed systemem. \n",
|
||
"\n",
|
||
"Jeśli projektowany system dotyczy interesów uczestników niebędących wykonawcami, pożądane może być sporządzenie szerszej listy: **Uczestnik – Cel**. \n",
|
||
"\n",
|
||
"Przykład tabeli dla systemu Cybertest2:\n",
|
||
"\n",
|
||
"<img src=\"obrazy/wykonawca-cel.png\" alt=\"Lista wykonawca-cel\" width=600px>\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Lista In-Out\n",
|
||
"Na podstawie listy Wykonawca-Cel, biorąc pod uwagę priorytety celów, tworzy się listę In-Out, która precyzyjnie określa zakres systemu. \n",
|
||
"\n",
|
||
"Przykład tabeli dla systemu Cybertest2:\n",
|
||
"<img src=\"obrazy/in-out.png\" alt=\"Lista In-Out\" width=600px>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# 2. Specyfikacja wymagań"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<div class=\"alert alert-block alert-success\">\n",
|
||
" \n",
|
||
"<h3>Specyfikacja wymagań</h3>\n",
|
||
" \n",
|
||
"Specyfikacja wymagań to dokument, w którym zebrano wszystkie oczekiwania stawiane przyszłemu systemowi (np. wymagania funkcjonalne i niefunkcjonalne aplikacji).\n",
|
||
" \n",
|
||
"</div>"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Wymagania użytkownika\n",
|
||
"**Wymagania użytkownika** to ogólny opis oczekiwań względem systemu w języku naturalnym, będacy rezultatem wstępnych rozmów wykonawcy z zamawiającym lub użytkownikiem.\n",
|
||
" * Skierowane są do:\n",
|
||
" * menedżerów zamwiającego.\n",
|
||
" * menedżerów wykonawcy,\n",
|
||
" * potencjalnych użytkowników systemu.\n",
|
||
" \n",
|
||
"Przykład wymagań użytkownika (system wspomagania tłumaczenia):\n",
|
||
"\n",
|
||
"<img src=\"obrazy/wymagania użytkownika.png\" alt=\"Przykład wymagań użytkownika\" width=400px>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Wymaganie systemowe\n",
|
||
"**Wymagania systemowe** to sformalizowany opis oczekiwań względem systemu.\n",
|
||
" * Skierowane są do:\n",
|
||
" * inżynierów zamawiającego,\n",
|
||
" * twórców oprogramowania, \n",
|
||
" * architektów systemu. \n",
|
||
" \n",
|
||
"Wymagania systemowe dzielą się na:\n",
|
||
" * funkcjonalne,\n",
|
||
" * niefunkcjonalne"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"#### Wymagania funkcjonalne\n",
|
||
" * Opisują funkcje (czynności, operacje) wykonywane przez system.\n",
|
||
" * Dotyczą rezultatów oczekiwanych przez użytkownika podczas kontaktu z systemem.\n",
|
||
" \n",
|
||
"Przykłady wymagań funkcjonalnych (system do prowadzenia testów na wykładzie):\n",
|
||
"\n",
|
||
"<img src=\"obrazy/wymaganie funkcjonalne1.png\" alt=\"Przykład wymagania funkcjonalnego\" width=600px>\n",
|
||
"\n",
|
||
"<img src=\"obrazy/wymaganie funkcjonalne2.png\" alt=\"Przykład wymagania funkcjonalnego\" width=600px>"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"#### Wymagania niefunkcjonalne\n",
|
||
" * Opisują warunki, przy których system musi realizować swoje funkcje:\n",
|
||
" * Wymagania dotyczące produktu\n",
|
||
" * Wymagania dotyczące procesu\n",
|
||
" * Wymagania zewnętrzne\n",
|
||
" * Wymagania niefunkcjonalne powinny być weryfikowalne. Realizowane jest to przy pomocy systemu miar opisujących wymagane cechy systemu.\n",
|
||
"\n",
|
||
"Przykłady miar wymagań niefunkcjonalnych:\n",
|
||
"\n",
|
||
"<img src=\"obrazy/miary.png\" alt=\"Przykłady miar wymagań niefunkcjonalnych\" width=600px>\n",
|
||
"\n",
|
||
"Przykład wymagań niefunkcjonalnych (system wspomagania tłumaczenia):\n",
|
||
"\n",
|
||
"<img src=\"obrazy/wymagania niefunkcjonalne.png\" alt=\"Przykład wymagań niefunkcjonalnych\" width=600px>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# 3. Opis przypadków użycia"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"<div class=\"alert alert-block alert-success\">\n",
|
||
" \n",
|
||
"<h3>Przypadek użycia (PU) </h3>\n",
|
||
" \n",
|
||
"Przypadek użycia określa umowę między uczestnikami systemu względem jego zachowania.\n",
|
||
"\n",
|
||
"<ul>\n",
|
||
"<li>Przypadek użycia opisuje zachowanie się systemu w różnych warunkach – w odpowiedzi na żądanie wykonawcy głównego.\n",
|
||
" <li>Przypadek użycia reprezentowany jest przez sekwencję <b> akcji </b> realizowanych przez system, które dają zauważalny efekt; Akcja to operacja atomowa, czyli taka, której nie można przerwać podczas wykonywania.</li>\n",
|
||
"</ul>\n",
|
||
"</div>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Elementy składowe opisu przypadku użycia \n",
|
||
"1. Wykonawca główny (który rozpoczyna wykonanie przypadku użycia)\n",
|
||
"2. Poziom celu\n",
|
||
"3. Warunki początkowe\n",
|
||
"4. Wyzwalacz\n",
|
||
"5. Gwarancje minimalne\n",
|
||
"6. Gwarancja powodzenia\n",
|
||
"7. Scenariusz powodzenia\n",
|
||
"8. Rozszerzenia scenariusza"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3.1. Przypadek użycia. Poziom celu\n",
|
||
"Poziom celu określa stopień ogólności przypadku użycia:\n",
|
||
" * Poziom streszczenia – najbardziej ogólny,\n",
|
||
" * Poziom użytkownika – pośredni,\n",
|
||
" * Poziom podfunkcji – najbardziej szczegółowy.\n",
|
||
" \n",
|
||
"#### Poziom streszczenia\n",
|
||
"Przypadki użycia o poziomie streszczenia pokazują kontekst, w którym występują przypadki użycia niższego poziomu:\n",
|
||
" * Pokazują kolejność działania przypadków użycia niższego poziomu,\n",
|
||
" * Stanowią spis treści przypadków użycia niższego poziomu (użytkownika).\n",
|
||
" \n",
|
||
"Przykład przypadku użycia o poziomie streszczenia (podkreśleniem oznaczone są przypadki niższego poziomu):\n",
|
||
"\n",
|
||
"<img src=\"obrazy/poziom streszczenia.png\" alt=\"Przykład przypadku użycia o poziomie streszczenia\" width=400px>\n",
|
||
"\n",
|
||
"#### Poziom użytkownika\n",
|
||
"Przypadki użycia o poziomie użytkownika opisują cel, który chce osiągnąć wykonawca główny, żeby wykonać swoje zadanie. \n",
|
||
" * Najczęściej spełniają warunek: „jedna osoba i jedno krótkie posiedzenie”.\n",
|
||
" * Przypadki te są najbardziej warte opisania w specyfikacji.\n",
|
||
" \n",
|
||
"Przykład przypadku użycia o poziomie użytkownika:\n",
|
||
"\n",
|
||
"<img src=\"obrazy/poziom użytkownika.png\" alt=\"Przykład przypadku użycia o poziomie użytkownika\" width=400px>\n",
|
||
"\n",
|
||
"#### Poziom podfunkcji\n",
|
||
"Przypadki użycia o poziomie podfunkcji opisują podcele, które są potrzebne do osiągnięcia celów użytkownika.\n",
|
||
" * Uwzględniane są w opisie przypadków tylko w sytuacjach absolutnie niezbędnych („nie musisz – nie pisz”).\n",
|
||
" * Trzy przykłady przypadków użycia o poziomie podfunkcji:\n",
|
||
" > Wybierz odpowiedź na pytanie testowe. \n",
|
||
" > Przejdź do następnego pytania. \n",
|
||
" > Zaloguj się w systemie. "
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3.2. Przypadek użycia. Warunki początkowe\n",
|
||
"**Warumek początkowy** to proste stwierdzenie o stanie świata w chwili otwarcia przypadku użycia. \n",
|
||
" * Warunek początkowy może być wyrażony za pomocą przypadków użycia, które musiały zajść przed otwarciem opisywanego przypadku. \n",
|
||
" * Warunek początkowy określa, co musi być zapewnione przed zezwoleniem na przypadek użycia. \n",
|
||
" \n",
|
||
" Przykład warunku początkowego (system do testów):\n",
|
||
"\n",
|
||
"<img src=\"obrazy/warunek początkowy.png\" alt=\"Przykład warunku początkowego\" width=400px>"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3.3. Przypadek użycia. Wyzwalacz\n",
|
||
"**Wyzwalacz** to zdarzenie, które automatycznie powoduje rozpoczęcie przypadku użycia.\n",
|
||
"\n",
|
||
" Przykłady wyzwalaczy (system do testów):\n",
|
||
" \n",
|
||
"<img src=\"obrazy/wyzwalacze.png\" alt=\"Przykłady wyzwalaczy\" width=300px>"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3.4. Przypadek użycia. Gwarancje minimalne\n",
|
||
"**Gwarancje minimalne** to najmniejsze obietnice składane przez system.\n",
|
||
" * Są realizowane zarówno wtedy, gdy cel wykonawcy głównego jest spełniony (scenariusz powodzenia) i gdy nie jest on spełniony. (Szczególnie w tym drugim przypadku minimalne gwarancje są istotne).\n",
|
||
" * Zapisywane są w postaci kilku stwierdzeń, które będą na pewno prawdziwe po zakończeniu przypadku użycia.\n",
|
||
" \n",
|
||
" Przykłady gwarancji minimalnych:\n",
|
||
" <img src=\"obrazy/gwarancje minimalne.png\" alt=\"Przykłady gwarancji minimalnych\" width=300px>"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3.5. Przypadek użycia. Gwarancja powodzenia\n",
|
||
"**Gwarancja powodzenia** ustala, jakie interesy uczestników są zaspokojone po udanym zakończeniu przypadku użycia (scenariusz powodzenia).\n",
|
||
" * Zapisywana jest w postaci prostych stwierdzeń, opisujących świat po udanym zakończeniu przypadku użycia.\n",
|
||
" * Często ma postać rozszerzenia minimalnych gwarancji.\n",
|
||
" \n",
|
||
"Przykłady gwarancji powodzenia:\n",
|
||
"\n",
|
||
" <img src=\"obrazy/gwarancja powodzenia.png\" alt=\"Przykłady gwarancji minimalnych\" width=300px>\n"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3.6. Przypadek użycia. Scenariusz powodzenia\n",
|
||
"**Scenariusz powodzenia** to ciąg akcji od rozpoczęcia do zakończenia przypadku użycia, który realizuje gwarancje powodzenia.\n",
|
||
"Scenariusz powodzenia opisuje najbardziej typowe zachowanie systemu i nie bierze pod uwagę zdarzeń niesprzyjających. \n",
|
||
"\n",
|
||
"**Wskazówki przy pisaniu scenariusza powodzenia:**\n",
|
||
"\n",
|
||
"#### 1. Używaj prostych składni zdań\n",
|
||
"<img src=\"obrazy/prosta składnia.png\" alt=\"Przykład prostej składni\" width=300px>\n",
|
||
"\n",
|
||
"### 2. Jasno i jednoznacznie wskaż podmiot czynności\n",
|
||
"<img src=\"obrazy/podmiot.png\" alt=\"Przykład podmiotu czynności\" width=300px>\n",
|
||
"\n",
|
||
"##### 3. Pisz \"z lotu ptaka\"\n",
|
||
"<img src=\"obrazy/lot ptaka.png\" alt=\"Przykład z lotu ptaka\" width=300px>\n",
|
||
"\n",
|
||
"##### 4. Przesuwaj proces wyraźnie do przodu\n",
|
||
"<img src=\"obrazy/proces.png\" alt=\"Przykład przesuwania procesu\" width=300px>\n",
|
||
"\n",
|
||
"##### 5. Stwierdzaj \"że\", a nie - sprawdzaj \"czy\"\n",
|
||
"<img src=\"obrazy/że.png\" alt=\"Przykład stwierdzania że\" width=300px>\n",
|
||
"\n",
|
||
"##### 6. Wyraźnie oznaczaj przypadki użycia, do których się odwołujesz\n",
|
||
"<img src=\"obrazy/odwołania.png\" alt=\"Przykłady odwołania\" width=300px>\n",
|
||
"\n",
|
||
"##### 7. Stosuj iteracje, jeśli jest taka potrzeba\n",
|
||
"<img src=\"obrazy/iteracje.png\" alt=\"Przykłady iteracji\" width=300px>"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3.7. Przypadek użycia. Rozszerzenia scenariusza\n",
|
||
"**Rozszerzenie scenariusza** to niestandardowe zachowanie systemu, które\n",
|
||
" * zaczyna się w konkretnym kroku w scenariuszu powodzenia,\n",
|
||
" * gdy zdarzają się niestandardowe sytuacje – zwane **warunkami rozszerzenia scenariusza**.\n",
|
||
" \n",
|
||
"#### Warunki rozszerzenia scenariusza\n",
|
||
"**Warunki rozszerzenie scenariusza** to sytuacje, w których system zachowuje się inaczej, niż to przewidziano w scenariuszu powodzenia.\n",
|
||
"* Warunki rozszerzenia sformułowane są z punktu widzenia systemu (co system może wykryć, a nie - co się stało).\n",
|
||
"<img src=\"obrazy/warunki rozszerzenia.png\" alt=\"Przykłady warunku rozszerzenia\" width=300px>\n",
|
||
"\n",
|
||
"##### Jak znajdować potencjalne rozszerzenia scenariusza?\n",
|
||
" * Przemyśl alternatywne ścieżki scenariusza, np.\n",
|
||
" * Wykonawca głóny postępuje niepoprawnie\n",
|
||
" * Brak działania wykonawcy głównego\n",
|
||
" * Wykonawca pomocniczy reaguje z opóźnieniem\n",
|
||
" * Przeanalizuj wszystkie wystąpienia wyrażeń typu „System stwierdza, że...”\n",
|
||
" * Przeanalizuj sytuacje awaryjne\n",
|
||
" * Awarie wewnętrzne projektowanego systemu, które należy wykryć i obsłużyć\n",
|
||
" * Awarie wewnętrzne systemu, które wystarczy wykryć"
|
||
]
|
||
},
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Podsumowanie - jak stworzyć specyfikację systemu informatycznego?\n",
|
||
" 1. Określenie koncepcji projektu, np. w postaci prezentacji lub diagramu \n",
|
||
" 2. Charakterystyka wykonawców \n",
|
||
" 3. Lista „Wwykonawca-Cel” \n",
|
||
" 4. Lista „In-Out” \n",
|
||
" 5. Wymagania funkcjonalne \n",
|
||
" 6. Wymaganie niefunkcjonalne \n",
|
||
" 7. Opis przypadków użycia "
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"author": "Krzysztof Jassem",
|
||
"email": "jassem@amu.edu.pl",
|
||
"kernelspec": {
|
||
"display_name": "Python 3",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"lang": "pl",
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.8.5"
|
||
},
|
||
"subtitle": "07. Specyfikacja projektu informatycznego[wykład]",
|
||
"title": "Przygotowanie do projektu badawczo-rozwojowego",
|
||
"year": "2021"
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
}
|