├── week10-Django ├── django_cinema │ ├── website │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_projection.py │ │ │ └── 0003_auto_20150521_1007.py │ │ ├── tests.py │ │ ├── admin.py │ │ ├── urls.py │ │ ├── templates │ │ │ ├── index.html │ │ │ └── movie.html │ │ ├── models.py │ │ └── views.py │ ├── django_cinema │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── wsgi.py │ │ └── settings.py │ ├── db.sqlite3 │ └── manage.py ├── oldschool_chat │ ├── website │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ └── 0001_initial.py │ │ ├── tests.py │ │ ├── admin.py │ │ ├── urls.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── index.html │ │ │ ├── register.html │ │ │ └── chat.html │ │ └── views.py │ ├── oldschool_chat │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── wsgi.py │ │ └── settings.py │ ├── db.sqlite3 │ └── manage.py ├── README.md └── 1-Cinema-Reservation-with-Django │ └── README.md ├── week9-SQLAlchemy ├── solutions │ ├── requirements.txt │ ├── settings.py │ ├── base.py │ ├── helpers.py │ ├── start.py │ ├── transaction_controller.py │ ├── bank_controller.py │ ├── models.py │ ├── bank_view.py │ └── authentication_controller.py ├── materials │ ├── settings.py │ ├── alchemy.py │ ├── classroom.py │ └── person.py ├── 1-Money-In-The-Bank │ ├── Client.py │ ├── tests │ │ ├── client_test.py │ │ └── sql_manager_test.py │ ├── sql_manager.py │ ├── start.py │ └── README.md └── README.md ├── week4-Music-Library ├── 1-Music-Library │ ├── requirements.txt │ ├── p.py │ ├── README.md │ └── music.py ├── materials │ └── args.py └── README.md ├── week7-Intro-to-SQL ├── 1-Scan-Bg-Web │ ├── db-solution │ │ ├── requirements.txt │ │ ├── servers.py │ │ ├── db_DROPS_ALL_DATABASES_creation.sql │ │ ├── links.py │ │ ├── domains.py │ │ ├── crawler.py │ │ └── collector.py │ ├── histogram.png │ ├── hist.py │ ├── plot_results.py │ ├── scanner.py │ └── README.md ├── 2-SQL-Starter │ ├── query_table.py │ └── create_and_insert.py ├── 3-Weekend-Challenge │ └── README.md └── README.md ├── week8-Team-Work ├── 3-Cinema-Reservation-System │ ├── settings.py │ ├── tables.sql │ ├── create_tables.py │ └── README.md ├── 2-SQL-Over-Northwind │ ├── northwind.db │ ├── Northwind.sql │ ├── Northwind_relations.png │ └── README.md ├── README.md ├── 1-The-Last-HR │ ├── hr_database_creation.py │ ├── README.md │ └── hr.py └── materials │ └── relationships.md ├── week5-Team-Work ├── 1-Dungeons-and-Pythons │ ├── level1.txt │ └── dungeon.py └── README.md ├── week2-OOP-Problems ├── Polyglot │ ├── polyglot.db │ ├── README.md │ └── polyglot.py ├── 2-File-System-Problems │ ├── generate_numbers.py │ ├── cat.py │ ├── duhs.py │ └── README.md ├── materials │ ├── point.py │ ├── program_arguments.md │ ├── panda.py │ └── working_with_files.md ├── 4-Fractions │ └── README.md ├── README.md └── 3-Cash-Desk │ ├── cashdesk.py │ ├── README.md │ └── cashdesk_test.py ├── week11-Advanced-Python ├── generators │ ├── Book.zip │ └── README.md └── decorators │ └── README.md ├── week3-TDD-and-Graphs ├── 2-Retrospecive │ └── README.md ├── README.md ├── materials │ ├── panda_json.py │ └── graph.py ├── 1-Bank-Account │ ├── bank.py │ ├── README.md │ └── bank_tests.py └── 3-Panda-Social-Network │ ├── README.md │ └── social.py ├── helpers ├── people ├── teams └── make_teams.py ├── config ├── Preferences.sublime-settings └── RadoRado.sublime-settings ├── week6-Pip-And-Http ├── README.md └── 1-Who-Follows-You-Back │ └── README.md ├── Application ├── README.md ├── 1-Fill-tetrahedron-with-water │ └── README.md ├── 2-Tetrahedron-filled-with-water │ └── README.md └── 3-Caesar-cipher │ └── README.md ├── week1-Python-Problems ├── README.md ├── 1-Warmups │ └── warmup.py ├── materials │ ├── how_to_run_your_python_code.md │ └── python_data_structures.md ├── 2-The-Real-Deal │ ├── solutions.py │ └── README.md └── 3-The-Final-Round │ └── solutions.py ├── .gitignore └── README.md /week10-Django/django_cinema/website/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/django_cinema/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/oldschool_chat/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/requirements.txt: -------------------------------------------------------------------------------- 1 | SQLAlchemy==1.0.4 2 | -------------------------------------------------------------------------------- /week4-Music-Library/1-Music-Library/requirements.txt: -------------------------------------------------------------------------------- 1 | mutagen==1.28 2 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/materials/settings.py: -------------------------------------------------------------------------------- 1 | DB_CONNECTION_STRING = "sqlite:///classroom.db" 2 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/db-solution/requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.3.2 2 | requests==2.7.0 3 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /week8-Team-Work/3-Cinema-Reservation-System/settings.py: -------------------------------------------------------------------------------- 1 | DB_NAME = "cinema.db" 2 | SQL_FILE = "tables.sql" 3 | 4 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /week5-Team-Work/1-Dungeons-and-Pythons/level1.txt: -------------------------------------------------------------------------------- 1 | SE.##....T 2 | #T##..###. 3 | #.###E###E 4 | #.E...###S 5 | ###T#####G 6 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week10-Django/django_cinema/db.sqlite3 -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week10-Django/oldschool_chat/db.sqlite3 -------------------------------------------------------------------------------- /week2-OOP-Problems/Polyglot/polyglot.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week2-OOP-Problems/Polyglot/polyglot.db -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/settings.py: -------------------------------------------------------------------------------- 1 | DATABASE = "sqlite:///bank.db" 2 | BLOCK_AFTER_N_FAILED_ATTEMPTS = 5 3 | BLOCK_FOR_N_MINUTES = 5 4 | -------------------------------------------------------------------------------- /week11-Advanced-Python/generators/Book.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week11-Advanced-Python/generators/Book.zip -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week7-Intro-to-SQL/1-Scan-Bg-Web/histogram.png -------------------------------------------------------------------------------- /week8-Team-Work/2-SQL-Over-Northwind/northwind.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week8-Team-Work/2-SQL-Over-Northwind/northwind.db -------------------------------------------------------------------------------- /week8-Team-Work/2-SQL-Over-Northwind/Northwind.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week8-Team-Work/2-SQL-Over-Northwind/Northwind.sql -------------------------------------------------------------------------------- /week8-Team-Work/2-SQL-Over-Northwind/Northwind_relations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Programming101-3/HEAD/week8-Team-Work/2-SQL-Over-Northwind/Northwind_relations.png -------------------------------------------------------------------------------- /week8-Team-Work/3-Cinema-Reservation-System/tables.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS Cinemas; 2 | 3 | CREATE TABLE Cinemas( 4 | cinema_id INTEGER PRIMARY KEY, 5 | cinema_name TEXT 6 | ); 7 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Main idea here - share the Base across different files. 3 | """ 4 | from sqlalchemy.ext.declarative import declarative_base 5 | 6 | Base = declarative_base() 7 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Movie, Projection, Rating 3 | 4 | admin.site.register(Movie) 5 | admin.site.register(Projection) 6 | admin.site.register(Rating) 7 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/helpers.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | 4 | def hash_password(password): 5 | sha256 = hashlib.sha256() 6 | 7 | sha256.update(password.encode("utf-8")) 8 | 9 | return sha256.hexdigest() 10 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | 4 | urlpatterns = [ 5 | url(r'^$', views.index), 6 | url(r'^show_movie/$', views.show_movie), 7 | url(r'^rate_movie/$', views.rate_movie) 8 | ] 9 | -------------------------------------------------------------------------------- /week4-Music-Library/1-Music-Library/p.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | 3 | def play(mp3Path): 4 | p = Popen(["mpg123", mp3Path], stdout=PIPE, stderr=PIPE) 5 | return p 6 | 7 | def stop(process): 8 | process.kill() 9 | 10 | p = play("music.mp3") 11 | stop(p) 12 | -------------------------------------------------------------------------------- /week8-Team-Work/3-Cinema-Reservation-System/create_tables.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import sys 3 | from settings import DB_NAME, SQL_FILE 4 | 5 | 6 | conn = sqlite3.connect(DB_NAME) 7 | 8 | with open(SQL_FILE, "r") as f: 9 | conn.executescript(f.read()) 10 | conn.commit() 11 | 12 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/2-Retrospecive/README.md: -------------------------------------------------------------------------------- 1 | # Write tests for all previous problems 2 | 3 | We must be sure that all of our previous problems have tests! 4 | 5 | So go back the weeks and implement tests for each problem. 6 | 7 | **For the set of problems from week0, you can do 1 test (for each set).** 8 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/django_cinema/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from django.contrib import admin 3 | 4 | from website import urls as website_urls 5 | 6 | urlpatterns = [ 7 | url(r'^admin/', include(admin.site.urls)), 8 | url(r'', include(website_urls)) 9 | ] 10 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/oldschool_chat/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from django.contrib import admin 3 | 4 | from website import urls as website_urls 5 | 6 | urlpatterns = [ 7 | url(r'^admin/', include(admin.site.urls)), 8 | url(r'', include(website_urls)) 9 | ] 10 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_cinema.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oldschool_chat.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /week4-Music-Library/materials/args.py: -------------------------------------------------------------------------------- 1 | # variadic arguments 2 | def sum(*arg, **kwargs): 3 | print(arg) 4 | print(kwargs) 5 | 6 | print(sum(1, 2, 3, 4, 5, 6, coef=-1)) 7 | 8 | # Позиционни аргументи и keyword аргументи 9 | def something(arg=1): 10 | print(arg) 11 | 12 | something() 13 | something(2) 14 | something(arg=3) 15 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url(r"^$", views.index, name="index"), 7 | url(r"^register/$", views.register, name="register"), 8 | url(r"^chat/", views.chat, name="chat"), 9 | url(r"^logout/$", views.chat_logout, name="logout"), 10 | ] 11 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/templates/index.html: -------------------------------------------------------------------------------- 1 |

All movies:

2 | 3 | {% for movie in movies %} 4 |

{{ movie.name }}

5 | 6 |

Projections for that movie:

7 | 8 | {% for projection in movie.projection_set.all %} 9 |

{{ projection }}

