├── 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 |
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 |
4 |
5 |
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 |
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 |
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 | 
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 |
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------