├── .gitignore ├── README.md ├── configuration └── Preferences.sublime-settings ├── week0 ├── README.md ├── simple_problems.md ├── simple_problems2.md └── simple_problems3.md ├── week1 ├── git.md └── git_problems.md ├── week2 ├── bank.py ├── bank_tests.py ├── csv2json.py ├── dnp.md ├── langs.csv ├── langs.json ├── materials.md ├── problems.md ├── problems2.md └── timer.py ├── week3 ├── materials.md └── problems.md ├── week4 ├── cinema.md ├── grocery_store.md ├── materials.md ├── movie_catalog.md └── problems.md ├── week5 ├── animals.db ├── create_animals_database.py ├── materials.md ├── problems.md └── zoo_problem.md ├── week6 └── problems.md └── week7 ├── materials.md └── problems.md /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Programming101 2 | 3 | The Github repo for the first course from [HackBulgaria](http://hackbulgaria.com) - Programming 101. 4 | 5 | Different materials will be commited here. Mosty programming tasks and Python unit test cases. 6 | 7 | Content will be separated in different folders, for the given week, starting with week0. 8 | 9 | ## How to use this repository 10 | 11 | The first separation if based on weeks - starting from week 0. 12 | 13 | __In each week, there are markdown files__ with task descriptions in them. 14 | 15 | Each week has a `materials.md` file, where we put the things that need to be checked first, before going into task-solving. 16 | 17 | Usually, you will find code samples, links and videos about the topic of the week. 18 | 19 | ## Outline of the course 20 | 21 | * __week0__ - Getting familiar with Python, Python tooling and Linux - shell interpreter, Sublime with PEP8 plugins, moving around the file systems. There are a lot of tasks to be solved, using only Python! 22 | * __week1__ - Getting familiar with Git and GitHub - everyone creates a GitHub account and starts using it to push code & start thinking with versions. Solving the [Polyglot](https://github.com/HackBulgaria/Polyglot) problem where everyone should get at least 8 different languages and platforms up and running! 23 | * __week2__ - Introducing __TDD!__ Starting with Python OOP and creating a console-based dungeon game, where you find Anacondas! You know, like a bigger Python :D Everything should be done in a Test-Driven matter. 24 | * __week3__ - Moving on with OOP problems and TDD - a lot of console applications! 25 | * __week4__ - Introducing sqlite3 and adding a database layer to our python console applications. Everything is done with database, OOP and in a Test-Driven fashion! 26 | * __week5__ - Introducing team-work problems and how to use Git & GitHub for handling a team of more than 1 person. We create a simulation of a Zoo - Teamwork + OOP + sqlite3 + TDD 27 | * __week6__ - Introducing basic security concepts - hashing, SQL Injection, hiding passwords from input, bruteforce protections. There is an app waiting to be refactored. 28 | * __week7__ - Introducig the ORM concept and using SqlAlchemy as an ORM library for Python. 29 | -------------------------------------------------------------------------------- /configuration/Preferences.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "caret_style": "solid", 3 | "color_scheme": "Packages/User/Tomorrow-Night (SL).tmTheme", 4 | "draw_white_space": "all", 5 | "ensure_newline_at_eof_on_save": true, 6 | "file_exclude_patterns": 7 | [ 8 | ".DS_Store", 9 | "*.pid", 10 | "*.pyc" 11 | ], 12 | "find_selected_text": true, 13 | "fold_buttons": false, 14 | "folder_exclude_patterns": 15 | [ 16 | ".git", 17 | "__pycache__", 18 | "env", 19 | "env3" 20 | ], 21 | "font_face": "Ubuntu Mono", 22 | "font_options": 23 | [ 24 | "subpixel_antialias", 25 | "no_bold" 26 | ], 27 | "font_size": 17, 28 | "highlight_line": true, 29 | "highlight_modified_tabs": true, 30 | "ignored_packages": 31 | [ 32 | "Vintage" 33 | ], 34 | "line_padding_bottom": 0, 35 | "line_padding_top": 0, 36 | "rulers": 37 | [ 38 | 72, 39 | 79 40 | ], 41 | "scroll_past_end": false, 42 | "show_full_path": true, 43 | "show_minimap": false, 44 | "soda_classic_tabs": true, 45 | "soda_folder_icons": true, 46 | "tab_size": 4, 47 | "theme": "Soda Dark 3.sublime-theme", 48 | "translate_tabs_to_spaces": true, 49 | "trim_trailing_white_space_on_save": true, 50 | "wide_caret": true, 51 | "word_wrap": true, 52 | "wrap_width": 80 53 | } 54 | -------------------------------------------------------------------------------- /week0/README.md: -------------------------------------------------------------------------------- 1 | # How to run your Python programs and test them. 2 | 3 | To illustrate things, let's imagine that we have to implement a python program, that calculates the sum of two integers. 4 | 5 | Here we have the code: 6 | 7 | ```python 8 | def awesome_sum(a, b): 9 | return a + b 10 | ``` 11 | 12 | And now, we want to test our function. 13 | 14 | We propose two different approaches for this: 15 | 16 | ## Via console 17 | 18 | ### Simple import 19 | 20 | If your source file is named ```solution.py```, do the following: 21 | 22 | * Navigate to the directory, where ```solution.py``` is located; 23 | * Run the python interpreter by typing ```py``` or ```python``` (Depends on the version you have. We need 3.3) 24 | * import the function - ```from solution import awesome_sum``` - The algorithm here is ```from file_name_without_py import function_name``` 25 | * Now you have run your funciton : 26 | 27 | ``` 28 | >>> awesome_sum(1,2) 29 | 3 30 | ``` 31 | 32 | ### main() method 33 | 34 | Add main method and a call to it in your ```solution.py``` file: 35 | 36 | ```python 37 | def awesome_sum(a, b): 38 | return a + b 39 | 40 | def main(): 41 | print(awesome_sum(1,3)) 42 | # more test cases to follow 43 | 44 | # This piece of code will call the main method, 45 | # When you run solution.py via the interpreter 46 | if __name__ == '__main__': 47 | main() 48 | ``` 49 | 50 | After this, you can just call the program with ```py```: 51 | 52 | ``` 53 | $ py solution.py 54 | 4 55 | ``` 56 | 57 | ## Unit Tests 58 | 59 | You can create a file, called ```awesome_sum_tests.py``` and have the following code: 60 | 61 | ```python 62 | 63 | from solution import awesome_sum 64 | import unittest 65 | 66 | class AwesomeSumTest(unittest.TestCase): 67 | def test_one_plus_one(self): 68 | self.assertEqual(2, awesome_sum(1,1)) 69 | 70 | # You can add more tests cases here 71 | 72 | if __name__ == '__main__': 73 | unittest.main() 74 | 75 | ``` 76 | 77 | And after this, just call: 78 | 79 | ``` 80 | $ py awesome_sum_tests.py 81 | .... 82 | ---------------------------------------------------------------------- 83 | Ran 1 tests in 0.000s 84 | 85 | OK 86 | ``` 87 | 88 | And you are one step closer to test-driven development. 89 | -------------------------------------------------------------------------------- /week0/simple_problems.md: -------------------------------------------------------------------------------- 1 | A bunch of simple problems, to warm up with Python. Those are the regular programming problems, that you meet in every beginners course. 2 | 3 | The solutions should be written for Python 3.* version. 4 | 5 | ## Problem 0 - N-th Fibonacci 6 | 7 | The most annoying problem of all. Implement a function, called ```nth_fibonacci(n)``` that returns the n-th fibonacci number, given by the argument. 8 | 9 | ### Signature 10 | 11 | ```python 12 | def nth_fibonacci(n): 13 | #implementation here 14 | ``` 15 | 16 | ### Test examples 17 | 18 | ``` 19 | >>> nth_fibonacci(1) 20 | 1 21 | >>> nth_fibonacci(2) 22 | 1 23 | >>> nth_fibonacci(3) 24 | 2 25 | >>> nth_fibonacci(10) 26 | 55 27 | >>> 28 | ``` 29 | 30 | ## Problem 1 - Sum all digits of a number 31 | 32 | Given an integer, implement a function, called ```sum_of_digits(n)``` that calculates the sum of the digits of n. 33 | 34 | If a negative number is given, the function should work as if it was positive. 35 | 36 | For example, if n is ```1325132435356```, the digit's sum is 43. 37 | If n is -10, the sum is 1 + 0 = 1 38 | 39 | Keep in mind that in Python, there is a special operator for integer division: 40 | 41 | ``` 42 | >>> 5 / 2 43 | 2.5 44 | >>> 5 // 2 45 | 2 46 | ``` 47 | 48 | ### Signature 49 | 50 | ```python 51 | def sum_of_digits(n): 52 | # implementation 53 | ``` 54 | 55 | ### Test examples 56 | 57 | ``` 58 | >>> sum_of_digits(1325132435356) 59 | 43 60 | >>> sum_of_digits(123) 61 | 6 62 | >>> sum_of_digits(6) 63 | 6 64 | >>> sum_of_digits(-10) 65 | 1 66 | ``` 67 | 68 | ## Problem 2 - Sum the minimum and maximum elements 69 | 70 | Given an array of integers, implement a function, called ```sum_of_min_and_max(arr)```, that calculates and returns the sum of the largest and the smallest integers in the array. 71 | 72 | Don't bother for the case when the array is empty. 73 | 74 | ### Signature 75 | 76 | ```python 77 | def sum_of_min_and_max(arr): 78 | # implementation 79 | ``` 80 | 81 | ### Test examples 82 | ``` 83 | >>> sum_of_min_and_max([1,2,3,4,5,6,8,9]) 84 | 10 85 | >>> sum_of_min_and_max([-10,5,10,100]) 86 | 90 87 | >>> sum_of_min_and_max([1]) 88 | 2 89 | ``` 90 | 91 | ## Problem 3 - Sum all divisors of an integer 92 | 93 | Given an integer, implement a function, called ```sum_of_divisors(n)``` that calculates the sum of all divisors of n. 94 | 95 | For example, the divisors of 8 are 1,2,4 and 8 and ```1 + 2 + 4 + 8 = 15``` 96 | The divisors of 7 are 1 and 7, which makes the sum = 8 97 | 98 | ### Signature 99 | 100 | ```python 101 | def sum_of_divisors(n): 102 | # implementation 103 | ``` 104 | 105 | ### Test examples 106 | 107 | ``` 108 | >>> sum_of_divisors(8) 109 | 15 110 | >>> sum_of_divisors(7) 111 | 8 112 | >>> sum_of_divisors(1) 113 | 1 114 | >>> sum_of_divisors(1000) 115 | 2340 116 | ``` 117 | 118 | ## Problem 4 - Check if integer is prime 119 | 120 | Given an integer, implement a function, called ```is_prime(n)``` which returns True if n is a prime number. You should handle the case with negative numbers too. 121 | 122 | A primer number is a number, that is divisible only by 1 and itself. 123 | 124 | 1 is not considered to be a prime number. [If you are curious why, find out here.](http://www.youtube.com/watch?v=IQofiPqhJ_s) 125 | 126 | ### Signature 127 | 128 | ```python 129 | def is_prime(n): 130 | # implementation 131 | ``` 132 | 133 | ### Test examples 134 | 135 | ``` 136 | >>> is_prime(1) 137 | False 138 | >>> is_prime(2) 139 | True 140 | >>> is_prime(8) 141 | False 142 | >>> is_prime(11) 143 | True 144 | >>> is_prime(-10) 145 | False 146 | ``` 147 | 148 | ## Problem 5 - Check if a number has a prime number of divisors 149 | 150 | Given an integer, implement a function, called ```prime_number_of_divisors(n)``` which returns True if the number of divisors of n is a prime number. False otherwise. 151 | 152 | For example, the divisors of 8 are 1,2,4 and 8, a total of 4. 4 is not a prime. 153 | The divisors of 9 are 1,3 and 9, a total of 3, which is a prime number. 154 | 155 | ### Signature 156 | 157 | ```python 158 | def prime_number_of_divisors(n): 159 | # Implementation 160 | ``` 161 | 162 | ### Test examples 163 | 164 | ``` 165 | >>> prime_number_of_divisors(7) 166 | True 167 | >>> prime_number_of_divisors(8) 168 | False 169 | >>> prime_number_of_divisors(9) 170 | True 171 | ``` 172 | 173 | ## Problem 6 - Are there n sevens in a row? 174 | 175 | Implement a function, called ```sevens_in_a_row(arr, n)```, which takes an array of integers ```arr``` and a number ```n > 0``` 176 | 177 | The function returns True, __if there are n consecutive sevens__ in ```arr``` 178 | 179 | For example, if ```arr``` is ```[10, 8, 7, 6, 7, 7, 7, 20, -7]``` and n is 3, the function should return True. Otherwise, it returns False 180 | 181 | ### Signature 182 | 183 | ```python 184 | def sevens_in_a_row(arr, n) 185 | ``` 186 | 187 | ### Test examples 188 | 189 | ``` 190 | >>> sevens_in_a_row([10,8,7,6,7,7,7,20,-7], 3) 191 | True 192 | >>> sevens_in_a_row([1,7,1,7,7], 4) 193 | False 194 | >>> sevens_in_a_row([7,7,7,1,1,1,7,7,7,7], 3) 195 | True 196 | >>> sevens_in_a_row([7,2,1,6,2], 1) 197 | True 198 | ``` 199 | 200 | ## Problem 7 - Integer Palindromes 201 | 202 | A palindrome is а word or a phrase or a number, that when reversed, stays the same. 203 | 204 | For example, the following sequences are palindromes : "azobi4amma4iboza" or "anna". 205 | 206 | But this time, we are not interested in words but numbers. 207 | A number palindrome is a number, that taken backwards, remains the same. 208 | 209 | For example, the numbers 1, 4224, 9999, 1221 are number palindromes. 210 | 211 | Implement a function, called ```is_int_palindrome(n)``` which takes an integer and returns True, if this integer is a palindrome. 212 | 213 | ### Signature 214 | 215 | ```python 216 | def is_int_palindrome(n): 217 | # implementation 218 | ``` 219 | 220 | ### Test examples 221 | 222 | ``` 223 | >>> is_int_palindrome(1) 224 | True 225 | >>> is_int_palindrome(42) 226 | False 227 | >>> is_int_palindrome(100001) 228 | True 229 | >>> is_int_palindrome(999) 230 | True 231 | >>> is_int_palindrome(123) 232 | False 233 | ``` 234 | 235 | ## Problem 8 - Number containing a single digit? 236 | 237 | Implement a function, called ```contains_digit(number, digit)``` which checks if ```digit``` is contained by the given ```number```. 238 | 239 | ```digit``` and ```number``` are integers. 240 | 241 | ### Signature 242 | 243 | ```python 244 | def contains_digit(number, digit): 245 | # Implementation 246 | ``` 247 | 248 | ### Test examples 249 | 250 | ``` 251 | >>> contains_digit(123, 4) 252 | False 253 | >>> contains_digit(42, 2) 254 | True 255 | >>> contains_digit(1000, 0) 256 | True 257 | >>> contains_digit(12346789, 5) 258 | False 259 | ``` 260 | 261 | ## Problem 9 - Number containing all digits? 262 | 263 | Implement a function, called ```contains_digits(number, digits)``` where ```digits``` is a __list of integers__ and a ```number``` is an integer. 264 | 265 | The function should return True if __all__ ```digits``` are contained by ```number``` 266 | 267 | ### Signature 268 | 269 | ```python 270 | def contains_digits(number, digits): 271 | # Implementation 272 | ``` 273 | 274 | ### Test examples 275 | 276 | ``` 277 | >>> contains_digits(402123, [0, 3, 4]) 278 | True 279 | >>> contains_digits(666, [6,4]) 280 | False 281 | >>> contains_digits(123456789, [1,2,3,0]) 282 | False 283 | >>> contains_digits(456, []) 284 | True 285 | ``` 286 | 287 | ## Problem 10 - Is number balanced? 288 | 289 | A number is called balanced, if we take the middle of it and the sums of the left and right parts are equal. 290 | 291 | For example, the number ```1238033``` is balanced, bacause it has a left part, equal to 123, and right part, equal ot 033. 292 | 293 | We have : ```1 + 2 + 3 = 0 + 3 + 3 = 6``` 294 | 295 | A number with only one digit is always balanced. 296 | 297 | Implement a function, called ```is_number_balanced(n)``` which checks if the given number is balanced. 298 | 299 | ### Signature 300 | 301 | ```python 302 | def is_number_balanced(n): 303 | # Implementation 304 | ``` 305 | 306 | ### Test examples 307 | 308 | ``` 309 | >>> is_number_balanced(9) 310 | True 311 | >>> is_number_balanced(11) 312 | True 313 | >>> is_number_balanced(13) 314 | False 315 | >>> is_number_balanced(121) 316 | True 317 | >>> is_number_balanced(4518) 318 | True 319 | >>> is_number_balanced(28471) 320 | False 321 | >>> is_number_balanced(1238033) 322 | True 323 | 324 | ``` 325 | 326 | ## Problem 11 - Counting substrings 327 | 328 | Implement a function, called ```count_substrings(haystack, needle)``` which returns the count of occurrences of the string ```needle``` in the string ```haystack```. 329 | 330 | __Don't count overlapped substings and take case into consideration!__ 331 | For overlapping substrings, check the "baba" example below. 332 | 333 | ### Signature 334 | 335 | ```python 336 | def count_substrings(haystack, needle): 337 | # Implementation 338 | ``` 339 | 340 | ### Test examples 341 | ``` 342 | >>> count_substrings("This is a test string", "is") 343 | 2 344 | >>> count_substrings("babababa", "baba") 345 | 2 346 | >>> count_substrings("Python is an awesome language to program in!", "o") 347 | 4 348 | >>> count_substrings("We have nothing in common!", "really?") 349 | 0 350 | >>> count_substrings("This is this and that is this", "this") # "This" != "this" 351 | 2 352 | ``` 353 | 354 | ## Problem 12 - Vowels in a string 355 | 356 | Implement a function, called ```count_vowels(str)``` which returns the count of all vowels in the given string ```str```. __Count uppercase vowels as well!__ 357 | 358 | The vowels are ```aeiouy```. 359 | 360 | ### Signature 361 | 362 | ```python 363 | def count_vowels(str): 364 | # Implementation 365 | ``` 366 | 367 | ### Test examples 368 | 369 | ``` 370 | >>> count_vowels("Python") 371 | 2 372 | >>> count_vowels("Theistareykjarbunga") #It's a volcano name! 373 | 8 374 | >>> count_vowels("grrrrgh!") 375 | 0 376 | >>> count_vowels("Github is the second best thing that happend to programmers, after the keyboard!") 377 | 22 378 | >>> count_vowels("A nice day to code!") 379 | 8 380 | ``` 381 | 382 | ## Problem 13 - Consonants in a string 383 | 384 | Implement a function, called ```count_consonants(str)``` which returns the count of all consonants in the given string ```str```. __Count uppercase consonants as well!__ 385 | 386 | The consonants are ```bcdfghjklmnpqrstvwxz```. 387 | 388 | ### Signature 389 | 390 | ```python 391 | def count_consonants(str): 392 | # Implementation 393 | ``` 394 | 395 | ### Test examples 396 | 397 | ``` 398 | >>> count_consonants("Python") 399 | 4 400 | >>> count_consonants("Theistareykjarbunga") #It's a volcano name! 401 | 11 402 | >>> count_consonants("grrrrgh!") 403 | 7 404 | >>> count_consonants("Github is the second best thing that happend to programmers, after the keyboard!") 405 | 44 406 | >>> count_consonants("A nice day to code!") 407 | 6 408 | ``` 409 | 410 | ## Problem 14 - Turn a number into a list of digits 411 | 412 | Implement a function, called ```number_to_list(n)``` which takes an integer ```n``` and returns a list, containing the digits of ```n``` 413 | 414 | ### Signature 415 | 416 | ```python 417 | def number_to_list(n): 418 | # Implementation 419 | ``` 420 | 421 | ### Test Examples 422 | 423 | ``` 424 | >>> number_to_list(123) 425 | [1, 2, 3] 426 | >>> number_to_list(99999) 427 | [9, 9, 9, 9, 9] 428 | >>> number_to_list(123023) 429 | [1, 2, 3, 0, 2, 3] 430 | ``` 431 | 432 | ## Problem 15 - Turn a list of digits into a number 433 | 434 | Implement a function, called ```list_to_number(digits)``` which takes a list of digits (integers) and returns the number, containing those digits. 435 | 436 | ### Signature 437 | 438 | ```python 439 | def list_to_number(digits): 440 | # Implementation 441 | ``` 442 | 443 | ### Test Examples 444 | 445 | ``` 446 | >>> list_to_number([1,2,3]) 447 | 123 448 | >>> list_to_number([9,9,9,9,9]) 449 | 99999 450 | >>> list_to_number([1,2,3,0,2,3]) 451 | 123023 452 | ``` 453 | 454 | ## Problem 16 - Biggest difference between two numbers 455 | 456 | Implement a function, called ```biggest_difference(arr)```, which takes an array of integers and returns the biggest difference between any two numbers from the array. 457 | 458 | For every two elements from the array ```a``` and ```b```, we are looking for the minimum of ```a - b``` or ```b - a``` 459 | 460 | ### Signature 461 | 462 | ```python 463 | def biggest_difference(arr): 464 | # Implementation 465 | ``` 466 | 467 | ### Test examples 468 | 469 | ``` 470 | >>> biggest_difference([1,2]) 471 | -1 472 | >>> biggest_difference([1,2,3,4,5]) 473 | -4 474 | >>> biggest_difference([-10, -9, -1]) 475 | -9 476 | >>> biggest_difference(range(100)) 477 | -99 478 | ``` 479 | 480 | ## Problem 17 - Slope style score 481 | 482 | Slope style featured as a new snowboarding discipline in the Sochi 2014 Winter Olympics. 483 | 484 | To get a medal, you need to get maximum score from the judges. 485 | 486 | All judges give you a score - a floating point number between 0 and 100. When all scores are given, the final score is calculated by the following algorithm: 487 | 488 | > Remove the largest and the smallest scores. From the rest, take the average. 489 | 490 | Implement a function, called ```slope_style_score(scores)``` where ```scores``` is a list of floating point numbers. 491 | 492 | The function should calculate and return the final score, according to the algorithm above. 493 | 494 | The final score should be rounded to two decimal points. This means that if we get a score ```94.66666666666667``` it should be rounded to ```94.66``` 495 | 496 | ### Signature 497 | 498 | ```python 499 | def slope_style_score(scores): 500 | # Implementation 501 | ``` 502 | 503 | ### Test examples 504 | 505 | ``` 506 | >>> slope_style_score([94, 95, 95, 95, 90]) 507 | 94.66 508 | >>> slope_style_score([60, 70, 80, 90, 100]) 509 | 80.0 510 | >>> slope_style_score([96, 95.5, 93, 89, 92]) 511 | 93.5 512 | ``` 513 | ## Problem 18 - Increasing sequence? 514 | 515 | Implement a function, called ```is_increasing(seq)``` where ```seq``` is a list of integers. 516 | 517 | The function should return True, if the given sequence is monotonously increasing. 518 | 519 | And before you skip this problem, because of the math terminology, let me explain: 520 | 521 | > A sequence is monotonously increasing if for every two elements a and b, that are next to each other (a is before b), we have a < b 522 | 523 | For example, ```[1,2,3,4,5]``` is monotonously increasing while ```[1,2,3,4,5,1]``` is not. 524 | 525 | ### Signature 526 | 527 | ```python 528 | def is_increasing(seq): 529 | # Implementation 530 | ``` 531 | 532 | ### Test examples 533 | 534 | ``` 535 | >>> is_increasing([1,2,3,4,5]) 536 | True 537 | >>> is_increasing([1]) 538 | True 539 | >>> is_increasing([5,6,-10]) 540 | False 541 | >>> is_increasing([1,1,1,1]) 542 | False 543 | ``` 544 | 545 | ## Problem 19 - Descreasing sequence? 546 | 547 | Implement a function, called ```is_decreasing(seq)``` where ```seq``` is a list of integers. 548 | 549 | The function should return True, if the given sequence is monotonously decreasing. 550 | 551 | And before you skip this problem, because of the math terminology, let me explain: 552 | 553 | > A sequence is monotonously decreasing if for every two elements a and b, that are next to each other (a is before b), we have a > b 554 | 555 | For example, ```[5,4,3,2,1]``` is monotonously decreasing while ```[1,2,3,4,5,1]``` is not. 556 | 557 | ### Signature 558 | 559 | ```python 560 | def is_decreasing(seq): 561 | # Implementation 562 | ``` 563 | 564 | ### Test examples 565 | 566 | ``` 567 | >>> is_decreasing([5,4,3,2,1]) 568 | True 569 | >>> is_decreasing([1,2,3]) 570 | False 571 | >>> is_decreasing([100, 50, 20]) 572 | True 573 | >>> is_decreasing([1,1,1,1]) 574 | False 575 | ``` 576 | 577 | ## Problem 20 - What is the sign? 578 | 579 | This problem is from the Python 2013 course in FMI. [Link to original problem statement.](http://2013.fmi.py-bg.net/tasks/1) 580 | 581 | Implement a function, called ```what_is_my_sign(day, month)``` which takes two integers (one for the day and one for the month) and returns the name of the zodiac for the given time period. 582 | 583 | Consider the following zodiac table ([Or check wikipedia](http://en.wikipedia.org/wiki/Zodiac#Table_of_dates)) : 584 | 585 | * Aries: 21 March – 20 April 586 | * Taurus: 21 April – 21 May 587 | * Gemini: 22 May – 21 June 588 | * Cancer: 22 June – 22 July 589 | * Leo: 23 July – 22 August 590 | * Virgo: 23 August – 23 September 591 | * Libra: 24 September – 23 October 592 | * Scorpio: 24 October – 22 November 593 | * Sagittarius: 23 November – 21 December 594 | * Capricorn: 22 December – 20 January 595 | * Aquarius: 21 January – 19 February 596 | * Pisces: 20 February – 20 March 597 | 598 | 599 | ### Signature 600 | 601 | ```python 602 | def what_is_my_sign(day, month): 603 | # Implementation 604 | ``` 605 | 606 | ### Test examples 607 | 608 | ``` 609 | >>> what_is_my_sign(5, 8) 610 | "Leo" 611 | >>> what_is_my_sign(29, 1) 612 | "Aquarius" 613 | >>> what_is_my_sign(30, 6) 614 | "Cancer" 615 | >>> what_is_my_sign(31, 5) 616 | "Gemini" 617 | >>> what_is_my_sign(2, 2) 618 | "Aquarius" 619 | >>> what_is_my_sign(8, 5) 620 | "Taurus" 621 | >>> what_is_my_sign(9, 1) 622 | "Capricorn" 623 | ``` 624 | 625 | ## Problem 21 - Integer prime factorization 626 | 627 | Given an integer ```n```, we can factor it in the following form: 628 | 629 | > n = p1^a1 * p2^a2 * ... * pn^an 630 | 631 | Where each p is a prime number and each a is an integer and p^a means p to the power of a. 632 | 633 | [This is called prime factorization.](http://mathworld.wolfram.com/PrimeFactorization.html) 634 | 635 | Lets see few examples 636 | 637 | > 10 = 2^1 * 5^1 638 | > 25 = 5^2 639 | > 356 = 2^2 * 89 ^ 1 640 | 641 | Implement a function, called ```prime_factorization(n)``` which takes an integer and returns a list of tuples ```(pi, ai)```, which is the result of the factorization. 642 | 643 | The list should be sorted in increasing order of the prime numbers. 644 | 645 | ### Signature 646 | 647 | ```python 648 | def prime_factorization(n): 649 | # Implementation 650 | ``` 651 | 652 | ### Test examples 653 | 654 | ``` 655 | >>> prime_factorization(10) 656 | [(2, 1), (5, 1)] # This is 2^1 * 5^1 657 | >>> prime_factorization(14) 658 | [(2, 1), (7, 1)] 659 | >>> prime_factorization(356) 660 | [(2, 2), (89, 1)] 661 | >>> prime_factorization(89) 662 | [(89, 1)] # 89 is a prime number 663 | >>> prime_factorization(1000) 664 | [(2, 3), (5, 3)] 665 | ``` 666 | ## Problem 22 - Calculate coins 667 | 668 | This problem is from the [Python 2013 course in FMI](http://2013.fmi.py-bg.net/) 669 | 670 | Implement a function, called ```calculate_coins(sum)``` where sum is a floating point number. 671 | 672 | The function should return a dictionary, that represents a way to get the sum with minimal number of coins. 673 | 674 | __The coins that we can use are with values 1,2,100,5,10,50,20.__ 675 | 676 | Check the examples below. 677 | 678 | ### Signature 679 | 680 | ```python 681 | def calculate_coins(sum): 682 | # Implementation 683 | ``` 684 | 685 | ### Test examples 686 | 687 | ``` 688 | >>> calculate_coins(0.53) 689 | {1: 1, 2: 1, 100: 0, 5: 0, 10: 0, 50: 1, 20: 0} # We pay with one coin of value 50 and two coins of value 2 and one coin of value 1 - that's the minimal number of coins to get to 0.53 690 | >>> calculate_coins(8.94) 691 | {1: 0, 2: 2, 100: 8, 5: 0, 10: 0, 50: 1, 20: 2} 692 | ``` 693 | -------------------------------------------------------------------------------- /week0/simple_problems2.md: -------------------------------------------------------------------------------- 1 | ## Problem 23 - Count words 2 | 3 | Given a list of strings, implement a function, called ```count_words(arr)``` which returns a dictionary of the following kind: 4 | 5 | ```python 6 | { "word" : count } 7 | ``` 8 | 9 | Where ```count``` is the count of occurrences of the __word__ in the list ```arr```. 10 | 11 | 12 | ### Signature 13 | 14 | ```python 15 | def count_words(arr): 16 | # Implementation 17 | ``` 18 | 19 | ### Test examples 20 | 21 | ``` 22 | >>> count_words(["apple", "banana", "apple", "pie"]) 23 | {'apple': 2, 'pie': 1, 'banana': 1} 24 | >>> count_words(["python", "python", "python", "ruby"]) 25 | {'ruby': 1, 'python': 3} 26 | ``` 27 | 28 | ## Problem 24 - Unique words 29 | 30 | Implement a function, called ```unique_words_count(arr)``` which returns the number of different words in ```arr```. 31 | 32 | ```arr``` is a list of strings. 33 | 34 | ### Signature 35 | 36 | ```python 37 | def unique_words_count(arr): 38 | # Implementation 39 | ``` 40 | 41 | ### Test examples 42 | 43 | ``` 44 | >>> unique_words_count(["apple", "banana", "apple", "pie"]) 45 | 3 46 | >>> unique_words_count(["python", "python", "python", "ruby"]) 47 | 2 48 | >>> unique_words_count(["HELLO!"] * 10) 49 | 1 50 | ``` 51 | 52 | ## Problem 25 - Group By 53 | This problem is from the [Python 2013 course in FMI](http://2013.fmi.py-bg.net/) 54 | You can see the original problem statement here - http://2013.fmi.py-bg.net/tasks/2 55 | 56 | Implement a function, called ```groupby(func, seq)``` which returns a dictionary, which keys are determined by the ```func``` argument. 57 | 58 | The values are items from ```seq``` 59 | 60 | ### Signature 61 | 62 | ```python 63 | def groupby(func, seq): 64 | # Implementation 65 | ``` 66 | 67 | ### Test examples 68 | 69 | ``` 70 | >>> groupby(lambda x: x % 2, [0,1,2,3,4,5,6,7]) 71 | {0: [0, 2, 4, 6], 1: [1, 3, 5, 7]} 72 | >>> groupby(lambda x: 'odd' if x % 2 else 'even', [1, 2, 3, 5, 8, 9, 10, 12]) 73 | {'even': [2, 8, 10, 12], 'odd': [1, 3, 5, 9]} 74 | >>> groupby(lambda x: x % 3, [0, 1, 2, 3, 4, 5, 6, 7]) 75 | {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5]} 76 | ``` 77 | 78 | ## Problem 26 - Spam and Eggs 79 | This is a problem from the [Python 2012 course in FMI](http://2012.fmi.py-bg.net/) 80 | 81 | You can see the original problem statement here - http://2012.fmi.py-bg.net/tasks/1 82 | 83 | Implement a function, called ```prepare_meal(number)``` which takes an integer and returns a string, generated by the following algorithm: 84 | 85 | __Еggs:__ 86 | 87 | If there is an integer ```n```, such that ```3^n``` divides ```number``` and ```n``` is the largest possible, the result should be a string, containing ```n``` times ```spam``` 88 | 89 | For example: 90 | 91 | ``` 92 | >>> prepare_meal(3) 93 | 'spam' 94 | >>> prepare_meal(27) 95 | 'spam spam spam' 96 | >>> prepare_meal(7) 97 | '' 98 | ``` 99 | 100 | __Spam:__ 101 | 102 | If number is divisible by 5, add ```and eggs``` to the result. 103 | 104 | For example: 105 | 106 | ``` 107 | >>> prepare_meal(5) 108 | 'eggs' 109 | >>> prepare_meal(15) 110 | 'spam and eggs' 111 | >>> prepare_meal(45) 112 | 'spam spam and eggs' 113 | ``` 114 | 115 | __Notice that in the first case, there is no "and". In the rest - there is.__ 116 | 117 | ### Signature 118 | 119 | ```python 120 | def prepare_meal(number): 121 | # Implementation 122 | ``` 123 | 124 | ### Test examples 125 | 126 | ``` 127 | >>> prepare_meal(5) 128 | "eggs" 129 | >>> prepare_meal(3) 130 | "spam" 131 | >>> prepare_meal(27) 132 | "spam spam spam" 133 | >>> prepare_meal(15) 134 | "spam and eggs" 135 | >>> prepare_meal(45) 136 | "spam spam and eggs" 137 | >>> prepare_meal(7) 138 | "" 139 | ``` 140 | 141 | ## Problem 27 - Reduce file path 142 | 143 | A file path in a Unix OS looks like this - ```/home/radorado/code/hackbulgaria/week0``` 144 | 145 | We start from the root - ```/``` and we navigate to the destination fodler. 146 | 147 | But there is a problem - if we have ```..``` and ```.``` in our file path, it's not clear where we are going to end up. 148 | 149 | * ```..``` means to go back one directory 150 | * ```.``` means to stay in the same directory 151 | * we can have more then one ```/``` between the directories - ```/home//code``` 152 | 153 | So for example : ```/home//radorado/code/./hackbulgaria/week0/../``` reduces to ```/home/radorado/code/hackbulgaria``` 154 | 155 | 156 | Implement a function, called ```reduce_file_path(path)``` which takes a string and returns the reduced version of the path. 157 | 158 | * Every ```..``` means that we have to go one directory back 159 | * Every ```.``` means that we are staying in the same directory 160 | * Every extra ```/``` is unnecessary 161 | * Always remove the last ```/``` 162 | 163 | ### Signature 164 | 165 | ```python 166 | def reduce_file_path(path): 167 | # Implementation 168 | ``` 169 | 170 | ### Test examples 171 | ``` 172 | >>> reduce_file_path("/") 173 | "/" 174 | >>> reduce_file_path("/srv/../") 175 | "/" 176 | >>> reduce_file_path("/srv/www/htdocs/wtf/") 177 | "/srv/www/htdocs/wtf" 178 | >>> reduce_file_path("/srv/www/htdocs/wtf") 179 | "/srv/www/htdocs/wtf" 180 | >>> reduce_file_path("/srv/./././././") 181 | "/srv" 182 | >>> reduce_file_path("/etc//wtf/") 183 | "/etc/wtf" 184 | >>> reduce_file_path("/etc/../etc/../etc/../") 185 | "/" 186 | >>> reduce_file_path("//////////////") 187 | "/" 188 | >>> reduce_file_path("/../") 189 | "/" 190 | ``` 191 | 192 | ## Problem 28 - Word from a^nb^n 193 | 194 | Implement a function, called ```is_an_bn(word)``` that checks if the given ```word``` is from the ```a^nb^n for n>=0``` language. Here, ```a^n``` means a to the power of n - __repeat the character "a" n times.__ 195 | 196 | Lets see few words from this language: 197 | 198 | * for ```n = 0```, this is the empty word ```""``` 199 | * for ```n = 1```, this is the word ```"ab"``` 200 | * for ```n = 2```, this is the word ```"aabb"``` 201 | * for ```n = 3```, this is the word ```"aaabbb"``` 202 | * and so on - first, you repeat the character "a" n times, and after this - repeat "b" n times 203 | 204 | The function should return True if the given ```word``` is from ```a^nb^n for n>=0" for some n. 205 | 206 | ### Signature 207 | 208 | ```python 209 | def is_an_bn(word): 210 | # Implementation 211 | ``` 212 | 213 | ### Test examples 214 | 215 | ``` 216 | >>> is_an_bn("") 217 | True 218 | >>> is_an_bn("rado") 219 | False 220 | >>> is_an_bn("aaabb") 221 | False 222 | >>> is_an_bn("aaabbb") 223 | True 224 | >>> is_an_bn("aabbaabb") 225 | False 226 | >>> is_an_bn("bbbaaa") 227 | False 228 | >>> is_an_bn("aaaaabbbbb") 229 | True 230 | ``` 231 | ## Problem 29 - Simplify fractions 232 | 233 | Implement a function, called ```simplify_fraction(fraction)``` that takes a tuple of the form ```(nominator, denominator)``` and simplifies the fraction. 234 | 235 | The function should return the fraction in it's irreducible form. 236 | 237 | For example, a fraction ```3/9``` can be reduced by dividing both the nominator and the denominator by 3. We end up with ```1/3``` which is irreducible. 238 | 239 | ### Signature 240 | 241 | ```python 242 | def simplify_fraction(fraction): 243 | # Implementation 244 | ``` 245 | 246 | ### Test examples 247 | 248 | ``` 249 | >>> simplify_fraction((3,9)) 250 | (1,3) 251 | >>> simplify_fraction((1,7)) 252 | (1,7) 253 | >>> simplify_fraction((4,10)) 254 | (2,5) 255 | >>> simplify_fraction((63,462)) 256 | (3,22) 257 | ``` 258 | 259 | ## Problem 30 - Sort array of fractions 260 | 261 | Implement a function, called ```sort_fractions(fractions)``` where ```fractions``` is a list of tuples of the form ```(nominator, denominator)```. 262 | 263 | Both the nominator and the denominator are integers. 264 | 265 | The function should return the list, sorted in increasing order. 266 | 267 | ### Signature 268 | 269 | ```python 270 | def sort_fractions(fractions): 271 | # Implementation 272 | ``` 273 | 274 | ### Test examples 275 | 276 | ``` 277 | >>> sort_fractions([(2, 3), (1, 2)]) 278 | [(1, 2), (2, 3)] 279 | >>> sort_fractions([(2, 3), (1, 2), (1, 3)]) 280 | [(1, 3), (1, 2), (2, 3)] 281 | >>> sort_fractions([(5, 6), (22, 78), (22, 7), (7, 8), (9, 6), (15, 32)]) 282 | [(22, 78), (15, 32), (5, 6), (7, 8), (9, 6), (22, 7)] 283 | ``` 284 | 285 | ## Problem 31 - Fibonacci lists 286 | 287 | Implement a function, called ```nth_fib_lists(listA, listB, n)``` which takes two list of integers as ```listA``` and ```listB``` and returns the n-th member of the fibonacci sequence, that is created by the following algorithm: 288 | 289 | * for n = 1, it's listA 290 | * for n = 2, it's listB 291 | * for n = 3, it's listA + listB where + is list concatenation 292 | * and so on, just like a fibonacci 293 | 294 | ### Signature 295 | 296 | ```python 297 | def nth_fib_lists(listA, listB, n) 298 | ``` 299 | 300 | ### Test examples 301 | 302 | ``` 303 | >>> nth_fib_lists([1], [2], 1) 304 | [1] 305 | >>> nth_fib_lists([1], [2], 2) 306 | [2] 307 | >>> nth_fib_lists([1, 2], [1, 3], 3) 308 | [1, 2, 1, 3] 309 | >>> nth_fib_lists([], [1, 2, 3], 4) 310 | [1, 2, 3, 1, 2, 3] 311 | >>> nth_fib_lists([], [], 100) 312 | [] 313 | ``` 314 | 315 | ## Problem 31.5 - Is member of nth fibonacci lists 316 | 317 | Implement a function, called ```member_of_nth_fib_lists(listA, listB, needle)``` which checks if ```needle``` is a part of the fibonacci sequence, created by ```listA``` and ```listB``` for the first two elements. 318 | 319 | ### Signature 320 | 321 | ```python 322 | def member_of_nth_fib_lists(listA, listB, needle): 323 | # Implement 324 | ``` 325 | 326 | ### Test examples 327 | 328 | ``` 329 | >>> member_of_nth_fib_lists([1, 2], [3, 4], [5, 6]) 330 | False 331 | >>> member_of_nth_fib_lists([1, 2], [3, 4], [1,2,3,4,3,4,1,2,3,4]) 332 | True 333 | >>> member_of_nth_fib_lists([7,11], [2], [7,11,2,2,7,11,2]) 334 | True 335 | >>> member_of_nth_fib_lists([7,11], [2], [11,7,2,2,7]) 336 | False 337 | ``` 338 | 339 | ## Problem 32 - Goldbach Conjecture 340 | 341 | Implement a function, called ```goldbach(n)``` which returns a list of tupples, that is the goldbach conjecture for the given number ```n``` 342 | 343 | The Goldbach Conjecture states: 344 | 345 | > Every even integer greater than 2 can be expressed as the sum of two primes. 346 | 347 | Keep in mind that there can be more than one combination of two primes, that sums up to the given number. 348 | 349 | __The result should be sorted by the first item in the tuple.__ 350 | 351 | For example: 352 | 353 | * ```4 = 2 + 2``` 354 | * ```6 = 3 + 3``` 355 | * ```8 = 3 + 5``` 356 | * ```10 = 3 + 7 = 5 + 5``` 357 | * ```100 = 3 + 97 = 11 + 89 = 17 + 83 = 29 + 71 = 41 + 59 = 47 + 53``` 358 | 359 | ### Signature 360 | 361 | ```python 362 | def goldbach(n): 363 | # Implementation 364 | ``` 365 | 366 | ### Test examples 367 | 368 | ``` 369 | >>> goldbach(4) 370 | [(2,2)] 371 | >>> goldbach(6) 372 | [(3,3)] 373 | >>> goldbach(8) 374 | [(3,5)] 375 | >>> goldbach(10) 376 | [(3,7), (5,5)] 377 | >>> goldbach(100) 378 | [(3, 97), (11, 89), (17, 83), (29, 71), (41, 59), (47, 53)] 379 | ``` 380 | 381 | ## Problem 33 - Magic Square 382 | 383 | Implement a function, called ```magic_square(matrix)``` that checks if the given array of arrays ```matrix``` is a magic square. 384 | 385 | A magic square is a square matrix, where the numbers in each row, and in each column, and the numbers in the forward and backward main diagonals, all add up to the same number. 386 | 387 | ### Signature 388 | 389 | ```python 390 | def magic_square(matrix): 391 | # Implementation 392 | ``` 393 | ### Test examples 394 | 395 | ``` 396 | >>> magic_square([[1,2,3], [4,5,6], [7,8,9]]) 397 | False 398 | >>> magic_square([[4,9,2], [3,5,7], [8,1,6]]) 399 | True 400 | >>> magic_square([[7,12,1,14], [2,13,8,11], [16,3,10,5], [9,6,15,4]]) 401 | True 402 | >>> magic_square([[23, 28, 21], [22, 24, 26], [27, 20, 25]]) 403 | True 404 | >>> magic_square([[16, 23, 17], [78, 32, 21], [17, 16, 15]]) 405 | False 406 | ``` 407 | 408 | ## Problem 34 - Is sudoku solved? 409 | 410 | Implement a function, called ```sudoku_solved(sudoku)```, that checks if the given ```sudoku``` matrix is solved correctly. 411 | 412 | ```sudoku``` is a 9x9 matrix of integers. 413 | 414 | A sudoku is solved correctly, if: 415 | 416 | * Each row contains the numbers from 1 do 9 without repeating a number 417 | * Each column contains the numbers from 1 to 9, without repeating a number 418 | * All 3x3 subgrids contains the numbers from 1 to 9, without repeating a number 419 | 420 | For better reference, check Wikipedia - http://en.wikipedia.org/wiki/Sudoku 421 | 422 | ### Signature 423 | 424 | ```python 425 | def sudoku_solved(sudoku): 426 | # Implementation 427 | ``` 428 | 429 | ### Test examples 430 | 431 | ``` 432 | >>> sudoku_solved([ 433 | [4, 5, 2, 3, 8, 9, 7, 1, 6], 434 | [3, 8, 7, 4, 6, 1, 2, 9, 5], 435 | [6, 1, 9, 2, 5, 7, 3, 4 ,8], 436 | [9, 3, 5, 1, 2, 6, 8, 7, 4], 437 | [7, 6, 4, 9, 3, 8, 5, 2, 1], 438 | [1, 2, 8, 5, 7, 4, 6, 3, 9], 439 | [5, 7, 1, 8, 9, 2, 4, 6, 3], 440 | [8, 9, 6, 7, 4, 3, 1, 5 ,2], 441 | [2, 4, 3, 6, 1, 5, 9, 8, 7] 442 | ]) 443 | True 444 | >>> sudoku_solved([ 445 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 446 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 447 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 448 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 449 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 450 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 451 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 452 | [1, 2, 3, 4, 5, 6, 7, 8, 9], 453 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 454 | ]) 455 | False 456 | ``` 457 | 458 | --- 459 | 460 | 461 | ## Bonus Round 1 - Division by zero 462 | 463 | Check this TopCoder problem - http://community.topcoder.com/stat?c=problem_statement&pm=12911&rd=15843 464 | 465 | Implement a Python program, that solves it. 466 | 467 | Here is a piece of code with unit tests, to cover the problem statement cases. It assumes that your file name is ```division_by_zero.py```. 468 | 469 | 470 | ```python 471 | # filename - division_by_zero_tests.py 472 | from division_by_zero import count_numbers 473 | import unittest 474 | 475 | 476 | class CountNumbersTest(unittest.TestCase): 477 | """Testing the 250 problem from TopCoder SRM 610 Round 1 Div 2""" 478 | def test_cases_from_problemt_statement(self): 479 | self.assertEqual(count_numbers([9, 2]), 3) 480 | self.assertEqual(count_numbers([8, 2]), 3) 481 | self.assertEqual(count_numbers([50]), 1) 482 | self.assertEqual(count_numbers([1, 5, 8, 30, 15, 4]), 11) 483 | self.assertEqual(count_numbers([1, 2, 4, 8, 16, 32, 64]), 7) 484 | self.assertEqual(count_numbers([6, 2, 18]), 7) 485 | 486 | if __name__ == '__main__': 487 | unittest.main() 488 | 489 | ``` 490 | 491 | To run the tests, simply type ```python division_by_zero_tests.py``` in your console. 492 | 493 | ## Bonus Round 2 - Magic String 494 | 495 | Check this TopCoder problem - http://community.topcoder.com/stat?c=problem_statement&pm=13004&rd=15842 496 | 497 | Implement a Python program, that solves it. 498 | 499 | Here is a piece of code with unit tests, to cover the problem statement cases. It assumes that your file name is ```magic_string.py```. 500 | 501 | ```python 502 | from magic_string import magic_string 503 | import unittest 504 | 505 | 506 | class MagicStringTest(unittest.TestCase): 507 | """Testing magic_string - 250 problem from TC SRM 609 Div 2""" 508 | def test_problem_statement_cases(self): 509 | self.assertEqual(2, magic_string(">><<><")) 510 | self.assertEqual(0, magic_string(">>>><<<<")) 511 | self.assertEqual(4, magic_string("<<>>")) 512 | self.assertEqual(20, 513 | magic_string( 514 | "<><<<>>>>><<>>>>><>><<<>><><><><<><<<<<><<>>><><><")) 515 | 516 | if __name__ == '__main__': 517 | unittest.main() 518 | 519 | ``` 520 | 521 | ## Bonus Round 3 - One Dimensional Robot 522 | 523 | Check this TopCoder problem - http://community.topcoder.com/stat?c=problem_statement&pm=13000&rd=15841 524 | 525 | Implement a Python program, that solves it. 526 | 527 | Here is a piece of code with unit tests, to cover the problem statement cases. It assumes that your file name is ```oned_robot.py```. 528 | 529 | ```python 530 | from oned_robot import final_position 531 | import unittest 532 | 533 | 534 | class OneDimensionalRobotTests(unittest.TestCase): 535 | """Testing the 250 problem from TC 608 SRM Div 250""" 536 | 537 | def test_final_position_problem_statement_cases(self): 538 | self.assertEqual(2, final_position("RRLRRLLR", 10, 10)) 539 | self.assertEqual(4, final_position("RRRRR", 3, 4)) 540 | self.assertEqual(-1, final_position("LLLLLLLLLLR", 2, 6)) 541 | self.assertEqual(20, final_position( 542 | "RRRRRRRLRRLRRRRRRRRRRRRLRLRRRRRRRRLRRRRRLRRRRRRRRR", 5, 20)) 543 | self.assertEqual(-30, final_position("RLRLLLLLLLRLLLRLLLLLLLLRLLLLLRLLLRRLLLLLRLLLLLRLLL", 34, 15)) 544 | 545 | if __name__ == '__main__': 546 | unittest.main() 547 | ``` 548 | -------------------------------------------------------------------------------- /week0/simple_problems3.md: -------------------------------------------------------------------------------- 1 | Problems with files :) 2 | 3 | Working with files in Python is easy. We are going to do a bunch of problems to illustrate the concept. 4 | 5 | But first, some API: 6 | 7 | __Reading files__: 8 | 9 | Lets have two files: 10 | 11 | * read.py - the python script 12 | * file.txt - the file we want to read 13 | 14 | ```python 15 | # read.py 16 | 17 | 18 | filename = "file.txt" 19 | file = open(filename, "r") # Here, "r" stands for open for reading 20 | 21 | # file is a file object 22 | # to get all the content: 23 | 24 | content = file.read() 25 | print(content) 26 | 27 | # when we are done, we close the file 28 | file.close() 29 | ``` 30 | 31 | If we want to iterate on every line, we can do the following: 32 | 33 | ```python 34 | for line in file: 35 | print(line) 36 | ``` 37 | 38 | Each line ends up with a special character, called line break - ```\n```. 39 | In order to get each line as an element of a list, you can do the following: 40 | 41 | ```python 42 | contents = file.read().split("\n") 43 | ``` 44 | 45 | __Writing files:__ 46 | 47 | Writing to files is just as easy: 48 | 49 | ```python 50 | # write.py 51 | 52 | 53 | filename = "file.txt" 54 | file = open(filename, "w") # Here, "w" stands for open for writing 55 | 56 | contents = ["Python is awesome.", "You should check it out!"] 57 | 58 | # Here, we are joining each element with a new line 59 | file.write("\n".join(contents)) 60 | 61 | # when we are done, we close the file 62 | file.close() 63 | ``` 64 | 65 | Now, if we run the following program: 66 | 67 | ```$ python write.py``` 68 | 69 | and check what's in ```file.txt```: 70 | 71 | ``` 72 | $ cat file.txt 73 | Python is awesome. 74 | You should check it out! 75 | ``` 76 | 77 | We will see our content! 78 | 79 | __File arguments:__ 80 | 81 | When we run our python scripts with ```$ python script.py```, ```script.py``` is an argument to the python program. 82 | 83 | We can do the same thing with our python scripts: 84 | 85 | ```$ python cat.py file.txt``` 86 | 87 | Here, for example, file.txt is an argument to the cat.py script. 88 | 89 | The simplest way to get your arguments is the following: 90 | 91 | ```python 92 | # argv.py 93 | import sys 94 | 95 | for arg in sys.argv: 96 | print(arg) 97 | ``` 98 | 99 | Now, if we run the following command on the shell, we will see the output 100 | 101 | ``` 102 | $ py argv.py hello.txt program.py script.py 103 | argv.py 104 | hello.txt 105 | program.py 106 | script.py 107 | ``` 108 | 109 | __IMPORTANT:__ The first argument is always the file name! 110 | 111 | 112 | ## Problem F1 - Implement the cat command - Print file contents 113 | 114 | In linux, there is a very useful command, called ```cat```: 115 | 116 | ``` 117 | $ cat file.txt 118 | This is some file 119 | And cat is printing it's contents 120 | ``` 121 | 122 | Implement a Python script, called ```cat.py``` that takes one argument - a filename and prints the contents of that file to the console. 123 | 124 | ### Boilerplate 125 | 126 | ```python 127 | # cat.py 128 | import sys 129 | 130 | 131 | def main(): 132 | pass 133 | 134 | if __name__ == '__main__': 135 | main() 136 | ``` 137 | 138 | ### Examples 139 | 140 | If we have ```file.txt``` in the same directory with ```cat.py```, and ```file.txt``` is with the following text: 141 | 142 | ``` 143 | Python is an awesome language! 144 | You should try it. 145 | ``` 146 | 147 | This is the result: 148 | ``` 149 | $ python cat.py file.txt 150 | Python is an awesome language! 151 | You should try it. 152 | ``` 153 | 154 | ## Problem F2 - Cat multiple file 155 | 156 | Implement a Python script, called ```cat2.py``` that takes multiple arguments - file names and prints the contents of all files to the console, in the order of the arguments. 157 | 158 | __The number of the files that are given as arguments is unknown.__ 159 | 160 | There should be a newline between every two files that are printed. 161 | 162 | ### Boilerplate 163 | 164 | ```python 165 | # cat2.py 166 | import sys 167 | 168 | 169 | def main(): 170 | pass 171 | 172 | if __name__ == '__main__': 173 | main() 174 | ``` 175 | 176 | ### Examples 177 | 178 | If we have two files - ```file1.txt``` and ```file2.txt``` in the same directory with ```cat2.py``` and: 179 | 180 | __file1.txt:__ 181 | ``` 182 | Python is an awesome language! 183 | You should try it. 184 | ``` 185 | 186 | __file2.txt:__ 187 | ``` 188 | Also, you can use Python at a lot of different places! 189 | ``` 190 | 191 | 192 | This is the result: 193 | ``` 194 | $ python cat2.py file1.txt file2.txt 195 | Python is an awesome language! 196 | You should try it. 197 | 198 | Also, you can use Python at a lot of different places! 199 | ``` 200 | 201 | ## Problem F3 - Generate file with random integers 202 | 203 | Implement a Python script, called ```generate_numbers.py``` that takes two arguments - a ```filename``` and an integer ```n```. 204 | 205 | The script should create a file with the ```filename``` and print ```n``` random integers, separated by ```" "``` 206 | 207 | For random integers, you can use: 208 | 209 | ```python 210 | from random import randint 211 | print(randint(1, 1000)) 212 | ``` 213 | 214 | ### Boilerplate 215 | 216 | ```python 217 | # generate_numbers.py 218 | import sys 219 | from random import randint 220 | 221 | 222 | def main(): 223 | pass 224 | 225 | if __name__ == '__main__': 226 | main() 227 | ``` 228 | 229 | ### Examples 230 | 231 | ``` 232 | $ python generate_numbers.py numbers.txt 100 233 | $ cat numbers.txt 234 | 612 453 555 922 120 840 173 98 994 461 392 739 982 598 610 205 13 604 304 591 830 313 534 47 945 26 975 338 204 51 299 611 699 712 544 868 2 80 472 101 396 744 950 561 378 553 777 248 53 900 209 817 546 12 920 219 38 483 176 566 719 196 240 638 812 630 315 209 774 768 505 268 358 39 783 78 94 293 187 661 743 89 768 549 106 837 687 992 422 30 897 174 844 148 88 472 808 598 341 749 235 | ``` 236 | 237 | ## Problem F4 - Sum integers from file 238 | 239 | Implement a Python script, called ```sum_numbers.py``` which takes one argument - a ```filename``` which has integers, separated by ```" "``` 240 | 241 | The script should print the sum of all integers in that file. 242 | 243 | ### Examples 244 | 245 | If we use the generated file from Problem 3: 246 | 247 | ``` 248 | $ python sum_numbers.py numbers.txt 249 | 47372 250 | ``` 251 | 252 | ## Problem F5 - Concatenate files into one 253 | 254 | Implement a Python script, called ```concat_files.py``` that takes multiple filenames as arguments. 255 | 256 | The script should concatenate all file contents into a single file, called ```MEGATRON``` (Capslock is by choice :D) 257 | 258 | Every time you run the script, do not delete the old contents of ```MEGATRON``` but append the new ones at the end of the file. 259 | 260 | ### Examples 261 | 262 | Again, let's have the following files: 263 | 264 | __file1.txt:__ 265 | ``` 266 | Python is an awesome language! 267 | You should try it. 268 | ``` 269 | 270 | __file2.txt:__ 271 | ``` 272 | Also, you can use Python at a lot of different places! 273 | ``` 274 | 275 | Running the script: 276 | 277 | ``` 278 | $ python concat_files.py file1.txt file2.txt 279 | $ cat MEGATRON 280 | Python is an awesome language! 281 | You should try it. 282 | 283 | Also, you can use Python at a lot of different places! 284 | $ python concat_files.py file1.txt file2.txt 285 | $ cat MEGATRON 286 | Python is an awesome language! 287 | You should try it. 288 | 289 | Also, you can use Python at a lot of different places! 290 | 291 | Python is an awesome language! 292 | You should try it. 293 | 294 | Also, you can use Python at a lot of different places! 295 | ``` 296 | 297 | ## Problem F6 - Count characters, words or lines 298 | 299 | Implement a Python script, called ```wc.py``` that takes two arguments: 300 | 301 | * A command, that can be one of the following : ```chars```, ```words```, ```lines``` 302 | * A filename 303 | 304 | The script should output, according to the command, the following: 305 | 306 | * For the command ```chars```, the number of characters in the file 307 | * For the command ```words```, the number of words in the file 308 | * For the command ```lines```, the number of lines in the file 309 | 310 | 311 | ### Examples 312 | 313 | Lets have the following text: 314 | 315 | __story.txt:__ 316 | 317 | ``` 318 | Now indulgence dissimilar for his thoroughly has terminated. Agreement offending commanded my an. Change wholly say why eldest period. Are projection put celebrated particular unreserved joy unsatiable its. In then dare good am rose bred or. On am in nearer square wanted. 319 | 320 | Of resolve to gravity thought my prepare chamber so. Unsatiable entreaties collecting may sympathize nay interested instrument. If continue building numerous of at relation in margaret. Lasted engage roused mother an am at. Other early while if by do to. Missed living excuse as be. Cause heard fat above first shall for. My smiling to he removal weather on anxious. 321 | 322 | Ferrars all spirits his imagine effects amongst neither. It bachelor cheerful of mistaken. Tore has sons put upon wife use bred seen. Its dissimilar invitation ten has discretion unreserved. Had you him humoured jointure ask expenses learning. Blush on in jokes sense do do. Brother hundred he assured reached on up no. On am nearer missed lovers. To it mother extent temper figure better. 323 | 324 | ``` 325 | 326 | __Print the chars:__ 327 | 328 | ``` 329 | $ python wc.py chars story.txt 330 | 1032 331 | ``` 332 | 333 | __Print the words:__ 334 | 335 | ``` 336 | $ python wc.py words story.txt 337 | 166 338 | ``` 339 | 340 | __Print the lines:__ 341 | 342 | ``` 343 | $ python wc.py lines story.txt 344 | 6 345 | ``` 346 | 347 | ## Problem F7 - Pizza delivery 348 | 349 | Implement a Python program, called ```pizza.py``` that will help us organize the pizza ordering! 350 | 351 | We are going to implement a program, that waits for commands from the user and acts on them. Some commands may affect other commands and the program loops forever, until ```finish``` command is issued. 352 | 353 | We are going to take input in Python like that: 354 | 355 | ```python 356 | command = input("Enter command>") 357 | ``` 358 | 359 | Here is an example start of the program: 360 | 361 | ``` 362 | $ python pizza.py 363 | Enter command> 364 | ``` 365 | 366 | The user can enter one of the following commands: 367 | 368 | * ```take ``` 369 | * ```status``` 370 | * ```save``` 371 | * ```list``` 372 | * ```load ``` 373 | * ```finish``` 374 | 375 | Now let's go through each of the commands: 376 | 377 | __take __ 378 | 379 | The take commands followed by a name and a price, adds the given Person with the given prices to the current order. 380 | 381 | One person can take many things, adding up to the total price for him. 382 | 383 | For example: 384 | 385 | ``` 386 | Enter command>take Rado 10.0 387 | Taking order from Rado for 10.00 388 | Enter command>take Rado 10 389 | Taking order from Rado for 10.00 390 | Enter command>take Ivan 6.43 391 | Taking order from Ivan for 6.43 392 | Enter command>take Maria 7.50 393 | Taking order from Maria for 7.50 394 | Enter command> 395 | ``` 396 | 397 | __status:__ 398 | 399 | The status command prints the current status of the order in the following format for each person: 400 | 401 | ``` 402 | Person - Total Sum 403 | ``` 404 | 405 | For example: 406 | 407 | ``` 408 | Enter command>take Rado 10.0 409 | Taking order from Rado for 10.00 410 | Enter command>take Rado 10 411 | Taking order from Rado for 10.00 412 | Enter command>take Ivan 6.43 413 | Taking order from Ivan for 6.43 414 | Enter command>take Maria 7.50 415 | Taking order from Maria for 7.50 416 | Enter command>status 417 | Rado - 20.00 418 | Ivan - 6.43 419 | Maria - 7.50 420 | Enter command> 421 | ``` 422 | 423 | __save:__ 424 | 425 | We should be able to save the current order in a file! 426 | 427 | When we issue the ```save``` command, the script should do the following: 428 | 429 | * Create a timestamped file, named ```orders_YYYY_mm_dd_hh_mm_ss``` where ```YYYY``` is the current year, ```mm``` the current month, ```dd``` the current day, ```hh``` the current hour, ```mm``` the current minutes and ```ss``` the current seconds. 430 | 431 | You can achieve the timestamp with the following code: 432 | 433 | ```python 434 | from time import time 435 | from datetime import datetime 436 | 437 | ts = time() 438 | stamp = datetime.fromtimestamp(ts).strftime('%Y_%m_%d_%H_%M_%S') 439 | ``` 440 | 441 | * Save the current order in that file. The formating of the current order should be the same formatting, when printing it with the ```status``` command. 442 | 443 | * After the file is saved, keep taking orders (Don't quit the program) 444 | 445 | __list:__ 446 | 447 | The list command shows all files with saved orders in the current directory. 448 | 449 | When displaying them, it adds unique number to each file, starting from one. This number will be used in the ```load``` command. 450 | 451 | See this example, where we do two saves and call ```list``` after that: 452 | 453 | ``` 454 | Enter command>take Ivan 10 455 | Taking order from Ivan for 10.00 456 | Enter command>take Maria 5.50 457 | Taking order from Maria for 5.50 458 | Enter command>take Rado 6.10 459 | Taking order from Rado for 6.10 460 | Enter command>save 461 | Saved the current order to orders_2014_03_01_11_00_00 462 | Enter command>take Maria 10.50 463 | Taking order from Maria for 10.50 464 | Enter command>save 465 | Saved the current order to orders_2014_03_01_11_00_08 466 | Enter command>list 467 | [1] - orders_2014_03_01_11_00_08 468 | [2] - orders_2014_03_01_11_00_00 469 | Enter command> 470 | 471 | ``` 472 | 473 | __load :__ 474 | 475 | The load command discards the current order and loads a saved one from a file. A second argument, a number, is given. This is the unique number for the file, showed in the ```list``` command. 476 | 477 | The algorithm for load is: 478 | 479 | * If you call ```load``` before ```list```, a message is displayed : ```Use list command before loading``` 480 | * If you load a file and the current order is not saved (Changes have been made after the last save or no save at all) - The program should display a warning message : 481 | 482 | ``` 483 | You have not saved the current order. 484 | If you wish to discard it, type load again. 485 | ``` 486 | * If you type ```load ``` again, the current order is discarded and it's replaced by the one saved in the file. You have to parse the file for that. 487 | 488 | * If you call ```load``` and you have an empty current order, you should load the file without a problem. 489 | 490 | __Check the examples:__ 491 | 492 | 493 | Loading without save: 494 | ``` 495 | Enter command>take Rado 10 496 | Taking order from Rado for 10.00 497 | Enter command>take Maria 5.50 498 | Taking order from Maria for 5.50 499 | Enter command>list 500 | [1] - orders_2014_03_01_11_00_08 501 | [2] - orders_2014_03_01_11_00_00 502 | Enter command>load 1 503 | You have unsaved order. 504 | If you wish to discard the current order, type load again 505 | Enter command> 506 | ``` 507 | 508 | 509 | Loading order: 510 | 511 | ``` 512 | $ cat orders_2014_03_01_11_00_00 513 | Maria - 5.50 514 | Ivan - 10.00 515 | Rado - 6.10% 516 | ``` 517 | 518 | ``` 519 | $ py pizza.py 520 | Enter command>list 521 | [1] - orders_2014_03_01_11_00_08 522 | [2] - orders_2014_03_01_11_00_00 523 | Enter command>load 2 524 | Loading orders_2014_03_01_11_00_00 525 | Enter command>status 526 | Maria - 5.50 527 | Rado - 6.10 528 | Ivan - 10.00 529 | Enter command> 530 | 531 | ``` 532 | 533 | __finish:__ 534 | 535 | The ```finish``` command is for exiting the program. 536 | 537 | Here is the algorithm for ```finish```: 538 | 539 | * If you type finish and you have unsaved changes, you will get the following message: 540 | 541 | ``` 542 | Enter command>finish 543 | You have not saved your order. 544 | If you wish to continue, type finish again. 545 | If you want to save your order, type save 546 | ``` 547 | 548 | * If you type finish again, the program will exit 549 | 550 | ``` 551 | Enter command>finish 552 | Finishing order. Goodbye! 553 | $ 554 | ``` 555 | 556 | __unknown command:__ 557 | 558 | If you type a command that is not supported by the program, print an error message. 559 | 560 | Something like this: 561 | 562 | ``` 563 | Enter command>OMG 564 | Unknown command! 565 | Try one of the following: 566 | take 567 | status 568 | save 569 | list 570 | load 571 | finish 572 | Enter command> 573 | 574 | ``` 575 | -------------------------------------------------------------------------------- /week1/git.md: -------------------------------------------------------------------------------- 1 | We are going to make our first steps in the Git and Github world. 2 | 3 | Before we step in, here are few very helpful link, that are going to guide us: 4 | 5 | * http://guides.github.com/ 6 | * http://scotch.io/bar-talk/git-cheat-sheet 7 | * http://stackoverflow.com/questions/7076164/terminology-used-by-git 8 | 9 | ## Step 0 - Terminology 10 | 11 | First, we have to get familiar with the terminology, used by Git. 12 | 13 | For a reference, you can check here - http://stackoverflow.com/questions/7076164/terminology-used-by-git 14 | 15 | ## Step 1 - Create yourself a Github account 16 | 17 | This is the easiest step. Open github.com and create yourself an account! 18 | 19 | ## Step 2 - Setup your local git 20 | 21 | __The most important thing is that we are not going to use the Git UI! Shell is how we play.__ 22 | 23 | For installing ```Git```, follow this link : http://git-scm.com/download/linux 24 | 25 | Or just type: 26 | 27 | ``` 28 | sudo apt-get intall git 29 | ``` 30 | 31 | After that, you have to follow the setup manual here - https://help.github.com/articles/set-up-git 32 | 33 | ## Step 3 - In the land of SSH 34 | 35 | This one is the most tricky part. You have to generate SSH keys and the public one to your Github profile. 36 | 37 | To get this done, follow the instructions here - https://help.github.com/articles/generating-ssh-keys 38 | 39 | Here, we are going to stop and explain how SSH works: 40 | 41 | ![SSH Diagram](http://images.devshed.com/wh/stories/sshprivatepublickey1.jpg) 42 | 43 | ## Step 4 - Create your first repository 44 | 45 | We are going to push the code we have written for week 0 in Github. This is going to be a good start! 46 | 47 | Create a repository, named something like ```HackBulgaria-Programming101``` in Github. 48 | 49 | __Do not click on__: ```Initialize this repository with a README```. We are going to see why in a second. 50 | 51 | ## Step 5 - Your repository is in Github. Now lets bring it local. 52 | 53 | Okay. 54 | 55 | First, create a folder somewhere on your hard drive, named just like your repository. 56 | 57 | You can use ```mkdir``` to do the trick: 58 | 59 | ``` 60 | $ mkdir HackBulgaria-Programming101 61 | $ cd HackBulgaria-Programming101/ 62 | ``` 63 | 64 | Now, we have to tell that this folder is a Git repository. We do that with the ```git init``` command: 65 | 66 | ``` 67 | $ git init 68 | Initialized empty Git repository in /home/radorado/code/hackbulgaria/TestGit/.git/ 69 | ``` 70 | 71 | That's it! No magic here. 72 | 73 | In order to make sure that the directory is a git repository, run the following command: 74 | 75 | ``` 76 | $ ls -la 77 | drwxrwxr-x 3 radorado radorado 4096 Mar 4 10:22 . 78 | drwxrwxr-x 10 radorado radorado 4096 Mar 4 10:22 .. 79 | drwxrwxr-x 7 radorado radorado 4096 Mar 4 10:22 .git 80 | ``` 81 | 82 | If you see the ```.git``` folder - everything is ok! 83 | 84 | Now, create a file, called ```README.md``` and add something to it. For example - ```I am using Git and I am feeling awesome!``` 85 | 86 | You can achieve that with the following commands: 87 | 88 | ``` 89 | $ touch README.md 90 | $ echo "I am using Git and I am feeling awesome" > README.md 91 | $ cat README.md 92 | I am using Git and I am feeling awesome 93 | ``` 94 | 95 | Now, we have to stop and look at this diagram: 96 | 97 | 98 | ![](http://i.stack.imgur.com/zLTpo.png) 99 | 100 | ![](http://progit.couchone.com/progit/_design/chacon/figures/18333fig0201-tn.png) 101 | 102 | Now, let's check the status with ```git status``` 103 | 104 | ``` 105 | $ git status 106 | # On branch master 107 | # 108 | # Initial commit 109 | # 110 | # Untracked files: 111 | # (use "git add ..." to include in what will be committed) 112 | # 113 | # README.md 114 | nothing added to commit but untracked files present (use "git add" to track) 115 | ``` 116 | 117 | We have to add ```README.md``` to the staged files with ```git add``` 118 | 119 | ``` 120 | $ git add README.md 121 | $ git status 122 | # On branch master 123 | # 124 | # Initial commit 125 | # 126 | # Changes to be committed: 127 | # (use "git rm --cached ..." to unstage) 128 | # 129 | # new file: README.md 130 | # 131 | ``` 132 | 133 | And commit it with ```git commit```. 134 | For now, we are going to use ```git commit``` with ```-m``` flag. 135 | 136 | ``` 137 | git commit -m 'Initial commit of README.md' 138 | [master (root-commit) f1faf78] Initial commit of README.md 139 | 1 file changed, 1 insertion(+) 140 | create mode 100644 README.md 141 | ``` 142 | 143 | ``` 144 | git status 145 | # On branch master 146 | nothing to commit (working directory clean) 147 | ``` 148 | 149 | Great! We have created our first commit and our first version of the file ```README.md``` 150 | 151 | 152 | ## Step 6 - What happened to Github? Bring the remote! 153 | 154 | So far, we have done things locally. Git can be used as a local repository and can work without Github! 155 | 156 | But if we want our code to live in the cloud, we have to connect with Github. 157 | This is achieved by adding a ```remote``` to our local git. 158 | 159 | If you run the following command, that lists all remotes, you will see nothing. 160 | 161 | ``` 162 | $ git remote -v 163 | ``` 164 | 165 | You have to add your remote. The algorithm is: 166 | 167 | ``` 168 | git remote add 169 | ``` 170 | 171 | So, for a user called ```RadoRado``` and a repo called ```TestGit```: 172 | 173 | ``` 174 | git remote add origin git@github.com:RadoRado/TestGit.git 175 | ``` 176 | 177 | Now if we check our remotes again: 178 | 179 | ``` 180 | $ git remote -v 181 | origin git@github.com:RadoRado/TestGit.git (fetch) 182 | origin git@github.com:RadoRado/TestGit.git (push) 183 | ``` 184 | 185 | All we have to do is make our first push! 186 | 187 | ``` 188 | $ git push -u origin master 189 | Counting objects: 3, done. 190 | Writing objects: 100% (3/3), 269 bytes, done. 191 | Total 3 (delta 0), reused 0 (delta 0) 192 | To git@github.com:RadoRado/TestGit.git 193 | * [new branch] master -> master 194 | Branch master set up to track remote branch master from origin. 195 | ``` 196 | 197 | The algorithm is the following: 198 | 199 | ``` 200 | git push 201 | ``` 202 | 203 | If we are pushing for the first time in the given repo, we use ```-u``` flag. 204 | -------------------------------------------------------------------------------- /week1/git_problems.md: -------------------------------------------------------------------------------- 1 | We are going to make baby steps with Git and Github. 2 | 3 | ## Problem 0 - README more 4 | 5 | Update your ```README.md``` file to be more descriptive of your repository. 6 | 7 | Here, ```.md``` stands for markdown, which is a language for formatting text. This very file is written in markdown. 8 | 9 | Try to format it, using this cheatsheet - https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet. 10 | 11 | Markdown is intuitive and you will soon feel it. 12 | 13 | ## Problem 1 - week0 to Github 14 | 15 | It's time to bring your solutions from week0 to Github! 16 | 17 | Organize your repository the following way: 18 | 19 | ``` 20 | /nth-fibonacci/solution.py 21 | /nth-fibonacci/tests.py 22 | ... 23 | ``` 24 | 25 | For each problem - create a folder with proper name. 26 | 27 | * ```solution.py``` is the file with the implemented function 28 | * ```tests.py``` is the file with unit tests for the given problem. Don't worry for the tests for now. 29 | 30 | Make sure you create one commit for each problem. We don't want all files in one commit! 31 | 32 | ## Problem 2 - Fork, Clone & Pull Request 33 | 34 | Fork the following repo: https://github.com/HackBulgaria/Programming101/. Look for the ```fork``` button in the upper-right corner. 35 | 36 | After this, clone it to your computer and start looking for typos and something that is not correct. 37 | 38 | You can even add a problem, if you want to! 39 | 40 | Commit & Push to the forked repo. And when you are ready - Open a pull request to the original repository! 41 | -------------------------------------------------------------------------------- /week2/bank.py: -------------------------------------------------------------------------------- 1 | class BankAccount(): 2 | """Docstring for our BankAccount""" 3 | def __init__(self): 4 | self.__balance = 0 5 | 6 | def get_balance(self): 7 | return self.__balance 8 | 9 | def deposit(self, amount): 10 | if amount < 0: 11 | return False 12 | 13 | self.__balance += amount 14 | 15 | return True 16 | 17 | def withdraw(self, amount): 18 | if self.__balance - amount < 0: 19 | return False 20 | 21 | self.__balance -= amount 22 | return True 23 | -------------------------------------------------------------------------------- /week2/bank_tests.py: -------------------------------------------------------------------------------- 1 | import bank 2 | import unittest 3 | 4 | 5 | class BankAccountTest(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.bank_account = bank.BankAccount() 9 | 10 | """Tests for BankAccount class""" 11 | def test_init_bank_account_object(self): 12 | self.assertEqual(0, self.bank_account.get_balance()) 13 | 14 | def test_deposit_money(self): 15 | result = self.bank_account.deposit(100) 16 | 17 | self.assertTrue(result) 18 | self.assertEqual(100, self.bank_account.get_balance()) 19 | 20 | def test_deposit_negative_amount_of_money(self): 21 | result = self.bank_account.deposit(-100) 22 | 23 | self.assertTrue(not result) 24 | self.assertEqual(0, self.bank_account.get_balance()) 25 | 26 | def test_withdraw_from_deposited_bank_account(self): 27 | self.bank_account.deposit(100) 28 | 29 | result = self.bank_account.withdraw(50) 30 | 31 | self.assertTrue(result) 32 | self.assertEqual(50, self.bank_account.get_balance()) 33 | 34 | def test_withdraw_from_bank_account_with_insufficient_funds(self): 35 | result = self.bank_account.withdraw(1000) 36 | 37 | self.assertTrue(not result) 38 | self.assertEqual(0, self.bank_account.get_balance()) 39 | 40 | if __name__ == '__main__': 41 | unittest.main() 42 | -------------------------------------------------------------------------------- /week2/csv2json.py: -------------------------------------------------------------------------------- 1 | import json 2 | from sys import argv 3 | from os.path import basename, splitext 4 | 5 | 6 | def csv_to_list(csv_data): 7 | csv_lines = csv_data.split("\n") 8 | header_fields = csv_lines[0].split(",") 9 | result = [] 10 | 11 | for i in range(1, len(csv_lines)): 12 | d = {} 13 | parts = csv_lines[i].split(",") 14 | parts_index = 0 15 | 16 | if len(parts) != len(header_fields): 17 | print(parts) 18 | continue 19 | 20 | for header in header_fields: 21 | d[header] = parts[parts_index] 22 | parts_index += 1 23 | 24 | result.append(d) 25 | 26 | return result 27 | 28 | 29 | def main(): 30 | csv_file = open(argv[1], "r") 31 | csv_list = csv_to_list(csv_file.read()) 32 | csv_file.close() 33 | 34 | json_filename = splitext(basename(argv[1]))[0] + ".json" 35 | json_file = open(json_filename, "w") 36 | json_file.write(json.dumps(csv_list, indent=4)) 37 | json_file.close() 38 | 39 | 40 | if __name__ == '__main__': 41 | main() 42 | -------------------------------------------------------------------------------- /week2/dnp.md: -------------------------------------------------------------------------------- 1 | # Dungeons and Pythons 2 | 3 | After building Python skills for 3 weeks, we are going to put everything that we know, into this epic game. 4 | 5 | __It is about dungeons and it is about Pythons!__ 6 | 7 | We are going to simulate a dungeon map, of multiple levels, a hero, that is brave enough to wander through the dungeons, and of course, Pythons, that wait at every corner, for something fresh to eat! 8 | 9 | We are going to build a console interface for our game too. 10 | 11 | And for all of this, we are going to stick to the TDD approach! 12 | 13 | So, enough words. Lets bring the game on! 14 | 15 | ## The Dungeon 16 | 17 | The Dungeon class is responsible for loading the map, taking care of the levels and moving the hero around the map. 18 | 19 | 20 | ### The Map 21 | 22 | Here is an example map: 23 | 24 | ``` 25 | S..P..........PPI############### 26 | .#########.##################### 27 | .....P.........................A 28 | .####.##.###.################### 29 | P####.##.###.#####.....######### 30 | .####.##.###.#####.###.##IIIIII# 31 | P####.I..###.#####.###.##P....I# 32 | .####P##P###.......###.##P###### 33 | P####.##.############.........A. 34 | I####A...######################G 35 | ``` 36 | 37 | Our maps are going to be represented as a matrix of strings, where each character, means something special: 38 | 39 | * ```S``` is the spawning point for our hero. There can be multiple spawning points 40 | * ```P``` is a Python - this is your enemy! Once you step into the Python, you start fighting with him 41 | * ```#``` is an obstacle - you cannot move through it 42 | * ```.``` is a walkable path 43 | * ```I``` is an item - it can be either a new weapon or a healing potion 44 | * ```A``` is a boss enemy - an Anaconda, ready to kick our ass 45 | * ```G``` is the Gateway, to the next level 46 | 47 | #### Levels 48 | 49 | Every game starts from level 1. If the hero reaches the gateway for the given level (Marked by ```G```), the game should load the next level. 50 | 51 | You can organize your maps in text files (In a folder called ```maps/```) where every file name is the following: ```level_1.txt```, ```level_2.txt``` etc. 52 | 53 | Once you are done with level 1, you load the map for level 2 and spawn the hero there! 54 | 55 | __If there are no next levels, the game is over and the hero has won!__ 56 | 57 | #### Map mechanics 58 | 59 | * ```S``` is the spawning point for our hero. There can be multiple spawning points in our map. When spawning a hero, choose one point by random. __Every other Spawning point becomes a walkable path.__ 60 | * ```P``` is a Python - this is your enemy! If a hero steps into a Python, this triggers a fight. If our hero dies, the game is over. 61 | * ```I``` is an item - __it can be either a new weapon or a healing potion.__ When a hero steps into it, troll a dice and decide what it should be. Also, pick at random, the attributes of the chosen weapon or a potion. 62 | * ```A``` is a boss Anaconda. Same as the Python, but stronger. 63 | * ```G``` is the Gateway, to the next level - when a hero steps into this, the next level is loaded. 64 | 65 | 66 | ### Moving the hero around the map 67 | 68 | There should be methods, to spawn and move the hero around the map: 69 | 70 | #### spawn 71 | 72 | ```spawn(player_name, hero_instance)``` - spawns a player __at a random spawning point.__ 73 | 74 | ```player_name``` __is a unique string identifier for the player.__ We are going to use ```player_name``` for moving our hero around the map. 75 | 76 | __For example:__ 77 | 78 | ``` 79 | >>> map.spawn("player_1", some_hero_instance) 80 | >>> map.print_map() 81 | H..!..........PPI############### 82 | .#########.##################### 83 | .....P.........................! 84 | .####.##.###.################### 85 | P####.##.###.#####.....######### 86 | .####.##.###.#####.###.##IIIIII# 87 | P####.I..###.#####.###.##P....I# 88 | .####P##P###.......###.##P###### 89 | P####.##.############.........A. 90 | I####A...######################G 91 | ``` 92 | 93 | #### move 94 | 95 | Now, implemented a method ```move(player_name, direction)``` where: 96 | 97 | * ```player_name``` is the unique identifier for the player 98 | * ```direction``` is ```up```, ```down```, ```left``` and ```right``` 99 | 100 | This should move the given player in the desired direction. 101 | 102 | If you move into a special object - handle the case. 103 | 104 | __For example:__ 105 | ``` 106 | >>> map.move("player_1", "right") 107 | .H.!..........PPI############### 108 | .#########.##################### 109 | .....P.........................! 110 | .####.##.###.################### 111 | P####.##.###.#####.....######### 112 | .####.##.###.#####.###.##IIIIII# 113 | P####.I..###.#####.###.##P....I# 114 | .####P##P###.......###.##P###### 115 | P####.##.############.........A. 116 | I####A...######################G 117 | ``` 118 | 119 | The method should return False, if the move is not possible (Outside the map or into an obstacle). Otherwise, True 120 | 121 | ## The Hero 122 | 123 | Our hero has the following attributes, that are taken as constructor arguments: 124 | 125 | * A ```name``` attribute 126 | * A ```health``` attribute 127 | * A ```nicknamе``` attribute 128 | 129 | The given ```health``` is the maximum health our hero can have. He cannot be over-healed! 130 | 131 | 132 | Once created, our hero starts with 10 damage, that he can do unarmed! 133 | 134 | __Our hero always starts unarmed, until he finds a better weapon for fighting.__ 135 | 136 | The methods for our hero are: 137 | 138 | ### known_as() method 139 | 140 | Add a ```known_as()``` method to our Hero, which returns a string, formatted in the following way: 141 | ```hero_name the hero_nickname``` 142 | 143 | __For example:__ 144 | 145 | ``` 146 | >>> h.known_as() 147 | Bron the DragonSlayer 148 | ``` 149 | 150 | ### get_health() 151 | 152 | Every hero starts with the given ```health```. 153 | 154 | __This ```health``` is the maximum health for the hero!__ 155 | 156 | When a hero reaches 0 ```health``` he is considered death. 157 | 158 | Add this attribute to our hero and implement the following methods: 159 | 160 | * ```is_alive()``` which returns True, if our hero is still alive. Otherwise - False. 161 | * ```get_health()``` which returns the current health 162 | 163 | ### take_damage(damage_points) 164 | 165 | Our hero can take damage which reduces his health. 166 | 167 | Implement a method, called ```take_damage(damage_points)``` where damage can be either integer or floating point value. 168 | 169 | This method should reduce the hero's health by ```damage``` 170 | 171 | __If we inflict more damage than we have health, health will always be equal to zero and we cannot get below that!__ 172 | 173 | ### take_healing(healing_potion) 174 | 175 | Our hero can also be healed! 176 | 177 | Implement a method, called ```take_healing(healing_potion)``` which heals our hero. 178 | 179 | The healing is done by using a special healing potion, with has an attribute - healing points, by which, our hero is healed. 180 | 181 | Here are the requirements: 182 | 183 | * If our hero is dead, the method should return False. It's too late to heal our hero 184 | * We cannot heal our hero above the maximum health, which is given by ```health``` 185 | * If healing is successful (Our hero is not dead), the method should return True 186 | 187 | ### equip_weapon(weapon) 188 | 189 | Our hero can find a weapon, while wandering around the dungeon. He should be able to equip that weapon! 190 | 191 | Once equipped, the weapon makes our hero stronger. 192 | 193 | ### has_weapon() 194 | 195 | Implement the following method for our hero - ```has_weapon()``` - return True, if the given Hero is already equipped with a weapon. Otherwise, False. 196 | 197 | ### attack() 198 | 199 | The main method for our hero! The method for attacking. 200 | 201 | This method should return the damaged, that is going to be outputted by our hero. 202 | 203 | Take into consideration: 204 | 205 | * If our hero is armed or unarmed 206 | * If the weapon is going to make a critical strike 207 | 208 | ## The Weapon 209 | 210 | The tool for war. 211 | 212 | Create a class ```Weapon```, which takes the following attributes as a constructor: 213 | 214 | * ```type``` - a string that represents the weapon. Imagine an axe! 215 | * ```damage``` - how many health points you are going to take away, if you hit! 216 | * ```critical_strike_percent``` - a floating number between 0 and 1 that gives the chance for double damage! 217 | 218 | Implement a method, called ```critical_hit()``` which returns True, if the weapon, striking now, should do 2x times the damage, according to the ```critical_strike_percent```. 219 | 220 | Otherwise, False 221 | 222 | __Example__: 223 | 224 | ``` 225 | >>> from weapon import Weapon 226 | >>> axe = Weapon("Mighty Axe", 25, 0.2) 227 | ``` 228 | 229 | ### Finding weapon on the map 230 | 231 | If you find a weapon on the map, and that weapon is better, our hero equips it automatically. 232 | 233 | __A weapon is better if it has both better critical strike chance and better damage.__ 234 | 235 | ## The Potion 236 | 237 | We are going to have really simple healing potions. 238 | 239 | The class for them is going to have only one attribute - ```healing_points``` - a number, by which our hero can be healed. 240 | 241 | If our hero finds a healing potion, he automatically drinks it! This item cannot be used during fight. 242 | 243 | ## The Python 244 | 245 | This is our Enemy. A big fat snake, that is going to bite us to death! 246 | 247 | Implement a Python class with the following attributes, given to the constructor: 248 | 249 | * ```health``` - This is the health of the Python 250 | * ```damage``` - This is the damage points, that the Python is doing when he is attacking. 251 | 252 | Implement (or inherit) the following methods, that are doing the same, as in our hero class. 253 | 254 | * ```take_damage(damage_points)``` 255 | * ```is_alive()``` 256 | * ```attack()``` 257 | 258 | When our Hero steps into a Python, we should start a Fight! 259 | 260 | ## The Boss - An Anaconda 261 | 262 | Bosses are stronger Pythons. They are just like the regular python, but with one extra twist: 263 | 264 | They accept a third argument, which is a ```berserk_tuple``` of the form ```(4, 1.5)``` 265 | 266 | The mechanics of the ```berserk_tuple``` is the following: 267 | 268 | * The first element is the number of attacks the boss have to make in order to, 269 | * Make a stronger attack, multiplied by the factor (The second tuple element) 270 | 271 | This means - every 4th attack, the Boss goes berserk and does more damage (Multiplied by the factor) 272 | 273 | __For example:__ 274 | 275 | Lets have a Boss with __10 damage__ and __berserk tuple - (4, 1.5)__ 276 | 277 | * Attack 1 - he makes 10 damage 278 | * Attack 2 - he makes 10 damage 279 | * Attack 3 - he makes 10 damage 280 | * Attack 4 - he makes 10 * 1.5 damage 281 | * Attack 5 - he makes 10 damage 282 | * ... 283 | * Attack 8 - he makes 10 * 1.5 damage 284 | * etc. 285 | 286 | ### Mechanics of a Boss Fight 287 | 288 | If we kill a boss, the following things happen: 289 | 290 | * Our maximum health is increased by 10% 291 | * Our weapon damage is increased by 5% 292 | * Our critical strike chance is increased by 3% 293 | * The hero is healed to his new maximum health 294 | 295 | ## The Fight 296 | 297 | Implement a ```Fight``` class, that simulates a fight between a Hero and an Enemy. 298 | The enemy can be a Python or a Boss! 299 | 300 | The class takes two arguments in the constructor: 301 | 302 | * A ```Hero``` instance 303 | * A ```Enemy``` instance 304 | 305 | The ```Fight``` class flips a coin and decides which will attack first. (Imagine, take random between 0 and 100. If it is < 50, hero attacks first, else enemy attacks first) 306 | 307 | Implement a ```simulate_fight()``` method, which starts a fight between the hero and the enemy, and prints out the status of the battle to the console. 308 | 309 | The fight ends, when either the hero or the enemy dies! 310 | 311 | __The method should return the winner of the Fight__ 312 | -------------------------------------------------------------------------------- /week2/langs.csv: -------------------------------------------------------------------------------- 1 | Language,Type,Website 2 | Python,dynamic,http://python.org/ 3 | Ruby,dynamic,https://www.ruby-lang.org/en/ 4 | JavaScript (Node),dynamic,http://nodejs.org/ 5 | C++,Static Typed,http://www.cplusplus.com/ 6 | Java,Static Typed,http://www.cplusplus.com/ 7 | -------------------------------------------------------------------------------- /week2/langs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Website": "http://python.org/", 4 | "Type": "dynamic", 5 | "Language": "Python" 6 | }, 7 | { 8 | "Website": "https://www.ruby-lang.org/en/", 9 | "Type": "dynamic", 10 | "Language": "Ruby" 11 | }, 12 | { 13 | "Website": "http://nodejs.org/", 14 | "Type": "dynamic", 15 | "Language": "JavaScript (Node)" 16 | }, 17 | { 18 | "Website": "http://www.cplusplus.com/", 19 | "Type": "Static Typed", 20 | "Language": "C++" 21 | }, 22 | { 23 | "Website": "http://www.cplusplus.com/", 24 | "Type": "Static Typed", 25 | "Language": "Java" 26 | } 27 | ] -------------------------------------------------------------------------------- /week2/materials.md: -------------------------------------------------------------------------------- 1 | # Materials for week2 in Hack Bulgaria 2 | 3 | This is going to keep all materials and lectures, that are worth watching for week 2 4 | 5 | ## Unit Tests and Test Driven Development 6 | 7 | We are going to open with a very important topic - unit testing. 8 | 9 | We are going to show how to do it in Python and after this - meditate on the Test Driven Development approach. 10 | 11 | In order to get most of the exercise, we recommend you to watch the following videos: 12 | 13 | * [Test Driven Development - A Love Story](http://www.youtube.com/watch?v=nBtO1UOK9Hs) [English] - A 30 minutes presentation about TDD in real life. 14 | * [A lecture by Stefan Kanev about Unit Tests and TDD from VarnaCondf](http://www.youtube.com/watch?v=knQRUY9Fs5o) [Bulgarian] 15 | * Check the Python docs for the UnitTest class - http://docs.python.org/3/library/unittest.html 16 | * The Three Rules of TDD - http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd - by Uncle Bob 17 | 18 | This will give you the essential knowledge, to rock the problems during the live exercise! 19 | 20 | ## Object Oriented Programming in Python 3 21 | 22 | * Maybe the best source is the documetation - http://docs.python.org/3.3/tutorial/classes.html 23 | * Another pretty good tutorial, can be found here - http://anandology.com/python-practice-book/object_oriented_programming.html 24 | -------------------------------------------------------------------------------- /week2/problems.md: -------------------------------------------------------------------------------- 1 | We are going to solve problems and add tests to them. 2 | Also, we are going to test the Test-Driven-Development approach, where we add the tests first. 3 | 4 | To get you started, lets take a look at the following trivial calculator program: 5 | 6 | ```python 7 | def add(a, b): 8 | return a + b 9 | 10 | 11 | def minus(a, b): 12 | return a - b 13 | 14 | 15 | def multiply(a, b): 16 | return a * b 17 | 18 | 19 | def float_division(a, b): 20 | return a / b 21 | 22 | ``` 23 | 24 | If we want to test the program, we can do one of the following: 25 | 26 | 1. Fire up the console and start testing the functions 27 | 2. Implement an automated unit tests, that will handle the things for us 28 | 29 | Of course, the approach in 1) is the simplest. But it does not add up! Everytime, we want to make sure our program is running, we have to __manually__ test everything! And this is a lot of time waste. 30 | 31 | Usually, approach 2) is the way to go - implement an automated Unit Test, that checks our program for us. 32 | 33 | Here is a simple Unit Test for the ```calculator.py``` program: 34 | 35 | ```python 36 | import calculator 37 | import unittest 38 | 39 | 40 | class CalculatorTest(unittest.TestCase): 41 | """Testing the very simple and trivial calculator module""" 42 | def test_add(self): 43 | self.assertEqual(5 + 6, calculator.add(5, 6)) 44 | 45 | def test_minus(self): 46 | self.assertEqual(5 - 6, calculator.minus(5, 6)) 47 | 48 | def test_multiply(self): 49 | self.assertEqual(5 * 5, calculator.multiply(5, 5)) 50 | 51 | def test_float_division(self): 52 | self.assertEqual(5 / 2, calculator.float_division(5, 2)) 53 | 54 | 55 | if __name__ == '__main__': 56 | unittest.main() 57 | 58 | ``` 59 | 60 | If we have the following files: 61 | 62 | ``` 63 | $ ls 64 | calculator.py calculator_tests.py 65 | ``` 66 | 67 | __We can run the test like that:__ 68 | 69 | ``` 70 | $ python calculator_tests.py 71 | .... 72 | ---------------------------------------------------------------------- 73 | Ran 4 tests in 0.000s 74 | 75 | OK 76 | ``` 77 | 78 | ## Problem 0 - Get the things going! 79 | 80 | So, to do something very simple, go to ```calculator.py``` and add two more functions: 81 | 82 | * ```integer_division(a, b)``` 83 | * ```modulo(a, b)``` 84 | 85 | Add two more tests for the new functions and make sure everything is running! 86 | 87 | 88 | ## Problem 1 - String Utils 89 | 90 | Implement a Python module, called ```string_utils.py```, with the following functions: 91 | 92 | * ```lines(text)``` - Takes a String argument ```text``` and returns a list of strings, where each element is each line in ```text``` 93 | * ```unlines(elements)``` - Takes a list of Strings as argument and returns a String, where each element from ```elements``` is joined with new line 94 | * ```words(text)``` - Takes a String argument ```text``` and returns a list of Strings, where each element is a word from ```text``` 95 | * ```unwords(elements)``` - The reverse function of ```words```. Takes a list of strings and returns a single string, where each element is joined by a single whitespace - ```" "``` 96 | 97 | Create a Unit Test file, called ```string_utils_tests.py``` and write as many test cases as you can think of, for those 4 functions. 98 | 99 | ## Problem 2 - To tab or to space? 100 | 101 | ### tabs_to_spaces 102 | 103 | When you have your ```string_utils.py``` tested and running, add the following function: 104 | 105 | * ```tabs_to_spaces(str, one_tab_n_spaces=4)``` - which takes a string and replaces all tabs in it, with the amount of ```one_tab_n_spaces``` of spaces. The default is ```1 tab = 4 spaces```. 106 | 107 | Here, a tab in a string is represented by the special ```'\t'``` symbol 108 | 109 | ``` 110 | >>> a = " string" 111 | >>> a 112 | '\tstring' 113 | ``` 114 | 115 | ### spacify.py 116 | 117 | When you have that tested, implement a Python script, called ```spacify.py```, which takes a filename as the only argument and replaces all tabs in that file with 4 spaces. 118 | 119 | Use the ```string_utils.py``` module! 120 | 121 | __For example:__ 122 | 123 | ``` 124 | $ echo '\tasdasd' > test 125 | $ python spacify.py test 126 | $ python 127 | >>> f = open("test", "r") 128 | >>> c = f.read() 129 | >>> c.index('\t') 130 | Traceback (most recent call last): 131 | File "", line 1, in 132 | ValueError: substring not found 133 | ``` 134 | 135 | ## Problem 3 - Test spacify.py 136 | 137 | Now, when you have the program ready, implement a Unit Test, to test if ```spacify.py``` is working correctly. 138 | 139 | You may want to ```setUp``` and ```tearDown``` your test to prepare examples. 140 | 141 | If you want to run a shell command from python, you can use the following: 142 | 143 | ```python 144 | from subprocess import call 145 | 146 | result = call("python program.py", shell=True) 147 | # result will hold the exit status code. 0 means success 148 | ``` 149 | 150 | ## Problem 4 - Count files by extension 151 | 152 | Implement a program, called ```ext.py```, which takes one argument - a file extension and prints to the output, the number of files with the given extension, that are located in the current directory (From where we run ```ext.py```) 153 | 154 | For example: 155 | 156 | ``` 157 | $ ls 158 | ext.py solution.py tests.py omg.txt program.cpp folder/ 159 | $ python ext.py .py 160 | 3 161 | $ python ext.py py 162 | 0 163 | $ python ext.py .cpp 164 | 1 165 | ``` 166 | 167 | __Implement a Unit Test, testing ```ext.py```__ 168 | 169 | ## Problem 5 - Count files by extension, extended 170 | 171 | Alter the program ```ext.py``` so it takes two arguments: 172 | 173 | * the first - destination folder 174 | * the second - file extension 175 | 176 | The program prints the number of files with the given file extension, __located in the destination folder.__ 177 | 178 | __Alter the tests, so they handle the new case.__ 179 | 180 | Examples: 181 | 182 | ``` 183 | $ ls 184 | ext.py solution.py tests.py omg.txt program.cpp folder/ 185 | $ ls folder/ 186 | 1.py 2.py 3.py 4.py program2.cpp holy_moly.cpp 187 | $ python ext.py folder/ .py 188 | 4 189 | $ ls /home/user/code 190 | default.py solution.py tests.py requirements.txt package.json 191 | $ python ext.py /home/user/code .json 192 | 1 193 | ``` 194 | 195 | ## Problem 6 - A simple bank account 196 | 197 | __Using the TDD technique__, implement a python module, called ```bank.py``` which simulates a bank account. 198 | 199 | The module looks like that: 200 | 201 | ```python 202 | def get_balance(): 203 | pass 204 | 205 | 206 | def deposit_money(amount): 207 | pass 208 | 209 | 210 | def withdraw_money(amount): 211 | pass 212 | ``` 213 | 214 | __The module should follow these rules:__ 215 | 216 | * Every bank account starts empty, with a zero balance 217 | * You cannot set or deposit negative amount of money - return False if such attempt is made 218 | * Your balance cannot get under 0 - if you attempt to make such transaction, return False 219 | 220 | 221 | __Remember the rules of TDD:__ 222 | 223 | 1. Write a test that fails 224 | 2. Write code so you can pass the test 225 | 3. Refactor your code while running the tests after every refactoring 226 | 4. Repeat from 1) 227 | 228 | 229 | ## Problem 7 - A simple timer 230 | 231 | __Using the TDD technique__, implement a python module, called ```timer.py``` which simulates a simple timer. 232 | 233 | The module looks like that (Check the docstrings, for explanation): 234 | 235 | ```python 236 | def start_timer(seconds): 237 | ''' 238 | Initiates the timer for the given seconds 239 | If you start a started time, this should return False 240 | ''' 241 | pass 242 | 243 | 244 | def decrease_time(): 245 | ''' 246 | Decreases the timer by 1 second. 247 | Returns True, if the decreasing is successful 248 | If the timer is not started, return False 249 | If the time is over, return False 250 | ''' 251 | pass 252 | 253 | 254 | def is_timer_running(): 255 | ''' 256 | Returns True, if the timer is running 257 | and there are still seconds left (> 0), 258 | else False 259 | ''' 260 | pass 261 | 262 | 263 | def stop_timer(): 264 | ''' 265 | Stops a running timer 266 | If the timer is running, return True 267 | If the timer is not running, return False 268 | ''' 269 | pass 270 | 271 | 272 | def seconds_left(): 273 | ''' 274 | Returns how many seconds are left from the timer 275 | If the timer is not started or if if it's finished, return 0 276 | ''' 277 | pass 278 | 279 | ``` 280 | 281 | __Remember the rules of TDD:__ 282 | 283 | 1. Write a test that fails 284 | 2. Write code so you can pass the test 285 | 3. Refactor your code while running the tests after every refactoring 286 | 4. Repeat from 1) 287 | 288 | ## Problem 8 - A tic-tac-toe game 289 | 290 | __Using the TDD technique__, implement a python module, called ```tictactoe.py``` which simulates a tic-tac-toe game. 291 | 292 | For a reference of the rules, check here - http://en.wikipedia.org/wiki/Tic-tac-toe 293 | 294 | __This time, we are not going to give you any specification for the python module.__ 295 | 296 | Just some requirements: 297 | 298 | * The game should be playable by two players (We don't want AI for now) 299 | * Player 1 always starts first 300 | * The two players must always play one after another 301 | * There should be a way, to represent the board as a string and print it 302 | * The game should end, when one of the two players wins 303 | 304 | Think about the problem. What kind of functions are you going to need? 305 | Write tests first and see if this can help you! 306 | 307 | __Remember the rules of TDD:__ 308 | 309 | 1. Write a test that fails 310 | 2. Write code so you can pass the test 311 | 3. Refactor your code while running the tests after every refactoring 312 | 4. Repeat from 1) 313 | 314 | ## Problem 9 - CSV to JSON 315 | 316 | Implement a python program, called ```csv2json.py``` which takes one argument - a CSV (Comma-separated values) file. 317 | 318 | The program should create a new file, with the same name as the given argument, but with ```json``` extension and convert the CSV contents to JSON. 319 | 320 | ### What is CSV? 321 | 322 | CSV, or comma-separated values, is a way to represent a tabular data in a text file. 323 | 324 | Lets have the following table: 325 | 326 | | Language | Type | Website | 327 | | ------------- |:-------------:| -----:| 328 | | Python | dynamic | http://python.org/ | 329 | | Ruby | dynamic | https://www.ruby-lang.org/en/ | 330 | | JavaScript (Node) | dynamic | http://nodejs.org/ | 331 | | C++ | Static typed | http://www.cplusplus.com/ | 332 | | Java | Static typed | http://www.java.com/en/ | 333 | 334 | This table, can be represented as the following CSV file: 335 | 336 | ```csv 337 | Language,Type,Website 338 | Python,dynamic,http://python.org/ 339 | Ruby,dynamic,https://www.ruby-lang.org/en/ 340 | JavaScript (Node),dynamic,http://nodejs.org/ 341 | C++,Static Typed,http://www.cplusplus.com/ 342 | Java,Static Typed,http://www.cplusplus.com/ 343 | ``` 344 | 345 | As you can see, the first line of the CSV file represents the fields (headers) of the table. 346 | 347 | Every next line is a row from the table, where the data from each cell is separated by a comma. 348 | 349 | ### What is JSON? 350 | 351 | A file format, widely used in JavaScript application (And now, everywhere :D), that was developed by Douglas Crockford. 352 | 353 | You can find the specification here - http://json.org/ 354 | 355 | For the table from above, it can be represented like that in JSON: 356 | 357 | ```json 358 | [ 359 | { 360 | "Website": "http://python.org/", 361 | "Type": "dynamic", 362 | "Language": "Python" 363 | }, 364 | { 365 | "Website": "https://www.ruby-lang.org/en/", 366 | "Type": "dynamic", 367 | "Language": "Ruby" 368 | }, 369 | { 370 | "Website": "http://nodejs.org/", 371 | "Type": "dynamic", 372 | "Language": "JavaScript (Node)" 373 | }, 374 | { 375 | "Website": "http://www.cplusplus.com/", 376 | "Type": "Static Typed", 377 | "Language": "C++" 378 | }, 379 | { 380 | "Website": "http://www.cplusplus.com/", 381 | "Type": "Static Typed", 382 | "Language": "Java" 383 | } 384 | ] 385 | ``` 386 | 387 | ### Examples 388 | 389 | Lets have the following ```langs.csv``` file: 390 | 391 | ``` 392 | Language,Type,Website 393 | Python,dynamic,http://python.org/ 394 | Ruby,dynamic,https://www.ruby-lang.org/en/ 395 | JavaScript (Node),dynamic,http://nodejs.org/ 396 | C++,Static Typed,http://www.cplusplus.com/ 397 | Java,Static Typed,http://www.cplusplus.com/ 398 | 399 | ``` 400 | 401 | When we run our script: 402 | 403 | ``` 404 | $ python csv2json.py lang.csv 405 | ``` 406 | 407 | We get the ```lang.json``` file with the following contents: 408 | 409 | ```json 410 | [ 411 | { 412 | "Website": "http://python.org/", 413 | "Type": "dynamic", 414 | "Language": "Python" 415 | }, 416 | { 417 | "Website": "https://www.ruby-lang.org/en/", 418 | "Type": "dynamic", 419 | "Language": "Ruby" 420 | }, 421 | { 422 | "Website": "http://nodejs.org/", 423 | "Type": "dynamic", 424 | "Language": "JavaScript (Node)" 425 | }, 426 | { 427 | "Website": "http://www.cplusplus.com/", 428 | "Type": "Static Typed", 429 | "Language": "C++" 430 | }, 431 | { 432 | "Website": "http://www.cplusplus.com/", 433 | "Type": "Static Typed", 434 | "Language": "Java" 435 | } 436 | ] 437 | ``` 438 | 439 | ### Tests 440 | 441 | After you are done, how can you test the program? Write tests to cover all cases. 442 | -------------------------------------------------------------------------------- /week2/problems2.md: -------------------------------------------------------------------------------- 1 | We are going to do some basic OOP problems, mixed with unit testing and TDD! 2 | 3 | In order to skip the long explanation that can be showed with code, we will jump to the problems. 4 | 5 | __When implementing the classes, use the TDD technique!__ 6 | 7 | ## Problem 0 - A hero. 8 | 9 | So lets start with something simple. We are going to implement a hero class, step by step. 10 | 11 | Implement a class, that represents a hero. Our hero is going to start simple: 12 | 13 | * Our hero has a ```name``` attribute 14 | * Our hero has a ```health``` attribute 15 | * Our hero has a ```nicknamе``` attribute 16 | 17 | If our hero is implemented in ```hero.py```, here is a simple usage in the __python interpreter__: 18 | 19 | ``` 20 | >>> import hero 21 | >>> h = hero.Hero("Bron", 100, "DragonSlayer") 22 | >>> h 23 | 24 | >>> h.name 25 | 'Bron' 26 | >>> h.nickname 27 | 'DragonSlayer' 28 | >>> h.health 29 | 100 30 | ``` 31 | ### known_as() method 32 | 33 | Add a ```known_as()``` method to our Hero, which returns a string, formatted in the following way: 34 | ```hero_name the hero_nickname``` 35 | 36 | __For example:__ 37 | 38 | ``` 39 | >>> h.known_as() 40 | Bron the DragonSlayer 41 | ``` 42 | 43 | ### get_health() 44 | 45 | Every hero starts with the given ```health```. 46 | 47 | __This ```health``` is the maximum health for the hero!__ 48 | 49 | When a hero reaches 0 ```health``` he is considered death. 50 | 51 | Add this attribute to our hero and implement the following methods: 52 | 53 | * ```is_alive()``` which returns True, if our hero is still alive. Otherwise - False. 54 | * ```get_health()``` which returns the current health 55 | 56 | ### take_damage(damage_points) 57 | 58 | So, our hero can take damage which reduces his health. 59 | 60 | Implement a method, called ```take_damage(damage_points)``` where damage can be either integer or floating point value. 61 | 62 | This method should reduce the hero's health by ```damage``` 63 | 64 | __If we inflict more damage than we have health, health will always be equal to zero and we cannot get below that!__ 65 | 66 | ### take_healing(healing_points) 67 | 68 | Our hero can also be healed! 69 | 70 | Implement a method, called ```take_healing(healing_points)``` which heals our hero. 71 | 72 | Here are the requirements: 73 | 74 | * If our hero is dead, the method should return False. It's too late to heal our hero 75 | * We cannot heal our hero above the maximum health, which is given by ```health``` 76 | * If healing is successful (Our hero is not dead), the method should return True 77 | 78 | ## Problem 1 - The Orc. 79 | 80 | In order to have a world of heroes, we must have a world of villains and bad guys. 81 | 82 | But this time, we are going one level up - we are having orcs! 83 | 84 | In ```orc.py```, implement an Orc class with the following attributes, given to the constructor 85 | 86 | * ```name``` - every orc has a name! A terrifying name. 87 | * ```health``` - the starting health for the Orc. This is also his maximum health 88 | * ```berserk_factor``` - a floating point number between 1 and 2. This factor is used, when the Orc goes berserk! __The output damage from the orc is multipled by that factor.__ (If a factor, larger than 2 or smaller than 1 is given, it is bounded by the limit (1 or 2)) 89 | 90 | Just like our hero, the orc has the same methods for healing, damage and life status: 91 | 92 | * get_health() 93 | * is_alive() 94 | * take_damage(damage_points) 95 | * take_healing(healing_points) 96 | 97 | ## Problem 2 - The Entity? Time to do some refactoring 98 | 99 | If we take a look at our Hero and our Orc, they have few things in common: 100 | 101 | * name 102 | * health 103 | * get_health() 104 | * is_alive() 105 | * take_damage(damage_points) 106 | * take_healing(healing_points) 107 | 108 | The OOP approach can save us some of the code repetition by doing inheritance. 109 | 110 | Create a class, called ```Entity``` which shares all the common things between a hero and an orc. 111 | 112 | After this, refactor your code and tests, in order to inherit from that Entity and still maintain the specific methods for each class. 113 | 114 | If you want to call a superconstructor, you can do it like so: 115 | 116 | ```python 117 | from entity import Entity 118 | 119 | 120 | class Hero(Entity): 121 | def __init__(self, name, health, nickname): 122 | super().__init__(name, health) 123 | # ... 124 | ``` 125 | 126 | Refactor your tests as well, so everything can pass! 127 | 128 | ### Problem 3 - The Weapon! 129 | 130 | We are going to make an army of heroes and an army of orcs and make them fight! 131 | But first, we are going to need some weapons. 132 | 133 | Create a class ```Weapon```, which takes the following attributes as a constructor: 134 | 135 | * ```type``` - a string that represents the weapon. Imagine an axe! 136 | * ```damage``` - how many health points you are going to take away, if you hit! 137 | * ```critical_strike_percent``` - a floating number between 0 and 1 that gives the chance for double damage! 138 | 139 | Implement a method, called ```critical_hit()``` which returns True, if the weapon, striking now, should do 2x times the damage, according to the ```critical_strike_percent```. 140 | 141 | Otherwise, False 142 | 143 | __Example__: 144 | 145 | ``` 146 | >>> from weapon import Weapon 147 | >>> axe = Weapon("Mighty Axe", 25, 0.2) 148 | ``` 149 | 150 | ### Problem 4 - Equipping some weapons 151 | 152 | It's time to Equip our fighters. 153 | 154 | In the ```Entity``` class, add the following methods: 155 | 156 | * ```has_weapon()``` - return True, if the given Entity is already equipped with a weapon 157 | * ```equip_weapon(weapon)``` - equips the given weapon to the Entity. If you equip a new weapon, the old one is discarded. 158 | * ```attack()``` - return the damage, that the given Entity is doing. If the entity has no weapon, the damage is 0 159 | 160 | In the ```Orc``` class, __override__ the ```attack()``` method to take into account the ```berserk_factor``` ! 161 | 162 | ### Problem 5 - A class for Fighting 163 | 164 | Now, it is time to make them fight! 165 | 166 | Implement a class ```Fight``` which takes two arguments to the constructor: 167 | 168 | * A ```Hero``` instance 169 | * An ```Orc``` instance 170 | 171 | The ```Fight``` class flips a coin and decides which will attack first. (Imagine, take random between 0 and 100. If it is < 50, hero attacks first, else orc attacks first) 172 | 173 | Implement a ```simulate_fight()``` method, which starts a fight between the hero and the orc, and prints out the status of the battle to the console. 174 | 175 | The fight ends, when either the hero or the orc dies! 176 | 177 | ### Problem 6 - Inside the dungeon 178 | 179 | Now, when we can fight our Hero and our Orc, it's time to make an adventure! 180 | 181 | But first, lets go step by step. Implement a class ```Dungeon```, that loads a dungeon map from a text file. 182 | 183 | The file path should be given as the only argument to the constructor. 184 | 185 | A dungeon, looks something like this: 186 | 187 | ``` 188 | S.##...... 189 | #.##..###. 190 | #.###.###. 191 | #.....###. 192 | ###.#####S 193 | ``` 194 | 195 | Where: 196 | 197 | * ```S``` means a starting point - you can spawn there. 198 | * ```#``` is an obstacle - you cannot go there 199 | * ```.``` is a walkable path - you can follow the dots. 200 | 201 | For example, lets have a file, called ```basic_dungeon.txt``` which represents the dungeon above. 202 | 203 | We create new dungeon like this: 204 | 205 | ``` 206 | >>> from dungeon import Dungeon 207 | >>> map = Dungeon("basic_dungeon.txt") 208 | >>> map.print_map() 209 | S.##...... 210 | #.##..###. 211 | #.###.###. 212 | #.....###. 213 | ###.#####S 214 | ``` 215 | 216 | For now, implement a method, called ```print_map()``` which prints the map to the output, in a formatted way. 217 | 218 | 219 | ### Problem 7 - It's spawning time. 220 | 221 | So, we want to spawn heroes and orcs in our map. To do so, there are __spawning points__ in the map, marked with ```S``` 222 | 223 | ### spawn 224 | 225 | Implement a method, called ```spawn(player_name, entity)``` where: 226 | 227 | * ```player_name``` is a string, uniquely identifying the player 228 | * ```entity``` is either Orc or Hero 229 | 230 | This one takes the first free spawning point in the map and populates it with: 231 | 232 | * ```H``` if entity is a Hero 233 | * ```O``` if entitity is an Orc 234 | 235 | The first free spawning point is the one, that we get if we start from top-left and walk left. 236 | 237 | If the spawning is successful - return True. Otherwise (If there are no more spawning points, return False) 238 | 239 | 240 | So, if we have the map above, let's take the following example: 241 | 242 | ``` 243 | >>> map.spawn("player_1", some_hero_instance) 244 | True 245 | >>> map.print_map() 246 | H.##...... 247 | #.##..###. 248 | #.###.###. 249 | #.....###. 250 | ###.#####S 251 | >>> map.spawn("player_2", some_orc_instance) 252 | True 253 | >>> map.print_map() 254 | H.##...... 255 | #.##..###. 256 | #.###.###. 257 | #.....###. 258 | ###.#####O 259 | ``` 260 | 261 | ### move 262 | 263 | Now, implemented a method ```move(player_name, direction)``` where: 264 | 265 | * ```player_name``` is the unique identifier for the player 266 | * ```direction``` is ```up```, ```down```, ```left``` and ```right``` 267 | 268 | This should move the given player in the desired direction. 269 | 270 | __For example:__ 271 | 272 | ``` 273 | >>> map.move("player_1", "right") 274 | True 275 | >>> map.print_map() 276 | .H##...... 277 | #.##..###. 278 | #.###.###. 279 | #.....###. 280 | ###.#####O 281 | >>> map.move("player_1", "up") 282 | False 283 | >>> map.move("player_2", "up") 284 | True 285 | >>> map.print_map() 286 | .H##...... 287 | #.##..###. 288 | #.###.###. 289 | #.....###O 290 | ###.#####. 291 | ``` 292 | 293 | Here are the cases: 294 | 295 | * If you move into an obstacle, return False and don't make the move. 296 | * If you move outside the map - return False and don't make the move. 297 | * If you move into an enemy, create a new Fight and simulate it! 298 | 299 | ## Dungeons and Pythons -------------------------------------------------------------------------------- /week2/timer.py: -------------------------------------------------------------------------------- 1 | def start_timer(seconds): 2 | ''' 3 | Initiates the timer for the given seconds 4 | If you start a started time, this should return False 5 | ''' 6 | pass 7 | 8 | 9 | def decrease_time(): 10 | ''' 11 | Drecreases the timer by 1 second. 12 | Returns True, if the decreasing is successful 13 | If the timer is not started, return False 14 | If the time is over, return False 15 | ''' 16 | pass 17 | 18 | 19 | def is_timer_running(): 20 | ''' 21 | Returns True or False, if the timer is running 22 | and there are still seconds left (> 0) 23 | ''' 24 | pass 25 | 26 | 27 | def stop_timer(): 28 | ''' 29 | Stops a running timer 30 | If the timer is running, return True 31 | If the timer is not running, return False 32 | ''' 33 | pass 34 | 35 | 36 | def seconds_left(): 37 | ''' 38 | Returns how many seconds are left from the timer 39 | If the timer is not started or if if it's finished, return 0 40 | ''' 41 | pass 42 | -------------------------------------------------------------------------------- /week3/materials.md: -------------------------------------------------------------------------------- 1 | ## Github Multiplayer 2 | 3 | During this week, we are going to start using GitHub to do some team work. We call that - __GitHub Multiplayer__! 4 | 5 | We are going to talk about workflows. Git and GitHub are just tools that will help us be more productive, when working in teams. But if we don't have an established workflow - everything is going to be chaos! 6 | 7 | Here are few selected resources for Git and Github: 8 | 9 | * The most general one is here - https://help.github.com/articles/what-are-other-good-resources-for-learning-git-and-github 10 | * Pull Requests - This is GitHub related (Not a Git feature). Pull Requests are one of the best visual ways, to work in teams, without the pain - https://help.github.com/articles/using-pull-requests 11 | * The workflow, used by GitHub is called the ```GitHub Flow```. This workflow is visually described here - http://guides.github.com/overviews/flow/ 12 | 13 | 14 | ## GitHub Flow 15 | 16 | If the Visual guide for the flow was not enough for you, you can check a bigger article on that topic here - http://scottchacon.com/2011/08/31/github-flow.html 17 | 18 | To sum it up, the GitHub flow is: 19 | 20 | ### 1. Anything in the master branch is deployable 21 | 22 | We can translate this to : "The code in master should run all tests without failing". 23 | 24 | ### 2. Create descriptive branches off of master 25 | 26 | This means, __it's not a good idea to use your names as branch names!__ 27 | 28 | Be descriptive about what you are coding in that branch. 29 | 30 | ### 3. Push to named branches constantly 31 | 32 | This means - every time you do a local commit, you should do push. 33 | 34 | ``` 35 | $ git push origin your-branch-name 36 | ``` 37 | 38 | ### 4. Open a pull request at any time 39 | 40 | Even if you have 10 lines of code, you can open a pull request to master, in order to notify other developers that you are working on a feature. 41 | 42 | The pull request is updated automatically with every commit you push to your branch and __it's a perfect place, to start a discussion about your code!__ 43 | 44 | ### 5. Merge only after pull request review 45 | 46 | This means - don't merge code that was not checked by other programmers. Remember that our ```master``` should be stable! 47 | 48 | ### 6. Deploy immediately after review 49 | 50 | Once the code is in master, there should be a way to deploy the changes automatically. 51 | 52 | We will concern ourselves with deployment, later in this course. 53 | 54 | ## Other workflows 55 | 56 | There are other workflows, similar to the GitHub one: 57 | 58 | * Check here - https://www.atlassian.com/git/workflows 59 | * And here - http://nvie.com/posts/a-successful-git-branching-model/ 60 | -------------------------------------------------------------------------------- /week3/problems.md: -------------------------------------------------------------------------------- 1 | This is a problem, designed to be coded in teams, for GitHub multiplayer exercise! 2 | 3 | ## The Command Line Mailist 4 | 5 | We are going to create a command-line tool, that will help us manage different mailing lists! 6 | 7 | We are going to store information for our users (Name and Email) and put them into different mail lists. 8 | 9 | We are going to implement some operations like: 10 | 11 | * CRUD of Lists and People (Create Read Update Delete) 12 | * Checking if we have already added the given email to the given list 13 | * Searching in all lists for a given email 14 | * Merging two lists into a new one 15 | * Exporting a list into JSON format 16 | 17 | The idea is to think how you can approach this problem as a team and work together, using GitHub, as your main tool! 18 | 19 | We are not going to be clear on the specifications, so you will have a room for creativity and taking your own design decisions. 20 | 21 | __There are three important things:__ 22 | 23 | * Use OOP 24 | * Use TDD 25 | * Code in Python 3.* 26 | 27 | ### Basic interaction with the program 28 | 29 | So lets imagine, our program is in the file ```mail.py```. Lets run the program: 30 | 31 | ``` 32 | $ python mail.py 33 | Hello Stranger! This is a cutting-edge, console-based mail-list! 34 | Type help, to see a list of commands. 35 | >help 36 | Here is a full list of commands: 37 | * show_lists - Prints all lists to the screen. Each list is assigned with a unique identifier 38 | * show_list - Prints all people, one person at a line, that are subscribed for the list. The format is: - 39 | * add - Starts the procedure for adding a person to a mail list. The program prompts for name and email. 40 | * update_subscriber - updates the information for the given subscriber in the given list 41 | * remove_subscriber - Removes the given subscriber from the given list 42 | * create - Creates a new empty list, with the given name. 43 | * update - Updates the given list with a new name. 44 | * search_email - Performs a search into all lists to see if the given email is present. Shows all lists, where the email was found. 45 | * merge_lists - merges list1 and list2 into a new list, with the given name. 46 | * export - Exports the given list into JSON file, named just like the list. All white spaces are replaced by underscores. 47 | * exit - this will quit the program 48 | ``` 49 | 50 | ### Listing lists and Listing users 51 | 52 | Lets imagine we have two mailing lists - ```Hack Bulgaria``` and ```HackFMI``` 53 | Our program should do the following: 54 | 55 | ``` 56 | >show_lists 57 | [1] Hack Bulgaria 58 | [2] HackFMI 59 | ``` 60 | 61 | Now, if we want to check all subscribers for list 1: 62 | 63 | ``` 64 | >show_list 1 65 | [1] Radoslav Georgeiv - radorado@hackbulgaria.com 66 | [2] Ivaylo Bachvaroff - ivo@hackbulgaria.com 67 | [3] Daniel Taskoff - dani@dani.com 68 | [4] .. and so on .. 69 | ``` 70 | 71 | And all subscribers for list 2: 72 | 73 | ``` 74 | >show_list 2 75 | [1] Radoslav Georgiev - radorado@hackbulgaria.com 76 | ``` 77 | 78 | If we want to show a list, that is not created: 79 | 80 | ``` 81 | > show_list 3 82 | List with unique identifier 3 was not found! 83 | ``` 84 | 85 | ### Adding subscribers to a given list: 86 | 87 | Now, if we want to add more subscribers to the HackFMI list, we can do so: 88 | 89 | ``` 90 | >add 2 91 | name>Ivaylo Bachvaroff 92 | email>ivo@hackbulgaria.com 93 | Ivaylo Bachvaroff was added to HackFMI! 94 | >add 2 95 | name> RadoRado 96 | email>radorado@hackbulgaria.com 97 | A person with the given email is already in the list! 98 | >show_list 2 99 | [1] Radoslav Georgiev - radorado@hackbulgaria.com 100 | [2] Ivaylo Bachvaroff - ivo@hackbulgaria.com 101 | ``` 102 | 103 | ### Updating the information for a subscriber in a list 104 | 105 | We should be able to change the name and email fields for a given subscriber in a given list. 106 | 107 | This is achieved with the command ```update_subscriber ``` 108 | 109 | After running the command, the program should prompt you for new name and email. If we leave an empty string, it means the field stays the same. 110 | 111 | ``` 112 | >update_subscriber 2 1 113 | Updating: Radoslav Georgeiv - radorado@hackbulgaria.com 114 | Pres enter if you want the field to remain the same 115 | enter new name> 116 | enter new email>radorado@hackfmi.com 117 | Subscriber updated: Radoslav Georgiev - radorado@hackfmi.com 118 | >update_subscriber 3 1 119 | List with unique identifier <3> was not found. 120 | >update_subscriber 2 10 121 | Subscriber with identifider <10> was not found in the list 122 | ``` 123 | 124 | ### Removing subscribers from a given list: 125 | 126 | We should have a way to delete subscribers from a given list. 127 | 128 | This is done with the ```remove_subscriber ``` command: 129 | 130 | ``` 131 | >add 2 132 | name>Ivan Ivanov 133 | email>ivan@ivanov.com 134 | >show_list 2 135 | [1] Radoslav Georgiev - radorado@hackbulgaria.com 136 | [2] Ivaylo Bachvaroff - ivo@hackbulgaria.com 137 | [3] Ivan Ivanov - ivan@ivanov.com 138 | >remove_subscriber 2 3 139 | was removed from the list 140 | >remove_subscriber 2 3 141 | Subscriber with identifider <3> was not found in the list 142 | >remove subscriber 10 1 143 | List with unique identifier <10> was not found. 144 | ``` 145 | 146 | ### Creating lists 147 | 148 | This is very simple: 149 | 150 | ``` 151 | >create HackBulgaria - Java 152 | New list was created 153 | >show_lists 154 | [1] Hack Bulgaria 155 | [2] HackFMI 156 | [3] HackBulgaria - Java 157 | ``` 158 | 159 | If we try to create an existing list, we get an error: 160 | 161 | ``` 162 | >create Hack Bulgaria 163 | A list with name already exists! 164 | ``` 165 | 166 | 167 | ### Updating lists 168 | 169 | Of course, we should be able to change the name for a given list. 170 | 171 | This is achieved by the ```update ``` command. 172 | 173 | ``` 174 | >update 1 HackBulgaria 175 | Updated to . 176 | >show_lists 177 | [1] HackBulgaria 178 | [2] HackFMI 179 | [3] HackBulgaria - Java 180 | >update 1 Hack Bulgaria 181 | Updated to . 182 | >show_lists 183 | [1] Hack Bulgaria 184 | [2] HackFMI 185 | [3] HackBulgaria - Java 186 | >update 10 THE_LIST_OF_ODIN 187 | List with unique identifier <10> was not found. 188 | ``` 189 | 190 | ### Removing lists 191 | 192 | This is the most scary command. We should be able to delete an entire list with all records inside. 193 | 194 | The command is ```delete ```. 195 | The program should prompt a ```(Y/N)``` message, in order to prevent from accidental deleting of lists. (It happens!) 196 | 197 | ``` 198 | >delete 3 199 | Are you sure you want to delete ? 200 | (Y/N)>Y 201 | was deleted. 202 | >delete 3 203 | List with unique identifier <3> was not found. 204 | >delete 1 205 | Are you sure you want to delete ? 206 | (Y/N)>N 207 | You crazy bastard. Stop playing with fire! 208 | ``` 209 | 210 | ### Searching emails 211 | 212 | We are going to have functionality to search our lists for a given email. 213 | 214 | We want to find all lists, where the given email is occurring as a subscriber! 215 | 216 | ``` 217 | >search_email dani@dani.com 218 | was found in: 219 | [1] Hack Bulgaria 220 | >search_email ivo@hackbulgaria.com 221 | was found in: 222 | [1] Hack Bulgaria 223 | [2] HackFMI 224 | >search_email anton@antonov.com 225 | was not found in the current mailing lists. 226 | ``` 227 | 228 | ### Merging lists 229 | 230 | This one is a bit tricky - we should be able to merge to existing mail lists into a new one: 231 | 232 | ``` 233 | >merge_lists 1 2 HACK_LIST 234 | Merged lists and into 235 | >show_lists 236 | [1] Hack Bulgaria 237 | [2] HackFMI 238 | [3] HackBulgaria - Java 239 | [4] HACK_LIST 240 | >show_list 4 241 | [1] Radoslav Georgeiv - radorado@hackbulgaria.com 242 | [2] Ivaylo Bachvaroff - ivo@hackbulgaria.com 243 | [3] Daniel Taskoff - dani@dani.com 244 | [4] .. and so on .. 245 | ``` 246 | 247 | There should be no duplicate entries in the new lists (One email, repeating two times) 248 | 249 | ### Exporting lists into JSON file 250 | 251 | We should be able to export a list into a JSON file. 252 | 253 | ``` 254 | >export 1 255 | Exported to 256 | ``` 257 | 258 | If we take a look at our JSON file: 259 | 260 | ```json 261 | [ 262 | { 263 | "name" : "Radoslav Georgiev", 264 | "email" : "radorado@hackbulgaria.com" 265 | }, 266 | { 267 | "name" : "Ivaylo Bachvaroff", 268 | "email" : "ivo@hackbulgaria.com" 269 | }, 270 | { 271 | "name" : "Daniel Taskoff", 272 | "email" : "dani@dani.com" 273 | } 274 | ] 275 | ``` 276 | 277 | ### Importing lists from JSON files 278 | 279 | Lets have the following json file, named ```Fools_and_Fools.json```: 280 | 281 | ```json 282 | [ 283 | { 284 | "name" : "Radoslav Georgiev", 285 | "email" : "radorado@hackbulgaria.com" 286 | }, 287 | { 288 | "name" : "Ivaylo Bachvaroff", 289 | "email" : "ivo@hackbulgaria.com" 290 | } 291 | ] 292 | ``` 293 | 294 | If we import the file, we should create a new list, called ```Fools and Fools``` with the given records inside: 295 | 296 | ``` 297 | >import Fools_and_Fools.json 298 | Creating a new list, called Fools and Fools. 299 | Loading 2 subscribers to the list. 300 | >show_lists 301 | [1] Hack Bulgaria 302 | [2] HackFMI 303 | [3] HackBulgaria - Java 304 | [4] HACK_LIST 305 | [5] Fools and Fools 306 | >show_list 5 307 | [1] Radoslav Georgeiv - radorado@hackbulgaria.com 308 | [2] Ivaylo Bachvaroff - ivo@hackbulgaria.com 309 | ``` 310 | 311 | 312 | ### The program should be persistent 313 | 314 | Check this magic: 315 | 316 | ``` 317 | $ python mail.py 318 | Hello Stranger! This is a cutting-edge, console-based mail-list! 319 | Type help, to see a list of commands. 320 | >show_lists 321 | [1] Hack Bulgaria 322 | [2] HackFMI 323 | [3] HackBulgaria - Java 324 | [4] HACK_LIST 325 | >create Test for Persistence 326 | New list was created 327 | >exit 328 | $ python mail.py 329 | Hello Stranger! This is a cutting-edge, console-based mail-list! 330 | Type help, to see a list of commands. 331 | >show_lists 332 | [1] Hack Bulgaria 333 | [2] HackFMI 334 | [3] HackBulgaria - Java 335 | [4] HACK_LIST 336 | [5] Test for Persistence 337 | ``` 338 | 339 | We should be able to save everything! You can use files for storage. 340 | -------------------------------------------------------------------------------- /week4/cinema.md: -------------------------------------------------------------------------------- 1 | # Cinema Reservation System 2 | We are going to cinema! But the reservation systems are down and the cinema officials don't let people without reservations. So we offered to make them a new reservation system that will allow us to go and watch the newest **"Aliens came, we built transformers and destroyed them..."** movie. 3 | 4 | ## Problem 0 - The database 5 | No complex stuff here. Just a few simple tables: 6 | 7 | **Movies** 8 | 9 | | id | name | rating | 10 | | ------------- |:-------------| :---: | 11 | |1|The Hunger Games: Catching Fire |7.9| 12 | |2|Wreck-It Ralph|7.8| 13 | |3|Her|8.3| 14 | 15 | **Projections** 16 | 17 | | id | movie_id | type | date | time | 18 | | ---|----------|:----:| :--: | :--: | 19 | |1|1|3D|2014-04-01|19:10 20 | |2|1|2D|2014-04-01|19:00 21 | |3|1|4DX|2014-04-02|21:00 22 | |4|3|2D|2014-04-05|20:20 23 | |5|2|3D|2014-04-02|22:00 24 | |6|2|2D|2014-04-02|19:30 25 | 26 | **Reservations** 27 | 28 | | id | username | projection_id | row | col | 29 | | ---|----------|---------------|:----:|:---:| 30 | |1|RadoRado|1|2|1| 31 | |2|RadoRado|1|3|5| 32 | |3|RadoRado|1|7|8| 33 | |4|Ivo|3|1|1| 34 | |5|Ivo|3|1|2| 35 | |6|Mysterious|5|2|3| 36 | |7|Mysterious|5|2|4| 37 | 38 | **Things to note** 39 | * For each projection we assume the hall will be a 10x10 matrix. 40 | * All data presented here is just an example. If you want, you can make up your own (perhaps you are the creator of the aforementioned movie and want to include it). 41 | 42 | ## Problem 1 - The CLI (Command-Line Interface) 43 | We don't need no GUIs! A console with green text and transparent background is all a hacker requires. 44 | Implement a python script called ```magic_reservation_system.py``` that takes magic commands and casts an appropriate spell. Here's an ancient page of Merlin's Book: 45 | * On spell ```show_movies``` - print all movies ORDERed BY rating 46 | * On spell ```show_movie_projections []``` - print all projections of a given movie for the given date (date is optional). 47 | 1. ORDER the results BY date 48 | 2. For each projection, show the total number of spots available. 49 | 50 | * On spell ```make_reservation``` - It's showtime! 51 | 1. Make the hacker choose a name and number of tickets 52 | 2. Cast ```show_movies``` and make the hacker choose a movie by id 53 | 3. Cast ```show_movie_projections``` for the chosen `````` and make the hacker choose a projection 54 | * *If the available spots for a projection are less than the number of tickets needed, print an appropriate message and stay at step 3*; 55 | 4. Cast a spell to show all available spots for the chosen projection 56 | 5. For each number of tickets, make the hacker choose a tuple ```(row, col)```. Check for tuple validity (10x10 matrix) and availability (reservations table) 57 | 6. Cast a spell to show the info in an appropriate format. Then prompt for ```finalize``` spell 58 | 7. On ```finalize``` spell, save all the info and wish a happy cinema! 59 | 0. **At each step, allow for ```give_up``` spell to be cast. This...wait for it...waaaiit... gives up the reservation!!!** (Thanks, cap'n) 60 | 61 | * On spell ```cancel_reservation ``` - disintegrate given person's reservation (**NOTE**: reservations cannot be so easily removed, but this is a magical system, after all) 62 | * On spell ```exit``` - close Pandora's Box before it's too late. 63 | * On spell ```help``` - show a list of learned spells 64 | 65 | 66 | **Things to note** 67 | * Notice how you are required to reuse code (or you'll be one messy hacker!!!). 68 | * Try not to build everything in one place. 69 | * Make use of the following techniques (Merlin used them to destroy the Decepticons): **OOP, TDD, SQL**. 70 | 71 | 72 | 73 | ## Examples 74 | 75 | ### Show movies 76 | 77 | ``` 78 | > show_movies 79 | Current movies: 80 | [1] - The Hunger Games: Catching Fire (7.9) 81 | [2] - Wreck-It Ralph (7.8) 82 | [3] - Her (8.3) 83 | ``` 84 | 85 | ### Show movie projections ### 86 | 87 | ``` 88 | > show_movie_projections 2 89 | Projections for movie 'Wreck-It Ralph': 90 | [5] - 2014-04-02 19:30 (2D) 91 | [6] - 2014-04-02 22:00 (3D) 92 | > show_movie_projections 1 2014-04-01 93 | Projections for movie 'The Hunger Games: Catching Fire' on date 2014-04-01: 94 | [1] - 19:00 (3D) 95 | [2] - 19:10 (2D) 96 | ``` 97 | 98 | 99 | ### Make a reservation 100 | 101 | ``` 102 | > make_reservation 103 | Step 1 (User): Choose name>Tedi 104 | Step 1 (User): Choose number of tickets> 2 105 | Current movies: 106 | [1] - The Hunger Games: Catching Fire (7.9) 107 | [2] - Wreck-It Ralph (7.8) 108 | [3] - Her (8.3) 109 | Step 2 (Movie): Choose a movie> 2 110 | Projections for movie 'Wreck-It Ralph': 111 | [5] - 2014-04-02 19:30 (2D) - 98 spots available 112 | [6] - 2014-04-02 22:00 (3D) - 100 spots availabe 113 | Step 3 (Projection): Choose a projection> 5 114 | Available seats (marked with a dot): 115 | 1 2 3 4 5 6 7 8 9 10 116 | 1 . . . . . . . . . . 117 | 2 . . X X . . . . . . 118 | 3 . . . . . . . . . . 119 | 4 . . . . . . . . . . 120 | 5 . . . . . . . . . . 121 | 6 . . . . . . . . . . 122 | 7 . . . . . . . . . . 123 | 8 . . . . . . . . . . 124 | 9 . . . . . . . . . . 125 | 10 . . . . . . . . . . 126 | Step 4 (Seats): Choose seat 1> (2,3) 127 | This seat is already taken! 128 | Step 4 (Seats): Choose seat 1> (15, 16) 129 | Lol...NO! 130 | Step 4 (Seats): Choose seat 1> (7,8) 131 | Step 4 (Seats): Choose seat 2> (7,7) 132 | This is your reservation: 133 | Movie: Wreck-It Ralph (7.8) 134 | Date and Time: 2014-04-02 19:30 (2D) 135 | Seats: (7,7), (7.8) 136 | Step 5 (Confirm - type 'finalize') > finalize 137 | Thanks. 138 | ``` 139 | 140 | # DISCLAIMER 141 | The purpose of these tasks is to train your casting (programming) skills. 142 | The purpose of these tasks is NOT to abide by the rules (we = hackers). 143 | If you decide that something is not structured/explained/invented enough, feel free to discuss it with us! 144 | Happy hacking! 145 | -------------------------------------------------------------------------------- /week4/grocery_store.md: -------------------------------------------------------------------------------- 1 | ## Problem X.1 - Grocery store 2 | 3 | With the knowledge gained by completing the previous exercises, you must create a grocery store, 4 | using OOP, TDD and SQL. 5 | The store will have a sqlite database storing all products that are available for sale. 6 | 7 | ### Creating the database structure 8 | Create a script called ```create_store.py``` that creates the following table structure: 9 | 10 | | id | name | price_per_kg | quantity_in_kg | 11 | | ---|:-----:| ------ | -------- | 12 | 13 | 14 | 15 | ### Importing data 16 | The data will be imported from a json file into the database. 17 | 18 | ```json 19 | [ 20 | { 21 | "name": "Potatoes", 22 | "price_per_kg": 1.2, 23 | "quantity_in_kg": 20 24 | }, 25 | { 26 | "name": "Carrots", 27 | "price_per_kg": 1.1, 28 | "quantity_in_kg": 6 29 | }, 30 | { 31 | "name": "Cucumbers", 32 | "price_per_kg": 2.2, 33 | "quantity_in_kg": 3 34 | }, 35 | { 36 | "name": "Lettuce", 37 | "price_per_kg": 2.1, 38 | "quantity_in_kg": 7 39 | }, 40 | { 41 | "name": "Tomatoes", 42 | "price_per_kg": 2.7, 43 | "quantity_in_kg": 10 44 | } 45 | ] 46 | ``` 47 | 48 | The now populated database 49 | 50 | | id | name | price_per_kg | quantity_in_kg | 51 | | ---|:-----:| ------ | -------- | 52 | 1| Potatoes | 1.20 | 20 | 53 | 2| Carrots | 1.1 | 6 | 54 | 3| Cucumbers | 2.20 | 3 | 55 | 4| Lettuce | 2.10 | 7 | 56 | 5| Tomatoes| 2.70 | 10 | 57 | 58 | 59 | ### Creating a parser 60 | After we created a database and added our products for sale in it, 61 | we would want to have a script that takes the following commands: 62 | Create a script called ```manage_store.py``` that takes the following commands: 63 | 64 | __Commands:__ 65 | * ```import_json ``` - Adds products from the json file to the database. 66 | * ```list_products``` - Prints out all products in the following format: "[id] name - price_per_kg BGN - quantity kg" 67 | * ```add_product``` - Prompts for data, needed to add a new product to the database. 68 | * ```delete_product ``` - Removes the product matching the product id in the database. 69 | * ```update_product ``` - Updates the product data matching the product id in the database. 70 | * ```export_json ``` - Exports the database to a json file. 71 | 72 | __Example usage of commands:__ 73 | ``` 74 | command>import_json spring_sale_products.json 75 | Imported spring_sale_products 76 | ``` 77 | 78 | ``` 79 | command>list_products 80 | [1] Potatoes - 1.20 BGN - 20 kg 81 | [2] Carrots - 1.1 BGN - 6 kg 82 | [3] Cucumbers - 2.2 BGN - 3 kg 83 | [4] Lettuce - 2.1 BGN - 7 kg 84 | [5] Tomatoes - 2.7 BGN - 10 kg 85 | ``` 86 | 87 | ``` 88 | command>add_product 89 | product name>Apples 90 | price per kg>2 91 | quantity>2 92 | Added Apples to products. 93 | ``` 94 | 95 | ``` 96 | command>delete_product 5 97 | Tomatoes removed. 98 | ``` 99 | 100 | ``` 101 | command>update_product 2 102 | product name>Carrots 103 | price per kg>1.8 104 | quantity>5 105 | ``` 106 | 107 | ``` 108 | command>export_json 123.json 109 | Products were exported to 123.json 110 | ``` 111 | 112 | And this is exactly how the 123.json file looks: 113 | 114 | ```json 115 | [ 116 | { 117 | "name": "Potatoes", 118 | "price_per_kg": 1.2, 119 | "quantity_in_kg": 20 120 | }, 121 | { 122 | "name": "Carrots", 123 | "price_per_kg": 1.8, 124 | "quantity_in_kg": 5 125 | }, 126 | { 127 | "name": "Cucumbers", 128 | "price_per_kg": 2.2, 129 | "quantity_in_kg": 3 130 | }, 131 | { 132 | "name": "Lettuce", 133 | "price_per_kg": 2.1, 134 | "quantity_in_kg": 7 135 | }, 136 | { 137 | "name": "Apples", 138 | "price_per_kg": 2, 139 | "quantity_in_kg": 2 140 | } 141 | ] 142 | ``` 143 | 144 | ## Problem X.2 - Grocery store customers 145 | Now that we have a good base, let's extend it with customers. 146 | 147 | 148 | ### Adding a new table 149 | Modify the ``create_database.py`` script and have it add another table for customers. 150 | The customers table should have the following structure: 151 | 152 | | id | name | kg_bought | money_spent | 153 | | ---|:-----:| ------ | -------- | 154 | 155 | 156 | ### Import customers data 157 | Again, the data will be imported from a json. File name is spring_customers.json 158 | 159 | ```json 160 | [ 161 | { 162 | "name": "Ivo", 163 | "kg_bought": 5.1, 164 | "money_spent": 17.7 165 | }, 166 | { 167 | "name": "Rado", 168 | "kg_bought": 3.2, 169 | "money_spent": 12.2 170 | } 171 | ] 172 | ``` 173 | 174 | 175 | The populated database with customers: 176 | 177 | | id | name | kg_bought | money_spent | 178 | | ---|:-----:| ------ | -------- | 179 | 1| Ivo | 5.1 | 17.7 | 180 | 2| Rado | 3.2 | 12.2 | 181 | 182 | 183 | ### Extending the parser 184 | We want to have some more commands that will make use of the new table. 185 | Most of them can be done with a little refactoring of the previous commands. 186 | 187 | 188 | __A little commands refactoring:__ 189 | * ``import_json`` to ``import_products_json`` 190 | * ``export_json`` to ``export_products_json`` 191 | 192 | And make sure you change it at all places, so it doesn't break your program! :smile: 193 | 194 | __Add these new commands:__ 195 | * ```import_json_customers ``` - Adds customers from the json file to the database. 196 | * ```list_customers``` - Prints out all customers in the following format: "[id] name - kg_bought kg - money_spent BGN" 197 | * ```add_customer``` - Prompts for data, needed to add a new customer to the database. 198 | * ```delete_customer ``` - Removes the customer matching the customer id in the database. 199 | * ```update_customer ``` - Updates the customer data matching the customer id in the database. 200 | * ```export_customers_json ``` - Exports the customers in the database to a json file. 201 | 202 | 203 | There's no need for example usage of commands since the commands are identical to the products commands. 204 | 205 | However here's how the exported json customers file will look: 206 | 207 | ``` 208 | command>add_customer 209 | name>Mityo 210 | kg bought>10 211 | money spent>21 212 | Added Mityo to customers. 213 | command>export_customers_json summer_customers.json 214 | Customers were exported to summer_customers.json 215 | ``` 216 | 217 | And this is exactly how the summer_customers.json file looks: 218 | 219 | ```json 220 | [ 221 | { 222 | "name": "Ivo", 223 | "kg_bought": 5.1, 224 | "money_spent": 17.7 225 | }, 226 | { 227 | "name": "Rado", 228 | "kg_bought": 3.2, 229 | "money_spent": 12.2 230 | } 231 | { 232 | "name": "Mityo", 233 | "kg_bought": 10, 234 | "money_spent": 21 235 | } 236 | ] 237 | ``` 238 | 239 | ## Bonus round: Sales 240 | After we passed all the obstacles, it's finally time to have sales. 241 | 242 | __Commands:__ 243 | * ```buy ``` - The given customer buys products available in the products table. 244 | 245 | __Example usage:__ 246 | 247 | ``` 248 | command>buy 1 249 | ~~~Ivo buys~~~ 250 | products - amount_in_kg>Apples - 2, Lettuce - 3.1 251 | ~~~Bill: 10.51~~~ 252 | command>list_products 253 | .... 254 | [4] Lettuce - 2.1 BGN - 3.9 kg 255 | [5] Apples - 2 BGN - 0 kg 256 | command>list_customers 257 | [1] Ivo - 10.2 kg - 28.21 BGN 258 | ... 259 | ``` 260 | 261 | ``` 262 | command>buy 2 263 | ~~~Rado buys~~~ 264 | products - amount_in_kg>Apples - 2, 265 | !Error: Apples out of stock. 266 | ``` 267 | 268 | ``` 269 | command>buy 2 270 | ~~~Rado buys~~~ 271 | products - amount_in_kg>Ambrosia - 5, 272 | !Error: Ambrosia not found in products. 273 | ``` 274 | 275 | ## (Not yet added) Bonus round 2: Extend customers 276 | Customers will now have a budget and can't godmode anymore. 277 | Buying products over their budget will fail and successful buys will reduce their budget. 278 | -------------------------------------------------------------------------------- /week4/materials.md: -------------------------------------------------------------------------------- 1 | # The materials for Week 4 2 | 3 | ## Databases, SQL and sqlite 4 | 5 | We are going to scratch the surface of the following topics: 6 | 7 | * Databases & Storing data in a structured way 8 | * What is Structured Query Language? 9 | * How to use sqlite with Python? 10 | 11 | ### Databases and SQL 12 | 13 | Of course, to get the definition straight, check Wikipedia - http://en.wikipedia.org/wiki/Database 14 | 15 | We are going to use Relational Databases for our purposes. 16 | 17 | RDBMS (Relational Database Management System) is a software, that handles few very important topics for us: 18 | 19 | * It has a server, that listens at a given port for open connections 20 | * It manages the concurency access to the data, removing race conditions 21 | * The data is stored in optimal data structures, which makes accessing the data very fas. 22 | * Usually, the RDBMS supports a query language, called SQL (Structured Query Language) 23 | 24 | We are going to use the most simple RDBMS - sqlite! It stores the entire data in a single file, located in the same directory as our scripts. 25 | 26 | __Few good materials for getting a grasp of SQL:__ 27 | 28 | * First, we will need some SQL syntax. This is a very good SQL tutorial - http://www.w3schools.com/sql/sql_intro.asp for learning the syntax! 29 | * Learn what is an SQL Injection (This will help you understand SQL) - https://www.youtube.com/watch?v=_jKylhJtPmI 30 | * SQL vs NoSQL is a good way to understand SQL :) - https://www.youtube.com/watch?v=rRoy6I4gKWU 31 | 32 | ### sqlite 33 | 34 | We are going to tackle with sqlite: 35 | 36 | * Check the Python documentation for sqlite - http://docs.python.org/2/library/sqlite3.html 37 | * You can check the web page for sqlite for better understanding it - https://sqlite.org/ 38 | * A very good tutorial on sqlite - http://www.pythoncentral.io/series/python-sqlite-database-tutorial/ 39 | -------------------------------------------------------------------------------- /week4/movie_catalog.md: -------------------------------------------------------------------------------- 1 | #Command Line Movie ~~Database~~ Catalog 2 | We are going to create a small movie catalog that will help us keep track of movies we have seen - their titles, rating and even the names of the actors who star in them. 3 | ## Creating the tables 4 | 5 | First, we would like to create two tables in our ```movie_catalog``` database. [Here's a little reminder how to create a table in SQLite](https://github.com/HackBulgaria/Polyglot/blob/master/polyglot_creation_script/create_db.py). Our tables should look like this (only in structure, however; the rest will be added later): 6 | 7 | movie_id|title|year|rating 8 | --------|-----|----|------ 9 | 1|"12 Angry Men"|1957|10 10 | 2|"12 Angry Men"|1997|8 11 | 3|"The Avengers"|2012|7 12 | 13 | actor_id|name 14 | --------|----- 15 | 1|Henry Fonda 16 | 2|"Robert Downey Jr." 17 | 3|Jennifer Lawrence 18 | 19 | Note that instead of ```CREATE TABLE``` you can use the command ```CREATE TABLE IF NOT EXISTS``` which (*duh*) creates a table only if it doesn't already exist in the database. 20 | 21 | When you create your tables, you can make the movie_id and the actor_id of the type ```INTEGER PRIMARY KEY``` which will free you of the burden of having to keep track of ids and count the rows of your tables. 22 | 23 | To represent the relationship between the two tables, it might be a good idea to create a third table that stores only movie_ids and actor_ids. For additional pointers, scroll to the end of the document. 24 | 25 | ## Commands 26 | 27 | Create a program ```movie_catalog.py``` that can take (and carry out) the following commands: 28 | 29 | - ```add_movie``` <-> The program promts the user to provide the *title* of the movie, the *year* it came out in and the *rating*. 30 | - ```add_actor []``` <-> Adds the actor with the given ``` actor_id``` to the cast of the movie corresponding to the movie_id provided. If you're adding to a movie an actor who hasn't been added *anywhere* before, you don't have to provide an id for the actor but be prompted for the name of the actor. 31 | - ```rate_movie ``` <-> You can rate a movie with an integer from 1 to 10. 32 | - ```find_movies ``` <-> Prints the titles of all movies with rating equal to the one provided. 33 | - ```actor_info ``` <-> Prints the name of the actor and the title of every movie he/she stars in. 34 | - ```movie_info ``` <-> Prints movie title, year, rating and the name of every actor who stars in the movie. 35 | - ```list_movies``` <-> Prints the titles of all movies in your catalog, along with their ids. 36 | - ```list_actors``` <-> Prints the names of all actors who star in the movies in your catalog, along with their ids. 37 | - ```exit``` <-> Summon the green fairy that will take you to Neverland. Or just exit the program. Whatever. 38 | 39 | If something is unclear, please look at the examples below. 40 | 41 | ### Adding movies 42 | Below you can see an example of how this process should look like. 43 | ``` 44 | > add_movie 45 | title> "12 Angry Men" 46 | year> 1957 47 | rating> 10 48 | "12 Angry Men" (1957) was added to your catalog! 49 | 50 | > add_movie 51 | title> "12 Angry Men" 52 | year> 1997 53 | rating> 8 54 | "12 Angry Men" (1997) was added to your catalog! 55 | 56 | > add_movie 57 | title> "The Avengers" 58 | year> 2012 59 | rating> 7 60 | "The Avengers" (2012) was added to your catalog! 61 | 62 | > add_movie 63 | title> "The Avengers" 64 | year> 2012 65 | rating> 7 66 | "The Avengers" (2012) is already in your catalog! 67 | ``` 68 | You can add two movies with the same title *if they have not been released in the same year*. If *both* the title *and* the year are the same, the program assumes that the movie is already in your catalog. 69 | 70 | ### Listing movies 71 | ``` 72 | > list_movies 73 | [1] "12 Angry Men" (1957) 74 | [2] "12 Angry Men" (1997) 75 | [3] "The Avengers" (2012) 76 | 77 | > add_movie 78 | title> "The Avengers" 79 | year> 1998 80 | rating> 6 81 | "The Avengers" (1998) was added to your catalog! 82 | 83 | > list_movies 84 | [1] "12 Angry Men" (1957) 85 | [2] "12 Angry Men" (1997) 86 | [3] "The Avengers" (2012) 87 | [4] "The Avengers" (1998) 88 | ``` 89 | 90 | ### Adding actors 91 | 92 | ``` 93 | > add_actor 3 94 | name> "Robert Downey Jr." 95 | Robert Downey Jr. was added to the list of actors of "The Avengers" (2012) 96 | 97 | >list_actors 98 | [1] Robert Downey Jr. 99 | 100 | > add_movie 101 | title> "Iron Man" 102 | year> 2008 103 | rating> 6 104 | "Iron Man" (2008) was added to your catalog! 105 | 106 | > add_actor 3 1 107 | Robert Downey Jr. was added to the list of actors of "Iron Man" (2008) 108 | ``` 109 | Note that the actor_id (the second id provided in the second example) is optional but the movie_id is not. You can't be an actor if you have not taken part in at least one movie. 110 | 111 | ### Lising actors 112 | 113 | ``` 114 | >list_actors 115 | [1] Robert Downey Jr. 116 | 117 | add_actor 1 118 | name> "Henry Fonda" 119 | Henry Fonda was added to the list of actors of "12 Angry Men" (1957) 120 | 121 | >list_actors 122 | [1] Robert Downey Jr. 123 | [2] Henry Fonda 124 | ``` 125 | 126 | ### Actor info 127 | 128 | ``` 129 | >list_actors 130 | [1] Robert Downey Jr. 131 | [2] Henry Fonda 132 | 133 | > actor_info 1 134 | Robert Downey Jr. stars in: 135 | [3] "The Avengers" (2012) 136 | [5] "Iron Man" (2008) 137 | 138 | > actor_info 2 139 | Henry Fonda stars in: 140 | [1] "12 Angry Men" (1957) 141 | ``` 142 | 143 | ### Movie info 144 | ``` 145 | > movie_info 1 146 | Title: "12 Angry Men" 147 | Year: (1957) 148 | Cast: Henry Fonda 149 | Rating: 5 150 | 151 | > movie_info 5 152 | Title: "Iron Man" 153 | Year: (2008) 154 | Cast: Robert Downey Jr. 155 | Rating: 5 156 | 157 | ``` 158 | 159 | ### Rating movies 160 | You can change the rating of the movie corresponding to the movie_id you provide. Here's how this should go: 161 | ``` 162 | > list_movies 163 | [1] "12 Angry Men" (1957) 164 | [2] "12 Angry Men" (1997) 165 | [3] "The Avengers" (2012) 166 | [4] "The Avengers" (1998) 167 | [5] "Iron Man" (2008) 168 | 169 | > rate_movie 1 170 | rating> 8 171 | 172 | > movie_info 1 173 | Title: "12 Angry Men" 174 | Year: (1957) 175 | Cast: Henry Fonda 176 | Rating: 8 177 | 178 | > rate_movie 1 179 | rating> 10 180 | 181 | > movie_info 1 182 | Title: "12 Angry Men" 183 | Year: (1957) 184 | Cast: Henry Fonda 185 | Rating: 10 186 | 187 | ``` 188 | 189 | ### Finding movies 190 | 191 | The program prints a list of all movies with rating equal to the rating provided by the user. 192 | ``` 193 | > rate_movie 2 194 | rating> 6 195 | 196 | > rate_movie 3 197 | rating> 7 198 | 199 | > find_movies 10 200 | [1] "12 Angry Men" (1957) 201 | 202 | > find_movies 7 203 | [3] "The Avengers" (2012) 204 | 205 | ``` 206 | 207 | ## Pointers and additional information 208 | 209 | Let's remind ourselves how the actor and the movie list looked like in the examples above: 210 | ``` 211 | [1] Robert Downey Jr. 212 | [2] Henry Fonda 213 | ``` 214 | ``` 215 | [1] "12 Angry Men" (1957) 216 | [2] "12 Angry Men" (1997) 217 | [3] "The Avengers" (2012) 218 | [4] "The Avengers" (1998) 219 | [5] "Iron Man" (2008) 220 | ``` 221 | 222 | In this example we have a *many-to-many relationship* between the tables "Movies" and "Actors" of our database. A many-to-many relationship is when one or more rows in a table are associated with one or more rows in another table. We have a table of movies that have casts of many different actors and a table of actors who can star in many different movies. 223 | 224 | It might be useful to create an additional third table that keeps track of the relationship between movies and actors. It might look like this: 225 | 226 | movie_id | actor_id 227 | ---------|--------- 228 | 1 | 2 229 | 3 | 1 230 | 5 | 1 231 | 232 | The table shows that actor with unique actor_id [2] - in our example that is Henry Fonda - stars in movie with unique movie_id [1] which is "12 Angry Men" (1957). Actor with actor_id [1] - Robert Downey Jr. - stars in movies with movie_ids [3] and [5] a.k.a. "The Avengers" (2012) and "Iron Man" (2008) 233 | 234 | **Useful Links**: 235 | 236 | [SQLite Documentation](http://www.sqlite.org/docs.html) 237 | 238 | [Creating tables and info about INTEGER PRIMARY KEY](http://www.sqlite.org/lang_createtable.html) 239 | 240 | [SQLite Problem from Monday with SQL basic commands] 241 | (https://github.com/HackBulgaria/Programming101/blob/master/week4/problems.md) 242 | 243 | [Video that shows how INTEGER PRIMARY KEY is used](https://www.youtube.com/watch?v=wWBXV8oNcJ0) 244 | 245 | ## Bonus Round 246 | 247 | If you want to, you can add the functions `````` and `````` which, surprisingly enough, *remove* actors and movies from the database. Note that if you remove a movie some of the actors who star in it might become jobless and have to be removed from the list of actors. You're not an actor if you haven't played in a single movie. 248 | -------------------------------------------------------------------------------- /week4/problems.md: -------------------------------------------------------------------------------- 1 | ## Problem 0 - SQLite Database Browser & Polyglot 2 | 3 | We are going to start with a GUI (Graphical User Interface) tool, for managing sqlite - just to see that the things are working correctly! 4 | 5 | If you are using Ubuntu, you can install [SQLite Database Browser](https://apps.ubuntu.com/cat/applications/sqlitebrowser/), which is simple enough for our needs! 6 | 7 | Now, go back to the [Polyglot repository](https://github.com/HackBulgaria/Polyglot), and locate the ```polyglot.db``` file and open it in the SQLite Database Browser! 8 | 9 | When the file is loaded, take a look at the tables. 10 | 11 | ## Problem 1 - sqlite3 shell tool 12 | 13 | Now, back to the console. 14 | 15 | __First, check if you have the tool for running sqlite3 shell client, by typing:__ 16 | 17 | ``` 18 | $ sqlite3 19 | ``` 20 | 21 | If you see: 22 | 23 | ``` 24 | SQLite version 3.7.9 2011-11-01 00:52:41 25 | Enter ".help" for instructions 26 | Enter SQL statements terminated with a ";" 27 | sqlite> 28 | ``` 29 | 30 | Everything is OK! If there is an error, you should install ```sqlite3```: 31 | 32 | ``` 33 | sudo apt-get install sqlite3 libsqlite3-dev 34 | ``` 35 | 36 | Now, navigate to the ```Polyglot``` directory and call the following command: 37 | 38 | ``` 39 | $ sqlite3 polyglot.db 40 | ``` 41 | 42 | And run the following SQL statement: 43 | 44 | ``` 45 | sqlite>SELECT * FROM languages; 46 | ``` 47 | 48 | You will get the following result: 49 | 50 | ``` 51 | 1|Python|google|0|A folder named Python was created. Go there and fight with program.py! 52 | 2|Go|200 OK|0|A folder named Go was created. Go there and try to make Google Go run. 53 | 3|Java|object oriented programming|0|A folder named Java was created. Can you handle the class? 54 | 4|Haskell|Lambda|0|Something pure has landed. Go to Haskell folder and see it! 55 | 5|C#|NDI=|0|Do you see sharp? Go to the C# folder and check out. 56 | 6|Ruby|https://www.ruby-lang.org/bg/|0|Ruby, ruby, rubyyy, aaahaaaahaa! (music). Go to Ruby folder! 57 | 7|C++|header files|0|Here be dragons! It's C++ time. Go to the C++ folder. 58 | 8|JavaScript|Douglas Crockford|0|NodeJS time. Go to JavaScript folder and Node your way! 59 | sqlite> SELECT * FROM languages; 60 | 1|Python|google|0|A folder named Python was created. Go there and fight with program.py! 61 | 2|Go|200 OK|0|A folder named Go was created. Go there and try to make Google Go run. 62 | 3|Java|object oriented programming|0|A folder named Java was created. Can you handle the class? 63 | 4|Haskell|Lambda|0|Something pure has landed. Go to Haskell folder and see it! 64 | 5|C#|NDI=|0|Do you see sharp? Go to the C# folder and check out. 65 | 6|Ruby|https://www.ruby-lang.org/bg/|0|Ruby, ruby, rubyyy, aaahaaaahaa! (music). Go to Ruby folder! 66 | 7|C++|header files|0|Here be dragons! It's C++ time. Go to the C++ folder. 67 | 8|JavaScript|Douglas Crockford|0|NodeJS time. Go to JavaScript folder and Node your way! 68 | ``` 69 | 70 | ## Problem 2 - Tables, tables everywhere! SELECT, UPDATE, INSERT, DELETE 71 | 72 | Now, we know that our languages table looks like this: 73 | 74 | | id | language | answer | answered | guide | 75 | | ------------- |:-------------:| --- | --- |-----:| 76 | 1|Python|google|0|A folder named Python was created. Go there and fight with program.py! 77 | 2|Go|200 OK|0|A folder named Go was created. Go there and try to make Google Go run. 78 | 3|Java|object oriented programming|0|A folder named Java was created. Can you handle the class? 79 | 4|Haskell|Lambda|0|Something pure has landed. Go to Haskell folder and see it! 80 | 5|C#|NDI=|0|Do you see sharp? Go to the C# folder and check out. 81 | 6|Ruby|https://www.ruby-lang.org/bg/|0|Ruby, ruby, rubyyy, aaahaaaahaa! (music). Go to Ruby folder! 82 | 7|C++|header files|0|Here be dragons! It's C++ time. Go to the C++ folder. 83 | 8|JavaScript|Douglas Crockford|0|NodeJS time. Go to JavaScript folder and Node your way! 84 | 85 | 86 | A ```SELECT``` statement, returns a list of rows. We ```SELECT``` by giving the name of the columns we want. 87 | 88 | Run the following SELECT statements in the sqilite3 shell: 89 | 90 | 1. 91 | ```sql 92 | SELECT id FROM languages; 93 | ``` 94 | 2. 95 | ```sql 96 | SELECT id, language FROM languages; 97 | ``` 98 | 3. 99 | ```sql 100 | SELECT id, language, answer, answered FROM languages; 101 | ``` 102 | 4. 103 | ```sql 104 | SELECT id, language FROM languages WHERE answered = 0; 105 | ``` 106 | 5. 107 | ```sql 108 | SELECT id, language FROM languages WHERE answered = 1; 109 | ``` 110 | 111 | __Now, lets update few languages to be answered. We will change the answered value for each language from 0 to 1:__ 112 | 113 | 1. 114 | ```sql 115 | UPDATE languages SET answered = 1 WHERE language = "Python"; 116 | ``` 117 | 2. 118 | ```sql 119 | SELECT id, language FROM languages WHERE answered = 1; 120 | ``` 121 | 122 | Now, if we run the ```polyglot.py``` program and give the ```list``` command, we will see: 123 | 124 | ``` 125 | DONE [1] - Python 126 | NOT_DONE [2] - Go 127 | NOT_DONE [3] - Java 128 | NOT_DONE [4] - Haskell 129 | NOT_DONE [5] - C# 130 | NOT_DONE [6] - Ruby 131 | NOT_DONE [7] - C++ 132 | NOT_DONE [8] - JavaScript 133 | ``` 134 | 135 | As you can see, if we control the data, we control the program! 136 | 137 | __Now, let's insert a new language in our table!__ 138 | 139 | Execute the following statements: 140 | 141 | * 142 | ```sql 143 | INSERT INTO languages(id, language, answer, answered, guide) VALUES(9, "PHP", "$$$", 0, "Can you handle this piece of PHP?"); 144 | ``` 145 | * 146 | ```sql 147 | SELECT language FROM languages; 148 | ``` 149 | 150 | Now, if we go to our Polyglot program, again, we will see that PHP was added too: 151 | 152 | ``` 153 | DONE [1] - Python 154 | NOT_DONE [2] - Go 155 | NOT_DONE [3] - Java 156 | NOT_DONE [4] - Haskell 157 | NOT_DONE [5] - C# 158 | NOT_DONE [6] - Ruby 159 | NOT_DONE [7] - C++ 160 | NOT_DONE [8] - JavaScript 161 | NOT_DONE [9] - PHP 162 | ``` 163 | 164 | Now, to finish the cycles of SQL statements, we are going to ```DELETE``` the added PHP language 165 | 166 | We can achieve that by running the following query: 167 | 168 | ```sql 169 | DELETE FROM languages WHERE language = "PHP"; 170 | ``` 171 | 172 | Now, if we run: 173 | 174 | ```sql 175 | SELECT language FROM languages; 176 | ``` 177 | 178 | We wont see PHP! 179 | 180 | ### SUID = CRUD 181 | 182 | We were talking about CRUD operations - Create, Read, Update, Delete of a given object. 183 | 184 | If we take a look at the SQL query statements, we can relate: 185 | 186 | * ```SELECT``` = Read 187 | * ```UPDATE``` = Update 188 | * ```INSERT``` = Create 189 | * ```DELETE``` = Delete 190 | 191 | ## Problem 3 - A simple Python Script to query the Polyglot database 192 | 193 | Copy ```polyglot.db``` to where you work, and create the following script: 194 | 195 | ```python 196 | import sqlite3 197 | 198 | conn = sqlite3.connect("polyglot.db") 199 | cursor = conn.cursor() 200 | 201 | result = cursor.execute("SELECT id, language FROM languages") 202 | 203 | for row in result: 204 | print(row) 205 | ``` 206 | 207 | Run the program and see the output! 208 | 209 | As you can see, connecting to sqlite is as simple as that! 210 | 211 | ## Problem 4 - Creating tables with Python 212 | 213 | If you see the [following file from the Polyglot problem](https://github.com/HackBulgaria/Polyglot/blob/master/polyglot_creation_script/create_db.py), you will see how a database is created! 214 | 215 | Now, implement a Python script, called ```create_company.py``` that creates the following table (Both structure and data): 216 | 217 | | id | name | monthly_salary | yearly_bonus | position | 218 | | ------------- |:-------------:| --- | --- |-----:| 219 | 1|Ivan Ivanov |5000|10000|Software Developer| 220 | 2|Rado Rado| 500|0|Technical Support Intern| 221 | 3|Ivo Ivo| 10000| 100000| CEO | 222 | 4| Petar Petrov | 3000 | 1000 | Marketing Manager| 223 | 5| Maria Georgieva | 8000 | 10000 | COO | 224 | 225 | After creating the sqlite database, implement a script, called ```manage_company.py``` that can take command input and do the following things: 226 | 227 | * On command ```list_employees``` - Prints out all employees, in the following format - "name - position" 228 | * On command ```monthly_spending``` - Prints out the total sum for monthly spending that the company is doing for salaries 229 | * On command ```yearly_spending``` - Prints out the total sum for one year of operation (Again, salaries) 230 | * On command ```add_employee```, the program starts to promt for data, to create a new employee. 231 | * On command ```delete_employee ```, the program should delete the given employee from thje database 232 | * On command ```update_employee ```, the program should prompt the user to change each of the fields for the given employee 233 | __Here is an example usage:__ 234 | 235 | ``` 236 | $ python manage_company.py 237 | command>list_employees 238 | 1 - Ivan Ivanov - Software Developer 239 | 2 - Rado Rado - Technical Support Intern 240 | 3 - Ivo Ivo - CEO 241 | 4 - Petar Petrov - Marketing Manager 242 | 5 - Maria Georgieva - COO 243 | command>monthly_spending 244 | The company is spending $26500 every month! 245 | command>yearly_spending 246 | The company is spending $439000 every year! 247 | command>add_employee 248 | name>Hristo Hristov 249 | monthly_salary>1200 250 | yearly_bonus>0 251 | position>Junior Software Developer 252 | command>list_employees 253 | command>list_employees 254 | 1 - Ivan Ivanov - Software Developer 255 | 2 - Rado Rado - Technical Support Intern 256 | 3 - Ivo Ivo - CEO 257 | 4 - Petar Petrov - Marketing Manager 258 | 5 - Maria Georgieva - COO 259 | 6 - Ivan Ivanov - Juniour Software Developer 260 | ``` 261 | 262 | ``` 263 | >delete_employee 6 264 | Ivan Ivanov wasa deleted. 265 | >list_employees 266 | 1 - Ivan Ivanov - Software Developer 267 | 2 - Rado Rado - Technical Support Intern 268 | 3 - Ivo Ivo - CEO 269 | 4 - Petar Petrov - Marketing Manager 270 | 5 - Maria Georgieva - COO 271 | ``` 272 | 273 | ``` 274 | >update_employee 2 275 | name>Rado Rado 276 | monthly_salary>1000 277 | yearly_bonus>1000 278 | position>Technical Support 279 | >list_employees 280 | 1 - Ivan Ivanov - Software Developer 281 | 2 - Rado Rado - Technical Support 282 | 3 - Ivo Ivo - CEO 283 | 4 - Petar Petrov - Marketing Manager 284 | 5 - Maria Georgieva - COO 285 | ``` 286 | -------------------------------------------------------------------------------- /week5/animals.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101/a97a7c28a3993a7a323a4e33562f1df54c00241a/week5/animals.db -------------------------------------------------------------------------------- /week5/create_animals_database.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | 4 | def create_animals_table(cursor): 5 | create_query = '''create table if not exists 6 | animals(species text, 7 | life_expectancy int, 8 | food_type text, 9 | gestation int, 10 | newborn_weight real, 11 | average_weight int, 12 | weight_age_ratio real, 13 | food_weight_ratio real)''' 14 | 15 | cursor.execute(create_query) 16 | 17 | 18 | def insert_species_into_table(cursor, species, life_expectancy, 19 | food_type, gestation, newborn_weight, average_weight, 20 | weight_age_ratio, food_weight_ratio): 21 | insert_query = "insert into animals values(?, ?, ?, ?, ?, ?, ?, ?)" 22 | cursor.execute(insert_query, 23 | (species, life_expectancy, food_type, 24 | gestation, newborn_weight, average_weight, 25 | weight_age_ratio, food_weight_ratio)) 26 | 27 | 28 | def main(): 29 | conn = sqlite3.connect("animals.db") 30 | cursor = conn.cursor() 31 | 32 | create_animals_table(cursor) 33 | 34 | animals = [("lion", 15, "carnivore", 3, 2, 200, 7.5, 0.035), 35 | ("tiger", 20, "carnivore", 4, 1, 250, 12, 0.06), 36 | ("red panda", 9, "herbivore", 4, 0.15, 5, 0.25, 1), 37 | ("kangaroo", 12, "herbivore", 9, 8, 50, 1.75, 0.1), 38 | ("koala", 15, "herbivore", 7, 1, 12, 0.5, 0.05), 39 | ("raccoon", 3, "herbivore", 2, 0.5, 7, 0.3, 0.35), 40 | ("baboon", 45, "herbivore", 6, 1, 41, 1, 0.074), 41 | ("impala", 15, "herbivore", 6, 1, 60, 0.33, 0.1), 42 | ("hippo", 45, "herbivore", 8, 30, 1500, 2.72, 25), 43 | ("cougar", 13, "carnivore", 3, 14, 80, 0.42, 0.075), 44 | ("goat", 18, "herbivore", 5, 5, 52, 0.217, 0.38)] 45 | 46 | for animal in animals: 47 | insert_species_into_table(cursor, *animal) 48 | 49 | conn.commit() 50 | conn.close() 51 | 52 | 53 | if __name__ == '__main__': 54 | main() -------------------------------------------------------------------------------- /week5/materials.md: -------------------------------------------------------------------------------- 1 | # pip and Python plugins 2 | 3 | We are going to deal with pip - the Python package manager that we are going to use in order to install 3rd party librariers. 4 | 5 | ## Glossary 6 | 7 | Here are the things, that we are going to use: 8 | 9 | * pip - A tool for installing & managing Python packages - https://pypi.python.org/pypi/pip 10 | * virtualenv - A tool for creating isolated Python environments - http://www.virtualenv.org/en/latest/virtualenv.html 11 | * PyPI - the Python Package Index, where all packages are listed - https://pypi.python.org/pypi 12 | 13 | The setup is as following: 14 | 15 | * Install ```pip``` via apt-get 16 | * Install ```virtualenv``` via ```pip``` 17 | * Run ```virtualenv``` in your project, to get your local ```pip``` 18 | * Install packages via pip install `````` 19 | 20 | ## How to Setup Everything 21 | 22 | A very good starting article is this one - http://www.dabapps.com/blog/introduction-to-pip-and-virtualenv-python/ 23 | 24 | The bad thing is that virtualenv creates env with python 2.7. We have to deal with that problem: 25 | 26 | ``` 27 | $ virtualenv -p /usr/bin/python3 yourenv 28 | $ source yourenv/bin/activate 29 | $ pip install package-name 30 | ``` 31 | 32 | Where ```/usr/bin/python3``` is the path to your python3 binary! 33 | -------------------------------------------------------------------------------- /week5/problems.md: -------------------------------------------------------------------------------- 1 | ## The GitHub Problem! 2 | 3 | Once you have ```virtualenv``` and ```pip``` set up, we are going to use the ```requests``` library. 4 | 5 | [Requests is a library](http://www.python-requests.org/) for making http calls that can be installed via pip. 6 | 7 | We are going to use the GitHub API! 8 | 9 | The API reference can be found here - https://developer.github.com/v3/ 10 | 11 | We are going to do the following things: 12 | 13 | 1. Fetch all public repositories for a given user 14 | 2. Download the repositories as a tarball / zip 15 | 3. Unzip them 16 | 4. Run some statistics on the files 17 | 5. When the script is done, delete all the downloaded files 18 | 19 | ### Some help - repos, downloading & unzipping 20 | 21 | To fetch all public repositories for a given user, you have to make request for: 22 | 23 | ``` 24 | https://api.github.com/users/:user 25 | ``` 26 | 27 | Where ```:user``` can be any account. For example: 28 | 29 | ``` 30 | https://api.github.com/users/RadoRado 31 | ``` 32 | 33 | To download a repo, you have to make a request to the following url: 34 | 35 | ``` 36 | https://github.com/RadoRado/Programming101/archive/master.zip 37 | ``` 38 | 39 | To disect the url, it has the following parts, that can be given as parameters: 40 | 41 | ``` 42 | https://github.com/:user/:repo_name/archive/master.zip 43 | ``` 44 | 45 | This will download the master branch. 46 | 47 | To work with zip files, you can check this reference - https://docs.python.org/3.4/library/zipfile.html 48 | 49 | ### Statistics 50 | 51 | When you have all repos for the given user downloaded, the script should output the following things: 52 | 53 | * For every repo, the number of lines of code that are written 54 | * For every repo, the number of different source files, grouped by their extension: 55 | 56 | ``` 57 | "*.js" - 5 files 58 | "*.css" - 3 files 59 | etc. 60 | ``` 61 | 62 | * For all repos (summary) - lines of code written 63 | * For all repos (summary) - number of different source files, grouped by their extension 64 | -------------------------------------------------------------------------------- /week5/zoo_problem.md: -------------------------------------------------------------------------------- 1 | # Zoo Problem - Simulating a zoo 2 | 3 | 4 | ## We'll be writing a program that will simulate the live going on in the zoo. 5 | 6 | Here are descriptions of the classes you should have to complete the task, but 7 | of course you're free to add more if you feel you'll need to. 8 | 9 | There is also a database file that has a table with possible animals, which 10 | you should use too ^^ 11 | 12 | 13 | ## Animal class should have the following attributes: 14 | 15 | * species 16 | * age 17 | * name - __You can have one unique name in one species.__ This means, you can have only one tiger, named "Lucho". And only one bear, named "Lucho". Etc. 18 | * gender 19 | * weight 20 | 21 | ## An animal can do the following things: 22 | 23 | * it can grow (increase its weight and age) 24 | * it can eat (consume food) 25 | * it can die (the closer the animal gets to its life expectancy, the higher is the chance of dying) 26 | 27 | ## Chance of dying 28 | 29 | To calculate the chance of dying, take the ratio of the current age of the animal and divide it by the life expectancy for the given species: 30 | 31 | ``` 32 | chance_of_dying = current_animal_year / life_expectancy 33 | ``` 34 | 35 | ## Zoo class should have: 36 | 37 | * animals, of course 38 | * capacity (how many animals it can accommodate) 39 | * budget (the money it has at the moment) 40 | 41 | ## A zoo can do the following things: 42 | 43 | * The zoo can accommodate an animal 44 | * It has daily incomes depending on how much animals it has (the more animals it has, the more interesting it will be to go into that zoo, right?) 45 | * It has daily outcomes depending on how much do the animals eat (every food has its price) 46 | * Sadly, animals can die in the zoo :/ 47 | * The animals reproduce half an year (6 months) after their gestation period is over. 48 | 49 | ## How an animal is born 50 | 51 | * If a male and a female of the same species are present, 52 | after gestation period a new animal is born. 53 | * After an animal is born, after 6 months the female is ready 54 | to reproduce again. 55 | * There's only one baby animal __which gender is 50/50 chance__ to be male or female. 56 | 57 | ## Costs of accomodating an animal 58 | 59 | * an animal brings 60$ back to the zoo per day 60 | * one kilo meat costs 4$ 61 | * one kilo grass, foliage or bamboo costs 2$ 62 | 63 | ## We wrote for you a database with animals 64 | 65 | You can open the ```animals.db``` file with sqlite browser! 66 | 67 | __The data in that table is very important!__ 68 | 69 | The table contains the following columns: 70 | * species (for example, a tiger) 71 | * life expectancy - how long is the species expected to live 72 | * food type - a carnivore (eats meat) or a herbivore (eats grass, etc.) 73 | * gestation period - how long does the female carry the baby (in months) 74 | * newborn and average weight (in kilos) 75 | * weight/age ratio - how much does the species grow for a month 'till average weight 76 | (when the species gets to average weight, it stops growing) 77 | * food/weight ratio - how much does the species eat (in kilos) per weight (also in kilos) 78 | 79 | __The ages in the table are in months__ 80 | 81 | 82 | ## The program, simulating a zoo 83 | 84 | We can have a lot of Zoos, but for the simulation, we are going to create one Zoo when the program starts and work with it. 85 | 86 | You should have the following interface to communicate with the written program: 87 | 88 | `see_animals` - prints all animals in the zoo in the following format: ` : , , ` 89 | 90 | `accommodate ` - adds an animal to the zoo 91 | 92 | `move_to_habitat ` - removes an animal from the zoo and returns it to its natural habitat 93 | 94 | `simulate ` where: 95 | `` is 'days', 'weeks', 'months' or 'years' and `` is a number. 96 | 97 | For every `` the program should print the current state of the zoo: 98 | 99 | * list the animals stats (in the format above) 100 | * prints if an animal has died 101 | * prints if an animal has been born 102 | * prints if the zoo doesn't have enough budget to pay for the food 103 | 104 | The order of happening is as follows: 105 | 106 | * Grows all animals 107 | * Checks if an animal is going to die 108 | * Checks if the zoo can afford to feed all animals 109 | * Checks if an animal is going to be born 110 | * Repeat 111 | 112 | Good luck! 113 | 114 | ## Not to forget!! 115 | 116 | And don't forget the three things we're trying to code with: 117 | * test driven development 118 | * objected oriented programming 119 | * databases 120 | -------------------------------------------------------------------------------- /week6/problems.md: -------------------------------------------------------------------------------- 1 | # Money In The Bank 2 | 3 | ## 1. Setup the project 4 | Project URL: https://github.com/HackBulgaria/money-in-the-bank 5 | 6 | Your first task is to set up the project! 7 | 8 | __Make a virtual environment__ and install all requirements in ```requirements.txt``` file. 9 | 10 | Then run the program from ```start.py```. __Try to run the tests.__ If everything is OK you are ready to continue. 11 | 12 | ## 2. SQL injection 13 | 14 | Take a look at the code. You may recognize that is not secure at all. Your second task is to protect your that program from SQL injections using prepared statements. 15 | 16 | You can try to login and give the following username: 17 | 18 | ``` 19 | ' OR 1 = 1 -- 20 | ``` 21 | 22 | and give anything for the password. You will login successfuly! 23 | 24 | 25 | __Make some unit tests to proof your security.__ 26 | 27 | ## 3. Strong passwords 28 | 29 | In this program you can register people and there are no any requirements for the password. 30 | 31 | To increase the level of security of your clients, they all must have a STRONG password. By strong we mean: 32 | 33 | * More then 8 symbols 34 | * Must have capital letters, and numbers and a special symbol 35 | * Username is not in the password (as a substring) 36 | 37 | Implement that requirements in the register and change password function and test them. 38 | 39 | ## 4. Hash them passwords! 40 | 41 | __All the passwords of your users are in plain text.__ 42 | 43 | You know that this is not good for security reasons. You have to store the passwords in a hash. Use the `SHA1` hashing algorithm. Make some research on that topic. 44 | 45 | Write some tests to proof your work. 46 | 47 | ## 5. Hide passwords while typing 48 | 49 | As a UI (User Interface) of your application you are using the console. 50 | 51 | If you are typing your password in a web form, it appears as `***`. Sadly, this is not the case in our console. 52 | 53 | __Make some research and fix that problem.__ 54 | 55 | No, you can not test that at all. :D 56 | 57 | ## 6. Bruteforce protection 58 | 59 | You can catch if a user tries to login too many times within a minute. If he tries 10 or 20 times, it can be a signal for a brute-force attack! You have to protect the bank software from that. 60 | 61 | __If someone enters 5 wrong passwords in a row, block him from trying for the next 5 minutes.__ 62 | 63 | This should work even if the user exits the bank software and tries to login again. 64 | 65 | It is a good idea to use the help of the database, to achieve that! 66 | __As always, don't forget the tests.__ 67 | 68 | ## 7. Reset password email 69 | 70 | Your customers need a reset password function! 71 | 72 | __Add an email field in the client module and in the database.__ 73 | Your command must look like this: 74 | 75 | ```send-reset-password Ivaylo``` 76 | 77 | It sends an email to the user, with a unique random hash code. 78 | 79 | ```reset-password Ivaylo``` 80 | 81 | That will ask you for the hash code, that was send to the user. If you know the hash it will led you to change your passwords. That proofs that you are the owner of that email. 82 | 83 | Try sending emails by using a gmail SMTP. GOOGLE IT! 84 | 85 | ## 8. Transactions in the bank 86 | 87 | Since this is a bank, every user should be able to make transactions to his bank account. 88 | 89 | For now, every user will have only 1 bank account. 90 | 91 | All users must be able to perform the following actions: 92 | 93 | * Deposit money in the bank account 94 | * Withdraw money from the bank account 95 | * Diplay the current balance from the bank accont 96 | 97 | The restrictions are as follow: 98 | 99 | * One cannot withdraw more money than the current balance 100 | 101 | Implement the following commands for every bank account. Those commands should work only if the user has been logged in to the system. 102 | 103 | Remember, TDD is your friend! 104 | 105 | 106 | ## 9. More secure transactions - TAN 107 | 108 | TAN (Transaction Authenticanon Number) is a extra layer of security. 109 | 110 | Now, for every transaction we want to make, we have to provide this extra piece of authentication - a 32 characters long code, that is unique for the user and the system! 111 | 112 | For example: 113 | 114 | ``` 115 | $$$>login 116 | Enter your username: rado 117 | Enter your password: 118 | Welcome you are logged in as: rado 119 | Logged>>deposit 120 | Enter amount: 1 000 000 121 | Enter TAN code: 8490c2c992d10003370509e7a008f659c8220b6db62e591449106d04a45174cc 122 | Transaction successful! 123 | 1 000 000 were deposited to the bank 124 | ``` 125 | 126 | Here are the details for the TAN codes: 127 | 128 | * TAN codes are required only for depositing and withdrawing transactions 129 | * __Every code can only be used once.__ After this, the code becomes inactive and cannot be given to complete a transactions 130 | * Once registered, a bank user starts without TAN codes - he have to ask for them 131 | 132 | Implement a command, called ```get-tan``` that does the following thing: 133 | 134 | * Can only be used for a logged-in user 135 | * Asks for the user password again 136 | * Emails the user a list of 10 unique TAN codes, that he can use for his next 10 transactions 137 | * If the command is called again, it says : `You have 10 remaining TAN codes to use` where 10 can be any number between 1 and 10 138 | * If there are 0 TAN codes remaining, generate 10 new for that user and email them to him! 139 | -------------------------------------------------------------------------------- /week7/materials.md: -------------------------------------------------------------------------------- 1 | # ORM with SQLAlchemy 2 | 3 | For the final week of the course, we are going to deal with ORM and the [Python library SQLAlchemy](http://www.sqlalchemy.org/)! 4 | 5 | The best way to get started with SQLAlchemy is to watch the video and follow the materials from here: http://www.sqlalchemy.org/library.html#introductiontosqlalchemy 6 | 7 | There are slides and code examples too! 8 | 9 | ## Step by step, working with SQLAlchemy 10 | 11 | We are going to take it slow and work step by step with the ORM. 12 | 13 | ### Install 14 | 15 | To install SQLAlchemy, we are going to use pip. __Don't forget to create virtualenv for your project!__ 16 | 17 | ``` 18 | pip install SQLAlchemy 19 | ``` 20 | 21 | This should install the latest version (Which is 0.9.4 by the time of writing this) 22 | 23 | 24 | ### Getting around the ORM system 25 | 26 | We will start with a small chunk of Python code, that will create a table, called `student` and map it to `Student` class. 27 | 28 | ```python 29 | from sqlalchemy.ext.declarative import declarative_base 30 | from sqlalchemy import Column, Integer, String 31 | from sqlalchemy import create_engine 32 | 33 | # A class that maps to a table, inherits from Base 34 | Base = declarative_base() 35 | 36 | 37 | # Our class will be mapped to a table with name student 38 | # Each field is a Column with the given type and constraints 39 | class Student(Base): 40 | __tablename__ = "student" 41 | id = Column(Integer, primary_key=True) 42 | name = Column(String) 43 | age = Column(Integer) 44 | 45 | 46 | engine = create_engine("sqlite:///university.db") 47 | # will create all tables 48 | Base.metadata.create_all(engine) 49 | ``` 50 | 51 | If we run this program, lets call it `orm.py`: 52 | 53 | ``` 54 | $ python orm.py 55 | $ ls 56 | orm.py env university.db 57 | ``` 58 | 59 | We are going to see that `university.db` was created. 60 | 61 | If we open the file with `sqlite3` and ask for `.tables`: 62 | 63 | ``` 64 | $ sqlite3 university.db 65 | SQLite version 3.7.9 2011-11-01 00:52:41 66 | Enter ".help" for instructions 67 | Enter SQL statements terminated with a ";" 68 | sqlite> .tables 69 | student 70 | ``` 71 | 72 | We will see that student table was created. 73 | 74 | ### Adding new record to our database 75 | 76 | Lets change `orm.py` to the following code: 77 | 78 | ```python 79 | from sqlalchemy.ext.declarative import declarative_base 80 | from sqlalchemy import Column, Integer, String 81 | from sqlalchemy import create_engine 82 | from sqlalchemy.orm import Session 83 | 84 | 85 | Base = declarative_base() 86 | 87 | 88 | class Student(Base): 89 | __tablename__ = "student" 90 | id = Column(Integer, primary_key=True) 91 | name = Column(String) 92 | age = Column(Integer) 93 | 94 | 95 | engine = create_engine("sqlite:///university.db") 96 | # will create all tables 97 | Base.metadata.create_all(engine) 98 | 99 | # Session is our Data Mapper 100 | session = Session(bind=engine) 101 | 102 | print("Adding new student to the database via the session object") 103 | student1 = Student(name="Rado", age=23) 104 | session.add(student1) 105 | session.commit() 106 | ``` 107 | 108 | Now if we run the script and check our database: 109 | 110 | ``` 111 | $ python orm.py 112 | Adding new student to the database via the session object 113 | $ sqlite3 university.db 114 | SQLite version 3.7.9 2011-11-01 00:52:41 115 | Enter ".help" for instructions 116 | Enter SQL statements terminated with a ";" 117 | sqlite> select * from student; 118 | 1|Rado|23 119 | ``` 120 | 121 | ### Adding multiple records & querying them 122 | 123 | Lets add 3 students and query back to display them. 124 | 125 | This is achieved by the following transformation of `orm.py`: 126 | 127 | ```python 128 | from sqlalchemy.ext.declarative import declarative_base 129 | from sqlalchemy import Column, Integer, String 130 | from sqlalchemy import create_engine 131 | from sqlalchemy.orm import Session 132 | 133 | 134 | Base = declarative_base() 135 | 136 | 137 | class Student(Base): 138 | __tablename__ = "student" 139 | id = Column(Integer, primary_key=True) 140 | name = Column(String) 141 | age = Column(Integer) 142 | 143 | def __str__(self): 144 | return "{} - {}".format(self.id, self.name) 145 | 146 | def __repr__(self): 147 | return self.__str__() 148 | 149 | 150 | engine = create_engine("sqlite:///university.db") 151 | # will create all tables 152 | Base.metadata.create_all(engine) 153 | 154 | # Session is our Data Mapper 155 | session = Session(bind=engine) 156 | 157 | print("Adding new student to the database via the session object") 158 | session.add_all([ 159 | Student(name="Rado", age=23), 160 | Student(name="Ivo", age=21), 161 | Student(name="Ivan", age=23)]) 162 | 163 | 164 | # SELECT * FROM student; 165 | all_students = session.query(Student).all() 166 | # list of Student objects 167 | print(all_students) 168 | ``` 169 | 170 | Now if we run the program: 171 | 172 | ``` 173 | $ python orm.py 174 | Adding new student to the database via the session object 175 | [1 - Rado, 2 - Ivo, 3 - Ivan] 176 | ``` 177 | 178 | This is a list of Student objects, but we have `__str__` and `__repr__` implemented. 179 | 180 | ### More on queyring - WHERE statement via filter 181 | 182 | Lets have the following `orm.py`: 183 | 184 | ```python 185 | from sqlalchemy.ext.declarative import declarative_base 186 | from sqlalchemy import Column, Integer, String 187 | from sqlalchemy import create_engine 188 | from sqlalchemy.orm import Session 189 | 190 | 191 | Base = declarative_base() 192 | 193 | 194 | class Student(Base): 195 | __tablename__ = "student" 196 | id = Column(Integer, primary_key=True) 197 | name = Column(String) 198 | age = Column(Integer) 199 | 200 | def __str__(self): 201 | return "{} - {}".format(self.id, self.name) 202 | 203 | def __repr__(self): 204 | return self.__str__() 205 | 206 | 207 | engine = create_engine("sqlite:///university.db") 208 | # will create all tables 209 | Base.metadata.create_all(engine) 210 | 211 | # Session is our Data Mapper 212 | session = Session(bind=engine) 213 | 214 | print("Adding new student to the database via the session object") 215 | session.add_all([ 216 | Student(name="Rado", age=23), 217 | Student(name="Ivo", age=21), 218 | Student(name="Ivan", age=23)]) 219 | 220 | 221 | # SELECT * FROM student WHERE name = "Rado"; 222 | rado = session.query(Student).filter(Student.name == "Rado").all() 223 | print(rado) 224 | 225 | # SELECT name, age FROM student WHERE age = 23 226 | twenty_three = session.query(Student.name, Student.age).\ 227 | filter(Student.age == 23).all() 228 | 229 | for student in twenty_three: 230 | print("Name {} with age {}".format(student.name, student.age)) 231 | ``` 232 | 233 | If we run the program, we will get the following output: 234 | 235 | ``` 236 | $ python orm.py 237 | Adding new student to the database via the session object 238 | [1 - Rado] 239 | Name Rado with age 23 240 | Name Ivan with age 23 241 | ``` 242 | 243 | ### We call it relationship. And we build it with Foreign Keys! 244 | 245 | Now, we are going to introduce another class (table), called Grade. 246 | 247 | We will keep many grades for our students. The relationship here is: 248 | 249 | * 1 student has many grades. 250 | * Many grades can be assigned to one student 251 | 252 | We will introduce foreign key, when creating the new class: 253 | 254 | ```python 255 | # One student can have many grades 256 | # So we will have one-to-many relationship here 257 | class Grade(Base): 258 | __tablename__ = "grade" 259 | id = Column(Integer, primary_key=True) 260 | value = Column(Float) 261 | student_id = Column(Integer, ForeignKey("student.id")) 262 | student = relationship("Student", backref="grades") 263 | ``` 264 | 265 | Here is the entire program. Don't forget to remove `university.db` before running. 266 | 267 | ```python 268 | from sqlalchemy.ext.declarative import declarative_base 269 | from sqlalchemy import Column, Integer, String, Float 270 | from sqlalchemy import create_engine 271 | from sqlalchemy import ForeignKey 272 | from sqlalchemy.orm import Session 273 | from sqlalchemy.orm import relationship 274 | 275 | 276 | Base = declarative_base() 277 | 278 | 279 | class Student(Base): 280 | __tablename__ = "student" 281 | id = Column(Integer, primary_key=True) 282 | name = Column(String) 283 | age = Column(Integer) 284 | 285 | def __str__(self): 286 | return "{} - {}".format(self.id, self.name) 287 | 288 | def __repr__(self): 289 | return self.__str__() 290 | 291 | 292 | # One student can have many grades 293 | # So we will have one-to-many relationship here 294 | class Grade(Base): 295 | __tablename__ = "grade" 296 | id = Column(Integer, primary_key=True) 297 | value = Column(Float) 298 | student_id = Column(Integer, ForeignKey("student.id")) 299 | student = relationship("Student", backref="grades") 300 | 301 | engine = create_engine("sqlite:///university.db") 302 | # will create all tables 303 | Base.metadata.create_all(engine) 304 | 305 | # Session is our Data Mapper 306 | session = Session(bind=engine) 307 | 308 | print("Adding new student to the database via the session object") 309 | session.add_all([ 310 | Student(name="Rado", age=23), 311 | Student(name="Ivo", age=21), 312 | Student(name="Ivan", age=23)]) 313 | 314 | 315 | # SELECT * FROM student WHERE name = "Rado"; 316 | rado = session.query(Student).filter(Student.name == "Rado").one() 317 | 318 | # Now, lets add some grades to rado: 319 | 320 | rado.grades = [Grade(value=6), Grade(value=5), Grade(value=3)] 321 | session.commit() 322 | 323 | # And add grades to ivo 324 | 325 | ivo = session.query(Student).filter(Student.name == "Ivo").one() 326 | ivo.grades.append(Grade(value=6)) 327 | 328 | # And now, lets see the avg of grades of Rado: 329 | from sqlalchemy import func 330 | 331 | avg_grades = session.query(func.avg(Grade.value)).\ 332 | filter(Student.id == ivo.id).\ 333 | one() 334 | print(avg_grades) 335 | 336 | ``` 337 | -------------------------------------------------------------------------------- /week7/problems.md: -------------------------------------------------------------------------------- 1 | # Problems to be solved by using SQLAlchemy's ORM 2 | 3 | ## Guidelines 4 | 5 | * Start with the database and first create your classes, that are going to be mapped. 6 | * Add the interface later - when you are OK with the classes and you are sure that they are mapping correctly to the tables. 7 | * Use sqlite 8 | * You can skip the tests for now. [__We are going to make this a spike!__](http://www.extremeprogramming.org/rules/spike.html) 9 | * Experiment with the library and don't be afraid if your program is not working correctly. The given specifications are not that strict! 10 | 11 | ## The "Do you even math?" game 12 | 13 | We are going to implement a dead-simple console game, that asks the user to give the right answer to a random mathematical expression. 14 | 15 | __For example:__ 16 | 17 | ``` 18 | What is the answer to 6 x 6? 19 | ?>36 20 | ?>Correct! 21 | 22 | What is the answer to 2 ^ 8? 23 | ?>10 24 | Incorrect! Ending game. You score is: 1 25 | ``` 26 | 27 | __We want to have the following features:__ 28 | 29 | * The game can be played by different user. In the beginning, a user can choose his playername to play with 30 | * We want to keep the score for every user that played the game 31 | * We want to be able to display a highscores table, that lists the top 10 users, played this game 32 | 33 | __The implementation details are up to you!__ 34 | 35 | ### Calculating the score 36 | 37 | We will have a function, that will calculate the score for each player. 38 | 39 | The game ends when a player gives a wrong answer to the asked mathematical expression. 40 | 41 | If we have `n` consecutive right answers, the final score is calculated by: 42 | 43 | ``` 44 | score(n) = n * n 45 | ``` 46 | 47 | It's just the square of `n` 48 | 49 | ### Example of playing the game 50 | 51 | ``` 52 | Welcome to the "Do you even math?" game! 53 | Here are your options: 54 | - start 55 | - highscores 56 | ?>start 57 | 58 | Enter your playername>radorado 59 | Welcome radorado! Let the game begin! 60 | 61 | Question #1: 62 | What is the answer to 6 x 6? 63 | ?> 36 64 | Correct! 65 | 66 | Question #2: 67 | What is the answer to 5 + 1? 68 | ?> 6 69 | Correct! 70 | 71 | Question #3: 72 | What is the answer to 1 + 1? 73 | ?> 3 74 | Incorrect! Ending game. You score is: 4 75 | ``` 76 | 77 | ``` 78 | ?>highscores 79 | 80 | This is the current top10: 81 | 82 | 1. radorado with 4 points 83 | ``` 84 | 85 | ## The Social Address book 86 | 87 | We are going to implement a simple program, that keeps a list of our friends and their corresponding social accounts (Facebook, Twitter, GitHub, email etc.) 88 | 89 | The social account types should not be predefined. 90 | 91 | We should be able to perform the following tasks: 92 | 93 | * Add new friend by giving his/her first and last name 94 | * Attach social accounts to him/her (Facebook, Twitter, GitHub, email, etc.) 95 | * Attach social accounts to already added friends in our list 96 | * List all social accounts for a given friend 97 | * List all specific accounts (For example - twitter) for all friends in our list 98 | * Perform a check which social account is present in all friends in our list. For example, everyone have a Facebook and Twitter account. 99 | 100 | __The implementation details are up to you!__ 101 | 102 | ### Example usage 103 | 104 | ``` 105 | >add_friend 106 | First Name: Rado 107 | Last Name: Rado 108 | Friend with unique id of 1 was created! 109 | >add_social 1 110 | Adding social account to Rado Rado: 111 | Social account type: twitter 112 | Handle or url?: @Rado_g 113 | @Rado_g added as twitter to Rado Rado 114 | Add more accounts? (y/n): n 115 | ``` 116 | 117 | Now, if we continue, check that the program is smart: 118 | 119 | ``` 120 | >add_friend 121 | First Name: Ivo 122 | Last Name: Ivo 123 | Friend with unique id of 2 was created! 124 | >add_social 1 125 | Adding social account to Ivo Ivo: 126 | Social account type: Twitter 127 | 128 | WAIT! We have found somethinf similar in the database: 129 | twitter 130 | Do you want me to use twitter instead of Twitter? (y/n): y 131 | Handle or url?: @IvoIvo 132 | @IvoIvo added as twitter to Ivo Ivo 133 | Add more accounts? (y/n): n 134 | ``` 135 | --------------------------------------------------------------------------------