10 | {% endfor %} 11 | {% endfor %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /week8-Team-Work/README.md: -------------------------------------------------------------------------------- 1 | # Relationships, Foreign Keys and JOINs 2 | 3 | We are going to model more complicated databases - with relationships. 4 | 5 | Check the following materials: 6 | 7 | * [What is a relationship between two tables? What is a foreign key?](materials/relationships.md) 8 | * [sqlite3 documentation about foreign keys](https://www.sqlite.org/foreignkeys.html) 9 | 10 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/2-SQL-Starter/query_table.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | db = sqlite3.connect("users.db") 4 | db.row_factory = sqlite3.Row 5 | 6 | cursor = db.cursor() 7 | 8 | select_one = """ 9 | SELECT email, id, gender 10 | FROM users 11 | WHERE id = 1 12 | """ 13 | 14 | cursor_result = cursor.execute(select_one) 15 | 16 | row = cursor_result.fetchone() 17 | print(row["email"]) 18 | 19 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/db-solution/servers.py: -------------------------------------------------------------------------------- 1 | class Servers: 2 | INSERT_SERVER_SQL = """ 3 | INSERT INTO Servers(link_id, url, server_string) 4 | VALUES(?, ?, ?) 5 | """ 6 | 7 | @classmethod 8 | def insert_server(cls, conn, link_id, url, server_string): 9 | cursor = conn.cursor() 10 | 11 | cursor.execute(INSERT_SERVER_SQL, (link_id, url, server_string)) 12 | -------------------------------------------------------------------------------- /helpers/people: -------------------------------------------------------------------------------- 1 | Николай Петков 2 | Севгин Мустафов 3 | Никола Павлов 4 | Пресиан Данаилов 5 | Сияна Плачкова 6 | Георги Балабанов 7 | Василена Байрактарова 8 | Цветан Христов 9 | Костадин Хамънов 10 | Антоний Павлов 11 | Анета Неделчева 12 | Петър Иванов 13 | Никола Цолов 14 | Стилян Танев 15 | Ивелин Тодоров 16 | Филип Генев 17 | Мария Цалта-Цветкова 18 | Владислав Атанасов 19 | Десислава Минева 20 | Венцислав Джукелов 21 | -------------------------------------------------------------------------------- /config/Preferences.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "draw_white_space": "all", 3 | "highlight_line": true, 4 | "highlight_modified_tabs": true, 5 | "tab_size": 4, 6 | "ignored_packages":[ 7 | "Vintage" 8 | ], 9 | "translate_tabs_to_spaces": true, 10 | "trim_trailing_white_space_on_save": true, 11 | "ensure_newline_at_eof_on_save": true, 12 | "detect_indentation": false, 13 | "draw_centered": false, 14 | } 15 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | class ChatMessage(models.Model): 5 | message = models.CharField(max_length=1000) 6 | timestamp = models.DateTimeField(auto_now_add=True) 7 | user = models.ForeignKey(User) 8 | 9 | def __str__(self): 10 | return "[{}]: {}: {} from {}".format(self.timestamp, self.id, self.message, self.user) 11 | 12 | -------------------------------------------------------------------------------- /helpers/teams: -------------------------------------------------------------------------------- 1 | Десислава Минева + Филип Генев = team! 2 | Никола Цолов + Николай Петков = team! 3 | Никола Павлов + Сияна Плачкова = team! 4 | Венцислав Джукелов + Петър Иванов = team! 5 | Севгин Мустафов + Василена Байрактарова = team! 6 | Владислав Атанасов + Пресиан Данаилов = team! 7 | Антоний Павлов + Цветан Христов = team! 8 | Костадин Хамънов + Стилян Танев = team! 9 | Георги Балабанов + Анета Неделчева = team! 10 | Ивелин Тодоров + Мария Цалта-Цветкова = team! 11 | -------------------------------------------------------------------------------- /week6-Pip-And-Http/README.md: -------------------------------------------------------------------------------- 1 | # Week6 - HTTP introduction 2 | 3 | We are going to have some fun with the HTTP protocol. This is the protocol responsible for the entire web to run properly. 4 | 5 | Here are the materials: 6 | 7 | * [HTTP slides from the Ruby on Rails course](http://rails.hackbulgaria.com/lectures/03#/19) 8 | * [Python's requests module](http://docs.python-requests.org/en/latest/) 9 | * [What Happens When guide for HTTP](https://github.com/alex/what-happens-when) 10 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/start.py: -------------------------------------------------------------------------------- 1 | from bank_controller import BankController 2 | from bank_view import BankView 3 | from settings import DATABASE, BLOCK_AFTER_N_FAILED_ATTEMPTS, BLOCK_FOR_N_MINUTES 4 | 5 | controller = BankController(DATABASE, 6 | block_after_n_logins=BLOCK_AFTER_N_FAILED_ATTEMPTS, 7 | block_for_n_minutes=BLOCK_FOR_N_MINUTES) 8 | view = BankView(controller) 9 | 10 | view.start_taking_commands() 11 | 12 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/transaction_controller.py: -------------------------------------------------------------------------------- 1 | from models import Client, BankAccount 2 | 3 | 4 | class TransactionController: 5 | 6 | def __init__(self, session): 7 | self.__session = session 8 | 9 | def create_account(self, user, account_name): 10 | account = BankAccount(balance=0, name=account_name) 11 | user.bank_accounts.append(account) 12 | 13 | self.__session.add(user) 14 | self.__session.commit() 15 | 16 | return account 17 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/django_cinema/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_cinema project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_cinema.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/oldschool_chat/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for oldschool_chat project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oldschool_chat.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/templates/index.html: -------------------------------------------------------------------------------- 1 |

Oldschool chat. You control the refresh!

2 | 3 |

Register here

4 | 5 |
6 | {% csrf_token %} 7 |
8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 |
16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/2-SQL-Starter/create_and_insert.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | connection = sqlite3.connect("users.db") 4 | cursor = connection.cursor() 5 | 6 | create_users_table = """ 7 | CREATE TABLE IF NOT EXISTS 8 | users(id INTEGER PRIMARY KEY, email TEXT, gender TEXT) 9 | """ 10 | 11 | insert_users = """ 12 | INSERT INTO users(email, gender) 13 | VALUES ("asd@asd.com", "male"), 14 | ("asd2@asd.com", "female") 15 | """ 16 | 17 | 18 | cursor.execute(create_users_table) 19 | cursor.execute(insert_users) 20 | connection.commit() 21 | -------------------------------------------------------------------------------- /Application/README.md: -------------------------------------------------------------------------------- 1 | # Application for Programming 101, third edition 2 | 3 | In order to apply, you have to solve the following 3 problems: 4 | 5 | - [1 Fill tetrahedron with water](https://github.com/HackBulgaria/Programming101-3/blob/master/Application/1-Fill-tetrahedron-with-water/README.md) 6 | - [2 Tetrahedron filled with water](https://github.com/HackBulgaria/Programming101-3/blob/master/Application/2-Tetrahedron-filled-with-water/README.md) 7 | - [3 Caesar cipher](https://github.com/HackBulgaria/Programming101-3/blob/master/Application/3-Caesar-cipher/README.md) 8 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/templates/register.html: -------------------------------------------------------------------------------- 1 |

Register for chat:

2 | 3 |
4 | {% csrf_token %} 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |
19 | 20 | 21 |
22 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/README.md: -------------------------------------------------------------------------------- 1 | # Week 3 - TDD & OOP Problems 2 | 3 | Materials to read: 4 | 5 | 1. [TDD from Stefan Kanev](https://www.youtube.com/watch?v=ToyPKRiQCQk) 6 | 2. [unittest documentation and examples](https://docs.python.org/3/library/unittest.html) 7 | 3. [Python errors & exceptions](https://docs.python.org/3.4/tutorial/errors.html) 8 | 4. [Graph algorithms in Python](https://www.python.org/doc/essays/graphs/) 9 | 5. [How to find the shortest path in an unweighted graph using BFS](materials/graph.py) 10 | 6. [How to serialize / deserializ a class in Python using JSON](materials/panda_json.py) 11 | -------------------------------------------------------------------------------- /week2-OOP-Problems/2-File-System-Problems/generate_numbers.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from random import randint 3 | from argv import has_arguments 4 | 5 | 6 | def n_random_numbers(n): 7 | return [randint(1, 1000) for x in range(n)] 8 | 9 | 10 | def main(): 11 | if has_arguments(2): 12 | filename = sys.argv[1] 13 | n = int(sys.argv[2]) 14 | 15 | f = open(filename, "w") 16 | 17 | contents = [str(x) for x in n_random_numbers(n)] 18 | 19 | f.write(" ".join(contents)) 20 | f.write("\n") 21 | f.close() 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/1-Money-In-The-Bank/Client.py: -------------------------------------------------------------------------------- 1 | class Client(): 2 | def __init__(self, id, username, balance, message): 3 | self.__username = username 4 | self.__balance = balance 5 | self.__id = id 6 | self.__message = message 7 | 8 | def get_username(self): 9 | return self.__username 10 | 11 | def get_balance(self): 12 | return self.__balance 13 | 14 | def get_id(self): 15 | return self.__id 16 | 17 | def get_message(self): 18 | return self.__message 19 | 20 | def set_message(self, new_message): 21 | self.__message = new_message 22 | -------------------------------------------------------------------------------- /week4-Music-Library/README.md: -------------------------------------------------------------------------------- 1 | # Week4 - More problems with Python OO & TDD 2 | 3 | Before we jump into teamwork, virtualenv, pip and SQL, we must polish our Python skills a bit more. 4 | 5 | Here are some Python-specific readings: 6 | 7 | * [Python default arguments](https://docs.python.org/3.4/tutorial/controlflow.html#default-argument-values) 8 | * [Python keyword arguments](https://docs.python.org/3.4/tutorial/controlflow.html#keyword-arguments) 9 | * [More on Python keyword arguments](http://stackoverflow.com/questions/1419046/python-normal-arguments-vs-keyword-arguments) 10 | * [Example for keyword arguments](materials/args.py) 11 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/templates/movie.html: -------------------------------------------------------------------------------- 1 |

{{ movie.name }}

2 | 3 |

Movie id: {{ movie.id }}

4 |

Movie rating: {{ avg_rating }}

5 | 6 |
7 |
8 | {% csrf_token %} 9 | 10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 | -------------------------------------------------------------------------------- /week1-Python-Problems/README.md: -------------------------------------------------------------------------------- 1 | # Problems for week1 2 | 3 | The main idea behind the problems for week1 is to get familiar with Python as a programming language & dust-off (if needed) our problem-solving skills! 4 | 5 | Here is an index of the materials you have to check for week0: 6 | 7 | 1. [How to test & run your python code](materials/how_to_run_your_python_code.md) 8 | 2. [Basic Python data structres](materials/python_data_structures.md) 9 | 3. [Linux Terminal commands cheat sheet](http://cli.learncodethehardway.org/bash_cheat_sheet.pdf) 10 | 4. [List Comprehensions](https://docs.python.org/3.4/tutorial/datastructures.html#list-comprehensions) 11 | -------------------------------------------------------------------------------- /week2-OOP-Problems/2-File-System-Problems/cat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def get_file_contents(filename): 5 | f = open(filename, "r") 6 | contents = f.read() 7 | f.close() 8 | 9 | return contents 10 | 11 | 12 | def has_arguments(count): 13 | return len(sys.argv[1:]) >= count 14 | 15 | 16 | def main(): 17 | if has_arguments(1): 18 | result = [] 19 | 20 | for filename in sys.argv[1:]: 21 | result.append(get_file_contents(filename)) 22 | 23 | print("\n".join(result)) 24 | else: 25 | print("There are no arguments") 26 | 27 | if __name__ == "__main__": 28 | main() 29 | 30 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Movie', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), 17 | ('name', models.CharField(max_length=100)), 18 | ('rating', models.FloatField()), 19 | ], 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /week10-Django/README.md: -------------------------------------------------------------------------------- 1 | # Django 2 | 3 | We are going to make an introduction to Django. 4 | 5 | First, we you need to cover some basic HTML and CSS (without the JavaScript): 6 | 7 | * Codecademy has a great track for HTML - http://www.codecademy.com/tracks/web 8 | 9 | 10 | For start, we recommend you to do that official Django tutorial: 11 | 12 | * https://docs.djangoproject.com/en/1.8/intro/tutorial01/ - from part1 to part6. This will get you started! 13 | * Here is our sample Django project - https://github.com/HackBulgaria/DjangoSample 14 | * The book we recommend about Django is this one - http://twoscoopspress.org/collections/everything/products/two-scoops-of-django-1-8 15 | -------------------------------------------------------------------------------- /helpers/make_teams.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from random import shuffle 3 | 4 | 5 | def make_teams(people, group_size=2): 6 | result = [] 7 | shuffle(people) 8 | 9 | while len(people) != 0: 10 | group = people[:group_size] 11 | result.append(tuple(group)) 12 | people = people[group_size:] 13 | 14 | return result 15 | 16 | 17 | def main(): 18 | people = ["Rado", "Ivo"] 19 | 20 | with open("people", "r") as f: 21 | people = f.read().split("\n") 22 | people = [person.strip() for person in people if person.strip() != ""] 23 | 24 | for team in make_teams(people): 25 | m1, m2 = team 26 | print("{} + {} = team!".format(m1, m2)) 27 | 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/db-solution/db_DROPS_ALL_DATABASES_creation.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS Domains; 2 | 3 | CREATE TABLE Domains( 4 | domain_id INTEGER PRIMARY KEY, 5 | domain TEXT UNIQUE, 6 | visited INTEGER 7 | ); 8 | 9 | DROP TABLE IF EXISTS Links; 10 | 11 | CREATE TABLE Links( 12 | link_id INTEGER PRIMARY KEY, 13 | url TEXT UNIQUE, 14 | domain_id INTEGER, 15 | FOREIGN KEY(domain_id) REFERENCES Domains(domain_id) 16 | ); 17 | 18 | DROP TABLE IF EXISTS Servers; 19 | 20 | CREATE TABLE Servers( 21 | server_id INTEGER PRIMARY KEY, 22 | link_id INTEGER, 23 | url TEXT, 24 | server_string TEXT, 25 | FOREIGN KEY(link_id) REFERENCES Links(link_id) 26 | ); 27 | 28 | 29 | INSERT INTO Domains(domain, visited) 30 | VALUES ("http://start.bg", 0); 31 | 32 | -------------------------------------------------------------------------------- /Application/1-Fill-tetrahedron-with-water/README.md: -------------------------------------------------------------------------------- 1 | #Fill tetrahedron with water. 2 | You have to implement a function with the following signature: `fill_tetrahedron(num)`. 3 | - The argument num is of type integer. 4 | - The function should return a float (double). 5 | 6 | ![Regular tetrahedron](http://upload.wikimedia.org/wikipedia/commons/7/70/Tetrahedron.gif) 7 | 8 | `fill_tetrahedron(num)` takes one argument that is the edge length of a Regular tetrahedron in centimeters. It should return the amount of water that can be filled in the tetrahedron. Return that amount in liters. 9 | 10 | You can use any language you know. 11 | 12 | Examples: 13 | ``` 14 | >>> fill_tetrahedron(100) 15 | 117.85 16 | ``` 17 | You can fill Regular tetrahedron with edge of 100 cm with 117.85 liters of water. 18 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/migrations/0002_projection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('website', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Projection', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)), 18 | ('projection_type', models.CharField(max_length=10)), 19 | ('when', models.DateTimeField()), 20 | ('movie', models.ForeignKey(to='website.Movie')), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/README.md: -------------------------------------------------------------------------------- 1 | # Security and ORM 2 | 3 | We are going to talk about database security and use SQLAlchemy as an ORM! 4 | 5 | Here are some materials: 6 | 7 | * [To Protect and Infect Part 1 from CCC](https://www.youtube.com/watch?v=sW-N7qQU-tA) 8 | * [To Protect and Infect Part 2 from CCC](https://www.youtube.com/watch?v=vILAlhwUgIU) 9 | * [Васил Колев Крокодила - Сигурност на системи от реалния свят от BurgasConf 2014](https://www.youtube.com/watch?v=wC4ET20NAjA) 10 | * [Black Hat 2013 - Exploiting Network Surveillance Cameras Like a Hollywood Hacker](https://www.youtube.com/watch?v=B8DjTcANBx0) 11 | * [Introduction to SQL Alchemy - a great video to watch!](http://www.sqlalchemy.org/library.html#introductiontosqlalchemy) 12 | * [Step by Step code examples for SQL Alchemy](materials/sqlalchemy.md) 13 | -------------------------------------------------------------------------------- /week5-Team-Work/README.md: -------------------------------------------------------------------------------- 1 | # The first teamwork week 2 | 3 | We are going to do a bunch of problems, while working in teams of 2. 4 | 5 | In order to have a nice flow, check the following materials: 6 | 7 | * [GitHub Flow & How to work in teams](https://guides.github.com/introduction/flow/) 8 | * [What is a Git branch](http://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is) 9 | * [What is a Pull Request](https://help.github.com/articles/using-pull-requests/) 10 | * [How to make Sublime default commit message editor for git](https://help.github.com/articles/associating-text-editors-with-git/#using-sublime-text-as-your-editor) 11 | * [GitHub training videos](https://www.youtube.com/watch?v=8oRjP8yj2Wo&index=1&list=PLg7s6cbtAD165JTRsXh8ofwRw0PqUnkVH) 12 | * [A series of Atlasian Git tutorials](https://www.atlassian.com/git/) 13 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/templates/chat.html: -------------------------------------------------------------------------------- 1 |

You are: {{ user.username }}

2 | 3 |

Logout here

4 | 5 |

Logged users:

6 | 7 | {% for user in logged_users %} 8 | {{ user}} , 9 | {% endfor %} 10 | 11 |
12 | {% for message in messages %} 13 |

[{{ message.timestamp }}]: {{ message.message }} from {{ message.user.username }}

14 | {% endfor %} 15 |
16 | 17 |
18 |
19 | {% csrf_token %} 20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /week2-OOP-Problems/materials/point.py: -------------------------------------------------------------------------------- 1 | # Immutable 2 | 3 | 4 | class MutablePoint2D: 5 | def __init__(self, x, y): 6 | self.x = x 7 | self.y = y 8 | 9 | def __str__(self): 10 | return "2DPoint::({}, {})".format(self.x, self.y) 11 | 12 | def __repr__(self): 13 | return "({}, {})".format(self.x, self.y) 14 | 15 | def move(self, dx, dy): 16 | self.x += dx 17 | self.y += dy 18 | 19 | 20 | class ImmutablePoint2D: 21 | def __init__(self, x, y): 22 | self.x = x 23 | self.y = y 24 | 25 | def __str__(self): 26 | return "2DPoint::({}, {})".format(self.x, self.y) 27 | 28 | def __repr__(self): 29 | return "({}, {})".format(self.x, self.y) 30 | 31 | def move(self, dx, dy): 32 | return ImmutablePoint2D(self.x + dx, self.y + dy) 33 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | class Movie(models.Model): 6 | name = models.CharField(max_length=100) 7 | 8 | def __str__(self): 9 | return "{}: {}".format(self.id, self.name) 10 | 11 | class Rating(models.Model): 12 | rating = models.IntegerField() 13 | movie = models.ForeignKey(Movie) 14 | 15 | def __int__(self): 16 | return self.rating 17 | 18 | class Projection(models.Model): 19 | projection_type = models.CharField(max_length=10) 20 | when = models.DateTimeField() 21 | movie = models.ForeignKey(Movie) 22 | 23 | 24 | def __str__(self): 25 | return "{} for {} at {}".format(self.projection_type, self.movie.name, self.when) 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /week2-OOP-Problems/2-File-System-Problems/duhs.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | 5 | def has_arguments(arg_count=1): 6 | return len(sys.argv[1:]) >= arg_count 7 | 8 | 9 | def bytes_to_gb(b): 10 | return b / (10 ** -9) 11 | 12 | 13 | def main(): 14 | if has_arguments(): 15 | path = sys.argv[1] 16 | total_bytes_size = 0 17 | 18 | for root, subsirs, files in os.walk(path): 19 | for file_to_count in files: 20 | try: 21 | total_bytes_size += os.path.getsize(os.path.join(root, file_to_count)) 22 | except FileNotFoundError as error: 23 | print(error) 24 | 25 | print(bytes_to_gb(total_bytes_size)) 26 | else: 27 | print("No arguments were given.") 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | 33 | -------------------------------------------------------------------------------- /week8-Team-Work/1-The-Last-HR/hr_database_creation.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | 4 | CREATE_STUDENTS = """ 5 | CREATE TABLE IF NOT EXISTS Students( 6 | student_id INTEGER PRIMARY KEY, 7 | name TEXT, 8 | github TEXT 9 | ) 10 | """ 11 | 12 | CREATE_COURSES = """ 13 | CREATE TABLE IF NOT EXISTS Courses( 14 | course_id INTEGER PRIMARY KEY, 15 | name TEXT 16 | ) 17 | """ 18 | 19 | STUDENTS_TO_COURSES = """ 20 | CREATE TABLE IF NOT EXISTS Students_to_Courses( 21 | student_id INTEGER, 22 | course_id INTEGER, 23 | FOREIGN KEY(student_id) REFERENCES Students(student_id), 24 | FOREIGN KEY(course_id) REFERENCES Courses(course_id) 25 | ) 26 | """ 27 | 28 | conn = sqlite3.connect("hr.db") 29 | cursor = conn.cursor() 30 | 31 | for table in [CREATE_STUDENTS, CREATE_COURSES, STUDENTS_TO_COURSES]: 32 | cursor.execute(table) 33 | 34 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/materials/alchemy.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | from sqlalchemy import Column, Integer, String 3 | from sqlalchemy import create_engine 4 | from sqlalchemy.orm import Session 5 | 6 | Base = declarative_base() 7 | 8 | class Person(Base): 9 | __tablename__ = "People" 10 | person_id = Column(Integer, primary_key=True) 11 | person_name = Column(String) 12 | person_gender = Column(String) 13 | 14 | def __str__(self): 15 | return self.person_name 16 | 17 | engine = create_engine("sqlite:///people_db") 18 | Base.metadata.create_all(engine) 19 | 20 | session = Session(bind=engine) 21 | 22 | gosho = Person(person_name="Gosho", person_gender="male") 23 | print(gosho) 24 | 25 | session.add(gosho) 26 | session.commit() 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/1-Money-In-The-Bank/tests/client_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | sys.path.append("..") 4 | 5 | from Client import Client 6 | 7 | 8 | class ClientTests(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.test_client = Client(1, "Ivo", 200000.00, "Bitcoin mining makes me rich") 12 | 13 | def test_client_id(self): 14 | self.assertEqual(self.test_client.get_id(), 1) 15 | 16 | def test_client_name(self): 17 | self.assertEqual(self.test_client.get_username(), "Ivo") 18 | 19 | def test_client_balance(self): 20 | self.assertEqual(self.test_client.get_balance(), 200000.00) 21 | 22 | def test_client_message(self): 23 | self.assertEqual(self.test_client.get_message(), "Bitcoin mining makes me rich") 24 | 25 | 26 | if __name__ == '__main__': 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /week2-OOP-Problems/materials/program_arguments.md: -------------------------------------------------------------------------------- 1 | # Program Arguments 2 | 3 | When we run our Python scripts with `$ python3.4 script.py`, `script.py` is an argument to the Python program. 4 | 5 | We can do the same thing with our Python scripts: 6 | 7 | ``` 8 | $ python3.4 cat.py file.txt 9 | ``` 10 | 11 | Here for example, `file.txt` is an argument to the `cat.py` script. 12 | 13 | The simplest way to get your arguments is the following: 14 | 15 | ```python 16 | # argv.py 17 | import sys 18 | 19 | for arg in sys.argv: 20 | print(arg) 21 | ``` 22 | 23 | Now, if we run the following command on the shell, we will see the output: 24 | 25 | ``` 26 | $ python3.4 argv.py hello.txt program.py script.py 27 | argv.py 28 | hello.txt 29 | program.py 30 | script.py 31 | ``` 32 | 33 | __IMPORTANT:__ In Python, the first argument is always the file name! 34 | 35 | 36 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='ChatMessage', 17 | fields=[ 18 | ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), 19 | ('message', models.CharField(max_length=1000)), 20 | ('timestamp', models.DateTimeField(auto_now_add=True)), 21 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/migrations/0003_auto_20150521_1007.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('website', '0002_projection'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Rating', 16 | fields=[ 17 | ('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)), 18 | ('rating', models.IntegerField()), 19 | ], 20 | ), 21 | migrations.RemoveField( 22 | model_name='movie', 23 | name='rating', 24 | ), 25 | migrations.AddField( 26 | model_name='rating', 27 | name='movie', 28 | field=models.ForeignKey(to='website.Movie'), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/materials/panda_json.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class Panda: 5 | def __init__(self, name, email, gender): 6 | self.name = name 7 | self.email = email 8 | self.gender = gender 9 | 10 | def __str__(self): 11 | return "{} - {} - {}".format(self.name, self.email, self.gender) 12 | 13 | def __repr__(self): 14 | return "Panda('{}', '{}', '{}')".format(self.name, 15 | self.email, self.gender) 16 | 17 | 18 | def serialize_to(path, data): 19 | json_string = json.dumps(data, indent=4) 20 | 21 | with open(path, "w") as f: 22 | f.write(json_string) 23 | 24 | 25 | def unserialize_from(path): 26 | with open(path, "r") as f: 27 | contents = f.read() 28 | 29 | return json.loads(contents) 30 | 31 | p = Panda("Ivo", "ivo@pandamail.com", "male") 32 | serialize_to("panda.json", repr(p)) 33 | print(unserialize_from("panda.json")) 34 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/hist.py: -------------------------------------------------------------------------------- 1 | class Histogram: 2 | 3 | def __init__(self): 4 | self.__hist = {} 5 | 6 | def add(self, value): 7 | if value not in self.__hist: 8 | self.__hist[value] = 0 9 | 10 | self.__hist[value] += 1 11 | 12 | def count(self, value): 13 | if value in self.__hist: 14 | return self.__hist[value] 15 | 16 | def items(self): 17 | return self.__hist.items() 18 | 19 | def __str__(self): 20 | return str(self.__hist) 21 | 22 | def __repr__(self): 23 | return self.__str__() 24 | 25 | def get_dict(self): 26 | return self.__hist 27 | 28 | def main(): 29 | h = Histogram() 30 | 31 | h.add(1) 32 | h.add(1) 33 | h.add(2) 34 | h.add(2) 35 | h.add(3) 36 | 37 | for key, count in h.items(): 38 | print("{}: {}".format(key, count)) 39 | 40 | if __name__ == "__main__": 41 | main() 42 | -------------------------------------------------------------------------------- /Application/2-Tetrahedron-filled-with-water/README.md: -------------------------------------------------------------------------------- 1 | #Tetrahedron filled with water. 2 | You have to implement a function with the following signature: `tetrahedron_filled(tetrahedrons, water)`. 3 | - The argument tetrahedrons is list of integers. Each is edge length of a Regular tetrahedron 4 | - The argument water is an integer. It is the amount of water that we have in liters. 5 | - The function should return a integer. 6 | 7 | `tetrahedron_filled(tetrahedrons, water)`.It should return the maximum number of tetrahedrons that can be field with hater liters of water. 8 | 9 | You can use any language you know. 10 | You can use the previous function. 11 | 12 | Examples: 13 | ``` 14 | >>> tetrahedron_filled([100, 20, 30], 10) 15 | 2 16 | ``` 17 | 18 | You can fill only 2 of this Regular tetrahedrons with 10 liters of water. First you are going to fill the second than the third tetrahedron. With the total amount of ~ 4 liters. You won't have water to fill the first tetrahedron. 19 | -------------------------------------------------------------------------------- /week2-OOP-Problems/4-Fractions/README.md: -------------------------------------------------------------------------------- 1 | # An Immutable Fraction class 2 | 3 | We want to create a simple fraction class: 4 | 5 | ```python 6 | class Fraction: 7 | 8 | def __init__(self, numerator, denominator): 9 | self.numerator = numerator 10 | self.denominator = denominator 11 | 12 | 13 | def __str__(self): 14 | return "{} / {}".format(self.numerator, self.denominator) 15 | 16 | 17 | def __repr__(self): 18 | return self.__str__() 19 | ``` 20 | 21 | Our fractions should be able to do the following operations: 22 | 23 | * `+` 24 | * `-` 25 | * `*` 26 | * `==` 27 | 28 | Implement the needed **dunder** methods in order to achieve that. 29 | 30 | **Each operation that mutates the fraction, like `+`, `-` and `*` should return a new `Fraction`!** 31 | 32 | Examples: 33 | 34 | ```python 35 | a = Fraction(1, 2) 36 | b = Fraction(2, 4) 37 | 38 | a == b # True 39 | 40 | a + b # 1 41 | a - b # 0 42 | a * b # 1 / 4 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/plot_results.py: -------------------------------------------------------------------------------- 1 | from hist import Histogram 2 | import matplotlib.pyplot as plt 3 | 4 | 5 | servers = { 6 | "apache": "Apache", 7 | "nginx": "nginx", 8 | "iis": "IIS", 9 | "lighttpd": "lighttpd" 10 | } 11 | h = Histogram() 12 | 13 | with open("result.txt", "r") as f: 14 | lines = f.read().split("\n") 15 | 16 | for line in lines: 17 | for server in servers: 18 | if server in line.lower(): 19 | count = line.split(":")[1] 20 | count = int(count) 21 | 22 | for _ in range(count): 23 | h.add(servers[server]) 24 | 25 | h = h.get_dict() 26 | print(h) 27 | keys = list(h.keys()) 28 | values = list(h.values()) 29 | 30 | X = list(range(len(keys))) 31 | 32 | plt.bar(X, list(h.values()), align="center") 33 | plt.xticks(X, keys) 34 | 35 | plt.title(".bg servers") 36 | plt.xlabel("Server") 37 | plt.ylabel("Count") 38 | 39 | plt.savefig("histogram.png") 40 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/materials/classroom.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | from sqlalchemy import Column, Integer, String 3 | from sqlalchemy import ForeignKey 4 | from sqlalchemy import create_engine 5 | from sqlalchemy.orm import Session 6 | from sqlalchemy.orm import relationship 7 | 8 | 9 | Base = declarative_base() 10 | 11 | class Student(Base): 12 | __tablename__ = "Students" 13 | student_id = Column(Integer, primary_key=True) 14 | student_fn = Column(String(20), unique=True) 15 | student_name = Column(String) 16 | 17 | class Grade(Base): 18 | __tablename__ = "Grades" 19 | grade_id = Column(Integer, primary_key=True) 20 | grade_value = Column(Integer) 21 | student_id = Column(Integer, ForeignKey("Students.student_id")) 22 | student = relationship(Student, backref="grades") 23 | 24 | engine = create_engine("sqlite:///classroom.sqlite3") 25 | 26 | Base.metadata.create_all(engine) 27 | 28 | session = Session(bind=engine) 29 | 30 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/db-solution/links.py: -------------------------------------------------------------------------------- 1 | class Links: 2 | 3 | INSERT_LINK_SQL = """ 4 | INSERT INTO Links(url, domain_id) 5 | VALUES(?, ?) 6 | """ 7 | 8 | UNVISITED_LINKS_SQL = """ 9 | SELECT link_id, url, links.domain_id, domain 10 | FROM links 11 | JOIN domains 12 | ON links.domain_id = domains.domain_id 13 | WHERE link_id NOT IN ( 14 | SELECT link_id FROM Servers 15 | ); 16 | """ 17 | 18 | @classmethod 19 | def get_unvisited_links(cls, conn): 20 | cursor = conn.cursor() 21 | result = cursor.execute(cls.UNVISITED_LINKS_SQL) 22 | 23 | return result.fetchall() 24 | 25 | @classmethod 26 | def insert_links(cls, conn, links, domain_id): 27 | try: 28 | for link in links: 29 | cursor = conn.cursor() 30 | cursor.execute(cls.INSERT_LINK_SQL, (link, domain_id)) 31 | conn.commit() 32 | except: 33 | pass 34 | -------------------------------------------------------------------------------- /Application/3-Caesar-cipher/README.md: -------------------------------------------------------------------------------- 1 | # Caesar cipher 2 | 3 | Caesar cipher is one of the simplest and most widely known encryption techniques. The method is named after Julius Caesar, who used it in his private correspondence. 4 | 5 | You have the implement a function with the following signature: caesar_encrypt(str, n). 6 | - The argument str is of type string 7 | - The argument n is of type integer. 8 | - The function should return an encrypted string 9 | 10 | The encryption can be represented using modular arithmetic by first transforming the letters into numbers, according to the scheme, A = 0, B = 1,..., Z = 25. Encryption of a letter x by a shift n can be described mathematically as, 11 | 12 | ![](http://upload.wikimedia.org/math/b/b/b/bbb819c72cda43180d98e6ade5cadb04.png) 13 | 14 | `Do not` encrypt any non-alphabetical characters! 15 | 16 | Example: 17 | 18 | ``` 19 | >>> caesar_encrypt("abc", 1) 20 | "bcd" 21 | >>> caesar_encrypt("ABC", 1) 22 | "BCD" 23 | >>> caesar_encrypt("abc", 2) 24 | "cde" 25 | >>> caesar_encrypt("aaa", 7) 26 | "hhh" 27 | >>> caesar_encrypt("xyz", 1) 28 | "yza" 29 | ``` 30 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/3-Weekend-Challenge/README.md: -------------------------------------------------------------------------------- 1 | # A simple weekend challenge 2 | 3 | Now, we know a lot of different things - HTTP, how to crawl websites, how to store data in `sqlite`. 4 | 5 | Now, lets combine that into something more advanced. 6 | 7 | We are going to upgrade the [1-Scan-Bg-Web problem](https://github.com/HackBulgaria/Programming101-3/tree/master/week7/1-Scan-Bg-Web) by adding few more things: 8 | 9 | * We want to crawl more websites. Lets say, crawl the entire [start.bg](http://www.start.bg/). We want everything we can get out of there. 10 | * Store the data in a `sqlite` database! **Crawl only websites that have not been crawled before!**. This will make our script more useful since if we crawl 10k sites and something breaks down at the 9999th, we are going to lose the entire information we have :( So add a database! 11 | * Make the script crawl every day, lets say at 12:30PM. [Figure out how to make this.](https://help.ubuntu.com/community/CronHowto) 12 | * Of course, have a script that exports the data in a histogram, using **matplotlib** 13 | 14 | That's it. 15 | 16 | Good luck! 17 | 18 | -------------------------------------------------------------------------------- /week2-OOP-Problems/README.md: -------------------------------------------------------------------------------- 1 | # Week 2 - Linux, Git и първи стъпки в ОOP 2 | 3 | **Materials for pre-reading:** 4 | 5 | 1. [Python PEP8 Style Guide](https://www.python.org/dev/peps/pep-0008/) - A Foolish Consistency is the Hobgoblin of Little Minds 6 | 2. [What is `apt-get`?](https://help.ubuntu.com/community/AptGet/Howto) 7 | 3. [What is Git?](http://git-scm.com/book/en/v2/Getting-Started-Git-Basics) 8 | 4. [Interactive Git tutorial for covering the.](https://try.github.io/levels/1/challenges/1) 9 | 5. [Working with the File System with Python.](https://docs.python.org/3.4/tutorial/inputoutput.html#reading-and-writing-files) 10 | 6. [Examples of Python API for working with the File System.](materials/working_with_files.md) 11 | 7. [How to read arguments for our programs? sys.argv](materials/program_arguments.md) 12 | 8. [Python & OOP, part 1, from the Python-FMI course 2015](http://fmi.py-bg.net/lectures/03-oop#1) 13 | 9. [Python & OOP, part 2, from the Python-FMI course 2015](http://fmi.py-bg.net/lectures/04-oop-2#1) 14 | 10. [Code examples for Python basic OOP usage](materials/oop.md) 15 | 11. [List of all dunder methods](https://docs.python.org/3.4/reference/datamodel.html#basic-customization) 16 | -------------------------------------------------------------------------------- /week2-OOP-Problems/Polyglot/README.md: -------------------------------------------------------------------------------- 1 | Polyglot 2 | ======== 3 | 4 | The problem where you meet new languages and compile them, to solve a puzzle! 5 | 6 | ## Setup 7 | 8 | You will need sqlite3 in order to run the Polyglot problem: 9 | 10 | ``` 11 | $ sudo apt-get install sqlite3 12 | ``` 13 | 14 | ## How to run the program? 15 | 16 | To start everything, type: 17 | 18 | ``` 19 | $ python3.4 polyglot.py 20 | 21 | Hello and Welcome! 22 | I am the compiler. 23 | You can ask me to output different source files. 24 | I will provide guides for compiling too.When you are ready, you can provide me with the answer from the code. 25 | And I will reveal a secret to you! 26 | Type help, to get you started. 27 | Enter command> 28 | 29 | ``` 30 | 31 | Now you can enter different commands in order to start the task: 32 | 33 | * `help` command will list you everything 34 | * `list` command will give you all languages and their status 35 | * `start ` will create a new folder with the given language. It is time to solve the language puzzle! (Ctrl-C to exit `polyglot.py`) 36 | * `answer ` - When you are ready with the given language, you can check if you answer is right. 37 | 38 | Good luck! 39 | 40 | Use Google & `apt-get` 41 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/materials/person.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | def map_python_type_to_sql_type(value): 4 | types = { 5 | int: "INTEGER", 6 | str: "TEXT" 7 | } 8 | 9 | value_type = type(value) 10 | 11 | if value_type in types: 12 | return types[value_type] 13 | 14 | 15 | def map_class_to_table(cls): 16 | members = inspect.getmembers(cls, lambda a:not(inspect.isroutine(a))) 17 | 18 | members = [m for m in members if not "__" in m[0]] 19 | 20 | create_sql = """ 21 | CREATE TABLE {}( 22 | {}) 23 | """.strip() 24 | 25 | columns = [] 26 | 27 | columns.append(""" 28 | {} INTEGER PRIMARY KEY 29 | """.format("{}_id".format(cls.__name__.lower())).strip()) 30 | 31 | for member in members: 32 | column_name = member[0] 33 | column_type = map_python_type_to_sql_type(member[1]) 34 | 35 | columns.append(""" 36 | {} {} 37 | """.format(column_name, column_type).strip()) 38 | 39 | columns = ",\n".join(columns) 40 | 41 | create_sql = create_sql.format(cls.__name__, columns) 42 | print(create_sql) 43 | 44 | 45 | class Person: 46 | name = "" 47 | age = 0 48 | 49 | map_class_to_table(Person) 50 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/1-Bank-Account/bank.py: -------------------------------------------------------------------------------- 1 | class BankAccount: 2 | def __init__(self, name, start_balance, currency): 3 | if start_balance < 0: 4 | raise ValueError("Start balance must be >= 0") 5 | 6 | self.__balance = start_balance 7 | self.__name = str(name) 8 | self.__currency = str(currency) 9 | self.__history = [] 10 | 11 | self.__make_history("Account was created") 12 | 13 | def __make_history(self, event): 14 | self.__history.append(event) 15 | 16 | def balance(self): 17 | self.__make_history("Balance check -> {}{}" 18 | .format(self.__balance, self.__currency)) 19 | return self.__balance 20 | 21 | def holder(self): 22 | return self.__name 23 | 24 | def currency(self): 25 | return self.__currency 26 | 27 | def deposit(self, amount): 28 | if amount < 0: 29 | raise ValueError("Cannot deposit negative money.") 30 | 31 | self.__balance += amount 32 | 33 | def withdraw(self, amount): 34 | if self.__balance - amount < 0: 35 | return False 36 | 37 | self.__balance -= amount 38 | return True 39 | 40 | def history(self): 41 | return self.__history 42 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/db-solution/domains.py: -------------------------------------------------------------------------------- 1 | class Domains: 2 | 3 | GET_UNVISITED_DOMAINS_SQL = """ 4 | SELECT domain_id, domain 5 | FROM Domains 6 | WHERE visited = 0 7 | """ 8 | 9 | INSERT_DOMAIN_SQL = """ 10 | INSERT INTO Domains(domain, visited) 11 | VALUES(?, ?) 12 | """ 13 | 14 | VISIT_DOMAIN_SQL = """ 15 | UPDATE Domains 16 | SET visited = 1 17 | WHERE domain_id = ? 18 | """ 19 | 20 | 21 | @classmethod 22 | def insert_domains(cls, conn, domains): 23 | try: 24 | cursor = conn.cursor() 25 | 26 | for domain in domains: 27 | cursor.execute(cls.INSERT_DOMAIN_SQL, (domain, 0)) 28 | 29 | conn.commit() 30 | except: 31 | pass 32 | 33 | @classmethod 34 | def visit_domain(cls, conn, domain_id): 35 | cursor = conn.cursor() 36 | 37 | cursor.execute(cls.VISIT_DOMAIN_SQL, (domain_id, )) 38 | 39 | conn.commit() 40 | 41 | @classmethod 42 | def get_all_unvisited_domains(cls, conn): 43 | cursor = conn.cursor() 44 | 45 | result = cursor.execute(cls.GET_UNVISITED_DOMAINS_SQL) 46 | 47 | return result.fetchmany() 48 | 49 | -------------------------------------------------------------------------------- /config/RadoRado.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "caret_extra_width": 1, 3 | "caret_style": "solid", 4 | "draw_minimap_border": true, 5 | "draw_white_space": "all", 6 | "enable_tab_scrolling": false, 7 | "ensure_newline_at_eof_on_save": true, 8 | "file_exclude_patterns": 9 | [ 10 | ".DS_Store", 11 | "*.pid", 12 | "*.pyc" 13 | ], 14 | "find_selected_text": true, 15 | "findreplace_small": true, 16 | "fold_buttons": false, 17 | "folder_exclude_patterns": 18 | [ 19 | ".git", 20 | "__pycache__", 21 | "env", 22 | "env3" 23 | ], 24 | "font_face": "Source Code Pro", 25 | "font_options": 26 | [ 27 | "subpixel_antialias", 28 | "no_bold", 29 | "no_round" 30 | ], 31 | "font_size": 19, 32 | "highlight_line": true, 33 | "highlight_modified_tabs": true, 34 | "ignored_packages": 35 | [ 36 | "Vintage" 37 | ], 38 | "line_padding_bottom": 0, 39 | "line_padding_top": 0, 40 | "open_files_in_new_window": false, 41 | "preview_on_click": false, 42 | "scroll_past_end": true, 43 | "scroll_speed": 5.0, 44 | "show_full_path": true, 45 | "tab_size": 4, 46 | "translate_tabs_to_spaces": true, 47 | "trim_trailing_white_space_on_save": true, 48 | "wide_caret": true, 49 | "word_wrap": true, 50 | "wrap_width": 80 51 | } 52 | 53 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/bank_controller.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from models import Client, LoginAttempt 3 | from base import Base 4 | 5 | from sqlalchemy import create_engine 6 | from sqlalchemy.orm import Session 7 | 8 | from authentication_controller import AuthenticatonController 9 | from transaction_controller import TransactionController 10 | 11 | from helpers import hash_password 12 | 13 | 14 | class BankController: 15 | 16 | def __init__(self, db_connection_string, block_after_n_logins = 5, block_for_n_minutes = 5): 17 | self.__engine = create_engine(db_connection_string) 18 | Base.metadata.create_all(self.__engine) 19 | 20 | self.__session = Session(bind=self.__engine) 21 | self.__auth = AuthenticatonController(self.__session, block_after_n_logins=block_after_n_logins, block_for_n_minutes=block_for_n_minutes) 22 | self.__transactions = TransactionController(self.__session) 23 | 24 | def __commit_changes(self, objects): 25 | self.__session.add_all(objects) 26 | self.__session.commit() 27 | 28 | def register(self, username, password): 29 | return self.__auth.register(username, password) 30 | 31 | def login(self, username, password): 32 | return self.__auth.login(username, password) 33 | 34 | def create_account(self, user, account_name): 35 | return self.__transactions.create_account(user, account_name) 36 | 37 | -------------------------------------------------------------------------------- /week2-OOP-Problems/materials/panda.py: -------------------------------------------------------------------------------- 1 | # клас и инстанции 2 | # dunder 3 | # Duck Typing 4 | 5 | 6 | class Panda: 7 | 8 | def __init__(self, name): 9 | self.age = 0 10 | self.weight = 30 11 | self.name = name 12 | 13 | # Мутиращи методи 14 | def grow_up(self): 15 | self.age += 1 16 | 17 | def eat(self, amount): 18 | self.weight += amount // 2 19 | 20 | def sleep(self): 21 | self.weight += 1 22 | 23 | def __add__(self, other): 24 | return Panda(" ".join([self.name, other.name])) 25 | 26 | def __int__(self): 27 | return self.weight 28 | 29 | def __str__(self): 30 | return "I am panda {} and I am {} old".format(self.name, self.age) 31 | 32 | def __repr__(self): 33 | return self.__str__() 34 | 35 | def __hash__(self): 36 | return hash(self.name + str(self.weight)) 37 | 38 | def __eq__(self, other): 39 | return self.name == other.name and self.weight == other.weight 40 | 41 | 42 | class PandaZoo: 43 | def __init__(self, pandas): 44 | self.pandas = pandas 45 | 46 | def __len__(self): 47 | return len(self.pandas) 48 | 49 | def __getitem__(self, index): 50 | return self.pandas[index] 51 | 52 | ivo = Panda("Ivo") 53 | rado = Panda("Rado") 54 | maria = Panda("Maria") 55 | 56 | zoo = PandaZoo([ivo, rado, maria]) 57 | print(len(zoo)) 58 | 59 | for panda in zoo: 60 | print(panda) 61 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from sqlalchemy import Column, Integer, Float, String, DateTime, Boolean 3 | from sqlalchemy import ForeignKey 4 | from sqlalchemy.orm import relationship 5 | 6 | from base import Base 7 | 8 | class Client(Base): 9 | __tablename__ = "Clients" 10 | id = Column(Integer, primary_key=True) 11 | username = Column(String, unique=True) 12 | password = Column(String) 13 | is_blocked = Column(Boolean) 14 | blocked_until = Column(DateTime) 15 | 16 | class BankAccount(Base): 17 | __tablename__ = "Bank_Accounts" 18 | id = Column(Integer, primary_key=True) 19 | balance = Column(Float) 20 | name = Column(String) 21 | user_id = Column(Integer, ForeignKey(Client.id)) 22 | user = relationship(Client, backref="bank_accounts") 23 | 24 | class LoginAttempt(Base): 25 | FAILED_ATTEMPT = "FAILED" 26 | SUCCESSFUL_ATTEMPT = "SUCCESS" 27 | AFTER_BLOCK = "AFTER BLOCK" 28 | 29 | __tablename__ = "Login_Attempts" 30 | id = Column(Integer, primary_key=True) 31 | attempt_status = Column(String) # "Success", "Failure" 32 | attempt_timestamp = Column(DateTime, default=datetime.datetime.utcnow) 33 | user_id = Column(Integer, ForeignKey(Client.id)) 34 | user = relationship(Client, backref="login_attempts") 35 | 36 | def __str__(self): 37 | return "{} for user {}".format(self.attempt_status, self.user_id) 38 | 39 | def __repr__(self): 40 | return self.__str__() 41 | -------------------------------------------------------------------------------- /week8-Team-Work/1-The-Last-HR/README.md: -------------------------------------------------------------------------------- 1 | # The Last HR 2 | 3 | The year is 2020 and the HR industry is in peril! 4 | 5 | Hack Bulgaria's univeristy is strong - teaching students Computer Science of a world class level! 6 | 7 | All the talents are there, and companies see no use for the headhunting and recruting agencies. 8 | 9 | You are one of the last HRs, and you have found a gaping whole in HackBulgaria's system. Blinded by their greatness, they forgot to close their API, listing all students - https://hackbulgaria.com/api/students/ 10 | 11 | If you have that information, you may be able to hunt them over facebook or linkedin. Glorious days! 12 | 13 | **But you know that Ivo is going to close that API after 1 day. You have to backup the information - there is no other way!** 14 | 15 | The tools you have are Python, `requests` and `sqlite3`. 16 | 17 | **Your task is to download the entire JSON and keep the data for each student, which courses he is / has been attending! Think of the relations between students and courses and build your tables well. The HR industry is counting on you. Their last hope!** 18 | 19 | Good luck! 20 | 21 | 22 | ## Interface for queries 23 | 24 | After you have the data, make a simple interface for querying that data: 25 | 26 | 1. List all students with their GitHub accounts. 27 | 2. List all courses, 28 | 3. List all students and for each student - list the courses he has been attending. 29 | 4. Find the students that have attented the most courses in Hack Bulgaria. 30 | 31 | 32 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/website/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, get_object_or_404, redirect 2 | from django.http import HttpResponse, HttpResponseNotFound 3 | 4 | from .models import Movie, Rating 5 | 6 | def index(request): 7 | movies = Movie.objects.all() 8 | 9 | return render(request, "index.html", locals()) 10 | 11 | def show_movie(request): 12 | movie_id = request.GET.get("movie_id") 13 | if movie_id is None: 14 | movie = Movie.objects.first() 15 | else: 16 | movie = get_object_or_404(Movie, pk=movie_id) 17 | ratings = movie.rating_set.all() 18 | total_ratings = 0 19 | avg_rating = 0.0 20 | 21 | for rating in ratings: 22 | total_ratings += int(rating) 23 | 24 | if len(ratings) == 0: 25 | avg_rating = 0 26 | else: 27 | avg_rating = total_ratings / len(ratings) 28 | 29 | 30 | return render(request, "movie.html", locals()) 31 | 32 | def rate_movie(request): 33 | if request.method == "POST": 34 | movie_id = request.POST.get("movie_id") 35 | rating = request.POST.get("rating") 36 | 37 | if movie_id is None or rating is None: 38 | return HttpResponse("Bad Request") 39 | 40 | movie = get_object_or_404(Movie, pk=movie_id) 41 | movie.rating_set.add(Rating(rating=rating)) 42 | movie.save() 43 | 44 | return redirect("/show_movie?movie_id={}".format(movie_id)) 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/db-solution/crawler.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import sys 3 | import requests 4 | import time 5 | 6 | 7 | from domains import Domains 8 | from links import Links 9 | from servers import Servers 10 | 11 | 12 | 13 | if len(sys.argv) <= 1: 14 | print("Provide database file") 15 | print("python3.4 collector.py crawler.db") 16 | sys.exit(1) 17 | 18 | db = sys.argv[1] 19 | 20 | conn = sqlite3.connect(db) 21 | conn.row_factory = sqlite3.Row 22 | 23 | 24 | def head_for_server(domain, url): 25 | target_url = domain + "/" + url 26 | print(target_url) 27 | headers = {} 28 | r = requests.head(target_url, 29 | headers=headers, 30 | allow_redirects=True, 31 | timeout=10) 32 | 33 | return { 34 | "url": r.url, 35 | "headers": r.headers 36 | } 37 | 38 | while True: 39 | unvisited_links = Links.get_unvisited_links(conn) 40 | 41 | if len(unvisited_links) == 0: 42 | print("Nothing to crawl, going to sleep") 43 | time.sleep(5) 44 | continue 45 | 46 | for link in unvisited_links: 47 | print("Going to {}".format(link["url"])) 48 | try: 49 | result = head_for_server(link["domain"], link["url"]) 50 | print("Got result for {}. It is {}".format(link["url"], result["url"])) 51 | Servers.insert_server(conn, link["link_id"], result["url"], result["headers"]["Server"]) 52 | except: 53 | pass 54 | 55 | conn.commit() 56 | 57 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/scanner.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | import requests 3 | from urllib.parse import urlparse 4 | from hist import Histogram 5 | 6 | 7 | def domain_from_url(url): 8 | parsed = urlparse(url) 9 | 10 | return parsed[0] + "://" + parsed[1] 11 | 12 | 13 | def has_tld(url, tld): 14 | return domain_from_url(url).endswith(tld) 15 | 16 | 17 | def get_html(url): 18 | return requests.get(url).text 19 | 20 | REGISTER = "http://register.start.bg" 21 | HEADERS = { 22 | "User-Agent": "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0" 23 | } 24 | 25 | visited = set() 26 | h = Histogram() 27 | links = [link.get("href") for link in BeautifulSoup(get_html(REGISTER)).find_all("a")] 28 | 29 | 30 | for link in links: 31 | if link is not None and "link.php" in link: 32 | try: 33 | target_url = REGISTER + "/" + link 34 | r = requests.head(target_url, headers=HEADERS, allow_redirects=True, timeout=10) 35 | 36 | target_url = domain_from_url(r.url) 37 | 38 | if target_url not in visited: 39 | visited.add(target_url) 40 | 41 | if has_tld(target_url, ".bg"): 42 | print(target_url) 43 | h.add(r.headers["Server"]) 44 | except Exception as e: 45 | print(e) 46 | 47 | result = [] 48 | 49 | for server, count in h.items(): 50 | result.append("{}: {}".format(server, count)) 51 | 52 | with open("result.txt", "w") as f: 53 | f.write("\n".join(result)) 54 | 55 | 56 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/1-Money-In-The-Bank/sql_manager.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from Client import Client 3 | 4 | conn = sqlite3.connect("bank.db") 5 | cursor = conn.cursor() 6 | 7 | 8 | def create_clients_table(): 9 | create_query = '''create table if not exists 10 | clients(id INTEGER PRIMARY KEY AUTOINCREMENT, 11 | username TEXT, 12 | password TEXT, 13 | balance REAL DEFAULT 0, 14 | message TEXT)''' 15 | 16 | cursor.execute(create_query) 17 | 18 | 19 | def change_message(new_message, logged_user): 20 | update_sql = "UPDATE clients SET message = '%s' WHERE id = '%s'" % (new_message, logged_user.get_id()) 21 | cursor.execute(update_sql) 22 | conn.commit() 23 | logged_user.set_message(new_message) 24 | 25 | 26 | def change_pass(new_pass, logged_user): 27 | update_sql = "UPDATE clients SET password = '%s' WHERE id = '%s'" % (new_pass, logged_user.get_id()) 28 | cursor.execute(update_sql) 29 | conn.commit() 30 | 31 | 32 | def register(username, password): 33 | insert_sql = "insert into clients (username, password) values ('%s', '%s')" % (username, password) 34 | cursor.execute(insert_sql) 35 | conn.commit() 36 | 37 | 38 | def login(username, password): 39 | select_query = "SELECT id, username, balance, message FROM clients WHERE username = '%s' AND password = '%s' LIMIT 1" % (username, password) 40 | 41 | cursor.execute(select_query) 42 | user = cursor.fetchone() 43 | 44 | if(user): 45 | return Client(user[0], user[1], user[2], user[3]) 46 | else: 47 | return False 48 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/db-solution/collector.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import sys 3 | from bs4 import BeautifulSoup 4 | import requests 5 | 6 | 7 | from domains import Domains 8 | from links import Links 9 | 10 | 11 | if len(sys.argv) <= 1: 12 | print("Provide database file") 13 | print("python3.4 collector.py crawler.db") 14 | sys.exit(1) 15 | 16 | db = sys.argv[1] 17 | 18 | conn = sqlite3.connect(db) 19 | conn.row_factory = sqlite3.Row 20 | 21 | 22 | def collect_domain(domain): 23 | inbound = set() 24 | outbound = set() 25 | others = set() 26 | 27 | print("Visiting {}".format(domain)) 28 | 29 | r = requests.get(domain) 30 | soup = BeautifulSoup(r.text) 31 | 32 | for link in soup.find_all("a"): 33 | href = link.get("href") 34 | 35 | if href is None: 36 | continue 37 | 38 | if "start.bg" in href and "javascript:" not in href: 39 | inbound.add(href) 40 | elif "link.php" in href: 41 | outbound.add(href) 42 | else: 43 | others.add(href) 44 | 45 | return { 46 | "inbound": inbound, 47 | "outbound": outbound, 48 | "others": others 49 | } 50 | 51 | 52 | while True: 53 | domains_to_visit = Domains.get_all_unvisited_domains(conn) 54 | 55 | if len(domains_to_visit) == 0: 56 | break 57 | 58 | for domain_row in domains_to_visit: 59 | result = collect_domain(domain_row["domain"]) 60 | 61 | domain_id = domain_row["domain_id"] 62 | 63 | Domains.visit_domain(conn, domain_id) 64 | Domains.insert_domains(conn, result["inbound"]) 65 | Links.insert_links(conn, result["outbound"], domain_id) 66 | 67 | -------------------------------------------------------------------------------- /week10-Django/1-Cinema-Reservation-with-Django/README.md: -------------------------------------------------------------------------------- 1 | # Cinema Reservation System with Django 2 | 3 | In order to get started with Django, you are going to have the following task: 4 | 5 | ## Getting Started - first exercise 6 | 7 | 1. Create a Django project, that will represent our cinema reserveration system. 8 | 2. Create a new app, called `website`, where you are going to add write your code. 9 | 3. Create models for all of the cinema logic - https://github.com/HackBulgaria/Programming101-3/tree/master/week8/3-Cinema-Reservation-System - Check for how to make foreign keys with Django - https://docs.djangoproject.com/en/1.8/topics/db/examples/many_to_one/ and how to make many-to-many - https://docs.djangoproject.com/en/1.8/topics/db/examples/many_to_many/ using Django's ORM 10 | 4. Create a index view, where you are listing all movies and for each movie, all projections for it. You will have to use Django's templates - https://docs.djangoproject.com/en/1.8/topics/templates/ 11 | 12 | 13 | Here is an example of the structure and the final HTML that want: 14 | 15 | 16 | ```html 17 |

Movies and projections:

18 | 19 |

The Hunger Games: Catching Fire

20 | 21 |

Rating of the movie: 7.9

22 | 23 | Projections: 24 | 25 |
    26 |
  • 2014-04-01 at 19:10 - 3D
  • 27 |
  • 2014-04-01 at 19:00 - 2D
  • 28 |
  • 2014-04-02 at 21:00 - 4DX
  • 29 |
30 | 31 |

Wreck-It Raplh

32 | 33 |

Rating of the movie: 7.8

34 | 35 | Projections: 36 | 37 |
    38 |
  • 2014-04-02 at 22:00 - 3D
  • 39 |
  • 2014-04-02 at 19:00 - 2D
  • 40 |
41 | 42 |

Her

43 | 44 |

Rating of the movie: 8.3

45 | 46 | Projections: 47 | 48 |
    49 |
  • 2014-04-05 at 20:20 - 2D
  • 50 |
51 | 52 | ``` 53 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/README.md: -------------------------------------------------------------------------------- 1 | # Scraping & Intro to SQL 2 | 3 | This week we are going to tap into web scraping and start saving data to sqlite, learning some basic SQL. 4 | 5 | Check the following materials: 6 | 7 | ## Databases and SQL 8 | 9 | Of course, to get the definition straight, check Wikipedia - http://en.wikipedia.org/wiki/Database 10 | 11 | We are going to use Relational Databases for our purposes. 12 | 13 | RDBMS (Relational Database Management System) is a software, that handles few very important topics for us: 14 | 15 | * It has a server, that listens at a given port for open connections 16 | * It manages the concurency access to the data, removing race conditions 17 | * The data is stored in optimal data structures, which makes accessing the data very fas. 18 | * Usually, the RDBMS supports a query language, called SQL (Structured Query Language) 19 | 20 | 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. 21 | 22 | __Few good materials for getting a grasp of SQL:__ 23 | 24 | * 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! 25 | * Learn what is an SQL Injection (This will help you understand SQL) - https://www.youtube.com/watch?v=_jKylhJtPmI 26 | * SQL vs NoSQL is a good way to understand SQL :) - https://www.youtube.com/watch?v=rRoy6I4gKWU 27 | 28 | ### sqlite 29 | 30 | We are going to tackle with sqlite: 31 | 32 | * Check the Python documentation for sqlite - https://docs.python.org/3.4/library/sqlite3.html 33 | * You can check the web page for sqlite for better understanding it - https://sqlite.org/ 34 | * A very good tutorial on sqlite - http://www.pythoncentral.io/series/python-sqlite-database-tutorial/ 35 | * A little tutorial on sqlite JOIN - http://www.techonthenet.com/sqlite/joins.php 36 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/1-Money-In-The-Bank/tests/sql_manager_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | import os 4 | 5 | sys.path.append("..") 6 | 7 | import sql_manager 8 | 9 | 10 | class SqlManagerTests(unittest.TestCase): 11 | 12 | def setUp(self): 13 | sql_manager.create_clients_table() 14 | sql_manager.register('Tester', '123') 15 | 16 | def tearDown(self): 17 | sql_manager.cursor.execute('DROP TABLE clients') 18 | 19 | @classmethod 20 | def tearDownClass(cls): 21 | os.remove("bank.db") 22 | 23 | def test_register(self): 24 | sql_manager.register('Dinko', '123123') 25 | 26 | sql_manager.cursor.execute('SELECT Count(*) FROM clients WHERE username = (?) AND password = (?)', ('Dinko', '123123')) 27 | users_count = sql_manager.cursor.fetchone() 28 | 29 | self.assertEqual(users_count[0], 1) 30 | 31 | def test_login(self): 32 | logged_user = sql_manager.login('Tester', '123') 33 | self.assertEqual(logged_user.get_username(), 'Tester') 34 | 35 | def test_login_wrong_password(self): 36 | logged_user = sql_manager.login('Tester', '123567') 37 | self.assertFalse(logged_user) 38 | 39 | def test_change_message(self): 40 | logged_user = sql_manager.login('Tester', '123') 41 | new_message = "podaivinototam" 42 | sql_manager.change_message(new_message, logged_user) 43 | self.assertEqual(logged_user.get_message(), new_message) 44 | 45 | def test_change_password(self): 46 | logged_user = sql_manager.login('Tester', '123') 47 | new_password = "12345" 48 | sql_manager.change_pass(new_password, logged_user) 49 | 50 | logged_user_new_password = sql_manager.login('Tester', new_password) 51 | self.assertEqual(logged_user_new_password.get_username(), 'Tester') 52 | 53 | if __name__ == '__main__': 54 | unittest.main() 55 | -------------------------------------------------------------------------------- /week8-Team-Work/2-SQL-Over-Northwind/README.md: -------------------------------------------------------------------------------- 1 | # Pure SQL 2 | 3 | We are going to leave Python for a moment. 4 | 5 | In the folder, there are 3 files: 6 | 7 | 8 | * `northwind.db` - sqlite3 database, ready for querying 9 | * `Northwind_relations.png` - this is the visual representation of the schema. 10 | * `Northwind.sql` - the sql file to create the database. It is imported in `northwind.db` 11 | 12 | We are going to make SQL queries to fetch data out from our database. 13 | 14 | We recommend you to use [sqliteman](https://apps.ubuntu.com/cat/applications/precise/sqliteman/) - for syntax highlighting of the SQL. 15 | 16 | 17 | ## Queries 18 | 19 | All queries are going to be some form of `SELECT`: 20 | 21 | 1. List all employees with their first name, last name and title. 22 | 2. List all employees from Seattle. 23 | 3. List all employees from London. 24 | 4. List all employees that work in the Sales department. 25 | 5. List all females employees that work in the Sales department. 26 | 6. List the 5 oldest employees. 27 | 7. List the first 5 hires of the company. 28 | 8. List the employee who reports to no one (the boss) 29 | 9. List all employes by their first and last name, and the first and last name of the employees that they report to. 30 | 10. Count all female employees. 31 | 11. Count all male employees. 32 | 12. Count how many employees are there from the different cities. For example, there are 4 employees from London. 33 | 13. List all OrderIDs and the employees (by first and last name) that have created them. 34 | 14. List all OrderIDs and the shipper name that the order is going to be shipped via. 35 | 15. List all contries and the total number of orders that are going to be shipped there. 36 | 16. Find the employee that has served the most orders. 37 | 17. Find the customer that has placed the most orders. 38 | 18. List all orders, with the employee serving them and the customer, that has placed them. 39 | 19. List for which customer, which shipper is going to deliver the order. 40 | 41 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/materials/graph.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | graph = { 4 | "1": ["2", "3", "5"], 5 | "2": ["4", "1"], 6 | "3": ["1", "6"], 7 | "4": ["2", "5", "6"], 8 | "5": ["4", "1"], 9 | "6": ["3", "4", "7"], 10 | "7": ["6", "8"], 11 | "8": ["7", "9"], 12 | "9": ["8", "10"], 13 | "10": ["9"], 14 | "11": ["12"], 15 | "12": ["11"] 16 | } 17 | 18 | 19 | def bfs_with_levels(graph, start, end): 20 | Q = [] 21 | visited = set() 22 | 23 | Q.append((0, start)) 24 | visited.add(start) 25 | 26 | while len(Q) != 0: 27 | node_data = Q.pop(0) 28 | print(node_data) 29 | current_level = node_data[0] 30 | current_node = node_data[1] 31 | 32 | if current_node == end: 33 | return current_level 34 | 35 | for neighbour in graph[current_node]: 36 | if neighbour not in visited: 37 | visited.add(neighbour) 38 | Q.append((current_level + 1, neighbour)) 39 | 40 | return -1 41 | 42 | 43 | def bfs(graph, start, end): 44 | visited = set() 45 | queue = [] 46 | # path_to[x] = y 47 | # if we go to x through y 48 | path_to = {} 49 | 50 | queue.append(start) 51 | visited.add(start) 52 | path_to[start] = None 53 | found = False 54 | path_length = 0 55 | 56 | while len(queue) != 0: 57 | current_node = queue.pop(0) 58 | if current_node == end: 59 | found = True 60 | break 61 | 62 | for neighbour in graph[current_node]: 63 | if neighbour not in visited: 64 | path_to[neighbour] = current_node 65 | visited.add(neighbour) 66 | queue.append(neighbour) 67 | 68 | if found: 69 | while path_to[end] is not None: 70 | path_length += 1 71 | end = path_to[end] 72 | 73 | print(json.dumps(path_to, sort_keys=True, indent=4)) 74 | return path_length 75 | 76 | result = bfs(graph, "1", "10") 77 | print(result) 78 | -------------------------------------------------------------------------------- /week8-Team-Work/1-The-Last-HR/hr.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import requests 3 | 4 | conn = sqlite3.connect("hr.db") 5 | cursor = conn.cursor() 6 | 7 | """ course_name -> id""" 8 | course_name_to_id = {} 9 | 10 | 11 | def create_student(conn, cursor, student): 12 | insert_query = """ 13 | INSERT INTO Students(name, github) 14 | VALUES(?, ?) 15 | """ 16 | 17 | cursor.execute(insert_query, (student["name"], student["github"])) 18 | conn.commit() 19 | 20 | return cursor.lastrowid 21 | 22 | 23 | 24 | def create_course(conn, cursor, course): 25 | insert_query = """ 26 | INSERT INTO Courses(name) 27 | VALUES(?) 28 | """ 29 | 30 | cursor.execute(insert_query, (course["name"], )) 31 | conn.commit() 32 | 33 | return cursor.lastrowid 34 | 35 | 36 | 37 | def create_relation(conn, cursor, student_id, course_id): 38 | insert_query = """ 39 | INSERT INTO Students_to_Courses(student_id, course_id) 40 | VALUES(?, ?) 41 | """ 42 | 43 | cursor.execute(insert_query, (student_id, course_id)) 44 | conn.commit() 45 | 46 | 47 | API_URL = "https://hackbulgaria.com/api/students/" 48 | students_data = requests.get(API_URL).json() 49 | all_students = len(students_data) 50 | counter = 0 51 | 52 | for student in students_data: 53 | student_id = create_student(conn, cursor, student) 54 | courses = student["courses"] 55 | 56 | print("Inserting {}".format(student["name"])) 57 | 58 | for course in courses: 59 | print("Inserting courses for {}".format(student["name"])) 60 | course_name = course["name"] 61 | print("Current course: {}".format(course_name)) 62 | if course_name in course_name_to_id: 63 | course_id = course_name_to_id[course_name] 64 | else: 65 | course_id = create_course(conn, cursor, course) 66 | course_name_to_id[course_name] = course_id 67 | 68 | 69 | create_relation(conn, cursor, student_id, course_id) 70 | 71 | print("Inserted {}".format(student["name"])) 72 | print("So far: {}/{}".format(counter, all_students)) 73 | counter += 1 74 | 75 | -------------------------------------------------------------------------------- /week2-OOP-Problems/3-Cash-Desk/cashdesk.py: -------------------------------------------------------------------------------- 1 | class Bill: 2 | def __init__(self, amount): 3 | if amount <= 0: 4 | raise ValueError 5 | 6 | if not isinstance(amount, int): 7 | raise TypeError 8 | 9 | self.__amount = amount 10 | 11 | def __int__(self): 12 | return self.__amount 13 | 14 | def __str__(self): 15 | return "A {}$ bill.".format(self.__amount) 16 | 17 | def __repr__(self): 18 | return self.__str__() 19 | 20 | def __eq__(self, other): 21 | return int(self) == int(other) 22 | 23 | def __hash__(self): 24 | return hash(self.__str__()) 25 | 26 | # Used for sorting 27 | def __lt__(self, other): 28 | return int(self) < int(other) 29 | 30 | 31 | class BillBatch: 32 | def __init__(self, bills): 33 | self.__bills = bills 34 | 35 | def __len__(self): 36 | return len(self.__bills) 37 | 38 | def __getitem__(self, index): 39 | return self.__bills[index] 40 | 41 | def total(self): 42 | return sum([int(bill) for bill in self.__bills]) 43 | 44 | 45 | class CashDesk: 46 | def __init__(self): 47 | self.money_holder = {} 48 | 49 | def __store_money(self, bill): 50 | if bill not in self.money_holder: 51 | self.money_holder[bill] = 1 52 | else: 53 | self.money_holder[bill] += 1 54 | 55 | def take_money(self, money): 56 | if isinstance(money, Bill): 57 | self.__store_money(money) 58 | elif isinstance(money, BillBatch): 59 | for bill in money: 60 | self.__store_money(bill) 61 | 62 | def total(self): 63 | m = self.money_holder 64 | return sum([int(bill) * m[bill] for bill in m]) 65 | 66 | def inspect(self): 67 | lines = [] 68 | total = self.total() 69 | 70 | lines.append("We have {}$ in the desk.".format(total)) 71 | 72 | if total > 0: 73 | lines.append("Bills are:") 74 | 75 | bills = list(self.money_holder.keys()) 76 | bills.sort() 77 | 78 | for bill in bills: 79 | line = "${} - {}".format(int(bill), self.money_holder[bill]) 80 | lines.append(line) 81 | return "\n".join(lines) 82 | -------------------------------------------------------------------------------- /week11-Advanced-Python/decorators/README.md: -------------------------------------------------------------------------------- 1 | # @accepts 2 | Make a decorator ``accepts`` that takes as many arguments as the function takes. That decorator specify the types of the arguments that your function takes. If any of the arguments does not match the type in the decorator raise a ``TypeError`` 3 | 4 | ## Examples 5 | ```python 6 | @accepts(str) 7 | def say_hello(name): 8 | return "Hello, I am {}".format(name) 9 | 10 | say_hello(4) 11 | 12 | TypeError: Argument 1 of say_hello is not str! 13 | ``` 14 | 15 | ```python 16 | @accepts(str) 17 | def say_hello(name): 18 | return "Hello, I am {}".format(name) 19 | 20 | say_hello("Hacker") 21 | ``` 22 | 23 | ```python 24 | @accepts(str, int) 25 | def deposit(name, money): 26 | print("{} sends {} $!".format(name, money)) 27 | return True 28 | 29 | deposit("RadoRado", 10) 30 | ``` 31 | 32 | Note that this is just a nice example. In real life you don't want use this! 33 | 34 | # @encrypt(key) 35 | 36 | Make a decorator ``encrypt`` that takes an integer. The decorator should encrypts the returned string of a function using the [Caesar Cipher](https://en.wikipedia.org/wiki/Caesar_cipher). That integer is the encryptions key. 37 | 38 | ## Example 39 | 40 | ```python 41 | @encrypt(2) 42 | def get_low(): 43 | return "Get get get low" 44 | 45 | get_low() 46 | 47 | Igv igv igv nqy 48 | ``` 49 | 50 | # @log(file_name) 51 | Make a decorator ``log`` that takes an ``file_name`` and writes in to this file a log. New line for every call of the decorated function. 52 | 53 | 54 | ## Example 55 | 56 | ```python 57 | @log('log.txt') 58 | @encrypt(2) 59 | def get_low(): 60 | return "Get get get low" 61 | 62 | get_low() 63 | 64 | Igv igv igv nqy 65 | ``` 66 | 67 | And the log file should look like this: 68 | 69 | ``` 70 | get_low was called at 2015-08-04 02:51:41.929980 71 | get_low was called at 2015-08-04 02:51:45.992980 72 | get_low was called at 2015-08-04 02:51:42.999923 73 | ``` 74 | 75 | # @performance(file_name) 76 | Make a decorator ``performance`` that takes an ``file_name`` and writes in to this file a log. New line for every call of the decorated function. This decorator should log the time needed for the decorated function to execute. 77 | 78 | ## Example 79 | 80 | ```python 81 | @performance('log.txt') 82 | def something_heavy(): 83 | sleep(2) 84 | return "I am done!" 85 | 86 | something_heavy() 87 | 88 | I am done! 89 | ``` 90 | 91 | And the log file should look like this: 92 | 93 | ``` 94 | get_low was called and took 2.00 seconds to complete 95 | get_low was called and took 2.10 seconds to complete 96 | ``` 97 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/1-Money-In-The-Bank/start.py: -------------------------------------------------------------------------------- 1 | import sql_manager 2 | 3 | 4 | def main_menu(): 5 | print("Welcome to our bank service. You are not logged in. \nPlease register or login") 6 | 7 | while True: 8 | command = input("$$$>") 9 | 10 | if command == 'register': 11 | username = input("Enter your username: ") 12 | password = input("Enter your password: ") 13 | 14 | sql_manager.register(username, password) 15 | 16 | print("Registration Successfull") 17 | 18 | elif command == 'login': 19 | username = input("Enter your username: ") 20 | password = input("Enter your password: ") 21 | 22 | logged_user = sql_manager.login(username, password) 23 | 24 | if logged_user: 25 | logged_menu(logged_user) 26 | else: 27 | print("Login failed") 28 | 29 | elif command == 'help': 30 | print("login - for logging in!") 31 | print("register - for creating new account!") 32 | print("exit - for closing program!") 33 | 34 | elif command == 'exit': 35 | break 36 | else: 37 | print("Not a valid command") 38 | 39 | 40 | def logged_menu(logged_user): 41 | print("Welcome you are logged in as: " + logged_user.get_username()) 42 | while True: 43 | command = input("Logged>>") 44 | 45 | if command == 'info': 46 | print("You are: " + logged_user.get_username()) 47 | print("Your id is: " + str(logged_user.get_id())) 48 | print("Your balance is:" + str(logged_user.get_balance()) + '$') 49 | 50 | elif command == 'changepass': 51 | new_pass = input("Enter your new password: ") 52 | sql_manager.change_pass(new_pass, logged_user) 53 | 54 | elif command == 'change-message': 55 | new_message = input("Enter your new message: ") 56 | sql_manager.change_message(new_message, logged_user) 57 | 58 | elif command == 'show-message': 59 | print(logged_user.get_message()) 60 | 61 | elif command == 'help': 62 | print("info - for showing account info") 63 | print("changepass - for changing passowrd") 64 | print("change-message - for changing users message") 65 | print("show-message - for showing users message") 66 | 67 | 68 | def main(): 69 | sql_manager.create_clients_table() 70 | main_menu() 71 | 72 | if __name__ == '__main__': 73 | main() 74 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/website/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponseBadRequest 3 | from django.contrib.auth.models import User 4 | from django.contrib.auth import authenticate, login, logout 5 | from django.contrib.auth.decorators import login_required 6 | 7 | from .models import ChatMessage 8 | 9 | def chat_logout(request): 10 | logout(request) 11 | return redirect("index") 12 | 13 | 14 | def index(request): 15 | if request.user.is_authenticated(): 16 | return redirect("chat") 17 | 18 | if request.method == "POST": 19 | username = request.POST.get("username") 20 | password = request.POST.get("password") 21 | user = authenticate(username=username, password=password) 22 | 23 | if user is not None: 24 | login(request, user) 25 | return redirect("chat") 26 | else: 27 | return redirect("index") 28 | 29 | else: 30 | return render(request, "index.html", locals()) 31 | 32 | @login_required(login_url="index") 33 | def chat(request): 34 | if request.method == "POST": 35 | message = request.POST.get("message") 36 | user = request.user 37 | 38 | user.chatmessage_set.add(ChatMessage(message=message)) 39 | user.save() 40 | return redirect("chat") 41 | else: 42 | messages = ChatMessage.objects.all() 43 | logged_users = User.objects.all() 44 | return render(request, "chat.html", locals()) 45 | 46 | 47 | def _validate_register(username, email, password): 48 | if username is None or username.strip() == "": 49 | return False 50 | 51 | if email is None or email.strip() == "": 52 | return False 53 | 54 | if password is None or password.strip() == "": 55 | return False 56 | 57 | return True 58 | 59 | 60 | def register(request): 61 | if request.user.is_authenticated(): 62 | return redirect("chat") 63 | 64 | if request.method == "POST": 65 | username = request.POST.get("username") 66 | email = request.POST.get("email") 67 | password = request.POST.get("password") 68 | 69 | if not _validate_register(username, email, password): 70 | return HttpResponseBadRequest("Something is missing") 71 | 72 | user = User.objects.create_user(username, email, password) 73 | return redirect("index") 74 | 75 | else: 76 | return render(request, "register.html", locals()) 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /week11-Advanced-Python/generators/README.md: -------------------------------------------------------------------------------- 1 | # Day 2 2 | 3 | ## chain 4 | 5 | Implement a function that takes two iterables and returns another one that concatenate the two iterables. 6 | 7 | 8 | ```python 9 | def chain(iterable_one, iterable_two): 10 | pass 11 | ``` 12 | 13 | ### Example 14 | 15 | ```python 16 | >>> list(chain(range(0, 4), range(4, 8))) 17 | [0, 1, 2, 3, 4, 5, 6, 7] 18 | ``` 19 | 20 | ## compress 21 | 22 | Implement a function that takes one iterables and one iterable mask. The mask is an collection that contains only ``True`` or ``False`` 23 | 24 | This function returns only this objects from the first collection that have ``True`` on their position in the mask. 25 | 26 | 27 | ```python 28 | def compress(iterable, mask): 29 | pass 30 | ``` 31 | 32 | ### Example 33 | 34 | ```python 35 | >>> list(compress(["Ivo", "Rado", "Panda"], [False, False, True])) 36 | ["Panda"] 37 | ``` 38 | 39 | ## cycle 40 | 41 | Implement a function that takes an iterable and returns endless concatenation of it. 42 | 43 | 44 | ```python 45 | def cycle(iterable): 46 | pass 47 | ``` 48 | 49 | ```python 50 | >>> endless = cycle(range(0,10)) 51 | for item in endless: 52 | print(item) 53 | ``` 54 | 55 | ## Book Reader 56 | You have some text files. They represent a book. Our book contains chapters. Each chapter starts with ``#`` at the beginning of the line. (Markdown book) 57 | 58 | Our book is made of many files. Each file has its number ``001.txt, 002.txt, 003.txt`` 59 | 60 | Each file may contain one or more chapters. 61 | 62 | [Link to the book](Book.zip) 63 | 64 | Write a program that displays on the console each chapter. You can only move forwards using the ``space button``. 65 | 66 | Try not to load the whole book in the memory. Use generator! 67 | 68 | ## Book Generator 69 | Make a python program that generates books. 70 | 71 | Your program should take the following parameters. 72 | 73 | - Chapters count 74 | - Chapter length range (in words) 75 | 76 | The words should be with random length and random char. The format of the book should be the same as previous task. Try to place some new lines in the chapters at random positions. The whole book must be in one file. 77 | 78 | 79 | Try to generate bigger book. Like 1-2G, and try to pass it to the previous program. 80 | 81 | ## Mouse Beep 82 | Make a generator that returns the current position of your mouse pointer. 83 | 84 | Then make a function that checks if your mouse is at the upper left corner of your screen. If it is your computer should make a beep sound. 85 | 86 | Ask Google for "How to get mouse cords" and "Python beep sound" -------------------------------------------------------------------------------- /week1-Python-Problems/1-Warmups/warmup.py: -------------------------------------------------------------------------------- 1 | def fibonacci(n): 2 | result = [] 3 | 4 | a = 1 5 | b = 1 6 | 7 | while len(result) < n: 8 | result.append(a) 9 | 10 | next_fib = a + b 11 | a = b 12 | b = next_fib 13 | 14 | 15 | return result 16 | 17 | 18 | def factorial(n): 19 | result = 1 20 | 21 | for x in range(1, n + 1): 22 | result *= x 23 | 24 | return result 25 | 26 | 27 | def sum_of_digits(n): 28 | return sum(to_digits(n)) 29 | 30 | 31 | def to_digits(n): 32 | return [int(x) for x in str(n)] 33 | 34 | 35 | # 145 = 1! + 4! + 5! 36 | def factorial_digits(n): 37 | return sum([factorial(x) for x in to_digits(n)]) 38 | 39 | 40 | def palindrome(obj): 41 | # list slicing 42 | return str(obj)[::-1] == str(obj) 43 | 44 | 45 | def count_digits(n): 46 | return sum([1 for x in to_digits(n)]) 47 | 48 | 49 | def to_number(digits): 50 | result = 0 51 | 52 | for digit in digits: 53 | digits_count = count_digits(digit) 54 | result = result * (10 ** digits_count) + digit 55 | 56 | return result 57 | 58 | 59 | def fibonacci_number(n): 60 | return to_number(fibonacci(n)) 61 | 62 | 63 | def count_vowels(string): 64 | vowels = "aeiouyAEIOUY" 65 | count = 0 66 | 67 | for ch in string: 68 | if ch in vowels: 69 | count += 1 70 | 71 | return count 72 | 73 | def count_consonants(string): 74 | consonants = "bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ" 75 | count = 0 76 | 77 | for ch in string: 78 | if ch in consonants: 79 | count += 1 80 | 81 | return count 82 | 83 | 84 | def char_histogram(string): 85 | result = {} 86 | 87 | for ch in string: 88 | if ch in result: 89 | result[ch] += 1 90 | else: 91 | result[ch] = 1 92 | 93 | return result 94 | 95 | 96 | def p_score(n): 97 | if palindrome(n): 98 | return 1 99 | 100 | s = n + int(str(n)[::-1]) 101 | 102 | return 1 + p_score(s) 103 | 104 | 105 | def even(n): 106 | return n % 2 == 0 107 | 108 | 109 | def odd(n): 110 | return not even(n) 111 | 112 | 113 | def is_hack(n): 114 | binary_n = bin(n)[2:] 115 | 116 | is_palindrome = palindrome(binary_n) 117 | has_odd_ones = odd(binary_n.count("1")) 118 | 119 | return is_palindrome and has_odd_ones 120 | 121 | 122 | def next_hack(n): 123 | n += 1 124 | 125 | while not is_hack(n): 126 | n += 1 127 | 128 | return n 129 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/1-Bank-Account/README.md: -------------------------------------------------------------------------------- 1 | # A Bank Account, the TDD way 2 | 3 | In order to test your TDD skills, we are going to start with a simple problem - a bank account! 4 | 5 | So, using a test-driven development approach, develop a `BankAccount` class, which behaves like that: 6 | 7 | ## Basic BankAccount usage 8 | 9 | Our `BankAccount` will have the following methods: 10 | 11 | * Constructor takes a `name` for the account, initial `balance` and a `currency` 12 | * `deposit(amount)` - deposits money of `amount` amount 13 | * `balance()` - returns the current balance 14 | * `withdraw(amount)` - takes `amount` money from the account. Returns `True` if it was successful. Otherwise, `False` 15 | * `__str__` should print: `"Bank account for {name} with balance of {amount}{currency}"` 16 | * `__int__` should return the balance of the `BankAccount` 17 | * `transfer_to(account, amount)` - transfers `amount` to `account` if they both have the same currencies! Returns `True` if successful. 18 | * `history()` - returns a list of strings, that represent the history of the bank account. Check examples below for more information. 19 | 20 | ```python 21 | >>> account = BankAccount("Rado", 0, "$") 22 | >>> print(account) 23 | 'Bank account for Rado with balance of 0$' 24 | >>> account.deposit(1000) 25 | >>> account.balance() 26 | 1000 27 | >>> str(account) 28 | 'Bank account for Rado with balance of 1000$' 29 | >>> int(account) 30 | 1000 31 | >>> account.history() 32 | ['Account was created', 'Deposited 1000$', 'Balance check -> 1000$', '__int__ check -> 1000$'] 33 | >>> account.withdraw(500) 34 | True 35 | >>> account.balance() 36 | 500 37 | >>> account.history() 38 | ['Account was created', 'Deposited 1000$', 'Balance check -> 1000$', '__int__ check -> 1000$', '500$ was withdrawed', 'Balance check -> 500$'] 39 | >>> account.withdraw(1000) 40 | False 41 | >>> account.balance() 42 | 500 43 | >>> account.history() 44 | ['Account was created', 'Deposited 1000$', 'Balance check -> 1000$', '__int__ check -> 1000$', '500$ was withdrawed', 'Balance check -> 500$', 'Withdraw for 1000$ failed.', 'Balance check -> 500$'] 45 | ``` 46 | 47 | Also, we should be able to transfer money from one account to another: 48 | 49 | ```python 50 | >>> rado = BankAccount("Rado", 1000, "BGN") 51 | >>> ivo = BankAccount("Ivo", 0, "BGN") 52 | >>> rado.transfer_to(ivo, 500) 53 | True 54 | >>> rado.balance() 55 | 500 56 | >>> ivo.balance() 57 | 500 58 | >>> rado.history() 59 | ['Account was created', 'Transfer to Ivo for 500BGN', 'Balance check -> 500BGN'] 60 | >>> ivo.history() 61 | ['Account was created', 'Transfer from Rado for 500BGN', 'Balance check -> 500BGN'] 62 | ``` 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Programming101-3 2 | 3 | ______ _ __ _____ __ 4 | | ___ \ (_) / || _ |/ | 5 | | |_/ / __ ___ __ _ _ __ __ _ _ __ ___ _ __ ___ _ _ __ __ _ `| || |/' |`| | 6 | | __/ '__/ _ \ / _` | '__/ _` | '_ ` _ \| '_ ` _ \| | '_ \ / _` | | || /| | | | 7 | | | | | | (_) | (_| | | | (_| | | | | | | | | | | | | | | | (_| | _| |\ |_/ /_| |_ 8 | \_| |_| \___/ \__, |_| \__,_|_| |_| |_|_| |_| |_|_|_| |_|\__, | \___/\___/ \___/ 9 | __/ | __/ | 10 | |___/ |___/ 11 | 12 | ![Harry Potter Python](http://i.imgur.com/KGrV41o.png "It's cool, isn't it?") 13 | 14 | The third issue of the Programming 101 course which covers Python, Linux and Git. The course starts March 2015 and is part of https://hackbulgaria.com 15 | 16 | ## Course Details 17 | 18 | * Starts the first week of March, 2015 19 | * Problems for application can be found in the `Applications` folder. 20 | 21 | ## Course Program 22 | 23 | We are going to work with a console & text editor a lot. Linux is a mandatory operating system for this course. 24 | 25 | * Introducing basic git usage - how to install, configure and clone stuff. 26 | * Introducing the editors we are going to work with - Sublime Text & Atom 27 | * We will start with basic Python tasks, to get familiar with the syntax and basic Python structures 28 | * Introducing PEP8 formatting & plugins. Getting religious about PEP8. 29 | * Introducing Linux & the shell - basic commands. We will install and compile a lot of things! 30 | * Introducing TDD (Test Driven Development) as a way of living & coding. Getting religious about TDD. 31 | * Solving lots of Python problems with TDD. 32 | * Introducing OOP concepts in Python & solving more abstract & complex problems with OOP & TDD 33 | * Introducing Database concepts with Python and `sqlite3` 34 | * Introducing teamwork problems & leveling up our git skills - working with branches, merges & pull requests. 35 | * Solving team problems, using Git, Python's OOP and TDD 36 | * Introducing to basic security concepts - SQL injections, hash functions & password hashing, bruteforce protection. 37 | * Introducing Python's `SQLAlchemy` ORM - working with more complex database problems. 38 | * Introducing basic web programming concepts - HTTP, HTML * CSS. 39 | * Introducing the `Flask` framework for web development - implementing a web application that works with database. 40 | * Learning how to deploy our web application - http servers & configuration. 41 | * Intoducing to multithreading and networking with Python. 42 | -------------------------------------------------------------------------------- /week1-Python-Problems/materials/how_to_run_your_python_code.md: -------------------------------------------------------------------------------- 1 | # How to test your code 2 | 3 | For example, lets imagine that we have a file, called `solution.py` with: 4 | 5 | ```python 6 | def sum(xs): 7 | result = 0 8 | 9 | for x in xs: 10 | result += x 11 | 12 | return result 13 | ``` 14 | 15 | If we want to test that, we have two options 16 | 17 | ## Option 1 - using REPL 18 | 19 | 1. Open a Python REPL 20 | 2. Load the function in REPL 21 | 3. Call the function with test data 22 | 23 | In the above example, we will have to do the following: 24 | 25 | **In a terminal window, navigate to the folder, where `solution.py` is located. Then start the REPL:** 26 | 27 | ```python 28 | $ python 29 | Welcome to Python v 3.4 .... 30 | >>> from solution import sum 31 | >>> sum([1, 2, 3]) 32 | 6 33 | ``` 34 | 35 | We are using the Python syntax for importing functions from other modules. 36 | 37 | [**You can check more about that here**](https://docs.python.org/3.4/tutorial/modules.html) 38 | 39 | 40 | If you introduce changes in the python file, you will have to **reload the module** from REPL. 41 | 42 | This is not the easiest thing to do: https://docs.python.org/3.4/library/importlib.html#importlib.reload 43 | 44 | 45 | ## Option 2 - main() function 46 | 47 | The biggest weakness of method 1 is the reloading part. 48 | 49 | We want to have a quick feedback-loop when testing our programs. 50 | 51 | The second option is to introduce the following code to our `solution.py` function: 52 | 53 | ```python 54 | def main(): 55 | print(sum([1, 2, 3]) 56 | print(sum(range(1, 10))) 57 | 58 | if __name__ == "__main__": 59 | main() 60 | ``` 61 | 62 | The magic part is the `if __name__` one. 63 | 64 | Lets see what is happening. 65 | 66 | To understand, create two files: `one.py` and `two.py` with identical code: 67 | 68 | ```python 69 | # for one.py and two.py 70 | 71 | print(__name__) 72 | ``` 73 | 74 | If you run both files via Python compiler, you will get: 75 | 76 | ``` 77 | $ python one.py 78 | __main__ 79 | $ python two.py 80 | __main__ 81 | ``` 82 | 83 | `__main__` is the name of the module and one python file is one module. 84 | 85 | **The module gets the name `__main__` if it is executed with the Python compiler!** 86 | 87 | Now, change the source of `two.py`: 88 | 89 | ```python 90 | # two.py 91 | import one 92 | 93 | print(__name__) 94 | 95 | ``` 96 | 97 | If we execute it: 98 | 99 | ``` 100 | $ python two.py 101 | one 102 | __main__ 103 | ``` 104 | 105 | The `__name__` of `one.py` is changed to `one`, because it is not executed via the Python compiler, but imported. 106 | 107 | This will help us later on, when we introduce tests. 108 | 109 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/1-Bank-Account/bank_tests.py: -------------------------------------------------------------------------------- 1 | from bank import BankAccount 2 | import unittest 3 | 4 | 5 | class BankAccountTest(unittest.TestCase): 6 | def setUp(self): 7 | self.currency = "BGN" 8 | self.account = BankAccount("Rado", 0, self.currency) 9 | 10 | def test_can_create_bank_account(self): 11 | self.assertTrue(isinstance(self.account, BankAccount)) 12 | 13 | def test_initial_zero_balance(self): 14 | self.assertEqual(self.account.balance(), 0) 15 | 16 | def test_initial_non_zero_balance(self): 17 | initial_balance = 100 18 | account = BankAccount("Test", initial_balance, "$") 19 | 20 | self.assertEqual(account.balance(), initial_balance) 21 | 22 | def test_negative_initial_amount(self): 23 | with self.assertRaises(ValueError): 24 | BankAccount("Test", -100, "$") 25 | 26 | def test_get_name(self): 27 | name = "Test" 28 | account = BankAccount(name, 0, "$") 29 | 30 | self.assertEqual(account.holder(), name) 31 | 32 | def test_get_name_when_name_not_string_but_object(self): 33 | name = ("Radoslav", "Georgiev") 34 | account = BankAccount(name, 0, "%") 35 | 36 | self.assertEqual(str(name), account.holder()) 37 | 38 | def test_get_currency(self): 39 | self.assertEqual(self.account.currency(), self.currency) 40 | 41 | def test_currency_always_string(self): 42 | currency = 1 43 | 44 | account = BankAccount("Test", 0, currency) 45 | 46 | self.assertEqual(str(currency), account.currency()) 47 | 48 | def test_deposit_in_empty_account(self): 49 | self.account.deposit(100) 50 | 51 | self.assertEqual(100, self.account.balance()) 52 | 53 | def test_deposit_in_not_empty_account(self): 54 | account = BankAccount("Test", 50, "$") 55 | account.deposit(50) 56 | 57 | self.assertEqual(account.balance(), 100) 58 | 59 | def test_deposit_negative_amount(self): 60 | with self.assertRaises(ValueError): 61 | self.account.deposit(-100) 62 | 63 | def test_withdraw_from_non_empty_account(self): 64 | self.account.deposit(100) 65 | result = self.account.withdraw(50) 66 | 67 | self.assertTrue(result) 68 | self.assertEqual(self.account.balance(), 50) 69 | 70 | def test_withdraw_from_empty_account(self): 71 | result = self.account.withdraw(50) 72 | 73 | self.assertIsNotNone(result) 74 | self.assertFalse(result) 75 | 76 | def test_history_when_account_is_created(self): 77 | account = BankAccount("Test", 0, "$") 78 | expected = ["Account was created"] 79 | 80 | self.assertEqual(account.history(), expected) 81 | 82 | def test_history_with_balance_check(self): 83 | account = BankAccount("Test", 0, "$") 84 | expected = ["Account was created", "Balance check -> 0$"] 85 | 86 | self.assertEqual(account.history(), expected) 87 | if __name__ == '__main__': 88 | unittest.main() 89 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/bank_view.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class BankView: 5 | 6 | def __init__(self, bank_controller): 7 | self.__bank = bank_controller 8 | self.__commands = { 9 | "user": { 10 | "create_account": self.create_account 11 | }, 12 | "menu": { 13 | "register": self.register, 14 | "login": self.login, 15 | }, 16 | "common": { 17 | "exit": self.exit, 18 | "help": self.help 19 | } 20 | } 21 | 22 | self.__user = None 23 | 24 | def __read_username_password(self): 25 | username = input("Enter username: ") 26 | password = input("Enter password: ") 27 | 28 | return (username, password) 29 | 30 | def __dispatch_command_from(self, command, command_state): 31 | states = [command_state, "common"] 32 | command_found = False 33 | 34 | for state in states: 35 | if command in self.__commands[state]: 36 | command_found = True 37 | self.__commands[state][command]() 38 | break 39 | 40 | if not command_found: 41 | print("Unknown command") 42 | 43 | def __take_menu_command(self): 44 | command = input("Enter command:" ) 45 | self.__dispatch_command_from(command, "menu") 46 | 47 | 48 | def __take_user_command(self): 49 | command = input("What do you want to do? ") 50 | self.__dispatch_command_from(command, "user") 51 | 52 | 53 | def __handle_login_error(self, message): 54 | print(message) 55 | 56 | def start_taking_commands(self): 57 | while True: 58 | if self.__user is None: 59 | self.__take_menu_command() 60 | else: 61 | self.__take_user_command() 62 | 63 | 64 | def login(self): 65 | username, password = self.__read_username_password() 66 | 67 | user = self.__bank.login(username, password) 68 | 69 | if not isinstance(user, str): 70 | self.__user = user 71 | print("You have logged in!") 72 | print("Hello {} with id {}".format(self.__user.username, self.__user.id)) 73 | else: 74 | self.__handle_login_error(user) 75 | 76 | def register(self): 77 | username, password = self.__read_username_password() 78 | 79 | succ = self.__bank.register(username, password) 80 | 81 | if succ: 82 | print("You have registered successfuly. Login now") 83 | else: 84 | print("Username {} already taken".format(username)) 85 | 86 | def create_account(self): 87 | account_name = input("Enter account name:") 88 | account = self.__bank.create_account(self.__user, account_name) 89 | 90 | print("Account with id {} was created".format(account.id)) 91 | 92 | def help(self): 93 | pass 94 | 95 | def exit(self): 96 | sys.exit(0) 97 | -------------------------------------------------------------------------------- /week1-Python-Problems/materials/python_data_structures.md: -------------------------------------------------------------------------------- 1 | # Data Structures 2 | 3 | In order to be albe to solve problems with Python, you have to use one of the following data-structures. 4 | 5 | We will make a quick overview of the main ones. Be sure to test them in the REPL! 6 | 7 | ## List 8 | 9 | It does not work like a traditional array. It is **heterogeneous!** This means it can store elements with different types in one list. 10 | 11 | ```python 12 | hacker_things = [1, 2, 3, 'Ivan', [10, 'Hack']] 13 | ``` 14 | 15 | We can get elements by index: 16 | 17 | ```python 18 | print(hacker_things[3]) # Ivan 19 | ``` 20 | 21 | We can iterate: 22 | 23 | ```python 24 | for thing in hacker_things: 25 | print('I tend to like ' + thing) 26 | ``` 27 | 28 | You can look at the lists interface here: https://docs.python.org/3.4/tutorial/datastructures.html#more-on-lists 29 | 30 | 31 | ## Tuple 32 | 33 | Tuples are much like lists but __they are immutable!__ 34 | 35 | super_heroes = ('Hackman', 'Spiderman', 'Hulk') 36 | 37 | ```python 38 | super_heroes[1] = 'Spindi' 39 | 40 | Traceback (most recent call last): 41 | File "", line 1, in 42 | TypeError: 'tuple' object does not support item assignment 43 | ``` 44 | 45 | The interface is identical to a list. You can see it in action [here](https://docs.python.org/3.4/tutorial/datastructures.html#tuples-and-sequences) 46 | 47 | 48 | ## Set 49 | 50 | Go get your math books. This is a real set. 51 | 52 | A set is an unordered collection with no duplicate elements. 53 | 54 | Basic usage includes membership testing and eliminating duplicate entries. 55 | 56 | Set objects also support mathematical operations like `union`, `intersection`, `difference`, and `symmetric difference`. 57 | 58 | 59 | ```python 60 | rappers = set() 61 | rappers.add('Eminem') 62 | rappers.add('Jay-Z') 63 | rappers.add('Eminem') 64 | 65 | if 'Jay-Z' in rappers: 66 | print("99 problems, but Jay ain't one") 67 | 68 | print(rappers) # {'Jay-Z', 'Eminem'} 69 | ``` 70 | 71 | **Sets are perfect for searching elements with `in`** since they can find them in `O(lgN)` time, in comparison to `O(n)` for lists. 72 | 73 | 74 | You can see more sets usage [here](https://docs.python.org/3.4/tutorial/datastructures.html#sets). 75 | 76 | 77 | ## Dictionaries 78 | __Are hash tables, also known as associative arrays!__ 79 | 80 | 81 | ```python 82 | youtube_views = { 83 | 'Gangnam Style': 2096709806, 84 | 'Baby': 1091538504, 85 | 'Waka Waka': 746709408, 86 | } 87 | 88 | print(youtube_views['Waka Waka']) # 746709408 89 | ``` 90 | 91 | Values are added by assigning them to `keys`. 92 | 93 | ```python 94 | youtube_views['Wrecking Ball'] = 709604432 95 | ``` 96 | If that `key` already exists, the value held by that key will be replaced. 97 | 98 | ```python 99 | youtube_views['Wrecking Ball'] = 859604432 100 | ``` 101 | 102 | ```python 103 | print(youtube_views) # { 'Wrecking Ball': 859604432 , 'Gangnam Style': 2096709806, 'Waka Waka': 746709408, 'Baby': 1091538504} 104 | ``` 105 | -------------------------------------------------------------------------------- /week2-OOP-Problems/materials/working_with_files.md: -------------------------------------------------------------------------------- 1 | # Working 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 | ``` 68 | $ python3.4 write.py 69 | 70 | ``` 71 | 72 | and check what is in `file.txt`: 73 | 74 | ``` 75 | $ cat file.txt 76 | Python is awesome. 77 | You should check it out! 78 | ``` 79 | 80 | We will see our content! 81 | 82 | __File arguments:__ 83 | 84 | When we run our Python scripts with `$ python3.4 script.py`, `script.py` is an argument to the Python program. 85 | 86 | We can do the same thing with our Python scripts: 87 | 88 | `$ python3.4 cat.py file.txt` 89 | 90 | Here for example, `file.txt` is an argument to the `cat.py` script. 91 | 92 | The simplest way to get your arguments is the following: 93 | 94 | ```python 95 | # argv.py 96 | import sys 97 | 98 | for arg in sys.argv: 99 | print(arg) 100 | ``` 101 | 102 | Now, if we run the following command on the shell, we will see the output: 103 | 104 | ``` 105 | $ python3.4 argv.py hello.txt program.py script.py 106 | argv.py 107 | hello.txt 108 | program.py 109 | script.py 110 | ``` 111 | 112 | __IMPORTANT:__ In Python, the first argument is always the file name! 113 | 114 | ## Using with statement 115 | 116 | In Python, there is a very helpful and powerful statement, [called `with`](http://effbot.org/zone/python-with-statement.htm) 117 | 118 | You can use it to open files: 119 | 120 | ```python 121 | with open("x.txt") as f: 122 | data = f.read() 123 | do something with data 124 | ``` 125 | 126 | This is a generalization for: 127 | 128 | ```python 129 | try: 130 | f = open("x.txt") 131 | data = f.read() 132 | do something with data 133 | finally: 134 | f.close() 135 | ``` 136 | -------------------------------------------------------------------------------- /week10-Django/django_cinema/django_cinema/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_cinema project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '51j&e9272w9*@v=2xsk3p3$)8#4d90^oflg#g2bg9c1wm_f(zj' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'website' 41 | ) 42 | 43 | MIDDLEWARE_CLASSES = ( 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | 'django.middleware.security.SecurityMiddleware', 52 | ) 53 | 54 | ROOT_URLCONF = 'django_cinema.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'django_cinema.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.sqlite3', 81 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 82 | } 83 | } 84 | 85 | 86 | # Internationalization 87 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 88 | 89 | LANGUAGE_CODE = 'en-us' 90 | 91 | TIME_ZONE = 'UTC' 92 | 93 | USE_I18N = True 94 | 95 | USE_L10N = True 96 | 97 | USE_TZ = True 98 | 99 | 100 | # Static files (CSS, JavaScript, Images) 101 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 102 | 103 | STATIC_URL = '/static/' 104 | -------------------------------------------------------------------------------- /week10-Django/oldschool_chat/oldschool_chat/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for oldschool_chat project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'g79rwi9_+d(v87s)hofl(p52@r5@4-r7ofez)d82^d_8tpo4%_' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'website' 41 | ) 42 | 43 | MIDDLEWARE_CLASSES = ( 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | 'django.middleware.security.SecurityMiddleware', 52 | ) 53 | 54 | ROOT_URLCONF = 'oldschool_chat.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'oldschool_chat.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.sqlite3', 81 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 82 | } 83 | } 84 | 85 | 86 | # Internationalization 87 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 88 | 89 | LANGUAGE_CODE = 'en-us' 90 | 91 | TIME_ZONE = 'UTC' 92 | 93 | USE_I18N = True 94 | 95 | USE_L10N = True 96 | 97 | USE_TZ = True 98 | 99 | 100 | # Static files (CSS, JavaScript, Images) 101 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 102 | 103 | STATIC_URL = '/static/' 104 | -------------------------------------------------------------------------------- /week2-OOP-Problems/3-Cash-Desk/README.md: -------------------------------------------------------------------------------- 1 | # The Cash Desk Problem 2 | 3 | We are going to train our OOP skill by implementing a few classes, which will represent a cash desk. 4 | 5 | The cash desk will do the following things: 6 | 7 | * Take money as single bills 8 | * Take money as batches (пачки!) 9 | * Keep a total count 10 | * Tell us some information about the bills it has 11 | 12 | ## The Bill class 13 | 14 | Create a class, called `Bill` which takes one parameter to its constructor - the `amount` of the bill - an integer. 15 | 16 | This class will only have **dunders** so you wont be afraid of them anymore! 17 | 18 | The class should implement: 19 | 20 | * `__str__` and `__repr__` 21 | * `__int__` 22 | * `__eq__` and `__hash__` 23 | 24 | Here is an example usage: 25 | 26 | ```python 27 | from cashdesk import Bill 28 | 29 | a = Bill(10) 30 | b = Bill(5) 31 | c = Bill(10) 32 | 33 | int(a) # 10 34 | str(a) # "A 10$ bill" 35 | print(a) # A 10$ bill 36 | 37 | a == b # False 38 | a == c # True 39 | 40 | money_holder = {} 41 | 42 | money_holder[a] = 1 # We have one 10% bill 43 | 44 | if c in money_holder: 45 | money_holder[c] += 1 46 | 47 | print(money_holder) # { "A 10$ bill": 2 } 48 | ``` 49 | 50 | 51 | ## The BatchBill class 52 | 53 | We are going to implement a class, which represents more than one bill. A `BatchBill`! 54 | 55 | The class takes a list of `Bills` as the single constructor argument. 56 | 57 | The class should have the following methods: 58 | 59 | * `__len__(self)` - returns the number of `Bills` in the batch 60 | * `total(self)` - returns the total amount of all `Bills` in the batch 61 | 62 | We should be able to iterate the `BatchBill` class with a for-loop. 63 | 64 | Here is an example: 65 | 66 | ```python 67 | from cashdesk import Bill, BillBatch 68 | 69 | values = [10, 20, 50, 100] 70 | bills = [Bill(value) for value in values] 71 | 72 | batch = BillBatch(bills) 73 | 74 | for bill in batch: 75 | print(bill) 76 | 77 | # A 10$ bill 78 | # A 20$ bill 79 | # A 50$ bill 80 | # A 100$ bill 81 | ``` 82 | 83 | In order to do that, you need to implement the following method: 84 | 85 | ```python 86 | def __getitem__(self, index): 87 | pass 88 | ``` 89 | 90 | ## The CashDesk classs 91 | 92 | Finally, implement a `CashDesk` class, which has the following methods: 93 | 94 | * `take_money(money)`, where `money` can be either `Bill` or `BatchBill` class 95 | * `total()` - returns the total amount of money currenly in the desk 96 | * `inspect()` - prints a table representation of the money - for each bill, how many copies of it we have. 97 | 98 | For example: 99 | 100 | ```python 101 | from cashdesk import Bill, BillBatch, CashDesk 102 | 103 | values = [10, 20, 50, 100, 100, 100] 104 | bills = [Bill(value) for value in values] 105 | 106 | batch = BillBatch(bills) 107 | 108 | desk = CashDesk() 109 | 110 | desk.take_money(batch) 111 | desk.take_money(Bill(10)) 112 | 113 | print(desk.total()) # 390 114 | desk.inspect() 115 | 116 | # We have a total of 390$ in the desk 117 | # We have the following count of bills, sorted in ascending order: 118 | # 10$ bills - 2 119 | # 20$ bills - 1 120 | # 50$ bills - 1 121 | # 100$ bills - 3 122 | 123 | ``` 124 | -------------------------------------------------------------------------------- /week6-Pip-And-Http/1-Who-Follows-You-Back/README.md: -------------------------------------------------------------------------------- 1 | # Who follows you back on GitHub 2 | 3 | ## Graph module 4 | 5 | Write a class `DirectedGraph`. 6 | 7 | __Example:__ 8 | ```python3 9 | graph = DirectedGraph() 10 | ``` 11 | 12 | ### Graph class 13 | 14 | The Graph should be: 15 | 16 | * Directed 17 | * Unweighted 18 | * Node names should be strings 19 | 20 | Don't bother making it more abstract to handle more cases. 21 | 22 | There should be the following public methods for the `Graph`: 23 | 24 | * `add_edge(node_a, node_b)` - adds an edge between two nodes. If the nodes does not exist, they should be created. 25 | * `get_neighbors_for(node)` - returns a list of nodes (strings) for the given `node` 26 | * `path_between(node_a, node_b)` - returns `true` if there is a path between `nodeA` and `nodeB`. Keep in mind that the graph is directed! 27 | 28 | ### Test the Graph class. 29 | 30 | Using Python's `unittest`, make a test-suite to test the `DirectedGraph` class. 31 | 32 | Make sure all public methods works just fine - create a test graph and assert that the methods are working. 33 | 34 | ## Who Follows you back? 35 | 36 | We are going to implement a console application, which can give the answer to the fundamental question of the universe -**Who follows you back on GitHub?** 37 | 38 | We want to have the following high-level functionality: 39 | 40 | * A script that calls the GitHub API and builds a social graph for a given user. 41 | * A console interface to ask questions about that user. 42 | 43 | We want to be able to ask the following questions: 44 | 45 | 1. Do you follow user with username `X`? This is a Yes/No question. 46 | 2. Does someone with username `X` follows you? This is a Yes/No question. 47 | 3. How many people from your followers follows you back? 48 | 49 | ### Implementation details 50 | 51 | * In order to fetch JSON for a given user you can make call to this API endpoint: [https://api.github.com/users/radorado](https://api.github.com/users/radorado). Just substitute `radorado` with your username. 52 | * Use the [GitHub API](https://developer.github.com/v3/) to fetch an user's followers, 53 | * Make sure to create yourself a GitHub Application from your [Settings panel](https://github.com/settings/applications) and obtain `client_id` and `client_secret`. This is because of API Rate Limiting - https://developer.github.com/v3/rate_limit/ 54 | * Make calls with your `client_id` and `client_secret` in order to have `5000` requests per hour! 55 | * Make a class that takes a given GitHub username and a **level of the social graph to build**, which uses the `DirectedGraph` module from the previous task. 56 | 57 | Be sure not to build graphs with level `>= 4` - it's going to take forever ;) 58 | 59 | **The class the represents the GitHub social network should have the following methods:** 60 | 61 | * `do_you_follow(user)` - returns True / False if you follow directly the given `user`, 62 | * `do_you_follow_indirectly(user)` - returns True / False if someone from the people you follow, follows the `user`. Limit your self to the `level` of the graph. 63 | * `does_he_she_follows(user)` - returns True / False if the given `user` follows you directly. 64 | * `does_he_she_follows_indirectly(user)` - returns True / False if the given `users` follows someone who follows you. 65 | * `who_follows_you_back()` - returns a list of usernames, that follows you and are followed by you or by the people you follow (limit yourself to the `level` of the graph) 66 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/3-Panda-Social-Network/README.md: -------------------------------------------------------------------------------- 1 | # We are going to make a social networks for Pandas 2 | 3 | This is the next big thing. We promise! 4 | 5 | ## Panda 6 | 7 | For our social network, we are going to need a `Panda` class which behaves like that: 8 | 9 | ```python 10 | ivo = Panda("Ivo", "ivo@pandamail.com", "male") 11 | 12 | ivo.name() == "Ivo" # True 13 | ivo.email() == "ivo@pandamail.com" # True 14 | ivo.gender() == "male" # True 15 | ivo.isMale() == True # True 16 | ivo.isFemale() == False # True 17 | ``` 18 | 19 | The `Panda` class also should be possible to: 20 | 21 | * Be turned into a string 22 | * Be hashed and used as a key in a dictionary (`__eq__` and `__hash__`) 23 | * **Make sure that the `email` is a valid email!** 24 | 25 | Two `Panda` instances are equal if they have matching `name`, `email` and `gender` attributes. 26 | 27 | ## SocialNetwork 28 | 29 | Now it is time for our social network! 30 | 31 | Implement a class, called `PandaSocialNetwork`, which has the following public methods: 32 | 33 | * `add_panda(panda)` - this method adds a `panda` to the social network. The panda has 0 friends for now. If the panda is already in the network, raise an `PandaAlreadyThere` error. 34 | * `has_panda(panda)` - returns `True` or `False` if the `panda` is in the network or not. 35 | * `make_friends(panda1, panda2)` - makes the two pandas friends. Raise `PandasAlreadyFriends` if they are already friends. **The friendship is two-ways** - `panda1` is a friend with `panda2` and `panda2` is a friend with `panda1`. **If `panda1` or `panda2` are not members of the network, add them!** 36 | * `are_friends(panda1, panda2)` - returns `True` if the pandas are friends. Otherwise, `False` 37 | * `friends_of(panda)` - returns a list of `Panda` with the friends of the given `panda`. Returns `False` if the `panda` is not a member of the network. 38 | * `connection_level(panda1, panda2)` - returns the connection level between `panda1` and `panda2`. If they are friends, the level is 1. Otherwise, count the number of friends you need to go through from `panda` in order to get to `panda2`. If they are not connected at all, return `-1`! Return `False` if one of the pandas are not member of the network. 39 | * `are_connected(panda1, panda2)` - return `True` if the pandas are connected somehow, between friends, or `False` otherwise. 40 | * `how_many_gender_in_network(level, panda, gender)` - returns the number of `gender` pandas (male of female) that in the `panda` network in that many `level`s deep. If `level == 2`, we will have to look in all friends of `panda` and all of their friends too. And count 41 | 42 | ## An example 43 | 44 | ```python 45 | network = PandaSocialNetwork() 46 | ivo = Panda("Ivo", "ivo@pandamail.com", "male") 47 | rado = Panda("Rado", "rado@pandamail.com", "male") 48 | tony = Panda("Tony", "tony@pandamail.com", "female") 49 | 50 | for panda in [ivo, rado, tony]: 51 | network.add(panda) 52 | 53 | network.make_friends(ivo, rado) 54 | network.make_friends(rado, tony) 55 | 56 | network.connection_level(ivo, rado) == 1 # True 57 | network.connection_level(ivo, tony) == 2 # True 58 | 59 | network.how_many_gender_in_network(1, rado, "female") == 1 # True 60 | ``` 61 | 62 | 63 | ## Save and laod from file 64 | 65 | The next thing our social network is going to do is ``saving to your hard drive``. 66 | 67 | ### social_network.save(file_name) 68 | 69 | Write a function that saves the hole social network to a file. The foramt ot that file is at your choice. You have to be abe to load it next time. So all the data in the network must be written down to that file. 70 | 71 | ### social_network.load(file_name) 72 | 73 | Write a function that loads the hole social network from a file. All the pandas and all the relations. 74 | -------------------------------------------------------------------------------- /week5-Team-Work/1-Dungeons-and-Pythons/dungeon.py: -------------------------------------------------------------------------------- 1 | class Dungeon: 2 | OBSTACLE = "#" 3 | SPAWNING_POINT = "S" 4 | ENEMY = "E" 5 | EXIT = "G" 6 | TREASURE = "T" 7 | WALKABLE_PATH = "." 8 | HERO = "H" 9 | DIRECTIONS = { 10 | "up": (-1, 0), 11 | "down": (1, 0), 12 | "left": (0, -1), 13 | "right": (0, 1) 14 | } 15 | 16 | @staticmethod 17 | def create_from_file(path): 18 | dungeon = [] 19 | with open(path, "r") as f: 20 | contents = f.read().split("\n") 21 | dungeon = [list(line) for line in contents if line.strip() != ""] 22 | 23 | return Dungeon(dungeon) 24 | 25 | 26 | def __init__(self, dungeon_matrix): 27 | self.__map = dungeon_matrix 28 | self.__spawning_points = self.__find_spawning_points() 29 | self.__hero = None 30 | self.__hero_position = None 31 | 32 | def get_spawning_points(self): 33 | return self.__spawning_points 34 | 35 | def __find_spawning_points(self): 36 | spawning_points = [] 37 | 38 | for row_index in range(0, len(self.__map)): 39 | for tile_index in range(0, len(self.__map[row_index 40 | ])): 41 | tile = self.__map[row_index][tile_index] 42 | if tile == Dungeon.SPAWNING_POINT: 43 | spawning_points.append((row_index, tile_index)) 44 | 45 | return spawning_points 46 | 47 | def __place_on_map(self, point, entity): 48 | self.__map[point[0]][point[1]] = entity 49 | 50 | def __can_make_move(self, point): 51 | x, y = point 52 | if x < 0 or x >= len(self.__map): 53 | return False 54 | 55 | if y < 0 or y >= len(self.__map[0]): 56 | return False 57 | 58 | if self.__map[x][y] == Dungeon.OBSTACLE: 59 | return False 60 | 61 | return True 62 | 63 | def __str__(self): 64 | return "\n".join(["".join(line) for line in self.__map]) 65 | 66 | def spawn(self, hero): 67 | if len(self.__spawning_points) == 0: 68 | raise Exception("Cannot spawn hero.") 69 | 70 | self.__hero = hero 71 | self.__hero_position = self.__spawning_points.pop(0) 72 | 73 | self.__place_on_map(self.__hero_position, Dungeon.HERO) 74 | 75 | def __trigger_action_on_move(self, position): 76 | x, y = position 77 | entity = self.__map[x][y] 78 | if entity == Dungeon.TREASURE: 79 | print("Hero found treasure") 80 | return False 81 | 82 | if entity == Dungeon.ENEMY: 83 | print("A fight starts") 84 | print("Enemy wins") 85 | return True 86 | 87 | 88 | def move(self, direction): 89 | if direction not in Dungeon.DIRECTIONS: 90 | raise Exception("{} is not a valid direction".format(direction)) 91 | 92 | dx, dy = Dungeon.DIRECTIONS[direction] 93 | hero_x, hero_y = self.__hero_position 94 | 95 | new_position = (hero_x + dx, hero_y + dy) 96 | 97 | if self.__can_make_move(new_position): 98 | self.__place_on_map((hero_x, hero_y), Dungeon.WALKABLE_PATH) 99 | is_dead = self.__trigger_action_on_move(new_position) 100 | 101 | if is_dead: 102 | self.spawn(self.__hero) 103 | return 104 | 105 | self.__hero_position = new_position 106 | self.__place_on_map(new_position, Dungeon.HERO) 107 | 108 | 109 | 110 | class Hero: 111 | pass 112 | 113 | def main(): 114 | d = Dungeon.create_from_file("level1.txt") 115 | print(d) 116 | print(d.get_spawning_points()) 117 | h = Hero() 118 | 119 | d.spawn(h) 120 | print(d) 121 | d.move("right") 122 | print(d) 123 | 124 | if __name__ == "__main__": 125 | main() 126 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/solutions/authentication_controller.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from models import Client, LoginAttempt 3 | 4 | from helpers import hash_password 5 | 6 | 7 | class AuthenticatonController: 8 | 9 | def __init__(self, session, block_after_n_logins = 5, block_for_n_minutes = 5): 10 | self.__session = session 11 | 12 | self.__settings = { 13 | "block_after_n_logins": block_after_n_logins, 14 | "block_for_n_minutes": block_for_n_minutes 15 | } 16 | 17 | def __commit_changes(self, objects): 18 | self.__session.add_all(objects) 19 | self.__session.commit() 20 | 21 | def __is_registered(self, username): 22 | try: 23 | user = self.__session.query(Client).filter(Client.username == username).one() 24 | return user 25 | except: 26 | return None 27 | 28 | def __success_login_attempt(self, user): 29 | user.login_attempts.append(LoginAttempt(attempt_status=LoginAttempt.SUCCESSFUL_ATTEMPT)) 30 | self.__commit_changes([user]) 31 | 32 | def __failed_login_attempt(self, user): 33 | user.login_attempts.append(LoginAttempt(attempt_status=LoginAttempt.FAILED_ATTEMPT)) 34 | self.__commit_changes([user]) 35 | 36 | def __block_user(self, user): 37 | user.is_blocked = True 38 | delta = datetime.timedelta(minutes=self.__settings["block_for_n_minutes"]) 39 | user.blocked_until = datetime.datetime.utcnow() + delta 40 | self.__commit_changes([user]) 41 | 42 | def __unblock_user(self, user): 43 | user.is_blocked = False 44 | user.blocked_until = None 45 | 46 | # Break the last 5 failed attempts 47 | user.login_attempts.append(LoginAttempt(attempt_status = LoginAttempt.AFTER_BLOCK, user_id = user.id)) 48 | self.__commit_changes([user]) 49 | 50 | def __can_login_after_block(self, user): 51 | now = datetime.datetime.utcnow() 52 | print(now) 53 | print(user.blocked_until) 54 | return now >= user.blocked_until 55 | 56 | def __can_login(self, user): 57 | if user.is_blocked: 58 | if self.__can_login_after_block(user): 59 | self.__unblock_user(user) 60 | return True 61 | return False 62 | 63 | block_after_n_logins = self.__settings["block_after_n_logins"] 64 | 65 | login_attemps = self.__session.query(LoginAttempt).filter(LoginAttempt.user_id == user.id).all() 66 | last_n_failed = [a.attempt_status == LoginAttempt.FAILED_ATTEMPT for a in login_attemps[-block_after_n_logins:]] 67 | 68 | if len(last_n_failed) < block_after_n_logins: 69 | return True 70 | 71 | if all(last_n_failed): 72 | self.__block_user(user) 73 | return False 74 | 75 | return True 76 | 77 | def register(self, username, password): 78 | if self.__is_registered(username) is not None: 79 | return False 80 | 81 | client = Client(username=username, 82 | password=hash_password(password), 83 | is_blocked=False, 84 | blocked_until = None) 85 | 86 | self.__commit_changes([client]) 87 | 88 | return True 89 | 90 | def login(self, username, password): 91 | user = self.__is_registered(username) 92 | 93 | if user is None: 94 | return "USER_NOT_EXISTS" 95 | 96 | if not self.__can_login(user): 97 | return "USER_BLOCKED" 98 | 99 | password = hash_password(password) 100 | 101 | if user.password == password: 102 | self.__success_login_attempt(user) 103 | return user 104 | 105 | self.__failed_login_attempt(user) 106 | return "FAILED_LOGIN" 107 | 108 | -------------------------------------------------------------------------------- /week1-Python-Problems/2-The-Real-Deal/solutions.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import pprint 3 | 4 | 5 | def sum_of_divisors(n): 6 | return sum([x for x in range(1, n + 1) if n % x == 0]) 7 | 8 | 9 | def count_of_divisors(n): 10 | return sum([1 for x in range(1, n + 1) if n % x == 0]) 11 | 12 | 13 | def is_prime(n): 14 | return n + 1 == sum_of_divisors(n) 15 | 16 | 17 | def prime_number_of_divisors(n): 18 | is_prime(count_of_divisors(n)) 19 | 20 | 21 | def to_digits(n): 22 | return [int(x) for x in str(n)] 23 | 24 | 25 | def contains_digit(number, digit): 26 | return digit in to_digits(number) 27 | 28 | 29 | def contains_digits(number, digits): 30 | for digit in digits: 31 | if not contains_digit(number, digit): 32 | return False 33 | 34 | return True 35 | 36 | 37 | def count_digits(n): 38 | return sum([1 for x in to_digits(n)]) 39 | 40 | 41 | def to_number(digits): 42 | result = 0 43 | 44 | for digit in digits: 45 | digits_count = count_digits(digit) 46 | result = result * (10 ** digits_count) + digit 47 | 48 | return result 49 | 50 | 51 | def is_number_balanced(n): 52 | numbs = to_digits(n) 53 | half = len(numbs) // 2 54 | 55 | left_numbs = numbs[0:half] 56 | if len(numbs) % 2 == 0: 57 | right_numbs = numbs[half:] 58 | else: 59 | right_numbs = numbs[half + 1:] 60 | 61 | return sum(left_numbs) == sum(right_numbs) 62 | 63 | 64 | def count_substrings(haystack, needle): 65 | return haystack.count(needle) 66 | 67 | 68 | def zero_insert(n): 69 | result = [] 70 | digits = to_digits(n) 71 | 72 | start = 0 73 | end = len(digits) 74 | 75 | while start < end - 1: 76 | x = digits[start] 77 | y = digits[start + 1] 78 | 79 | result.append(x) 80 | 81 | if (x + y) % 10 == 0 or x == y: 82 | result.append(0) 83 | 84 | start += 1 85 | 86 | result.append(digits[start]) 87 | 88 | return to_number(result) 89 | 90 | 91 | def sum_matrix(matr): 92 | result = 0 93 | 94 | for row in matr: 95 | result += sum(row) 96 | 97 | return result 98 | 99 | 100 | def sum_matrix2(matr): 101 | # Using list comprehensions 102 | return sum([sum(row) for row in matr]) 103 | 104 | 105 | # We are centered at 4. 106 | # How to move to get to 4's neighbors 107 | # 1 2 3 108 | # 8 >4< 7 109 | # 9 5 6 110 | NEIGHBORS = [ 111 | (-1, -1), (0, -1), (1, -1), # Get to 1, 2 and 3 112 | (-1, 0), (1, 0), # Get to 8 and 7 113 | (-1, 1), (0, 1), (1, 1)] # Get to 9, 5 and 6 114 | 115 | 116 | def within_bounds(m, at): 117 | if at[0] < 0 or at[0] >= len(m): 118 | return False 119 | 120 | if at[1] < 0 or at[1] >= len(m[0]): 121 | return False 122 | 123 | return True 124 | 125 | 126 | def bomb(m, at): 127 | if not within_bounds(m, at): 128 | return m 129 | 130 | target_value = m[at[0]][at[1]] 131 | dx, dy = 0, 1 132 | 133 | for position in NEIGHBORS: 134 | position = (at[dx] + position[dx], at[dy] + position[dy]) 135 | 136 | if within_bounds(m, position): 137 | position_value = m[position[dx]][position[dy]] 138 | # This min() is not to go less than zero 139 | m[position[dx]][position[dy]] -= min(target_value, position_value) 140 | 141 | return m 142 | 143 | 144 | def matrix_bombing_plan(m): 145 | result = {} 146 | 147 | for i in range(0, len(m)): 148 | for j in range(0, len(m[0])): 149 | bombed = bomb(copy.deepcopy(m), (i, j)) 150 | result[(i, j)] = sum_matrix(bombed) 151 | 152 | return result 153 | 154 | 155 | def main(): 156 | m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 157 | result = matrix_bombing_plan(m) 158 | 159 | pp = pprint.PrettyPrinter() 160 | pp.pprint(result) 161 | 162 | if __name__ == '__main__': 163 | main() 164 | 165 | -------------------------------------------------------------------------------- /week2-OOP-Problems/3-Cash-Desk/cashdesk_test.py: -------------------------------------------------------------------------------- 1 | # TDD 2 | # 1. Write test that fails 3 | # 2. Make it work. 4 | # 3. Refactor while testing 5 | # 4. Loop back to 1) 6 | import unittest 7 | from cashdesk import Bill, BillBatch, CashDesk 8 | 9 | 10 | class CashDeskTest(unittest.TestCase): 11 | 12 | def setUp(self): 13 | self.test_bill = Bill(10) 14 | 15 | def test_create_new_bill_class(self): 16 | self.assertTrue(isinstance(self.test_bill, Bill)) 17 | 18 | def test_create_int_value_from_bill(self): 19 | self.assertEqual(int(self.test_bill), 10) 20 | 21 | def test_amount_in_bill(self): 22 | 23 | with self.assertRaises(AttributeError): 24 | self.test_bill.amount 25 | 26 | def test_str_dunder_for_bill(self): 27 | self.assertEqual(str(self.test_bill), "A 10$ bill.") 28 | 29 | def test_repr_dunder_for_bill(self): 30 | self.assertEqual(repr(self.test_bill), "A 10$ bill.") 31 | 32 | def test_eq_between_bills_when_not_same(self): 33 | bill1 = Bill(10) 34 | bill2 = Bill(11) 35 | 36 | self.assertTrue(bill1 != bill2) 37 | 38 | def test_eq_between_bills_when_same(self): 39 | bill1 = Bill(10) 40 | bill2 = Bill(10) 41 | 42 | self.assertTrue(bill1 == bill2) 43 | 44 | def test_bills_are_orderable(self): 45 | self.assertTrue(Bill(5) < Bill(10)) 46 | 47 | def test_can_hash_bill(self): 48 | self.assertIsNotNone(hash(self.test_bill)) 49 | 50 | def test_can_put_bill_in_dictionary(self): 51 | money_holder = {} 52 | bill = Bill(10) 53 | 54 | money_holder[bill] = 1 55 | 56 | self.assertTrue(bill in money_holder) 57 | 58 | def test_value_error_raises_from_negative_amount(self): 59 | with self.assertRaises(ValueError): 60 | Bill(-10) 61 | 62 | def test_value_error_raises_from_zero_amount(self): 63 | with self.assertRaises(ValueError): 64 | Bill(0) 65 | 66 | def test_type_error_raises_from_float_amount(self): 67 | with self.assertRaises(TypeError): 68 | Bill(0.5) 69 | 70 | def test_can_create_billbatch(self): 71 | batch = BillBatch([]) 72 | self.assertTrue(isinstance(batch, BillBatch)) 73 | 74 | def test_can_create_billbatch_with_bills(self): 75 | bills = [Bill(value) for value in range(1, 10)] 76 | batch = BillBatch(bills) 77 | 78 | self.assertEqual(len(batch), len(range(1, 10))) 79 | self.assertEqual(batch.total(), sum(range(10))) 80 | 81 | def test_can_for_loop_a_billbatch(self): 82 | bills = [Bill(value) for value in range(1, 10)] 83 | batch = BillBatch(bills) 84 | total = 0 85 | 86 | for bill in batch: 87 | total += int(bill) 88 | 89 | self.assertEqual(total, sum(range(1, 10))) 90 | 91 | def test_can_create_cashdesh(self): 92 | desk = CashDesk() 93 | self.assertTrue(isinstance(desk, CashDesk)) 94 | 95 | def test_cashdesk_take_only_bills(self): 96 | desk = CashDesk() 97 | 98 | desk.take_money(Bill(10)) 99 | desk.take_money(Bill(5)) 100 | 101 | self.assertEqual(desk.total(), 15) 102 | 103 | def test_cashdesk_take_only_batch(self): 104 | desk = CashDesk() 105 | batch = BillBatch([Bill(10), Bill(5)]) 106 | 107 | desk.take_money(batch) 108 | 109 | self.assertEqual(desk.total(), 15) 110 | 111 | def test_inspect_on_empty_desk(self): 112 | desk = CashDesk() 113 | expected = "We have 0$ in the desk." 114 | 115 | self.assertEqual(desk.inspect(), expected) 116 | 117 | def test_inspect_with_full_desk(self): 118 | desk = CashDesk() 119 | 120 | desk.take_money(Bill(1)) 121 | desk.take_money(Bill(2)) 122 | desk.take_money(Bill(10)) 123 | 124 | expected = ["We have 13$ in the desk."] 125 | expected.append("Bills are:") 126 | expected.append("$1 - 1") 127 | expected.append("$2 - 1") 128 | expected.append("$10 - 1") 129 | 130 | self.assertEqual("\n".join(expected), desk.inspect()) 131 | 132 | if __name__ == '__main__': 133 | unittest.main() 134 | -------------------------------------------------------------------------------- /week2-OOP-Problems/2-File-System-Problems/README.md: -------------------------------------------------------------------------------- 1 | # Problems with files 2 | 3 | ## Implement the cat command - Print file contents 4 | 5 | In Linux, there is a very useful command, called `cat`: 6 | 7 | ``` 8 | $ cat file.txt 9 | This is some file 10 | And cat is printing it's contents 11 | ``` 12 | 13 | Implement a Python script, called `cat.py` that takes one argument - a filename and prints the contents of that file to the console. 14 | 15 | ### Boilerplate 16 | 17 | ```python 18 | # cat.py 19 | import sys 20 | 21 | 22 | def main(): 23 | pass 24 | 25 | if __name__ == '__main__': 26 | main() 27 | ``` 28 | 29 | ### Examples 30 | 31 | If we have `file.txt` in the same directory with `cat.py`, and `file.txt` is with the following text: 32 | 33 | ``` 34 | Python is an awesome language! 35 | You should try it. 36 | ``` 37 | 38 | This is the result: 39 | ``` 40 | $ python3.4 cat.py file.txt 41 | Python is an awesome language! 42 | You should try it. 43 | ``` 44 | 45 | ## Cat multiple files 46 | 47 | 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. 48 | 49 | __The number of the files that are given as arguments is unknown.__ 50 | 51 | There should be a newline between every two files that are printed. 52 | 53 | ### Boilerplate 54 | 55 | ```python 56 | # cat2.py 57 | import sys 58 | 59 | 60 | def main(): 61 | pass 62 | 63 | if __name__ == '__main__': 64 | main() 65 | ``` 66 | 67 | ### Examples 68 | 69 | If we have two files - `file1.txt` and `file2.txt` in the same directory with `cat2.py` and: 70 | 71 | __file1.txt:__ 72 | ``` 73 | Python is an awesome language! 74 | You should try it. 75 | ``` 76 | 77 | __file2.txt:__ 78 | ``` 79 | Also, you can use Python at a lot of different places! 80 | ``` 81 | 82 | 83 | This is the result: 84 | ``` 85 | $ python3.4 cat2.py file1.txt file2.txt 86 | Python is an awesome language! 87 | You should try it. 88 | 89 | Also, you can use Python at a lot of different places! 90 | ``` 91 | 92 | ## Generate file with random integers 93 | 94 | Implement a Python script, called `generate_numbers.py` that takes two arguments - a `filename` and an integer `n`. 95 | 96 | The script should create a file with the `filename` and print `n` random integers, separated by `" "`. 97 | 98 | For random integers, you can use: 99 | 100 | ```python 101 | from random import randint 102 | print(randint(1, 1000)) 103 | ``` 104 | 105 | ### Boilerplate 106 | 107 | ```python 108 | # generate_numbers.py 109 | import sys 110 | from random import randint 111 | 112 | 113 | def main(): 114 | pass 115 | 116 | if __name__ == '__main__': 117 | main() 118 | ``` 119 | 120 | ### Examples 121 | 122 | ``` 123 | $ python3.4 generate_numbers.py numbers.txt 100 124 | $ cat numbers.txt 125 | 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 126 | ``` 127 | 128 | ## Sum integers from file 129 | 130 | Implement a Python script, called `sum_numbers.py` that takes one argument - a `filename` which has integers, separated by `" "`. 131 | 132 | The script should print the sum of all integers in that file. 133 | 134 | ### Examples 135 | 136 | If we use the generated file from Problem 3: 137 | 138 | ``` 139 | $ python3.4 sum_numbers.py numbers.txt 140 | 47372 141 | ``` 142 | 143 | ## Implement an alternative to du -h command 144 | 145 | In linux, if we want to know the size of a directory, we use the `du` command. For example: 146 | 147 | ``` 148 | $ du -hs /home/radorado/code 149 | 2,3G /home/radorado/code 150 | ``` 151 | 152 | * `-h` flag is for "human readable" which means we get the size in gigabytes, not bytes. 153 | * `-s` flag is for silent. We don't want to print every file that we go through. 154 | 155 | In a file called `duhs.py`, implement the logic of `du -hs /some/path`, where `/some/path` is obtained as an argument to the file. 156 | 157 | Example usage: 158 | 159 | ``` 160 | $ python3.4 duhs.py /home/radorado/code 161 | /home/radorado/code size is: 2.3G 162 | ``` 163 | 164 | **THIS IS NOT THE SOLUTION WE WANT:** 165 | 166 | ```python 167 | from subprocess import call 168 | import sys 169 | 170 | path = sys.argv[1] 171 | 172 | call(["du", "-s", "-h", path]) 173 | ``` 174 | 175 | ### Hints 176 | 177 | * Check the [`os`](https://docs.python.org/3.4/library/os.html) python module. 178 | * Many of the methods raise errors. In order to deal with an error you can do the following things: 179 | 180 | ```python 181 | try: 182 | os.something(path) 183 | except FileNotFoundError as error: 184 | print(error) 185 | ``` 186 | 187 | WHen you `except` the error, it wont crash your program. 188 | -------------------------------------------------------------------------------- /week4-Music-Library/1-Music-Library/README.md: -------------------------------------------------------------------------------- 1 | # A Music Library 2 | 3 | We are going to implement a music library + crawler for the music on our computer. 4 | 5 | ## Songs 6 | 7 | First, we are going to need a `Song` class which we want to initialize like that: 8 | 9 | ```python 10 | s = Song(title="Odin", artist="Manowar", album="The Sons of Odin", length="3:44") 11 | ``` 12 | 13 | **Length can have hours!:** 14 | 15 | Those are all valid lengths: 16 | 17 | * `"3:44"` 18 | * `"1:30:44"` 19 | 20 | 21 | The methods we want for our `Song` are: 22 | 23 | * `__str__` - should be able to turn the song into the following string: `"{artist} - {title} from {album} - {length}"` 24 | * Our `Song` should be hashabe! Implement `__eq__` and `__hash__` 25 | * `length(seconds=True)` should return the length in seconds. 26 | * `length(minutes=True)` should return the length in minutes (omit the seconds) 27 | * `length(hours=True)` should return the length in hours (omit minutes and seconds that does not add up to a full hour) 28 | * `length()` should return the string representation of the length 29 | 30 | ## Playlist class 31 | 32 | We are going to implement a collection for our songs. 33 | 34 | The playlist should be initialized with a name: 35 | 36 | ```python 37 | code_songs = Playlist(name="Code", repeat=True, shuffle=True) 38 | ``` 39 | 40 | * `repeat` should be defaulted to `False` 41 | * `shuffle` should be defaulted to `False` 42 | 43 | The `Playlist` should behave like that: 44 | 45 | * `add_song(song)` and `remove_song(song)` are self explanatory. 46 | * `add_songs(songs)` should add a list of `songs`. 47 | * `total_length()` should return a string representation of tha total length of all songs in the playlist 48 | * `artists()` - returns a histogram of all artists in the playlist. For each artist, we must have the count of songs in the playlist. 49 | * `next_song()` should return a `Song` instance from the order of the playlist. If `repeat=True`, when our playlist reaches the end, it should loop back again at the beginning. If `shuffle=True`, everytime we call `next_song()`, we should get a different song. **Make it randomize so it wont repeat a played song unless every song from the playlist has been played!** 50 | * `pprint_playlist()` should print the playlist to the console in a table-like view. Here is an example: 51 | 52 | ``` 53 | | Artist | Song | Length | 54 | | --------|------------------|--------| 55 | | Manowar | Odin | 3:44 | 56 | | Manowar | The Sons of Odin | 6:26 | 57 | ``` 58 | 59 | ## Saving and Loading the playlist 60 | 61 | We should be able to save and load a playlist to a JSON file. 62 | 63 | * `save()` should be a instance method, that saves the playlist to a JSON filed, which has the name of the playlist. If the name has whitespaces in it, replace them with `"-"` - the so-called dasherize. 64 | * `load(path)` should be a `@staticmethod` that returns a new `Playlist` instance. 65 | 66 | Example usage: 67 | 68 | ```python 69 | code = Playlist("For Code") 70 | # ... adding songs ... 71 | 72 | # Saves to For-Code.json 73 | code.save() 74 | ``` 75 | 76 | Later on: 77 | 78 | ```python 79 | code = Playlist.load("For-Code.json") 80 | code.name == "For Code" # True 81 | ``` 82 | 83 | Save the `*.json` files in a specified folder, for instance, called `playlist-data`. Make the `load` function look there too. 84 | 85 | ## MusicCrawler 86 | 87 | Create a `MusicCrawler` class that creates playlists and songs objects from real files on your file system. Most of .mp3 and .ogg files have tags that describe them. Tags hold information about the title, artist, album etc. Our task is to crawl a directory full of .mp3 or .ogg files and create a new playlist by reading the files' tags. 88 | 89 | __We recommend you [mutagen](http://mutagen.readthedocs.org/en/latest/#) to read audio metadata__ 90 | 91 | Example: 92 | 93 | ```python 94 | >>> crawler = MusicCrawler("/home/ivaylo/Music/") 95 | >>> playlist = crawler.generate_playlist() 96 | ``` 97 | 98 | As you see, the constructor takes only one argument - the `path` of the directory from which you should read all the audio files. 99 | 100 | `generate_playlist()` method craws all the files and returns a new playlist full of songs. 101 | 102 | ## MusicPlayer * 103 | 104 | **This is a hard problem. Solve only if you want to.** 105 | 106 | If you want to make this program usable you should implement a console interface for it. __You don't have to test this class!__ Implement this in the way you like it. Make it as user friendly as a console can be. 107 | 108 | This class should only parse user commands and call methods from the above classes. 109 | 110 | In order to play music from the console, you can use the `mpg123` command: 111 | 112 | ``` 113 | $ sudo apt-get install mpg123 114 | ``` 115 | 116 | After this, you can do: 117 | 118 | ``` 119 | $ mpg123 music.mp3 120 | ``` 121 | 122 | In order to make the console responsive while the music is playing, you may take a look at this code: 123 | 124 | ```python 125 | from subprocess import Popen, PIPE 126 | 127 | def play(mp3Path): 128 | p = Popen(["mpg123", mp3Path], stdout=PIPE, stderr=PIPE) 129 | return p 130 | 131 | def stop(process): 132 | process.kill() 133 | 134 | p = play("music.mp3") 135 | stop(p) 136 | ``` 137 | -------------------------------------------------------------------------------- /week2-OOP-Problems/Polyglot/polyglot.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from subprocess import call 3 | import sys 4 | 5 | 6 | def parse_command(command): 7 | return tuple(command.split(" ")) 8 | 9 | 10 | def is_command(command_tuple, command_string): 11 | return command_tuple[0] == command_string 12 | 13 | 14 | def fetch_languages(conn): 15 | cursor = conn.cursor() 16 | query = "SELECT id, language, guide, answered FROM languages" 17 | result = [] 18 | 19 | for row in cursor.execute(query): 20 | result.append(row) 21 | 22 | return result 23 | 24 | 25 | def create_language_folder(language): 26 | call("mkdir {}".format(language), shell=True) 27 | 28 | 29 | def create_language_source(language, filename, source): 30 | file = open(language + "/" + filename, "w") 31 | file.write(source) 32 | file.close() 33 | 34 | 35 | def create_menu(): 36 | menu = ["Hello and Welcome!", 37 | "I am the compiler.", 38 | "You can ask me to output different source files.", 39 | "I will provide guides for compiling too." 40 | "When you are ready, you can provide me with the answer \ 41 | from the code.", 42 | "And I will reveal a secret to you!", 43 | "Type help, to get you started."] 44 | 45 | return "\n".join(menu) 46 | 47 | 48 | def create_help(): 49 | help = ["Here is the list of commands:", 50 | "", 51 | "list - This will list all available languages", 52 | "start - This will start you with the language #n", 53 | "answer - This will check your answer for language n", 54 | "", 55 | "Your objective is to get all answers right!", 56 | "But first, you have to finish the code for the compiler,", 57 | "since it is not complete!"] 58 | 59 | return "\n".join(help) 60 | 61 | 62 | def get_language_answered_state(answered): 63 | if answered == 0: 64 | return "NOT_DONE" 65 | 66 | return "DONE" 67 | 68 | 69 | # should consider database 70 | def create_language_list(conn): 71 | languages = fetch_languages(conn) 72 | pattern = "{} [{}] - {}" 73 | l = lambda x: pattern.format(get_language_answered_state(x[3]), x[0], x[1]) 74 | languages = map(l, languages) 75 | 76 | return "\n".join(languages) 77 | 78 | 79 | def open_connection(database): 80 | conn = sqlite3.connect(database) 81 | return conn 82 | 83 | 84 | def trigger_start(conn, command): 85 | cursor = conn.cursor() 86 | lang_id = int(command[1]) 87 | query_lang = "SELECT language, guide, answered FROM languages WHERE id = ?" 88 | result_lang = cursor.execute(query_lang, (lang_id, )).fetchone() 89 | 90 | if get_language_answered_state(result_lang[2]) == "DONE": 91 | print("Hey, you have DONE this. Go get another language!") 92 | return 93 | 94 | query_sources = "SELECT file_name, source FROM sources WHERE lang_id = ?" 95 | result_source = cursor.execute(query_sources, (lang_id, )) 96 | 97 | create_language_folder(result_lang[0]) 98 | 99 | for source_row in result_source: 100 | create_language_source(result_lang[0], source_row[0], source_row[1]) 101 | 102 | print("You have made a choice!") 103 | print(result_lang[1]) 104 | sys.exit(0) 105 | 106 | 107 | def trigger_unknown_command(): 108 | unknown_command = ["Error: Unknown command!", 109 | "Why don't you type help,", 110 | "to see a list of commands."] 111 | 112 | return "\n".join(unknown_command) 113 | 114 | 115 | def check_answer(conn, lang_id, answer): 116 | query = "SELECT COUNT(id) FROM languages WHERE id = ? AND answer = ?" 117 | cursor = conn.cursor() 118 | 119 | result = cursor.execute(query, (lang_id, answer)).fetchone() 120 | 121 | return result[0] == 1 122 | 123 | 124 | def complete_answer(conn, lang_id): 125 | query = "UPDATE languages SET answered = 1 WHERE id = ?" 126 | conn.cursor().execute(query, (lang_id,)) 127 | conn.commit() 128 | 129 | 130 | def trigger_answer(conn, command): 131 | lang_id = int(command[1]) 132 | answer = command[2] 133 | 134 | if len(command) > 3: 135 | partial_answer = [] 136 | for i in range(2, len(command)): 137 | partial_answer.append(command[i]) 138 | answer = " ".join(partial_answer) 139 | 140 | print("Your answer is: {}".format(answer)) 141 | 142 | if check_answer(conn, lang_id, answer): 143 | complete_answer(conn, lang_id) 144 | print("You got that right! Bravo!") 145 | print("Now, on to the next one!") 146 | else: 147 | print("Noope. This is not the right answer. Try again.") 148 | 149 | 150 | def main(): 151 | conn = open_connection("polyglot.db") 152 | 153 | print(create_menu()) 154 | 155 | while True: 156 | command = parse_command(input("Enter command>")) 157 | 158 | if is_command(command, "help"): 159 | print(create_help()) 160 | elif is_command(command, "list"): 161 | print(create_language_list(conn)) 162 | elif is_command(command, "start"): 163 | trigger_start(conn, command) 164 | elif is_command(command, "answer"): 165 | trigger_answer(conn, command) 166 | elif is_command(command, "finish"): 167 | break 168 | else: 169 | print(trigger_unknown_command()) 170 | 171 | conn.close() 172 | 173 | if __name__ == '__main__': 174 | main() 175 | -------------------------------------------------------------------------------- /week3-TDD-and-Graphs/3-Panda-Social-Network/social.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class Panda: 5 | def __init__(self, name, email, gender): 6 | self.__name = name 7 | self.__gender = gender 8 | self.__email = email 9 | 10 | def name(self): 11 | return self.__name 12 | 13 | def email(self): 14 | return self.__email 15 | 16 | def gender(self): 17 | return self.__gender 18 | 19 | def __str__(self): 20 | return "{} - {} - {}".format(self.name(), self.email(), self.gender()) 21 | 22 | def __repr__(self): 23 | return "Panda('{}', '{}', '{}')".format(self.name(), self.email(), self.gender()) 24 | 25 | def to_json(self): 26 | return self.__repr__() 27 | 28 | def __hash__(self): 29 | return hash(self.__str__()) 30 | 31 | def __eq__(self, other): 32 | return self.name() == other.name() and self.email() == other.email() \ 33 | and self.gender() == other.gender() 34 | 35 | 36 | class PandaSocialNetwork: 37 | def __init__(self): 38 | self.network = {} 39 | 40 | def has_panda(self, panda): 41 | return panda in self.network 42 | 43 | def add_panda(self, panda): 44 | if self.has_panda(panda): 45 | raise Exception("Panda already there") 46 | 47 | self.network[panda] = [] 48 | 49 | def are_friends(self, panda1, panda2): 50 | if panda1 not in self.network or panda2 not in self.network: 51 | return False 52 | 53 | return panda1 in self.network[panda2] and \ 54 | panda2 in self.network[panda1] 55 | 56 | def make_friends(self, panda1, panda2): 57 | if not self.has_panda(panda1): 58 | self.add_panda(panda1) 59 | 60 | if not self.has_panda(panda2): 61 | self.add_panda(panda2) 62 | 63 | if self.are_friends(panda1, panda2): 64 | raise Exception("Pandas are already friends") 65 | 66 | self.network[panda1].append(panda2) 67 | self.network[panda2].append(panda1) 68 | 69 | def panda_connections(self, panda): 70 | print(self.network) 71 | connections = {} 72 | q = [] 73 | visited = set() 74 | 75 | q.append((0, panda)) 76 | visited.add(panda) 77 | 78 | while len(q) != 0: 79 | panda_data = q.pop(0) 80 | current_level = panda_data[0] 81 | current_node = panda_data[1] 82 | print(current_node) 83 | connections[current_node] = current_level 84 | 85 | for neighbour in self.network[current_node]: 86 | # print(neighbour) 87 | if neighbour not in visited: 88 | visited.add(neighbour) 89 | q.append((current_level + 1, neighbour)) 90 | 91 | return connections 92 | 93 | def connection_level(self, panda1, panda2): 94 | panda_table = self.panda_connections(panda1) 95 | 96 | if panda2 not in panda_table: 97 | return -1 98 | 99 | return panda_table[panda2] 100 | 101 | def genders_in_network(self, level, gender, panda): 102 | panda_table = self.panda_connections(panda) 103 | counter = 0 104 | 105 | for panda in panda_table: 106 | p_level = panda_table[panda] 107 | if p_level != 0 and p_level <= level and panda.gender() == gender: 108 | counter += 1 109 | 110 | return counter 111 | 112 | def __repr__(self): 113 | for_save = {} 114 | 115 | for panda in self.network: 116 | friends = [repr(panda_friend) for panda_friend in self.network[panda]] 117 | for_save[repr(panda)] = friends 118 | 119 | return json.dumps(for_save, indent=True) 120 | 121 | def save(self, filename): 122 | with open(filename, "w") as f: 123 | f.write(self.__repr__()) 124 | 125 | @staticmethod 126 | def load(filename): 127 | network = PandaSocialNetwork() 128 | with open(filename, "r") as f: 129 | contents = f.read() 130 | json_network = json.loads(contents) 131 | 132 | for panda in json_network: 133 | for friends in json_network[panda]: 134 | # ВИЕ ТРЯБВА САМИ ДА ГО НАПРАВИТЕ ПО НЯКАКЪВ НАЧИН 135 | p1 = eval(panda) 136 | p2 = eval(friends) 137 | if not network.are_friends(p1, p2): 138 | network.make_friends(p1, p2) 139 | 140 | return network 141 | 142 | 143 | # A -> B -> C -> D -> A 144 | p1 = Panda("A", "asda", "male") 145 | p2 = Panda("B", "asdasd", "female") 146 | p3 = Panda("C", "asdads", "male") 147 | p4 = Panda("D", "asdada", "female") 148 | 149 | network = PandaSocialNetwork() 150 | network.make_friends(p1, p2) 151 | network.make_friends(p2, p3) 152 | network.make_friends(p3, p4) 153 | network.make_friends(p4, p1) 154 | 155 | network.save("network.json") 156 | network2 = PandaSocialNetwork.load("network.json") 157 | 158 | # print(repr(network2)) 159 | result = network2.panda_connections(p1) 160 | print(json.dumps({repr(panda): result[panda] for panda in result}, indent=True)) 161 | -------------------------------------------------------------------------------- /week9-SQLAlchemy/1-Money-In-The-Bank/README.md: -------------------------------------------------------------------------------- 1 | # Money In The Bank 2 | 3 | ## 0. Setup the project 4 | 5 | Your first task is to set up the project! You can find the code within that folder. 6 | 7 | Then run the program from `start.py`. 8 | 9 | __Try to run the tests.__ If everything is OK you are ready to continue. 10 | 11 | ## 1. Refactor it 12 | 13 | We have provided you a "piece of shit" code. Yep. Blame us all you want. But you need to provide some refactoring and make it look cleaner. 14 | 15 | **You have tests. Use them for easier refactoring!** 16 | 17 | ## 2. SQL injection 18 | 19 | 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. 20 | 21 | You can try to login and give the following username: 22 | 23 | ``` 24 | ' OR 1 = 1 -- 25 | ``` 26 | 27 | and give anything for the password. You will login successfuly! 28 | 29 | __Make some unit tests to proof your security.__ 30 | 31 | 32 | ## 3. Strong passwords 33 | 34 | In this program you can register people and there are no any requirements for the password. 35 | 36 | To increase the level of security of your clients, they all must have a STRONG password. By strong we mean: 37 | 38 | * More then 8 symbols 39 | * Must have capital letters, and numbers and a special symbol 40 | * Username is not in the password (as a substring) 41 | 42 | Implement that requirements in the register and change password function and test them. 43 | 44 | ## 4. Hash them passwords! 45 | 46 | __All the passwords of your users are in plain text.__ 47 | 48 | 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. 49 | 50 | Write some tests to proof your work. 51 | 52 | You would find this useful -> http://www.pythoncentral.io/hashing-strings-with-python/ 53 | 54 | ## 5. Hide passwords while typing 55 | 56 | As a UI (User Interface) of your application you are using the console. 57 | 58 | If you are typing your password in a web form, it appears as `***`. Sadly, this is not the case in our console. 59 | 60 | [__Make some research and fix that problem.__](https://docs.python.org/3.4/library/getpass.html#getpass.getpass) 61 | 62 | No, you can not test that at all. :D 63 | 64 | ## 6. Bruteforce protection 65 | 66 | 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. 67 | 68 | __If someone enters 5 wrong passwords in a row, block him from trying for the next 5 minutes.__ 69 | 70 | This should work even if the user exits the bank software and tries to login again. 71 | 72 | It is a good idea to use the help of the database, to achieve that! 73 | __As always, don't forget the tests.__ 74 | 75 | ## 7. Reset password email 76 | 77 | Your customers need a reset password function! 78 | 79 | __Add an email field in the client module and in the database.__ 80 | Your command must look like this: 81 | 82 | ``` 83 | send-reset-password Ivaylo 84 | ``` 85 | 86 | It sends an email to the user, with a unique random hash code. 87 | 88 | ``` 89 | reset-password Ivaylo 90 | ``` 91 | 92 | 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. 93 | 94 | Try sending emails by using a gmail SMTP. GOOGLE IT! 95 | 96 | ## 8. Transactions in the bank 97 | 98 | Since this is a bank, every user should be able to make transactions to his bank account. 99 | 100 | For now, every user will have only 1 bank account. 101 | 102 | All users must be able to perform the following actions: 103 | 104 | * Deposit money in the bank account 105 | * Withdraw money from the bank account 106 | * Diplay the current balance from the bank accont 107 | 108 | The restrictions are as follow: 109 | 110 | * One cannot withdraw more money than the current balance 111 | 112 | Implement the following commands for every bank account. Those commands should work only if the user has been logged in to the system. 113 | 114 | Remember, TDD is your friend! 115 | 116 | 117 | ## 9. More secure transactions - TAN 118 | 119 | TAN (Transaction Authenticanon Number) is a extra layer of security. 120 | 121 | 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! 122 | 123 | For example: 124 | 125 | ``` 126 | $$$>login 127 | Enter your username: rado 128 | Enter your password: 129 | Welcome you are logged in as: rado 130 | Logged>>deposit 131 | Enter amount: 1 000 000 132 | Enter TAN code: 8490c2c992d10003370509e7a008f659c8220b6db62e591449106d04a45174cc 133 | Transaction successful! 134 | 1 000 000 were deposited to the bank 135 | ``` 136 | 137 | Here are the details for the TAN codes: 138 | 139 | * TAN codes are required only for depositing and withdrawing transactions 140 | * __Every code can only be used once.__ After this, the code becomes inactive and cannot be given to complete a transactions 141 | * Once registered, a bank user starts without TAN codes - he have to ask for them 142 | 143 | Implement a command, called ```get-tan``` that does the following thing: 144 | 145 | * Can only be used for a logged-in user 146 | * Asks for the user password again 147 | * Emails the user a list of 10 unique TAN codes, that he can use for his next 10 transactions 148 | * 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 149 | * If there are 0 TAN codes remaining, generate 10 new for that user and email them to him! 150 | -------------------------------------------------------------------------------- /week8-Team-Work/3-Cinema-Reservation-System/README.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 | -------------------------------------------------------------------------------- /week8-Team-Work/materials/relationships.md: -------------------------------------------------------------------------------- 1 | # Database relationships 2 | 3 | In the relational database world, relations are a key concept that helps us model our data. 4 | 5 | **The idea is to define how two tables are connected in terms of their data.** 6 | 7 | Lets have the following example: 8 | 9 | * We want to have a databases, holding users in our blog system. We will have the following schemas: 10 | 11 | ```sql 12 | CREATE TABLE Users( 13 | user_id INTEGER PRIMARY KEY, 14 | user_name TEXT, 15 | user_email TEXT 16 | ) 17 | 18 | CREATE TABLE Posts( 19 | post_id INTEGER PRIMARY KEY, 20 | post_title TEXT, 21 | post_content TEXT, 22 | author INTEGER 23 | ) 24 | ``` 25 | 26 | The interesting part is the connection between a post and his author. 27 | 28 | Should we keep the author's name? Or his email? Or should we use the fact that PK will always give us a unique row from `Users`. 29 | 30 | Then, what happens if we keep in the `author` column, the `user_id` of the given user, that wrote the post? 31 | 32 | We can have data like that: 33 | 34 | | user_id | user_name | user_email | 35 | |---------|-----------|---------------------------| 36 | | 1 | RadoRado | radorado@hackbulgaria.com | 37 | | 2 | Ivo | ivaylo@hackbulgaria.com | 38 | | 3 | Tony | tony@hackbulgaria.com | 39 | 40 | and 41 | 42 | | post_id | post_title | post_content | author | 43 | |---------|-------------|-------------------------|--------| 44 | | 1 | Hello World | The first blog post | 1 | 45 | | 2 | New courses | Something interesting.. | 3 | 46 | 47 | 48 | Then the `author` column holds the `user_id` of the given user. We have now built a relationship! 49 | 50 | * **Such relationship is called 1:N or one-to-many!** 51 | * The `author` column is called a **foreign key**, because it holds values that are values of a primary key column elsewhere! 52 | 53 | ## 1:N relationship 54 | 55 | The one-to-many relationship can be described as follows: 56 | 57 | * One user can have many articles 58 | * One article has only 1 author (user) 59 | 60 | This means that we can have repating values in the `author` column in `Posts`. 61 | 62 | **1:N relationships are build using Foreign keys.** 63 | 64 | ## N:M relationship 65 | 66 | This is called **many-to-many** relationship. 67 | 68 | Lets have the following example: 69 | 70 | * We want to have students 71 | * We want to have courses 72 | * One student can go to multiple courses 73 | * One course can be attented by mutiple students 74 | 75 | How do we model such relationship in our database? 76 | 77 | For many-to-many relations, we always need something, that is called a **junction table!** 78 | 79 | Our tables would look like that: 80 | 81 | **Students:** 82 | 83 | | student_id | student_name | 84 | |------------|--------------| 85 | | 1 | Ivo | 86 | | 2 | Maria | 87 | | 3 | Tony | 88 | | 4 | Rado | 89 | | 5 | Rosi | 90 | | 6 | Ani | 91 | 92 | and **Courses:** 93 | 94 | | course_id | course_name | 95 | |-----------|---------------| 96 | | 1 | 101 | 97 | | 2 | Java | 98 | | 3 | JS | 99 | | 4 | Ruby on Rails | 100 | | 5 | NodeJS | 101 | | 6 | Algorithms | 102 | 103 | and the **junction table**: 104 | 105 | | student_id | course_id | 106 | |------------|-----------| 107 | | 1 | 1 | 108 | | 2 | 1 | 109 | | 3 | 1 | 110 | | 4 | 2 | 111 | | 5 | 2 | 112 | | 6 | 6 | 113 | 114 | 115 | As you can see, each row in the junction table tells us which student is attending which course! This is our relation. 116 | 117 | **In the junction table, both columns are foreign keys to the tables in the many-to-many relation!** 118 | 119 | 120 | ## Foreign Keys 121 | 122 | We have two type of keys - Primary (PK) and Foreign (FK). 123 | 124 | If a column is a PK for the table, it can hold only unique values. And one value from that column should be able to "identify" the entire row. If we search in the `WHERE` clause with a PK, we should always get 1 value. 125 | 126 | FK are different. They are used to express relations between two tables. 127 | 128 | In the previous examples, the `author` column was a FK column. `student_id` and `course_id` in the junction table are FKs too. 129 | 130 | Here is the deal with the FK: 131 | 132 | * This is a **constraint** over what values can be inserted in the column, defined as a foreign key. 133 | * Usually, when you define your table, you say that a given column is going to be a FK to another table's PK column. 134 | 135 | Lets see the SQL for that: 136 | 137 | 138 | ```sql 139 | CREATE TABLE Users( 140 | user_id INTEGER PRIMARY KEY, 141 | user_name TEXT, 142 | user_email TEXT 143 | ) 144 | 145 | CREATE TABLE Posts( 146 | post_id INTEGER PRIMARY KEY, 147 | post_title TEXT, 148 | post_content TEXT, 149 | author INTEGER, 150 | FOREIGN KEY(author) REFERENCES Users(user_id) 151 | ) 152 | ``` 153 | 154 | There is an additional `FOREIGN KEY` statement. This is the required thing. 155 | 156 | Now if we run sqlite3: 157 | 158 | ``` 159 | sqlite> PRAGMA foreign_keys = ON; 160 | sqlite> INSERT INTO Posts(post_title, post_content, author) 161 | ... VALUES("Test", "Test", 1); 162 | SQL error: foreign key constraint failed 163 | ``` 164 | 165 | **Here is the SQL for the Student-Courses tables:** 166 | 167 | ```sql 168 | CREATE TABLE Students( 169 | student_id INTEGER PRIMARY KEY, 170 | student_name TEXT 171 | ) 172 | 173 | CREATE TABLE Courses( 174 | course_id INTEGER PRIMARY KEY, 175 | course_name TEXT 176 | ) 177 | 178 | CREATA TABLE Student_To_Course( 179 | student_id INTEGER, 180 | course_id INTEGER, 181 | FOREIGN KEY(student_id) REFERENCES Students(student_id), 182 | FOREIGN KEY(course_id) REFERENCES Courses(course_id) 183 | ) 184 | ``` 185 | 186 | Now, when you have the general idea, you can read more about FK's here - https://www.sqlite.org/foreignkeys.html 187 | -------------------------------------------------------------------------------- /week4-Music-Library/1-Music-Library/music.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import random 3 | import time 4 | from tabulate import tabulate 5 | import json 6 | 7 | 8 | class SongLength: 9 | def __init__(self, length): 10 | self.length = length 11 | self.hours = 0 12 | self.minutes = 0 13 | self.seconds = 0 14 | 15 | parts = [int(part.strip()) for part in length.split(":")] 16 | 17 | if len(parts) == 3: 18 | self.hours = parts[0] 19 | self.minutes = parts[1] 20 | self.seconds = parts[2] 21 | elif len(parts) == 2: 22 | self.minutes = parts[0] 23 | self.seconds = parts[1] 24 | else: 25 | raise ValueError("Length not proper format: {}".format(length)) 26 | 27 | def get_hours(self): 28 | return self.hours 29 | 30 | def get_minutes(self): 31 | return self.hours * 60 + self.minutes 32 | 33 | def get_seconds(self): 34 | return self.get_minutes() * 60 + self.seconds 35 | 36 | 37 | class Song: 38 | def __init__(self, title, artist, album, length): 39 | self.title = title 40 | self.artist = artist 41 | self.album = album 42 | self.length = length 43 | self.__lengthObject = SongLength(length) 44 | 45 | def __str__(self): 46 | return "{} - {} from {} - {}"\ 47 | .format(self.artist, self.title, self.album, self.length) 48 | 49 | def __repr__(self): 50 | return self.__str__() 51 | 52 | def __eq__(self, other): 53 | return self.__dict__ == other.__dict__ 54 | 55 | def __hash__(self): 56 | return hash(self.__str__()) 57 | 58 | def prepare_json(self): 59 | song_dict = self.__dict__ 60 | return {key: song_dict[key] for key in song_dict if not key.startswith("_")} 61 | 62 | def get_length(self, seconds=False, minutes=False, hours=False): 63 | if not seconds and not minutes and not hours: 64 | return self.__length 65 | 66 | if seconds: 67 | return self.__lengthObject.get_seconds() 68 | 69 | if minutes: 70 | return self.__lengthObject.get_minutes() 71 | 72 | if hours: 73 | return self.__lengthObject.get_hours() 74 | 75 | 76 | class Playlist: 77 | def __init__(self, name, repeat="NONE", shuffle=False): 78 | self.__songs = [] 79 | self.__name = name 80 | self.repeat = repeat 81 | self.shuffle = shuffle 82 | self.__current_song_index = 0 83 | self.__shuffle_played_songs = set() 84 | 85 | def add_song(self, song): 86 | self.__songs.append(song) 87 | 88 | def remove_song(self, song): 89 | try: 90 | self.__songs.remove(song) 91 | except ValueError: 92 | pass 93 | 94 | def total_length(self): 95 | total_seconds = sum([song.get_length(seconds=True) for song in self.__songs]) 96 | return str(datetime.timedelta(seconds=total_seconds)) 97 | 98 | def artists(self): 99 | all_artists = [song.artist for song in self.__songs] 100 | return {name: all_artists.count(name) for name in all_artists} 101 | 102 | def __has_next_song(self): 103 | return self.__current_song_index < len(self.__songs) 104 | 105 | def __shuffle(self): 106 | song = random.choice(self.__songs) 107 | 108 | while song in self.__shuffle_played_songs: 109 | song = random.choice(self.__songs) 110 | 111 | self.__shuffle_played_songs.add(song) 112 | 113 | if len(self.__shuffle_played_songs) == len(self.__songs): 114 | self.__shuffle_played_songs = set() 115 | 116 | return song 117 | 118 | def next_song(self): 119 | if self.repeat == "SONG": 120 | return self.__songs[self.__current_song_index] 121 | 122 | if self.shuffle: 123 | return self.__shuffle() 124 | 125 | if not self.__has_next_song() and self.repeat == "NONE": 126 | raise Exception("End of playlist") 127 | 128 | if not self.__has_next_song() and self.repeat == "PLAYLIST": 129 | self.__current_song_index = 0 130 | 131 | song = self.__songs[self.__current_song_index] 132 | self.__current_song_index += 1 133 | 134 | return song 135 | 136 | def pprint_playlist(self): 137 | headers = ["Artist", "Song", "Length"] 138 | table = [] 139 | 140 | for song in self.__songs: 141 | table.append([song.artist, song.title, song.length]) 142 | 143 | print(tabulate(table, headers=headers)) 144 | 145 | def prepare_json(self): 146 | data = { 147 | "name": self.__name, 148 | "songs": [song.prepare_json() for song in self.__songs] 149 | } 150 | 151 | return data 152 | 153 | def save(self, indent=True): 154 | filename = self.__name.replace(" ", "-") + ".json" 155 | 156 | with open(filename, "w") as f: 157 | f.write(json.dumps(self.prepare_json(), indent=indent)) 158 | 159 | @staticmethod 160 | def load(filename): 161 | with open(filename, "r") as f: 162 | contents = f.read() 163 | data = json.loads(contents) 164 | p = Playlist(data["name"]) 165 | 166 | for dict_song in data["songs"]: 167 | song = Song(artist=dict_song["artist"], title=dict_song["title"], album=dict_song["album"], length=dict_song["length"]) 168 | p.add_song(song) 169 | 170 | return p 171 | 172 | 173 | def test_load(): 174 | p = Playlist.load("Manowar-songs.json") 175 | try: 176 | while True: 177 | song = p.next_song() 178 | print(str(song)) 179 | time.sleep(1) 180 | except Exception as e: 181 | print(e) 182 | 183 | 184 | def test_save(): 185 | 186 | s = Song(album="The Sons of Odin", title="Odin", artist="Manowar", length="3:44") 187 | s1 = Song(album="The Sonds of Odin", title="Sons of Odin", artist="Manowar", length="6:08") 188 | p = Playlist("Manowar songs", repeat="SONG") 189 | p.add_song(s) 190 | p.add_song(s1) 191 | p.add_song(Song(album="Fallen", title="Bring Me To Life (radio edit)", artist="Evanesence", length="3:30")) 192 | 193 | p.pprint_playlist() 194 | 195 | p.save() 196 | 197 | 198 | test_load() 199 | 200 | -------------------------------------------------------------------------------- /week7-Intro-to-SQL/1-Scan-Bg-Web/README.md: -------------------------------------------------------------------------------- 1 | # Crawl the bulgarian web! 2 | 3 | We are goint to make a histogram of the server software that runs bulgarian webpages (that end in .bg) 4 | 5 | ## Crawling vs. API 6 | 7 | We are going to distinguish making **HTTP calls to an API** and **crawling websites**! 8 | 9 | API means that there is a way to get information in a nicely-structured way. Most of the APIs return JSON data. 10 | 11 | For example, GitHub gives us an API: https://api.github.com/users/radorado, which returns JSON. JSON is good because we can easily parse it into data-structures. 12 | 13 | But most of the websites do not provide any API. So we have to work for our information - parsing the HTML structure of the websites. **This is called crawling** 14 | 15 | ## Histogram class 16 | 17 | First, we are going to start simple. 18 | 19 | Provide a simple `Histogram` class, which can be used like that: 20 | 21 | ```python 22 | h = Histogram() 23 | 24 | h.add("Apache") 25 | h.add("Apache") 26 | h.add("nginx") 27 | h.add("IIS") 28 | h.add("nginx") 29 | 30 | h.count("Apache") == 2 # True 31 | h.count("nginx") == 2 # True 32 | h.count("IIS") == 1 # True 33 | h.count("IBM Web Server") is None # True 34 | 35 | 36 | for key, count in h.items():c 37 | print("{}: {}".format(key, count)) 38 | # Apache: 2 39 | # nginx: 2 40 | # IIS: 1 41 | 42 | h.get_dict() == {"Apache": 2, "nginx": 2, "IIS": 1} # True 43 | ``` 44 | 45 | ## What serves that website? 46 | 47 | In order to understand what is the server, running given web site, we need to make HTTP request (for example GET), and see the response headers. 48 | 49 | **There is a `Server` header which holds a string with the information.** 50 | 51 | For example: 52 | 53 | ``` 54 | $ curl -I https://hackbulgaria.com 55 | HTTP/1.1 200 OK 56 | Server: nginx/1.6.2 57 | Date: Sat, 25 Apr 2015 21:05:43 GMT 58 | Content-Type: text/html; charset=utf-8 59 | Connection: keep-alive 60 | Vary: Accept-Encoding 61 | X-Frame-Options: SAMEORIGIN 62 | Vary: Cookie 63 | Strict-Transport-Security: max-age=31536000 64 | X-Content-Type-Options: nosniff 65 | X-Frame-Options:: deny 66 | ``` 67 | 68 | As you can see, the `Server: nginx/1.6.2` header gives us the information that `hackbulgaria.com` runs `nginx`. 69 | 70 | That's great. 71 | 72 | How about Python? 73 | 74 | ```python 75 | >>> import requests 76 | >>> r = requests.get("https://hackbulgaria.com") 77 | >>> print(r.headers["Server"]) 78 | 'nginx/1.6.2' 79 | ``` 80 | 81 | Easy as a pie! 82 | 83 | ## Crawling & Information 84 | 85 | Now, if we want to crawl all websites (or almost all websites) that end in .bg, we are going to need a list of them. 86 | 87 | We can a big list, around 10 000 pages, from here - http://register.start.bg/ 88 | 89 | The catch is - there is no API. We will have to crawl our information. **Find all links in the HTML of the page.** 90 | 91 | Our hint here is - use [BeautifulSoup](http://www.crummy.com/software/BeautifulSoup/) in order to parse the HTML of the page into more meaningful structures. 92 | 93 | Check the structure of the website. There are links that look like `link.php?id=64722`. They are the key to solve that problem. Play with them - see what you can do. 94 | 95 | ## Histogram of the results 96 | 97 | While you crawl all the 10 000 pages, create a histogram of the different web servers that run them. 98 | 99 | When you are done, save the result in a file. 100 | 101 | You are going to have results which look like that: 102 | 103 | ``` 104 | ... 105 | Apache/2.4.7 (Unix) PHP/5.4.25: 1 106 | Apache/2.4.7 (Win64) OpenSSL/1.0.1g mod_fcgid/2.3.9: 1 107 | Apache/2.4.9 (Unix) OpenSSL/1.0.1e-fips mod_bwlimited/1.4: 1 108 | Apache/2: 7 109 | Apache: 384 110 | Apache-Coyote/1.1: 1 111 | Apache mod_fcgid/2.3.7 mod_auth_pgsql/2.0.3: 2 112 | Apashi 1.1 mod_fcgid/2.3.5: 1 113 | cloudflare-nginx: 5 114 | Host.bg redirect server based on Apache/1.3.31 (Unix): 2 115 | IBM_HTTP_Server: 2 116 | lighttpd/1.4.22: 1 117 | Microsoft-IIS/5.0: 5 118 | Microsoft-IIS/6.0: 15 119 | Microsoft-IIS/7.0: 3 120 | Microsoft-IIS/7.5: 23 121 | Microsoft-IIS/8.0: 2 122 | Microsoft-IIS/8.5: 3 123 | nginx/0.7.62: 1 124 | nginx/0.7.65: 1 125 | nginx/0.7.67: 1 126 | nginx/0.8.53: 1 127 | nginx/1.0.10: 3 128 | ... 129 | ``` 130 | 131 | ## Consolidate Results & Plot Them 132 | 133 | As you can see, there are a lot of different versions of Apache or nginx or IIS. If we want to make a more general analysis, we are going to consolidate all Apache results into a single "Apache" entity. 134 | 135 | Do so and plot the resulted histogram in a histogram chart that looks something like that: 136 | 137 | ![](histogram.png) 138 | 139 | 140 | You can use [matplotlib](http://matplotlib.org/) for ploting. 141 | 142 | ## Hints 143 | 144 | Read this only if you have done most of hte problem. 145 | 146 | ### User Agent 147 | 148 | If you send request from python's `requests` it looks like that: 149 | 150 | ``` 151 | GET / HTTP/1.1 152 | Host: localhost:8000 153 | User-Agent: python-requests/2.3.0 CPython/3.4.2 Linux/3.16.0-34-generic 154 | Accept-Encoding: gzip, deflate 155 | Accept: */* 156 | ``` 157 | 158 | As you can see, the `User-Agent` header says python-requests. This is not good. 159 | 160 | If we want to send a request that looks like google chrome, we can check here - http://www.useragentstring.com/pages/Chrome/ 161 | 162 | In order to do this, we will use the `headers=` keyword for `requests.get` 163 | 164 | ```python 165 | our_headers = { 166 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" 167 | } 168 | 169 | requests.get("http://localhost:8000", headers=our_headers) 170 | ``` 171 | 172 | This will look like a Google Chrome request, which is good when we are crawling. 173 | 174 | ### HEAD instead of GET 175 | 176 | When we make a `GET` request, we download the entire HTML. This can be slow, especially on slow internet connections. 177 | 178 | There is another HTTP verb that we can use. It is called `HEAD`. 179 | 180 | This request asks only for the server headers and not for the content - exactly what we need. 181 | 182 | Read the [requests documentation](http://docs.python-requests.org/en/latest/user/quickstart/) to see how to make a `HEAD` request. 183 | 184 | ### Ploting from dictionary 185 | 186 | When you are ready and you have a dictionary with results, you can plot the histogram like that: (it is ugly!) 187 | 188 | ```python 189 | from hist import Histogram 190 | import matplotlib.pyplot as plt 191 | 192 | h = Histogram() 193 | 194 | # .. results .. 195 | 196 | h = h.get_dict() 197 | keys = list(h.keys()) 198 | values = list(h.values()) 199 | 200 | X = list(range(len(keys))) 201 | 202 | plt.bar(X, list(h.values()), align="center") 203 | plt.xticks(X, keys) 204 | 205 | plt.title(".bg servers") 206 | plt.xlabel("Server") 207 | plt.ylabel("Count") 208 | 209 | plt.savefig("histogram.png") 210 | ``` 211 | 212 | -------------------------------------------------------------------------------- /week1-Python-Problems/3-The-Final-Round/solutions.py: -------------------------------------------------------------------------------- 1 | import calendar 2 | 3 | 4 | def count_words(words): 5 | return {key: words.count(key) for key in words} 6 | 7 | 8 | def unique_words(words): 9 | return len([key for key in count_words(words)]) 10 | 11 | 12 | def unique_words2(words): 13 | return len(set(words)) 14 | 15 | 16 | def dedup(items): 17 | found = set() 18 | result = [] 19 | 20 | for item in items: 21 | if item not in found: 22 | result.append(item) 23 | found.add(item) 24 | 25 | return result 26 | 27 | 28 | def nan_expand(times): 29 | result = "" 30 | if times == 0: 31 | return "" 32 | 33 | for i in range(times): 34 | result += "Not a " 35 | result += "NaN" 36 | 37 | return result 38 | 39 | 40 | def iterations_of_nan_expand(expanded): 41 | nan_table = {} 42 | n = len(expanded) 43 | 44 | current_index = 0 45 | 46 | while True: 47 | current_expand = nan_expand(current_index) 48 | nan_table[current_expand] = current_index 49 | 50 | if len(current_expand) > n: 51 | break 52 | 53 | if expanded in nan_table: 54 | return nan_table[expanded] 55 | 56 | return False 57 | 58 | 59 | def iterations_of_nan_expand2(expanded): 60 | if nan_expand(expanded.count("Not a")) == expanded: 61 | return expanded.count("Not a") 62 | return False 63 | 64 | 65 | def is_prime(n): 66 | if n <= 1: 67 | return False 68 | 69 | start = 2 70 | 71 | while start < n: 72 | if n % start == 0: 73 | return False 74 | 75 | start += 1 76 | 77 | return True 78 | 79 | 80 | def next_prime(n): 81 | n += 1 82 | 83 | while not is_prime(n): 84 | n += 1 85 | 86 | return n 87 | 88 | 89 | def divide_count(n, k): 90 | times = 0 91 | 92 | while n != 1 and n % k == 0: 93 | times += 1 94 | n = n // k 95 | 96 | return times 97 | 98 | 99 | def prime_factorization(n): 100 | result = [] 101 | 102 | current_prime = 2 103 | 104 | while n != 1: 105 | times = divide_count(n, current_prime) 106 | 107 | if times != 0: 108 | result.append((current_prime, times)) 109 | n = n // current_prime ** times 110 | 111 | current_prime = next_prime(current_prime) 112 | 113 | return result 114 | 115 | 116 | def counter_of_dividers(n, prime_number): 117 | divider_counter = 0 118 | while n % prime_number == 0: 119 | divider_counter += 1 120 | n /= prime_number 121 | return divider_counter 122 | 123 | 124 | def prime_factorization2(n): 125 | primes = [x for x in range(2, n+1) if is_prime(x)] 126 | return [(num, counter_of_dividers(n, num)) for num in primes if counter_of_dividers(n, num) != 0] 127 | 128 | 129 | def take_same(items): 130 | first = items[0] 131 | n = len(items) 132 | index = 1 133 | result = [first] 134 | 135 | while index < n and items[index] == first: 136 | result.append(items[index]) 137 | index += 1 138 | 139 | return result 140 | 141 | 142 | def group(items): 143 | result = [] 144 | 145 | while len(items) != 0: 146 | current_group = take_same(items) 147 | result.append(current_group) 148 | 149 | items = items[len(current_group):] 150 | 151 | return result 152 | 153 | 154 | def max_consecutive(items): 155 | return max([len(g) for g in group(items)]) 156 | 157 | 158 | def reduce_file_path(path): 159 | result = [] 160 | parts = [part for part in path.split("/") if part not in [".", ""]] 161 | 162 | while len(parts) != 0: 163 | part = parts.pop() 164 | 165 | if part == "..": 166 | if len(parts) != 0: 167 | parts.pop() 168 | else: 169 | result.insert(0, part) 170 | 171 | return "/" + "/".join(result) 172 | 173 | 174 | def reduce_file_path2(path): 175 | result = "/" 176 | folders = [word for word in path.split("/") if word != "" and word != "."] 177 | l = len(folders) 178 | locations = [folders[i] for i in range(l - 1) if folders[i] != ".." and i + 1 <= l and folders[i +1] != ".."] 179 | 180 | if l != 0 and folders[l-1] != "..": 181 | locations.append(folders[l-1]) 182 | 183 | result += "/".join(locations) 184 | 185 | return result 186 | 187 | 188 | def groupby(func, seq): 189 | result = {} 190 | 191 | for element in seq: 192 | if func(element) in result: 193 | result[func(element)].append(element) 194 | else: 195 | result[func(element)] = [element] 196 | 197 | return result 198 | 199 | 200 | def prepare_meal(number): 201 | result = "" 202 | count3 = 0 203 | while number % 3 == 0: 204 | count3 += 1 205 | number /= 3 206 | 207 | result += " ".join(["spam" for i in range(count3)]) 208 | if number % 5 == 0: 209 | result += " ".join(["eggs" if count3 == 0 else " and eggs"]) 210 | 211 | return result 212 | 213 | 214 | def same_characters(letter, string): 215 | return all([letter == ch for ch in string]) 216 | 217 | 218 | def is_an_bn(word): 219 | word_length = len(word) 220 | if word_length % 2 == 0: 221 | a = word[: word_length // 2] 222 | b = word[word_length // 2:] 223 | 224 | return same_characters("a", a) and same_characters("b", b) 225 | 226 | return False 227 | 228 | 229 | def to_digits(n): 230 | return [int(x) for x in str(n)] 231 | 232 | 233 | def count_digits(n): 234 | return sum([1 for x in to_digits(n)]) 235 | 236 | 237 | def is_credit_card_valid(number): 238 | s = 0 239 | numbs = to_digits(number) 240 | 241 | if count_digits(number) % 2 != 0: 242 | for i in range(len(str(number))): 243 | if i % 2 == 0: 244 | s += numbs[i] 245 | else: 246 | s += sum(to_digits(numbs[i] * 2)) 247 | 248 | return s % 10 == 0 249 | 250 | return False 251 | 252 | 253 | def goldbach(n): 254 | primes = [x for x in range(2, n+1) if is_prime(x)] 255 | combos = [] 256 | for n1 in primes: 257 | if n1 <= n / 2: 258 | combos.append([(n1, n2) for n2 in primes if n1 + n2 == n]) 259 | 260 | return [combo[0] for combo in combos if combo != []] 261 | 262 | 263 | def magic_square(matrix): 264 | s = [] 265 | 266 | # Sum of rows: 267 | for row in matrix: 268 | s.append(sum(row)) 269 | 270 | # Sum of columns: 271 | for i in range(0, len(matrix)): 272 | s.append(sum([row[i] for row in matrix])) 273 | 274 | # Sum of diagonals: 275 | s.append(sum([matrix[i][i] for i in range(len(matrix))])) 276 | 277 | i = 0 278 | result = 0 279 | for j in range(len(matrix) - 1, -1, -1): 280 | result += matrix[i][j] 281 | i += 1 282 | s.append(result) 283 | 284 | return all([s[0] == s[i] for i in range(len(s))]) 285 | 286 | 287 | FRIDAY_INDEX = 4 288 | def friday_years(start, end): 289 | count_friday_years = 0 290 | 291 | for year in range(start, end): 292 | count_fidays_in_year = 0 293 | for month in range(1, 13): 294 | month_calendar = calendar.monthcalendar(year, month) 295 | for week in month_calendar: 296 | if week[FRIDAY_INDEX] != 0: 297 | count_fidays_in_year += 1 298 | 299 | if count_fidays_in_year == 53: 300 | count_friday_years += 1 301 | 302 | return count_friday_years 303 | -------------------------------------------------------------------------------- /week1-Python-Problems/2-The-Real-Deal/README.md: -------------------------------------------------------------------------------- 1 | # After we have done our warmup problems 2 | 3 | ## Sum all divisors of an integer 4 | 5 | Given an integer, implement a function, called `sum_of_divisors(n)` that calculates the sum of all divisors of `n`. 6 | 7 | For example, the divisors of 8 are 1, 2, 4 and 8 and `1 + 2 + 4 + 8 = 15`. 8 | The divisors of 7 are 1 and 7, which makes the sum = 8. 9 | 10 | ### Signature 11 | 12 | ```python 13 | def sum_of_divisors(n): 14 | pass 15 | ``` 16 | 17 | ### Test examples 18 | 19 | ```python 20 | >>> sum_of_divisors(8) 21 | 15 22 | >>> sum_of_divisors(7) 23 | 8 24 | >>> sum_of_divisors(1) 25 | 1 26 | >>> sum_of_divisors(1000) 27 | 2340 28 | ``` 29 | 30 | ## Check if integer is prime 31 | 32 | Given an integer, implement a function, called `is_prime(n)` which returns True if `n` is a prime number. 33 | You should handle the case with negative numbers too. 34 | 35 | A prime number is a number, that is divisible only by 1 and itself. 36 | 37 | 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) 38 | 39 | ### Signature 40 | 41 | ```python 42 | def is_prime(n): 43 | pass 44 | ``` 45 | 46 | ### Test examples 47 | 48 | ```python 49 | >>> is_prime(1) 50 | False 51 | >>> is_prime(2) 52 | True 53 | >>> is_prime(8) 54 | False 55 | >>> is_prime(11) 56 | True 57 | >>> is_prime(-10) 58 | False 59 | ``` 60 | 61 | ## Check if a number has a prime number of divisors 62 | 63 | Given an integer, implement a function, called `prime_number_of_divisors(n)`, which returns True if the number of `n`'s divisors is a prime number, False otherwise. 64 | 65 | For example, the divisors of 8 are 1, 2, 4 and 8, a total of 4. 4 is not a prime number. 66 | The divisors of 9 are 1, 3 and 9, a total of 3. 3 is a prime number. 67 | 68 | ### Signature 69 | 70 | ```python 71 | def prime_number_of_divisors(n): 72 | pass 73 | ``` 74 | 75 | ### Test examples 76 | 77 | ```python 78 | >>> prime_number_of_divisors(7) 79 | True 80 | >>> prime_number_of_divisors(8) 81 | False 82 | >>> prime_number_of_divisors(9) 83 | True 84 | ``` 85 | 86 | ## Number containing a single digit? 87 | 88 | Implement a function, called `contains_digit(number, digit)` which checks if `digit` is contained in the given `number`. 89 | 90 | `digit` and `number` are integers. 91 | 92 | ### Signature 93 | 94 | ```python 95 | def contains_digit(number, digit): 96 | pass 97 | ``` 98 | 99 | ### Test examples 100 | 101 | ```python 102 | >>> contains_digit(123, 4) 103 | False 104 | >>> contains_digit(42, 2) 105 | True 106 | >>> contains_digit(1000, 0) 107 | True 108 | >>> contains_digit(12346789, 5) 109 | False 110 | ``` 111 | 112 | ## Number containing all digits? 113 | 114 | Implement a function, called `contains_digits(number, digits)` where `digits` is a __list of integers__ and `number` is an integer. 115 | 116 | The function should return True if __all__ `digits` are contained in `number`. 117 | 118 | ### Signature 119 | 120 | ```python 121 | def contains_digits(number, digits): 122 | pass 123 | ``` 124 | 125 | ### Test examples 126 | 127 | ```python 128 | >>> contains_digits(402123, [0, 3, 4]) 129 | True 130 | >>> contains_digits(666, [6,4]) 131 | False 132 | >>> contains_digits(123456789, [1,2,3,0]) 133 | False 134 | >>> contains_digits(456, []) 135 | True 136 | ``` 137 | 138 | ## Is number balanced? 139 | 140 | A number is called balanced, if we take the middle of it and the sums of the left and right parts are equal. 141 | 142 | For example, the number `1238033` is balanced, because it's left part is `123` and right part is `033`. 143 | 144 | We have : `1 + 2 + 3 = 0 + 3 + 3 = 6`. 145 | 146 | A number with only one digit is always balanced! 147 | 148 | Implement the function `is_number_balanced(n)` that checks if `n` is balanced. 149 | 150 | ### Signature 151 | 152 | ```python 153 | def is_number_balanced(n): 154 | pass 155 | ``` 156 | 157 | ### Test examples 158 | 159 | ```python 160 | >>> is_number_balanced(9) 161 | True 162 | >>> is_number_balanced(11) 163 | True 164 | >>> is_number_balanced(13) 165 | False 166 | >>> is_number_balanced(121) 167 | True 168 | >>> is_number_balanced(4518) 169 | True 170 | >>> is_number_balanced(28471) 171 | False 172 | >>> is_number_balanced(1238033) 173 | True 174 | 175 | ``` 176 | 177 | ## Counting substrings 178 | 179 | Implement the function `count_substrings(haystack, needle)`. It returns the count of occurrences of the string `needle` in the string `haystack`. 180 | 181 | __Don't count overlapped substings and take case into consideration!__ 182 | For overlapping substrings, check the "baba" example below. 183 | 184 | ### Signature 185 | 186 | ```python 187 | def count_substrings(haystack, needle): 188 | pass 189 | ``` 190 | 191 | ### Test examples 192 | 193 | ```python 194 | >>> count_substrings("This is a test string", "is") 195 | 2 196 | >>> count_substrings("babababa", "baba") 197 | 2 198 | >>> count_substrings("Python is an awesome language to program in!", "o") 199 | 4 200 | >>> count_substrings("We have nothing in common!", "really?") 201 | 0 202 | >>> count_substrings("This is this and that is this", "this") # "This" != "this" 203 | 2 204 | ``` 205 | 206 | ## Zero Insertion 207 | 208 | Given an integer, implement the function `zero_insert(n)`, which returns a new integer, constructed by the following algorithm: 209 | 210 | * If two neighboring digits are the same (like `55`), insert a 0 between them (`505`) 211 | * Also, if we add two neighboring digits and take their module by 10 (`% 10`) and the result is 0 - add 0 between them. 212 | 213 | For example, if we have the number `116457`, result will be: `10160457`: 214 | 215 | * 1 and 1 are the same, so we insert 0 between them 216 | * `6 + 4 % 10 = 0`, so we insert 0 between them. 217 | 218 | 219 | ### Examples 220 | 221 | ```python 222 | >>> zero_insert(116457) 223 | 10160457 224 | >>> zero_insert(55555555) 225 | 505050505050505 226 | >>> zero_insert(1) 227 | 1 228 | >>> zero_insert(6446) 229 | 6040406 230 | ``` 231 | 232 | ## Sum Numbers in Matrix 233 | 234 | You are given a `NxM` matrix of integer numbers. 235 | 236 | Implement a function, called `sum_matrix(m)` that returns the sum of all numbers in the matrix. 237 | 238 | The matrix will be represented as nested lists in Python. 239 | 240 | ### Examples: 241 | 242 | ```python 243 | >>> m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 244 | >>> sum_matrix(m) 245 | 45 246 | >>> m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 247 | >>> sum_matrix(m) 248 | 0 249 | >>> m = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] 250 | >>> sum_matrix(m) 251 | 55 252 | ``` 253 | 254 | ## Matrix Bombing 255 | 256 | You are givn a `NxM` matrix of integer numbers. 257 | 258 | We can drop a bomb at any place in the matrix, which has the following effect: 259 | 260 | * All of the **3 to 8 neighbours** (depending on where you hit!) of the target are reduced by the value of the target. 261 | * Numbers can be reduced only to 0 - they cannot go to negative. 262 | 263 | For example, if we have the following matrix: 264 | 265 | ``` 266 | 10 10 10 267 | 10 9 10 268 | 10 10 10 269 | ``` 270 | 271 | and we drop bomb at `9`, this will result in the following matrix: 272 | 273 | ``` 274 | 1 1 1 275 | 1 9 1 276 | 1 1 1 277 | ``` 278 | 279 | Implement a function called `matrix_bombing_plan(m)`. 280 | 281 | The function should return a dictionary where keys are positions in the matrix, represented as tuples, and values are the total sum of the elements of the matrix, after the bombing at that position. 282 | 283 | The positions are the standard indexes, starting from `(0, 0)` 284 | 285 | For example if we have the following matrix: 286 | 287 | ``` 288 | 1 2 3 289 | 4 5 6 290 | 7 8 9 291 | ``` 292 | 293 | and run the function, we will have: 294 | 295 | ```python 296 | {(0, 0): 42, 297 | (0, 1): 36, 298 | (0, 2): 37, 299 | (1, 0): 30, 300 | (1, 1): 15, 301 | (1, 2): 23, 302 | (2, 0): 29, 303 | (2, 1): 15, 304 | (2, 2): 26} 305 | ``` 306 | 307 | We can see that if we drop the bomb at `(1, 1)` or `(2, 1)`, we will do the most damage! 308 | 309 | --------------------------------------------------------------------------------