diff --git a/course-projects/README.md b/course-projects/README.md new file mode 100644 index 00000000..ebfc625e --- /dev/null +++ b/course-projects/README.md @@ -0,0 +1,34 @@ +# Указания за разработка на курсов проект + +1. Проектите са индивидуални + +2. Mоже да изберете един от предложените проекти или да измислите и предложите свой (приема се след наше одобрение - изпратете предложението си на екипа на курса) + +3. Всички проекти трябва да покриват следните общи условия: + - Демонстрират знания за + - вход-изход с файлове + - многонишково програмиране + - мрежова комуникация + - Имат unit тестове с поне 50% line code coverage + - Имат валидация на входните данни + - Имат добър обектно-ориентиран дизайн + - Използват подходящи изключения за докладване на грешки + - Използват подходящи структури от данни и ефективни алгоритми + - Имат четлив и добре структуриран код + - Ползват в имплементацията си само Java SE и външни библиотеки и API-та, изучавани в курса или изрично указани в условието. При нужда или желание да ползвате нещо допълнително, обсъдете с екипа на курса + +# Варианти за теми + +| # | Тема | +|:---|:--------------------------------------------------------- | +| 1 | [Authentication Server](auth-server.md) | +| 2 | [Bookmarks Manager](bookmarks.md) | +| 3 | [Dungeons Online](dungeons.md) | +| 4 | [Food Analyzer](food-analyzer.md) | +| 5 | [Split(NotSo)Wise](splitwise.md) | +| 6 | [Spotify](spotify.md) | +| 7 | [Torrent Server](torrent.md) | +| 8 | [Password Vault](password-vault.md) | +| 9 | [Todoist](todoist.md) | +| 10 | [Cryptocurrency Wallet Manager](cryptocurrency-wallet.md) | +| 11 | По ваш избор | diff --git a/course-projects/auth-server.md b/course-projects/auth-server.md new file mode 100644 index 00000000..ccd7f480 --- /dev/null +++ b/course-projects/auth-server.md @@ -0,0 +1,193 @@ +# Authentication Server :cop: + +Когато се занимаваме с програмиране, няма как да избегнем въпросите, свързани със security. Обикновено имаме система, съставена от няколко компонента (наречени *микросървиси*) и един security server, който отговаря за комуникацията между потребителя и системата и тази между микросървисите да бъде защитена. + +### Authentication vs Authorization + +Най-общо, *authentication* наричаме проверката, дали потребителят, със своето име и парола, който опитва да се логне в системата, съществува в нея. + +*Authorization* проверката, от друга страна, се извършва срещу вече логнати потребители. Тя се състои в това дали потребителят има право да извърши операцията, която иска - ако има, му позволяваме, ако не - извеждаме подходящо съобщение за грешка. + +Например, всички ние сме потребители в СУСИ. Когато се логваме, минаваме през *authentication check*. Когато Стойо нанася оценките от курса, СУСИ се уверява, че той има необходимите права да нанася оценки и чак тогава му позволява да го направи. + +### Audit Log + +[Audit Log-ът](https://en.wikipedia.org/wiki/Audit_trail) е дневник, който пази информация за различни събития, свързани със сигурността, които са настъпили в системата ни. Такива събития могат да са например промяна на атрибутите на даден потребител, или пък негово нетипично или рисковано поведение. + +#### А каква е ползата от това да се пази audit log на системата? + +Чрез audit logging, бързо можем да открием кой е направил грешна конфигурация на системата ни, или пък кой (и от къде) е направил многократни неуспешни опити да се логне. Във втория сценарий, можем да продължим с анализа - с едно и също потребителско име ли е опитал да се логне, ако не, може би е опитал да направи DoS (Denial of Service) атака, проверяваме дали е от един IP адрес (хакерите в днешно време не са толкова глупави). Ако пък потребителят е един и същ, може това да е опит да се "разбие" профила му. + +Използването на audit logging е задължително за всяко по-сериозно приложение, платформа или онлайн услуга. В нашата задача ще използваме един простичък механизъм, но в практиката съществуват немалко препятствия, които трябва да се вземат под внимание. Няколко линка с информация за по-любопитните: + +- [What Are Audit Logs? | blogpost by Rodney Smith](https://rollout.io/blog/audit-logs/) +- [Audit logs for security and compliance | blogpost by Daniel Berman | long one, contains Linux examples of audit logging](https://logz.io/blog/audit-logs-security-compliance/) +- [Audit Log | Martin Fowler short blogpost](https://martinfowler.com/eaaDev/AuditLog.html) + +## Условие + +Имплементирайте ***сървър***, който да се грижи за authentication и authorization на потребители, както и конзолен ***клиент*** (CLI, Command-Line Interface), който да може да си комуникира с него. Сървърът трябва да поддържа паралелна работа с множество клиенти. + +Когато един потребител "влезе" в системата, за него се създава т. нар. ***потребителска сесия***. Тя има уникален идентификатор и време, за което е валидна. При успешен вход, потребителят получава този идентификатор и чрез него може да изпълнява операциите, които изискват authentication и authorization. + +Потребителите на Authentication Server-а са три типа: + +- unauthenticated +- authenticated +- admin (подтип на authenticated потребители) + +Сървърът предлага различен набор от функционалности на различните типове потребители. Затова, може да разделим и достъпните операции на няколко типа. + +### Unsecured команди + +За изпълнението на unsecured командите, не е необходима валидна потребителска сесия, тоест не е нужно да подаваме session ID като параметър. + +- **Регистрация** + + Добавяне на нов потребител, само ако вече не съществува друг потребител с такова име. След успешна регистрация, потребителят бива логнат автоматично. + + ```bash + register --username --password --first-name --last-name --email + ``` + +- **Вход** + + Потребителите могат да влизат в системата по два начина: + + - чрез своето потребителско име и парола + - чрез уникален идентификатор на вече съществуваща login сесия. + + **Сесията** е обект, който пази уникален идентификатор (*session ID*) и *time-to-live (ttl)*. + + В един момент един потребител може да има само една валидна сесия. Тя се създава в момента на успешен login, и session ID-то се връща на потребителя, за да може той да го преизползва (например от друг терминал). + След изтичане на time-to-live периода, сесията бива унищожена - потребителят e logout-нат и след това не може да преизползва това session ID за логване, а трябва да го направи със своето потребителско име и парола. + + Командите могат да имат следният вид: + + ```bash + login -–username --password + login -–session-id + ``` + +#### Failed login + + При надвишаване на определен брой неуспешни опити за вход в системата, потребителят се lock-ва за определен период от време. Потребителят може да направи следващ опит за логин едва след като това време изтече. + Броят неуспешни опити и lock периодът са част от конфигурацията на сървъра. + +### Secured команди + +Това са командите, достъпни за потребителите, които имат валидна сесия. Всички имат задължителен параметър session ID. +Също могат да бъдат разделени на две групи - такива, които са достъпни за всички потребители и такива, които са само за администратори. + +#### Основни команди + +- **Промяна на потребителските данни** + + Потребителите могат да променят своите *данни* (потребителско име, име, фамилия и мейл): + + ```bash + update-user -–session-id -–new-username --new-first-name --new-last-name --new-email . + ``` + + Всички параметри освен --session-id в тази команда са опционални. + +- **Промяна на парола** + + Потребителите могат да променят паролата си, като всички параметри в командата са задължителни. + + ```bash + reset-password -–session-id –-username --old-password --new-password + ``` + +- **Изход от системата** + + При изход, сесията се инвалидира и подаденото session ID не може да бъде използвано повторно за логин. + + ```bash + logout –-session-id + ``` + +#### Администраторски команди + +- **Добавяне на администраторски права на потребител** + + Администраторите могат да създават нови администратори. Правата на новите администратори влизат в действие веднага, без да има необходимост от повторен логин. + + ```bash + add-admin-user –-session-id –-username + ``` +- **Премахване на администраторски права на потребител** + + Администраторите могат да премахват правата на други администратори. Правата се премахват веднага, без необходимост от повторен логин. + + ```bash + remove-admin-user –-session-id –-username + ``` + Aдминистраторът може да премахне дори своите собствени права, освен, ако не е единствен администратор на сървъра. + +- **Премахване на потребител** + + Администраторите имат право да премахнат даден регистриран потребител. + + ```bash + delete-user –-session-id –-username + ``` + + При изтриване на user, се премахва всяка пазена информация за него (без тази в audit log-a), както и се инвалидира сесията му, ако има валидна такава. + +## Audit Log + +Както споменахме, системата ни ще съхранява и audit log-ове. Трябва да се пазят като текстови файлове на файловата система в предварително конфигурирана директория. + +### Типове събития, който ще записваме + +- **Неуспешен опит за вход** + + При неуспешен опит за вход логваме следното: + + - Timestamp + - Тип на събитието (failed login) + - Извършител - потребителят, който опитва да извърши действието и неговия IP адрес + +- **Изпълнение на команди, променящи ресурси на системата** (добавяне/премахване на администратор). Логовете, които се пишат при промяна на ресурс са два: + + - когато операцията започне: + + - Timestamp + - ID на операцията + - Тип на събитието (configuration change) + - Извършител - потребителят, който опитва да извърши действието и неговото IP + - какво се променя - кой user е засегнат и дали са премахнати или добавени права + + - когато операцията завърши: + + - Timestamp + - ID на операцията (същото, като това при започване) + - Тип на събитието (configuration change) + - Извършител - потребителят, който опитва да извържи действието и неговото IP + - резултата от нея - дали е била успешна или не + +Форматът на логовете е по ваш избор. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при команда на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in ". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Уточнения + +- User информацията трябва да се пази във файл (ще играе ролята на база от данни за сървъра), като паролата **не трябва** да се пази в plain text +- При повторен login за даден потребител с username и password, предишната създадена сесия (ако има такава) трябва да се инвалидира и да се създаде нова + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/bookmarks.md b/course-projects/bookmarks.md new file mode 100644 index 00000000..13a8d7b9 --- /dev/null +++ b/course-projects/bookmarks.md @@ -0,0 +1,113 @@ +# Bookmarks Manager :paperclip: + +## Условие + +Създайте приложение за удобно съхранение и организиране на линкове (*bookmarks*). + +### Сървър + +Предоставя следните функционалности на клиента: +- регистриране и вход с потребителско име и парола +- добавяне на нови bookmarks +- премахване на bookmarks +- групиране на bookmarks +- търсене на bookmarks по заглавие или ключова дума + +:bulb: Bookmark-ите на потребителите трябва да се съхраняват във файлове на сървъра. + +:bulb: Всеки добавен bookmark трябва да има *заглавие*. То трябва да отговаря на съдържанието на **** таг-а от `html`-а на страницата. + +:bulb: При добавяне на нов bookmark, сървърът трябва да `extract`-не ключовите думи от страницата, за да могат потребителите да търсят по тях. + +- Ключовите думи са най-използваните думи в страницата. Погрижете се предварително да премахнете пунктуационни знаци, специални символи, `stopwords` и т.н. +- Опитайте се да сведете думите до техните корени, използвайки някой [`stemming`](https://en.wikipedia.org/wiki/Stemming) алгоритъм. Най-простият от тях, който можете да използвате, е `Suffix-stripping algorithm`: +> - if the word ends in 'ed', remove the 'ed' +> - if the word ends in 'ing', remove the 'ing' +> - if the word ends in 'ly', remove the 'ly' + +- За лесно `parse`-ване на `html`, може да използвате библиотеката [JSoup](https://jsoup.org/). + +### Клиент + +Клиентът трябва да има `command line interface` със следните команди: + + +#### Регистриране +```bash +register +``` +Регистрира нов потребител в приложението с име и парола + +#### Вход +```bash +login +``` + +#### Създаване на нова група за съхранение на bookmarks +```bash +new-group +``` + +#### Добавяне на нов bookmark в група +```bash +add-to {--shorten} +``` +Добавя bookmark в конкретна група. Aко опцията `--shorten` е налична, сървърът трябва да съкрати оригиналния bookmark, използвайки API-то на [Bitly](https://dev.bitly.com/api-reference), документирано [тук](https://dev.bitly.com). + +#### Премахване на bookmark от група +```bash +remove-from +``` + +#### List-ване на всички bookmarks +```bash +list - извежда списък с всички линкове на потребителя +``` + +#### List-ване на всички bookmarks от група +```bash +list --group-name - извежда списък с всички линкове от дадената група +``` + +#### Търсене на bookmarks по тагове +```bash +search --tags [ ...] +``` +#### Търсене на bookmarks по заглавие +```bash +search --title - връща всички линкове, в чиито заглавия се среща <title> +``` + +#### Изчистване на невалидни линкове +```bash +cleanup - премахва всички bookmarks, чиито линкове вече не са валидни (т.е при /GET на bookmark-a се връща статус код 404 Not Found) +``` + +#### Добавяне на bookmarks от Chrome +```bash +import-from-chrome - добавя всички bookmarks от Google Chrome +``` + +`Google Chrome` съхранява вашите `bookmarks` на файловата система в `json` формат. Файлът с `bookmarks` се намира в различни директории в зависимост от операционната система: +- **Windows** - `AppData\Local\Google\Chrome\User Data\Default` +- **Linux** - `~/.config/google-chrome/Default/` +- **MacOS** - `/Users/<Your UserName>/Library/Application\ Support/Google/Chrome` + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при команда на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/cryptocurrency-wallet.md b/course-projects/cryptocurrency-wallet.md new file mode 100644 index 00000000..bee1f317 --- /dev/null +++ b/course-projects/cryptocurrency-wallet.md @@ -0,0 +1,115 @@ +# Cryptocurrency Wallet Manager :moneybag: :money_with_wings: + +Напишете клиент-сървър приложение, което симулира личен портфейл за криптовалути. Криптовалутите са един от най-популярните варианти за инвестиране в момента. Интерес към тях имат както опитни инвеститори, така и любители. + +## Условие + +Създайте клиентско конзолно приложение, което приема потребителски команди, изпраща ги за обработка на сървъра, приема отговора му и го предоставя на потребителя в human-readable формат. + +*Note*: Командите и output-ът в условието са примерни, свободни сте да ги преименувате и форматирате. Единственото условие е, те да бъдат интуитивни. За улеснение на потребителя, може да имплементирате команда help. + +## Функционални изисквания + +- **register** - регистрация на потребител с username и password. Регистрираните потребители се пазят във файл на сървъра - той служи като база от данни, като паролата не трябва да се пази в plain text. При спиране и повторно пускане, сървърът може да зареди в паметта си вече регистрираните потребители. + +- **login** - потребителят влиза в системата + +- **deposit-money** - добавя определена сума пари към портфейла на потребителя. Тъй като API-то, което ще използваме в задачата, работи с долари, ще приемем, че сумите, които депозираме, ще са в долари. + + ```bash + $ deposit-money 10000.00 + ``` +- **list-offerings** - предоставя информацията за всички налични криптовалути, от които потребителят може да купува. Информацията за наличните валути взимаме от [CoinAPI](#CoinAPI)-то. + ```bash + $ list-offerings + ``` +- **buy** - купува количество от дадена криптовалута за определената сума пари. Сумата трябва да е налична в портфейла на потребителя. + ```bash + $ buy --offering=<offering_code> --money=<amount> + ``` +- **sell** - продава конкрента криптовалута. Сумата, получена от печалбата, остава в портфейла на потребителя. + ```bash + $ sell --offering=<offering_code> + ``` +- **get-wallet-summary** - предоставя цялостна информацията за портфейла на потребителя - информация за всички активни инвестиции към момента на изпълнение на командата, за парите в портфейла му. + ```bash + $ get-wallet-summary + ``` +- **get-wallet-overall-summary** - предоставя информацията за цялостната печалба/загуба от инвестициите на потребителя. Приложението сравнява цената за всяка криптовалута от момента на купуване и текущата ѝ цена, за да получи цялостната информация. + ```bash + $ get-wallet-overall-summary + ``` +## CoinAPI +Информацията за криптовалутите, от която сървърът има нужда, е достъпна чрез публично безплатно REST API - [CoinAPI](https://www.coinapi.io/). + +Заявките към REST API-то изискват автентикация с API key, какъвто може да получите като се регистрирате [тук](https://www.coinapi.io/pricing?apikey). + +*Note*: Функционалността, която предлага API-то, е доста обширна, но ние ще се фокусираме на описаната [тук](https://docs.coinapi.io/#list-all-assets). Тя би била достатъчна за функционалността на проекта. + +Следните endpoints от API-то биха ви били полезни: +- **GET /v1/assets** - връща инфомрация за всички достъпи asset-и в JSON формат +- **GET /v1/assets/{asset_id}** - връща инфромация за конкретен asset в JSON формат. + + - **Пример:** +```bash + GET /v1/assets/BTC +``` + +```bash +[ + { + "asset_id": "BTC", + "name": "Bitcoin", + "type_is_crypto": 1, + "data_start": "2010-07-17", + "data_end": "2021-01-24", + "data_quote_start": "2014-02-24T17:43:05.0000000Z", + "data_quote_end": "2021-01-24T19:07:51.7954142Z", + "data_orderbook_start": "2014-02-24T17:43:05.0000000Z", + "data_orderbook_end": "2020-08-05T14:38:38.3413202Z", + "data_trade_start": "2010-07-17T23:09:17.0000000Z", + "data_trade_end": "2021-01-24T19:08:47.4460000Z", + "data_symbols_count": 46840, + "volume_1hrs_usd": 9160288508835.92, + "volume_1day_usd": 197928243055426.88, + "volume_1mth_usd": 11571260516151083.22, + "price_usd": 31304.448721266051267349441838, + "id_icon": "4caf2b16-a017-4e26-a348-2cea69c34cba" + } +] +``` + + - Сървърът трябва да кешира получената от API-то информация като тя ще е валидна само за период от 30 минути, заради постоянно променящата се цена на криптовалутите. + - Обмислете как оптимално да структурирате заявките до API-то. + - Може да използвате полето ***typeiscrypto*** от отговора, за да филтрирате само asset-ите, които са криптовалути. + - ***offeringcode*** от клиентските команди съответства на ***asset_id***-то от CoinAPI-то. + - Тъй като API-то връща огромен набор от валути, може да изберете да показвате на потребителя само първите 50, 100 или 150 валути от резултата или да филтрирате по някакъв друг критерий. Този избор оставяме на вас. + +## Нефункционални изисквания + +- Сървърът може да обслужва множество потребители паралелно. +- Сървърът пази информацията за потребителите и техните портфейли по начин, който му позволява след спиране или рестартиране да може да зареди тази информация отново. Помислете за подходящ формат на данни, в който може да съхранявате информацията от сървъра. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при логин на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Уточнения + +- Валидацията на потребителския вход е задължителна, т.е. покрийте всички сценарии, за които се сетите с невалиден вход - било то null, грешно форматиране, невалиден тип на данните и т.н. +- Командите и output-ът от тях са примерни. Свободни сте да представите ваша интерпретация на командите, както и да добавите нови. Единствената им цел тук е да помогнат за разбирането на условието. +- Всякакви допълнителни функционалности, за които се сетите, са добре дошли. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/dungeons.md b/course-projects/dungeons.md new file mode 100644 index 00000000..1b334e38 --- /dev/null +++ b/course-projects/dungeons.md @@ -0,0 +1,110 @@ +# Dungeons Online :video_game: + +Време е да хванем нашата Single-Player скучна игра от началото на курса и да я превърнем в супер яка Online игра. + +## Условие + +Реализирайте клиент-сървърна [MMORPG](https://en.wikipedia.org/wiki/Massively_multiplayer_online_role-playing_game), в която различни играчи ще могат да развиват своите герои и да водят ожесточени битки помежду си. + +## Основни елементи на играта + +### Герои + +Героите ще се управляват от клиентската част на приложението. + +За улеснение, нека приемем, че **едновременно ще могат да играят по 9 героя**, всеки с уникален номер (от 1 до 9), които ще се визуализират на картата. + +Те ще могат да: +- се движат по картата +- събират съкровища +- се бият с `Minions` и други играчи. + +Всеки герой трябва да има конкретен *level*. При всяка проведена битка, както и за всяко взето съкровище, героят трябва да вдига *experience*, като при достигане на нужния *experience* за следващoто ниво, то да *level-up*-ва. + +Героите също ще си имат `Backpack`, в която ще пазят събраните съкровища и при желание ще могат да консумират или просто да изхвърлят някое от тях. Нека тя да има капацитет от **10** предмета. + +Когато даден герой умре, нека да се изхвърля случаен предмет от раницата му (ако има такъв). + +Всеки герой има `Stats` (*health*, *mana*, *attack*, *deffense*). В началото, нека `Stats`-овете им да бъдат: +- **health: 100** +- **mana: 100** +- **attack: 50** +- **deffense: 50** + +При вдигане на всеки *level*, да се добавят към `Stats`-овете по **10 health**, **10 mana**, **5 аttack** и **5 deffense**. + +Героите ще могат да носят оръжия, както и да правят магии, които ще намират и научават чрез събиране на съкровища по картата. + +### Minions + +Това са статични чудовища по картата, които играчите ще могат да атакуват и да си вдигат чрез тях нивото. Minion-ите също ще имат *level* и `Stats`, който ще се определя от *level*-а им. Колкото по- голям *level* е даден `Minion`, толкова повече *experience* ще носи на играча, който го убие. + +### Treasure + +#### 1. Weapon + +Всеки `Weapon` си има *level* и *attack*, като героят ще може да се бие с него само ако има *level* >= на *level*-a на `Weapon`-a. В противен случай просто ще може да си го вземе в раницата. + +Когато героят се екипира с оръжието, към неговата атака ще се добави атаката на оръжието. + +#### 2. Spell + +Тук важат същите условия, както при оръжието, като `Spell`-ът допълнително си има *manaCost*. Героят ще може да изпълни `Spell`-a само ако има >= *mana* на неговия *manaCost*. + + - **Mana potion** + + Съдържа точки *mana*, които зарежда на героя. + +- **Health potion** + + Съдържа точки *health*, които зарежда на героя. + +### Map + +Нека картата да изглежда по подобен начин: +``` + . . # # . . 1 . . T + T # # . 3 # # # . # + . . . . M . # . M T + 2 M . . . . . . # # +``` + където: + + * \# - Препятствие, през което героите не могат да минават. + * T - Съкровище. + * . - Свободна позиция. + * M - Minion. + * 1-3 - Играчи отговарящи на съответните индекси. + +## Забележки + +Героите и Враговете трябва да имплементират интерфейса `Actor`. + +## Функционалности, които трябва да реализирате + + 1. Играчите трябва да могат да се движат по картата. Когато даден играч се премести, динамично се обновява картата и на останалите играчи, които се намират на картата. + + 2. Играчите трябва да могат да събират съкровища по картата. Когато даден играч вземе съкровище, то на останалите играчи трябва съкровището да изчезне от картата. + За да вземе някое съкровище, играчът трябва да се намира на мястото, където е съкровището. + + 3. Играчите трябва да могат да си пращат предмети от раниците на други играчи, като за да се случи това, те трябва да се намират на една и съща позиция. + + 4. Играчите трябва да могат да се бият както с Minion-и по картата, така и с други играчи. За да се проведе битка с Minion или друг играч, трябва участниците да се намират на една и съща позиция. Като даден Minion бъде убит, той изчезва от картата и се появява нов, на някоя свободна позиция по картата. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при команда на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/food-analyzer.md b/course-projects/food-analyzer.md new file mode 100644 index 00000000..5b2faa39 --- /dev/null +++ b/course-projects/food-analyzer.md @@ -0,0 +1,92 @@ +# Food Analyzer :hamburger: + +В наши дни е особено важно да знаем какво ядем и да следим калориите, които консумираме. Нека си улесним живота като имплементираме приложение, което да ни информира за състава и енергийната стойност на хранителните продукти в менюто ни. + +Един от най-изчерпателните и достоверни източници на подобна информация е [FoodData Central](https://fdc.nal.usda.gov/), базата данни за храни и техния състав на [U.S. Department of Agriculture](https://www.usda.gov/). За наше щастие, тази информацията е достъпна и чрез публично безплатно REST API, което е документирано [тук](https://fdc.nal.usda.gov/api-guide.html). + +Ще имплементираме Food Analyzer като многонишково клиент-сървър приложение. + +## Food Analyzer Server + +- Сървърът трябва да може да обслужва множество клиенти едновременно. +- Сървърът получава команди от клиентите и връща подходящ резултат. +- Сървърът извлича необходимите му данни от гореспоменатото *RESTful API* и запазва (кешира) резултата в локалната си файлова система. + + Например, при получаване на командата `get-food raffaello treat`, сървърът прави следната *HTTP GET* заявка: https://api.nal.usda.gov/fdc/v1/foods/search?query=raffaello%20treat&requireAllWords=true&api_key=API_KEY (където API_KEY е валиден API key) и получава *HTTP response* със статус код *200* и с тяло следния *JSON*: + +```javascript +{ + "foodSearchCriteria": { + "query": "raffaello treat", + "generalSearchInput": "raffaello treat", + "pageNumber": 1, + "requireAllWords": true + }, + "totalHits": 1, + "currentPage": 1, + "totalPages": 1, + "foods": [ + { + "fdcId": 415269, + "description": "RAFFAELLO, ALMOND COCONUT TREAT", + "dataType": "Branded", + "gtinUpc": "009800146130", + "publishedDate": "2019-04-01", + "brandOwner": "Ferrero U.S.A., Incorporated", + "ingredients": "VEGETABLE OILS (PALM AND SHEANUT). DRY COCONUT, SUGAR, ALMONDS, SKIM MILK POWDER, WHEY POWDER (MILK), WHEAT FLOUR, NATURAL AND ARTIFICIAL FLAVORS, LECITHIN AS EMULSIFIER (SOY), SALT, SODIUM BICARBONATE AS LEAVENING AGENT.", + "allHighlightFields": "", + "score": 247.10071 + } + ] +} +``` + +Заявките към REST API-то изискват автентикация с API key, какъвто може да получите като се регистрирате [тук](https://fdc.nal.usda.gov/api-key-signup.html). + +От данните за продукта, ни интересува описанието му от полето `description` (`RAFFAELLO, ALMOND COCONUT TREAT`) и уникалния му идентификатор, `fdcId` (`415269`). Някои продукти, по-точно тези с `data type` `Branded`, имат също и GTIN или UPC код, `gtinUpc` (`009800146130`). + +**Бележка:** GTIN, или [Global Trade Item Number](https://en.wikipedia.org/wiki/Global_Trade_Item_Number) и UPC, или [Universal Product Code](https://en.wikipedia.org/wiki/Universal_Product_Code), са индентификатори на продукти, кодирани в баркод. С други думи, въпросният код е числото, кодирано в баркода на опаковката на продуктите. + +![UPC Barcode](images/upc-barcode.gif) + +Сървърът кешира получената информация на локалната файлова система. При получаване на заявка, сървърът първо трябва да провери дали в кеша вече съществува информация за дадения продукт, и ако е така, директно да върне тази информация, вместо да направи нова заявка към REST API-то. + +## Food Analyzer Client + +Клиентът осъществява връзка с *Food Analyzer Server* на определен порт, чете команди от стандартния вход, изпраща ги към сървъра и извежда получения резултат на стандартния изход в human-readable формат. Клиентът може да изпълнява следните команди: + +- `get-food <food_name>` - извежда информацията, описана по-горе, за даден хранителен продукт. Ако сървърът върне множество продукти с даденото име, се извежда информация за всеки от тях. Ако пък липсва информация за продукта, се извежда подходящо съобщение. +- `get-food-report <food_fdcId>` - по даден уникален идентификатор на продукт (`fdcId`) извежда име на продукта, съставки (`ingredients`), енергийна стойност (калории), съдържание на белтъчини, мазнини, въглехидрати и фибри. +- `get-food-by-barcode --code=<gtinUpc_code>|--img=<barcode_image_file>` - извежда информация за продукт по неговия баркод, *ако такава е налична в кеша на сървъра* (обърнете внимание, че REST API-то не поддържа търсене на продукт по `gtinUpc` код или баркод изображение). Задължително е да подадем един от двата параметъра: или `code`, или файл, съдържащ баркод изображение (като пълен път и име на файла на локалната файлова система на клиента). Ако са указани и двата параметъра, `img` параметърът се игнорира. + +За да реализирате търсене по баркод изображение, ще имате нужда от Java библиотека или уеб услуга, с които да конвертирате изображение на баркод към числото, което кодира. Може да ползвате open source библиотеката [ZXing "Zebra Crossing"](https://github.com/zxing/zxing) или [ZXing уеб услугата](https://zxing.org/w/decode.jspx). + +За тестови цели, може да си генерирате баркод изображения [тук](https://barcode.tec-it.com/en/UPCA), друг вариант е да потърсите в Google изображение или директно да снимате някой баркод на опаковка. + +### Пример за валидни входни данни + +```bash +get-food beef noodle soup +get-food-report 415269 +get-food-by-barcode --img=D:\Photos\BarcodeImage.jpg --code=009800146130 +``` + +Всякакви допълнителни функционалности, за които се сетите, са добре дошли. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при команда на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/images/peer-to-peer.png b/course-projects/images/peer-to-peer.png new file mode 100644 index 00000000..591690aa Binary files /dev/null and b/course-projects/images/peer-to-peer.png differ diff --git a/course-projects/images/upc-barcode.gif b/course-projects/images/upc-barcode.gif new file mode 100644 index 00000000..5829cd65 Binary files /dev/null and b/course-projects/images/upc-barcode.gif differ diff --git a/course-projects/password-vault.md b/course-projects/password-vault.md new file mode 100644 index 00000000..eb216f05 --- /dev/null +++ b/course-projects/password-vault.md @@ -0,0 +1,118 @@ +# Password Vault :closed_lock_with_key: + +В днешни времена, едно от най-важните неща е да предпазваме личната си информация в Интернет. Една от фаталните грешки, която правят доста хора, е да използват една и съща парола във всички сайтове или да използват слаби пароли, включващи думи от речника, имена и т.н. Можем да подобрим своята киберсигурност като създадем приложение, което да реши всички тези проблеми. + +Нашият мениджър на пароли ще представлява client-server приложение, като сървърът ще се грижи за всички нужди на клиентите, а те само трябва да се удостоверят пред него. Клиентите ще могат да се регистрират, login-ват, logout-ват, за да докажат самоличността си, след което ще могат да си запазват супер-сигурните пароли и след това да си ги взимат/променят. + +## Server + +- Сървърът трябва да може да обслужва множество клиенти едновременно +- Сървърът получава команди от клиентите и връща подходящ отговор +- Сървърът трябва да може да проверява дали дадена парола е компрометирана и не е сигурна за ползване +- Сървърът трябва да пази паролите по сигурен начин +- Сървърът трябва да генерира надеждни пароли + +### Проверка за компрометирани пароли + +При добавяне на нова парола от потребител за даден сайт (`add-password facebook.com pesho@gmail.com P@ssw0rd`), сървърът трябва да провери дали паролата не е била компрометирана и ако е, да върне подходяща грешка. +За целта може да използвате следното [API](https://www.enzoic.com/docs-passwords-api/). +Заявките към REST API-то изискват authentication чрез API key, какъвто може да получите като се регистрирате [тук](https://www.enzoic.com/free-trial-2/). + +За по-лесно използване, заместете вашите *<api_key>* и *<api_secret>* [тук](https://repl.it/@ZdravkoGyurov/ExtrasmallReasonableFrontend#index.js) и използвайте резултата за правeне на заявки. + +Примерна *HTTP GET* заявка: +URL: +https://api.enzoic.com/passwords?**sha1**=a94a8fe5ccb19ba61c4c0873d391e987982fbbd3&**md5**=098f6bcd4621d373cade4e832627b4f6&**sha256**=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 \ +Header: `authorization: basic base64("api_key:api_secret")` + +Примерен *HTTP response*: + +``` javascript +{ + "revealedInExposure":true, + "relativeExposureFrequency":4, + "exposureCount":2735 +} +``` + +### Хеширане и криптиране + +При регистрация, паролата на новия потребител трябва да се пази хеширана на сървъра, а всяка негова парола за даден сайт трябва да се пази криптирана, за да може при нужда да се декриптира. +Използвайте хеширащи и криптиращи алгоритми по ваш избор. + +## Client + +Клиентът трябва да има command line interface със следните команди + +#### Регистрация в системата +- `register <user> <password> <password-repeat>` +- регистрира нов потребител, като при сървъра *(във файл, съдържащ паролите на всички потребители)* се запазва паролата в хеширан вид и се създава нов файл, за съхраняване на всички пароли *(криптирани, за да може да се връщат на потребителя, но да не се запазват в чист вид)* на този потребител + +#### Вход в системата +- `login <user> <password>` +- login-ва потребител, при неактивност от една минута, потребителят трябва да бъде logout-нат автоматично + +#### Изход от системата +- `logout` +- logout-ва потребител + +#### Извличане на парола за даден сайт +- `retrieve-credentials <website> <user>` +- връща потребителското име и паролата на потребителя, с които се е регистрирал в дадения сайт + +#### Генериране на парола +- `generate-password <website> <user>` +- генерира и запазва парола на потребителя за даден сайт, като се посочва и потребителското име, с коeто ще се направи регистрацията в сайта + +#### Добавяне на парола +- `add-password <website> <user> <password>` +- добавя подадената от потребителя парола, като сървърът проверява дали тази парола е сигурна чрез REST API-то, и ако е, я запазва + +#### Премахване на парола +- `remove-password <website> <user>` +- премахва запазена парола на потребителя за даден сайт + +#### Прекратяване на връзката +- `disconnect` - прекратява връзката между клиента и сървъра + +### Пример за валидни входни данни и отговори + +``` bash +> login pesho peshopass +> Login successful + +> generate-password facebook.com pesho@gmail.com +> _rrR~S>k$[8+Ps/x2WyaFv + +> retrieve-credentials facebook.com pesho@gmail.com +> _rrR~S>k$[8+Ps/x2WyaFv + +> remove-password facebook.com pesho@gmail.com +> Password successfully removed + +> logout +> Logout successful + +> disconnect +> Disconnected +``` + +Всякакви допълнителни функционалности, за които се сетите, са добре дошли. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при команда на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/splitwise.md b/course-projects/splitwise.md new file mode 100644 index 00000000..3c3e70f2 --- /dev/null +++ b/course-projects/splitwise.md @@ -0,0 +1,156 @@ +# Split(NotSo)Wise :money_with_wings: + +Напишете клиент-сървър приложение с функционалност, наподобяваща тази на [Splitwise](https://www.splitwise.com/). + +Splitwise цели улесняване на поделянето на сметки между приятели и съквартиранти и намаляване на споровете от тип "само аз купувам бира в това общежитие". + +## Условие + +Създайте клиентско конзолно приложение, което приема потребителски команди, изпраща ги за обработка на сървъра, приема отговора му и го предоставя на потребителя в четим формат. + +*Note*: Командите и output-a в условието са примерни, свободни сте да ги преименувате и форматирате. Единственото условие е те да бъдат интуитивни. За улеснение на потребителя, може да имплементирате команда help. + +### Функционални изисквания + +- Регистрация на потребител с username и password; Регистрираните потребители се пазят във файл при сървъра - той служи като база от данни. При спиране и повторно пускане, сървърът може да зареди в паметта си вече регистрираните потребители. + +- Login; +- Регистриран потребител може да: + - добавя вече регистрирани потребители във Friend List на база техния username. Например: + ```bash + $ add-friend <username> + ``` + - създава група, състояща се от няколко, вече регистрирани, потребители: + + ```bash + $ create-group <group_name> <username> <username> ... <username> + ``` + Групите се създават от един потребител, като всяка група включва трима или повече потребители. Можете да си представяте, че "приятелските" отношения са група от двама човека. + + - добавя сума, платена от него, в задълженията на: + - друг потребител от friend list-a му: + ```bash + $ split <amount> <username> <reason_for_payment> + ``` + - група, в която участва: + + ```bash + $ split-group <amount> <group_name> <reason_for_payment> + ``` + + - получава своя статус - сумите, които той дължи на приятелите си и в групите си и сумите, които дължат на него. Например: + ```bash + $ get-status + Friends: + * Pavel Petrov (pavel97): Owes you 10 LV + + Groups + * 8thDecember + - Pavel Petrov (pavel97): Owes you 25 LV + - Hristo Hristov (ico_h): Owes you 25 LV + - Harry Gerogiev (harryharry): You owe 5 LV + ``` + Може да визуализирате всички групи и приятели или само тези, при които има "неуредени сметки". + + +- Нововъведена сума се дели поравно между всички участници в групата или наполовина, ако се дели с потребител от Friend List-a. + +- Когато един потребител А дължи пари на друг потребител B, задължението може да бъде "погасено" (с подходяща команда) само от потребител B. + ```bash + $ payed <amount> <username> + ``` + Например: + ```bash + $ get-status + Friends: + * Pavel Petrov (pavel97): Owes you 10 LV + * Hristo Hristov (ico_h): You owe 5 LV + + $ payed 5 pavel97 + Pavel Petrov (pavel97) payed you 5 LV. + Current status: Owes you 5 LV + + $ get-status + Friends: + * Pavel Petrov (pavel97): Owes you 5 LV + * Hristo Hristov (ico_h): You owe 5 LV + ``` + +- Когато един потребител А дължи на потребител B сума (например 5$), но преди да ги върне на B добави друга сума, която той е платил (например 5$), тогава дължимите суми и на двамата се преизчисляват (дължимата сума на А ще стане 2.50$, B все още не дължи нищо, но има да взима 2.50$). + ```bash + $ get-status + Friends: + * Pavel Petrov (pavel97): Owes you 10 LV + * Hristo Hristov (ico_h): You owe 5 LV + + $ split 5 ico_h limes and oranges + Splitted 5 LV between you and Hristo Hristov. + Current status: You owe 2.50 LV + + $ get-status + Friends: + * Pavel Petrov (pavel97): Owes you 5 LV + * Hristo Hristov (ico_h): You owe 2.50 LV + ``` + +- При всяко влизане на потребителя в системата, той получава известия, ако негови приятели са добавяли суми или "погасявали" дългове. +Например: + ```bash + $ login alex alexslongpassword + Successful login! + No notifications to show. + ``` + или + ```bash + $ login alex alexslongpassword + Successful login! + *** Notifications *** + Friends: + Misho approved your payment 10 LV [Mixtape beers]. + + Groups: + * Roomates: + You owe Gery 20 LV [Tanya Bday Present]. + + * Family: + You owe Alex 150 LV [Surprise trip for mom and dad] + ``` +- Потребителят може да види история на плащанията, извършени от него. Тази история се пази във файл на сървъра. + +- (***Бонус***) Сървърът предоставя възможност за currency conversion. Валутата по подразбиране е български лева, като потребителят може да я смени по всяко време на изпълнение на програмата, чрез подходяща команда (например `switch-currency EUR`). Всички суми, които потребителят дължи и дължат на него към този момент, преминават в избраната валута. + + Чрез HTTP заявка до някое публично API (например https://exchangeratesapi.io/) вземете текущите стойности на валутите и обработете response-a. + +### Нефункционални изисквания + +- Сървърът може да обслужва множество потребители паралелно. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackTraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при логин на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Уточнения + +- Както може би се досещате, това не е система за банкови транзакции и плащането на съответните суми не ни интересува как се случва. + + Нека си представим, че Ана и Ива са съквартирантки. Ана плаща общежитията и на двете и записва това в приложението. После Ива ѝ дава парите, които дължи, а Ана влиза в приложението и записва, че Ива ѝ се е издължила. Ива не може сама да каже "Аз платих моята част". + +- Валидацията на потребителския вход е задължителна, т.е. покрийте всички сценарии, за които се сетите с невалиден вход - било то null, грешно форматиране, невалиден тип на данните и т.н. + +- Командите и output-a от тях са примерни. Ако не ви допадат или не ги разбирате, сте свободни да използвате други. Единствената им цел тук е да помогнат за разбирането на условието. + +- Всякакви допълнителни функционалности, за които се сетите, са добре дошли. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/spotify.md b/course-projects/spotify.md new file mode 100644 index 00000000..d49a6f6d --- /dev/null +++ b/course-projects/spotify.md @@ -0,0 +1,109 @@ +# Spotify :notes: + +[`Spotify`](https://www.spotify.com/) е платформа за `stream`-ване на музика, която предоставя на потребителите достъп до милиони песни на изпълнители от цял свят. + +> `Stream`-ването е метод за предаване на данни, използван обикновено за мултимедийни файлове. При него възпроизвеждането на съдържанието върху устройството на потребителя започва още с достъпването му, без да се налага то отначало да бъде изтеглено изцяло като файл и после да се стартира в подходящ плеър. Предаването на данните протича едновременно с възпроизвеждането, затова е необходима постоянна мрежова свързаност. + +## Условие + +Създайте приложение по подобие на `Spotify`, състоящо се от две части - сървър и клиент. + +### **Spotify Server** + +Предоставя следните функционалности на клиента: +- регистриране в платформата чрез **email** и **парола** (**потребителите трябва да се съхраняват във файл**) +- login в платформата чрез **email** и **парола** +- съхраняване на набор от песни, достъпни на потребителите за слушане +- търсене на песни +- създаване на статистика на най-слушаните песни от потребителите +- създаване на плейлисти (**плейлистите трябва да се съхраняват във файлове**) +- добавяне на песни към плейлисти +- връщане на информация за даден плейлист +- `stream`-ване на песни + +### **Spotify Client** + +`Spotify` клиентът трябва да има `command line interface` със следните команди: + +```bash +register <email> <password> +login <email> <password> +disconnect +search <words> - връща всички песни, в чиито имена или имената на изпълнителите им, се среща потърсената дума (или думи) +top <number> - връща списък с *number* от най-слушаните песни в момента, сортиран в намаляващ ред +create-playlist <name_of_the_playlist> +add-song-to <name_of_the_playlist> <song> +show-playlist <name_of_the_playlist> +play <song> +stop +``` + +## Забележки: + +1. За да можете да изпълнявате песни от `Spotify` клиента, използвайте API-то `javax.sound.sampled`. +2. `javax.sound.sampled` работи само с файлове във [wav](https://en.wikipedia.org/wiki/WAV) формат, затова всички песни, които имате на сървъра, трябва да са **.wav** +3. `javax.sound.sampled` предоставя два начина за възпроизвеждане на музика - чрез `Clip` и `SourceDataLine`. `Clip` се използва когато имаме `non-real-time` музикални данни (файл), които могат да бъдат предварително заредени в паметта. +`SourceDataLine` се използва за `stream`-ване на данни, като например голям музикален файл, който не може да се зареди в паметта наведнъж, или за данни, които предварително не са известни. (за повече информация [тук](https://docs.oracle.com/javase/tutorial/sound/playing.html)) + + За целите на проекта, трябва да използвате `SourceDataLine`. + 1. За да създадем [`SourceDataLine`](https://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/SourceDataLine.html) първо трябва да знаем конкретния формат на данните, които ще получаваме по мрежата. Това става с класа [`AudioFormat`](https://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/AudioFormat.html). За да успеем да възпроизведем дадена песен при клиента, трябва предварително да знаем какъв е този формат. + + 2. Преди сървърът да започне да ни `stream`-ва песента, той трябва да ни даде(прати) информация за формата на данните. Класът `AudioFormat` не е `Serializable`, т.е не можем да изпратим на клиента директно обект от тип `AudioFormat`. + + 3. За да вземем формата на песента на сървъра, можем да използваме следния код: + ```java + AudioFormat audioFormat = AudioSystem.getAudioInputStream(new File(song)).getFormat(); + ``` + + 4. Данните, които са необходими на клиента, са всички полета от конструктора на `AudioFormat`. Те могат да бъдат достъпени чрез съответните `getter` методи: + ```java + AudioFormat(AudioFormat.Encoding encoding, float sampleRate, int sampleSizeInBits, int channels, int frameSize, float frameRate, boolean bigEndian) + ``` + + 5. След като сървърът е изпратил формата на данните, клиентът вече е готов да създаде съответния `SourceDataLine` обект, чрез който ще се възпроизвежда песента. + ```java + Encoding encoding = ...; + int sampleRate = ...; + ... + AudioFormat format = new AudioFormat(encoding, sampleRate, sampleSizeInBits, channels, frameSize, frameRate, bigEndian); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); + + SourceDataLine dataLine = (SourceDataLine) AudioSystem.getLine(info); + dataLine.open(); + dataLine.start(); // Имайте предвид, че SourceDataLine.start() пуска нова нишка. За повече информация, може да проверите имплементацията. + ``` + 6. За да запишем данни в `SourceDataLine` обекта (данните, които искаме да възпроизведем) използваме следния метод: + ```java + dataLine.write(byte[] b, int off, int len); + ``` + + 7. За тестови цели, можем да си пуснем песен (non-real-time) със следния код: + + ```java + AudioInputStream stream = AudioSystem.getAudioInputStream(new File("<music>.wav")); + SourceDataLine dataLine = AudioSystem.getSourceDataLine(stream.getFormat()); + dataLine.open(); + dataLine.start(); + + while (true); + ``` + 4. Валидирайте по подходящ начин командите + 5. При възникване на програмна грешка, извеждайте в конзолата смислени съобщения на потребителя - не се предполага клиентът да има умения да `troubleshoot`-ва `exception`-и, затова `printStackTrace()` не е добър вариант. Хврълените `exception`-и записвайте във файл. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при команда на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/todoist.md b/course-projects/todoist.md new file mode 100644 index 00000000..05743e1b --- /dev/null +++ b/course-projects/todoist.md @@ -0,0 +1,146 @@ +# Todoist :clipboard: :heavy_check_mark: + +Напишете клиент-сървър приложение с функционалност, наподобяваща тази на [Todoist](https://www.todoist.com/). + +Todoist e task manager или, иначе казано, приложение, което менажира to-do-list задачки. То цели улесняването на създаване, организиране и проследяване на прогреса на прости задачи от всекидневието или на големи проекти. Задачите може да са всекидневни: "да купя вечеря"; "да нахраня кучето", до цял проект, който да проследи създаването на дипломна работа. + +## Условие + +Създайте клиентско конзолно приложение, което приема потребителски команди, изпраща ги за обработка на сървъра, приема отговора му и го предоставя на потребителя в human-readable формат. + +*Note*: Командите и output-ът в условието са примерни, свободни сте да ги преименувате и форматирате. Единственото условие е, те да бъдат интуитивни. За улеснение на потребителя, може да имплементирате команда help. + +## Функционални изисквания + +### Задача +Една задача се състои от следните атрибути: + - **name** - име на задачата + - **date** (optional) - дата, на която искаме да изпълним задачата + - **due date** (optional) - дата, до която със сигурност трябва да изпълним задачата + - **description** (optional) - описание на задачата + +*Note*: Приемаме, че една задача се характеризира от името си и датата, ако има такава. Задачи без date отиват в така наречения Inbox - задачи, без фиксиран date. Следователно, в Inbox-а не можем да имаме повече от една задача с едно и също име. За конкретен ден, също не можем да имаме повече от една задача с едно и също име. При изпълнението на командите, ако не укажем параметъра за дата, приемаме, че задачата се очаква да бъде в Inbox-a. + +- **register** - регистрация на потребител с username и password. Регистрираните потребители се пазят във файл на сървъра - той служи като база от данни. При спиране и повторно пускане, сървърът може да зареди в паметта си вече регистрираните потребители. + +- **login** - потребителят влиза в системата + +- **add-task** - добавяне на нова задача. Ако задачата няма --date, тя бива добавена директно в Inbox-а. + ```bash + $ add-task --name=<name> --date=<date> --due-date=<due-date> --description=<description> + ``` +- **update-task** - промяна на някой от атрибутите на задача. Единственият атрибут, който не можем да променяме, е името. + ```bash + $ update-task --name=<name> --date=<date> --due-date=<due-date> --description=<description> + ``` +- **delete-task** - изтриване на задача. + ```bash + $ delete-task --name=<task_name> + $ delete-task --name=<task_name> --date=<date> + ``` + + - **get-task** - предоставя информацията за дадена задача в human-readable формат. + ```bash + $ get-task --name=<task_name> + $ get-task --name=<task_name> --date=<date> + ``` + - **list-tasks** - предоставя информацията за всички задачи от конкретен ден или от Inbox-a. Командата приема и опционален флаг completed и може да изведе и информация за всички вече завършени задачи. + ```bash + $ list-tasks + $ list-tasks --completed=true + $ list-tasks --date=<date> + ``` +- **list-dashboard** - предоставя информацията за всички задачи от днешния ден. + ```bash + $ list-dashboard + ``` +- **finish-task** - маркира конкретната задача като завършена. + ```bash + $ finish-task --name=<name> + ``` + +### Колаборация +Колаборация (или collaboration) представлява споделен проект, в който могат да се добавят задачи, които са видими за всички участници в него. Задачите в колаборацията имат още един опционален атрибут - assignee, т.е. потребителят, който ще работи по задачата. + + - **add-collaboration** - добавя нова колаборация. + ```bash + $ add-collaboration --name=<name> + ``` + - **delete-collaboration** - изтрива колаборация. Една колаборация може да бъде изтрита само от потребителя, който я е създал. Изтриването на колаборацията автоматично трябва да изтрие и всички задачи, които са част от нея. + ```bash + $ delete-collaboration --name=<name> + ``` + - **list-collaborations** - предоставя информацията всички колаборации на съответния потребител. Това включва такива, които той е създал и такива към които е добавен. + ```bash + $ list-collaborations + ``` + - **add-user** - добавя потребител към колаборацията. + ```bash + $ add-user --collaboration=<name> --user=<username> + ``` + - **assign-task** - задава assignee за дадена задача в колаборацията. + ```bash + $ assign-task --collaboration=<name> --user=<username> --task=<name> + ``` + - **list-tasks** --collaboration - предоставя информацията за всички задачи от колаборацията. + ```bash + $ list-tasks --collaboration=<name> + ``` + - **list-users --collaboration** - извежда всички потребители, които са част от колаборацията. + ```bash + $ list-users --collaboration=<name> + ``` + +### Етикет (Бонус) + +Етикетите (или labels) представляват споделен лесен начин, по който можем да групираме няколко задачи. Ако например имаме няколко различни задачи, свързани с университета, можем да им сложим етикет *university*. + +- **add-label** - добавя нов етикет, който е видим само в акаунта на потребителя, който изпълнява командата. + ```bash + $ add-label --name=<name> + ``` +- **delete-label** - изтриване на етикет. + ```bash + $ delete-label --name=<name> + ``` +- **list-labels** - предоставя информация за всички етикети, които потребителят е създал. + ```bash + $ list-labels + ``` +- **label-task** - добавя етикет на дадена задача. + ```bash + $ label-task --name=<name> --date=<date> --label=<name> + ``` + - **list-tasks --label** - предоставя информацията за всички задачи, на които е поставен даденият етикет. + ```bash + $ list-tasks --label=<name> + ``` + +## Нефункционални изисквания + +- Сървърът може да обслужва множество потребители паралелно. +- Сървърът пази информацията за потребители, задачи, колаборации по такъв начин, който му позволява след спиране или рестартиране да може да зареди тази информация отново. Помислете за подходящ формат на данни, в който може да съхранявате информацията от сървъра. + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване на програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stacktraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при логин на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Уточнения + +- Валидацията на потребителския вход е задължителна, т.е. покрийте всички сценарии, за които се сетите с невалиден вход - било то null, грешно форматиране, невалиден тип на данните и т.н. +- Командите и output-ът от тях са примерни. Свободни сте да представите ваша интерпретация на командите, както и да добавите нови. Единствената им цел тук е да помогнат за разбирането на условието. +- Всякакви допълнителни функционалности, за които се сетите, са добре дошли. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех! diff --git a/course-projects/torrent.md b/course-projects/torrent.md new file mode 100644 index 00000000..72982259 --- /dev/null +++ b/course-projects/torrent.md @@ -0,0 +1,78 @@ +# Peer-to-peer file exchange (torrent сървър) :busts_in_silhouette: + +Да се имплементира опростен вариант на peer-to-peer система за обмен на файлове, която използва централен сървър за откриване на потребители и данни. + +## Условие + +Системата трябва да се състои от две части: +1. Сървър, който съхранява метаданни за наличните файлове. +2. Клиенти, които теглят файлове от други клиенти – клиентите взимат информация от сървъра за това къде (от кой клиент) даден файл може да бъде свален. + +Системата предоставя функционалност за сваляне на файлове от различни потребители. + +Всеки потребител може да сваля файл от всеки друг. Има централен сървър, който съхранява информация за потребителите и файловете, които могат да бъдат свалени от тях (файлове не се свалят и съхраняват на централния сървър). + +Сървърът трябва да може да работи с много клиенти едновременно. + +Сървърът съхранява информация за активните потребители в паметта (изберете подходяща структура от данни) – имената, адресите и портовете им (p2p обмен изисква да се знаят адресите на peer-ите); файловете, които могат да бъдат изтеглени от тях (абсолютен път). Тази информация се обновява, когато клиент регистрира файлове за изтегляне (команда *register* по-долу) и когато клиент затвори връзката със сървъра. + +Клиентите могат да се свързват един с друг (peer-to-peer communication). За целта е нужно всеки да реализира „мини сървър“ при себе си. Този мини-сървър трябва да може да обработва командата download, описана по-долу. Можете да изберете дали мини-сървърът да обработва само една заявка за изтегляне или много паралелни заявки (ако е една - докато тя бива обработвана, оставащите заявки за изтегляне към мини-сървъра трябва да чакат). + +Клиентът също така съхранява при себе си съответствие <*username* – *user* *IP:port*>. +Пример: + +``` +Pesho123 – 127.0.0.1:1234 +Gosho321 – 127.0.0.1:2314 +``` +За да получи клиентът тази информация, той регулярно (през 30 секунди) пита сървъра за регистрираните потребители и техните адреси. Данните се записват във файл в указния по-горе формат. + +### Клиент + +Клиентът изпълнява следните команди: +1. `register <user> <file1, file2, file3, …., fileN>` - позволява на клиентите да „обявят“ кои файлове са налични за сваляне от тях. Чрез параметъра username, потребителят може да зададе свое уникално име (името, с което сървърът ще асоциира съответното IP). + +2. `unregister <user> <file1, file2, file3, …., fileN>` - потребителят обявява, че от него вече не могат да се свалят файловете <file1, file2, file3, …., fileN>. + + ***Забележка***: За простота, не се интересуваме от security аспектите на решението, т.е. не е нужно да реализирате автентикация, която да гарантира, че даден потребител може да отрегистрира само файловете, които самият той е регистрирал. + +3. `list-files` – връща файловете, налични за изтегляне, и потребителите, от които могат да бъдат изтеглени. +4. `download <user> <path to file on user> <path to save>` - изтегля дадения файл от съответния потребител. + +### Сървър + +Сървърът трябва да може да обработва изброените команди по подходящ начин: +1. При получаване на `register <user> <file1, file2, file3, …., fileN>`, сървърът обновява информацията за този потребител (добавя информация, че тези файлове са налични за сваляне от съответния потребител). +2. При получаване на `unregister <user> <file1, file2, file3, …., fileN>`, обновява информацията за този потребител (съответните файлове вече не са налични за сваляне от този потребител). +3. При получаване на `list-files`, връща регистрираните в сървъра файлове във формат `<user> : <path to file>` +4. При изпълняването на `download <user> <path to file on user> <path to save>`, не се случва комуникация с централния сървър. + + **a.** Клиентът определя *IP адреса* и *порта* на потребителя, от който може да бъде изтеглен даденият файл (от локалния си mapping). + + **b.** Клиентът изпраща командата на мини-сървъра на потребителя, определен в стъпка (а) + + **c.** Мини-сървърът изпраща файла + + **d.** След като потребителят получи файла, автоматично изпълнява командата register <user> <path to saved file>. По този начин информацията в главния сървър за потребителите, притежаващи този файл, се обновява. + +Една подсказваща **диаграма**: + +![Peer-to-Peer Diagram](images/peer-to-peer.png) + +## Съобщения за грешки + +При неправилно използване на програмата, на потребителя да се извеждат подходящи съобщения за грешка. + +При възникване програмна грешка, на потребителя да се извежда само уместна за него информация. Техническа информация за самата грешка и stackтraces да се записват във файл на файловата система - няма определен формат за записване на грешката. + +Например, нерелевантно е при команда на потребител и възникнал проблем с мрежовата комуникация, да се изписва грешка от вида на "IO exception occurred: connection reset", по-подходящо би било "Unable to connect to the server. Try again later or contact administrator by providing the logs in <path_to_logs_file>". + +При възникване на програмна грешка от страна на сървъра, подходящо съобщение се изписва на конзолата и във файл, като освен това, във файла се записва допълнителна информация (например, при заявка на кой потребител е възникнала грешката, ако въобще е обвързана с потребителско взаимодействие) и stacktraces. + +## Submission + +Качете в грейдъра `.zip` архив на познатите директории `src` и `test`. Ако пакетирате допълнителни файлове (които не са .java), те трябва да са в корена на архива, на нивото на `src` и `test`. +В грейдъра няма да има автоматизирани референтни тестове. +Проектът ви трябва да е качен в грейдъра не по-късно от 18:00 в деня преди датата на защитата. + +Успех!