├── playground
├── packages
│ ├── pkg
│ │ ├── foo1.py
│ │ ├── foo2.py
│ │ ├── foo3
│ │ │ └── __init__.py
│ │ └── __init__.py
│ ├── random.py
│ └── demo.py
├── test_demo.py
├── mocks
│ ├── calendar.py
│ └── test_calendar.py
├── demo.py
├── property.py
├── closures.py
├── context_managers.py
├── generators.py
└── teams_generator.py
├── week11
├── examples
│ ├── serializer
│ │ ├── __init__.py
│ │ ├── main.py
│ │ ├── fields.py
│ │ └── serializers.py
│ ├── eval_exec.py
│ ├── anonymous_classes.py
│ ├── setattr.py
│ ├── class_deconstruction.py
│ ├── ducktyping.py
│ ├── base_tracking.py
│ ├── registry.py
│ ├── class_decorators.py
│ ├── decorators.py
│ ├── metaclass.py
│ └── declarative.py
└── README.md
├── week13
├── hackademy
│ ├── education
│ │ ├── __init__.py
│ │ ├── migrations
│ │ │ ├── __init__.py
│ │ │ ├── 0002_auto_20200525_1459.py
│ │ │ └── 0001_initial.py
│ │ ├── templates
│ │ │ ├── courses
│ │ │ │ ├── new_experiment_success.html
│ │ │ │ ├── session.html
│ │ │ │ ├── edit_course.html
│ │ │ │ ├── create.html
│ │ │ │ ├── base.html
│ │ │ │ ├── detail.html
│ │ │ │ ├── list.html
│ │ │ │ └── new_experiment.html
│ │ │ └── index.html
│ │ ├── tests.py
│ │ ├── apps.py
│ │ ├── views
│ │ │ ├── __init__.py
│ │ │ └── courses.py
│ │ ├── admin.py
│ │ ├── models.py
│ │ └── urls.py
│ ├── hackademy
│ │ ├── __init__.py
│ │ ├── urls.py
│ │ ├── asgi.py
│ │ ├── wsgi.py
│ │ └── settings.py
│ └── manage.py
├── 01.Models-and-Admin
│ └── README.md
└── 02.Views
│ └── README.md
├── week10
├── CinemaReservation
│ ├── hack_cinema
│ │ ├── __init__.py
│ │ ├── movies
│ │ │ ├── views.py
│ │ │ ├── controllers.py
│ │ │ ├── models.py
│ │ │ ├── queries.py
│ │ │ └── movies_gateway.py
│ │ ├── users
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── controllers.py
│ │ │ ├── views.py
│ │ │ └── users_gateway.py
│ │ ├── settings.py
│ │ ├── db_schema
│ │ │ ├── __init__.py
│ │ │ ├── users.py
│ │ │ └── movies.py
│ │ ├── cinema.db
│ │ ├── db.py
│ │ └── index_view.py
│ ├── example
│ │ └── cinema.db
│ ├── main.py
│ └── README.md
└── README.md
├── week03
├── example.json
├── README.md
├── json_tutorial.py
├── 01.FractionsOOP
│ ├── README.md
│ ├── fraction.py
│ └── test_fraction.py
├── 03.Polynomials
│ └── README.md
├── 05.BowlingGame
│ └── README.md
├── 04.MusicLibrary
│ └── README.md
└── 02.CashDesk
│ └── README.md
├── Application
├── 1.Sensors
│ ├── sensors_data1.txt
│ ├── distance2.png
│ ├── distance3.png
│ └── README.md
├── 3.RottingApples
│ ├── day0.jpg
│ ├── day3.jpg
│ ├── day7.jpg
│ ├── day100.jpg
│ └── README.md
└── 2.CorrectBrackets
│ └── README.md
├── partners
├── hacksoft.png
└── hedgeserv.jpg
├── week07
├── README.md
├── 02.PipAndVirtualenv
│ └── README.md
└── 01.ContextManagers
│ ├── silence_exception.py
│ ├── measure_performance.py
│ ├── README.md
│ ├── test_silence_exception.py
│ └── test_measure_performance.py
├── week04
├── README.md
├── 01.Mixins
│ ├── solution
│ │ ├── mixins.py
│ │ └── test_mixins.py
│ └── README.md
└── 02.BeloteDeclarations
│ └── README.md
├── week06
├── 02.Generators
│ ├── Book.zip
│ ├── chain.py
│ └── README.md
├── README.md
└── 01.Decorators
│ ├── required.py
│ └── README.md
├── week01
├── 02.DiveIntoPython
│ ├── nokia.jpg
│ ├── biggest_palindrome_optimized.py
│ └── README.md
├── 03.FinalRound
│ ├── Anagrams
│ │ └── README.md
│ ├── GoldbachConjecture
│ │ └── README.md
│ ├── CreditCard
│ │ └── README.md
│ └── MatrixBombing
│ │ └── README.md
├── 01.PythonProblems
│ ├── week1_solutions.py
│ └── word_counter.py
└── README.md
├── week09
├── 01.SQL-Intro
│ ├── movies_schema.png
│ ├── README.md
│ └── movies.sql
├── 03.SQL-and-Python
│ ├── README.md
│ ├── tasks
│ │ ├── 03.Break-the-Cipher
│ │ │ ├── secret_keeper.py
│ │ │ └── README.md
│ │ ├── 02.Vehicle-Repair-Manager
│ │ │ ├── vehicle_repair_management_db_schema.png
│ │ │ └── README.md
│ │ └── 01.Business-Card-Catalog
│ │ │ └── README.md
│ └── demo
│ │ ├── hash.py
│ │ ├── register_form.py
│ │ ├── setup_users_database_with_plain_text_passwords.py
│ │ └── setup_users_database_with_hashed_passwords.py
└── 02.SQL-Subqueries-JOINs-Groups
│ ├── tasks
│ ├── 01.PC
│ │ ├── pc.db
│ │ ├── pc_schema.png
│ │ └── README.md
│ └── 02.Ships
│ │ ├── ships.db
│ │ ├── ships_schema.png
│ │ └── README.md
│ ├── joins_reminder.jpg
│ └── README.md
├── week05
└── README.md
├── week02
├── README.md
├── 01.LinuxCommands
│ ├── duhs.py
│ └── README.md
├── 02.CancellationPolicy
│ ├── README.md
│ ├── solution.py
│ └── tests.py
└── 03.MoreTesting
│ └── README.md
├── week12
├── README.md
└── Crawler
│ └── README.md
├── LICENSE
├── README.md
├── .gitignore
└── week08
├── 02.Mocks
└── README.md
└── 01.Graphs
└── README.md
/playground/packages/pkg/foo1.py:
--------------------------------------------------------------------------------
1 | x = 100
--------------------------------------------------------------------------------
/week11/examples/serializer/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/week13/hackademy/education/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/week13/hackademy/hackademy/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/movies/views.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/week13/hackademy/education/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/movies/controllers.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/users/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/playground/test_demo.py:
--------------------------------------------------------------------------------
1 | class TestDemo:
2 | print('testing')
3 |
--------------------------------------------------------------------------------
/playground/packages/pkg/foo2.py:
--------------------------------------------------------------------------------
1 | from .foo1 import x
2 |
3 | y = 1000
4 |
--------------------------------------------------------------------------------
/playground/packages/random.py:
--------------------------------------------------------------------------------
1 | def randint(x, y=100):
2 | return 42
3 |
--------------------------------------------------------------------------------
/week03/example.json:
--------------------------------------------------------------------------------
1 | {"name": "Marto", "age": 24, "interests": ["sport", "AI"]}
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/settings.py:
--------------------------------------------------------------------------------
1 | DB_NAME = 'cinema.db'
2 |
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/new_experiment_success.html:
--------------------------------------------------------------------------------
1 | Bravoo!
--------------------------------------------------------------------------------
/week10/README.md:
--------------------------------------------------------------------------------
1 | ## MVC
2 |
3 | https://whimsical.com/N8yXvUnG6y5Bhgxs28mYDV
4 |
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/session.html:
--------------------------------------------------------------------------------
1 | {{ request.session.counter }}
--------------------------------------------------------------------------------
/week13/hackademy/education/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/Application/1.Sensors/sensors_data1.txt:
--------------------------------------------------------------------------------
1 | 1,1,100
2 | 1,2,100
3 | 5,5,20
4 | 4,4,80
5 | 3,3,10
6 | 3,5,0
7 | 3,6,200
--------------------------------------------------------------------------------
/partners/hacksoft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/partners/hacksoft.png
--------------------------------------------------------------------------------
/partners/hedgeserv.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/partners/hedgeserv.jpg
--------------------------------------------------------------------------------
/week07/README.md:
--------------------------------------------------------------------------------
1 | # Week 07
2 |
3 | - [Context Mangers](https://slides.com/hackbulgaria/python-101-9th-context-managers/#/)
4 |
--------------------------------------------------------------------------------
/playground/packages/pkg/foo3/__init__.py:
--------------------------------------------------------------------------------
1 | # from ..foo2 import x
2 | from pkg.foo2 import x
3 |
4 | print('In foo2 init: ', x)
5 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/db_schema/__init__.py:
--------------------------------------------------------------------------------
1 | from .users import CREATE_USERS
2 | from .movies import CREATE_MOVIES
3 |
--------------------------------------------------------------------------------
/week11/README.md:
--------------------------------------------------------------------------------
1 | # Week 11
2 |
3 | * [Python Metaprogramming](https://slides.com/hackbulgaria/python-metaprogramming-eba69f)
4 |
--------------------------------------------------------------------------------
/week04/README.md:
--------------------------------------------------------------------------------
1 | # Week 04 - Mixins
2 |
3 | - [Multiple Inheritance & Mixins](https://slides.com/hackbulgaria/python-101-9th-mixins#/)
4 |
--------------------------------------------------------------------------------
/week06/02.Generators/Book.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week06/02.Generators/Book.zip
--------------------------------------------------------------------------------
/week13/hackademy/education/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class EducationConfig(AppConfig):
5 | name = 'education'
6 |
--------------------------------------------------------------------------------
/Application/1.Sensors/distance2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/Application/1.Sensors/distance2.png
--------------------------------------------------------------------------------
/Application/1.Sensors/distance3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/Application/1.Sensors/distance3.png
--------------------------------------------------------------------------------
/week01/02.DiveIntoPython/nokia.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week01/02.DiveIntoPython/nokia.jpg
--------------------------------------------------------------------------------
/Application/3.RottingApples/day0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/Application/3.RottingApples/day0.jpg
--------------------------------------------------------------------------------
/Application/3.RottingApples/day3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/Application/3.RottingApples/day3.jpg
--------------------------------------------------------------------------------
/Application/3.RottingApples/day7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/Application/3.RottingApples/day7.jpg
--------------------------------------------------------------------------------
/week09/01.SQL-Intro/movies_schema.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week09/01.SQL-Intro/movies_schema.png
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/README.md:
--------------------------------------------------------------------------------
1 | # SQL - Subqueries, JOINs, Groups
2 |
3 | Presentation: https://slides.com/hackbulgaria/sql-python-security-issues#/
4 |
--------------------------------------------------------------------------------
/Application/3.RottingApples/day100.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/Application/3.RottingApples/day100.jpg
--------------------------------------------------------------------------------
/week10/CinemaReservation/example/cinema.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week10/CinemaReservation/example/cinema.db
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/cinema.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week10/CinemaReservation/hack_cinema/cinema.db
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/index.html:
--------------------------------------------------------------------------------
1 |
Welcome to Hackademy!
2 |
3 |
6 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/tasks/03.Break-the-Cipher/secret_keeper.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 |
4 | def make_it_secret(message):
5 | return hashlib.md5(message.encode()).hexdigest()
6 |
--------------------------------------------------------------------------------
/playground/packages/demo.py:
--------------------------------------------------------------------------------
1 | import pkg
2 |
3 | from pkg import foo2
4 |
5 | from random import *
6 |
7 | print(pkg.foo1.x, foo2.y)
8 |
9 | print('rand int: ', randint(0, 11))
10 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/tasks/01.PC/pc.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week09/02.SQL-Subqueries-JOINs-Groups/tasks/01.PC/pc.db
--------------------------------------------------------------------------------
/week11/examples/eval_exec.py:
--------------------------------------------------------------------------------
1 | code = '''
2 | for i in range(0, 3):
3 | print(i)
4 | '''
5 |
6 | exec(code)
7 |
8 | expression = '1 + 2*(3**3)'
9 | print(eval(expression))
10 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/joins_reminder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week09/02.SQL-Subqueries-JOINs-Groups/joins_reminder.jpg
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/edit_course.html:
--------------------------------------------------------------------------------
1 | Edit: {{ course }}
2 |
--------------------------------------------------------------------------------
/week06/README.md:
--------------------------------------------------------------------------------
1 | # Week 06
2 |
3 | - [Decorators](https://slides.com/hackbulgaria/python101-9th-decorators/#/)
4 | - [Generators](https://slides.com/hackbulgaria/python101-9th-iterators-generators)
5 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/tasks/02.Ships/ships.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week09/02.SQL-Subqueries-JOINs-Groups/tasks/02.Ships/ships.db
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/create.html:
--------------------------------------------------------------------------------
1 | Add new course
2 |
3 |
7 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/tasks/01.PC/pc_schema.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week09/02.SQL-Subqueries-JOINs-Groups/tasks/01.PC/pc_schema.png
--------------------------------------------------------------------------------
/week13/hackademy/education/views/__init__.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 |
4 | def index(request):
5 | return render(request, 'index.html', {})
6 |
7 |
8 | import education.views.courses
9 |
--------------------------------------------------------------------------------
/playground/mocks/calendar.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 |
4 | my_datetime = datetime
5 |
6 |
7 | def is_weekday():
8 | today = my_datetime.today()
9 | return (0 <= today.weekday() < 5)
10 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/tasks/02.Ships/ships_schema.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week09/02.SQL-Subqueries-JOINs-Groups/tasks/02.Ships/ships_schema.png
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/base.html:
--------------------------------------------------------------------------------
1 | Home
2 |
3 |
4 | {% block content %} {% endblock %}
5 |
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/detail.html:
--------------------------------------------------------------------------------
1 | {% extends "courses/base.html" %} {% block content %}
2 | Edit
3 | {{ course.name }}
4 | {% endblock %}
5 |
--------------------------------------------------------------------------------
/week11/examples/anonymous_classes.py:
--------------------------------------------------------------------------------
1 | def create_mock_object(**kwargs):
2 | cls = type('', (object, ), {})
3 | obj = cls()
4 |
5 | for key, value in kwargs.items():
6 | setattr(obj, key, value)
7 |
8 | return obj
9 |
--------------------------------------------------------------------------------
/week13/hackademy/hackademy/urls.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.urls import path, include
3 |
4 | urlpatterns = [
5 | path('education/', include('education.urls')),
6 | path('admin/', admin.site.urls),
7 | ]
8 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/movies/models.py:
--------------------------------------------------------------------------------
1 | class MovieModel:
2 | def __init__(self, *, id, title, year, rating):
3 | self.id = id
4 | self.title = title
5 | self.year = year
6 | self.rating = rating
7 |
--------------------------------------------------------------------------------
/playground/demo.py:
--------------------------------------------------------------------------------
1 | class A:
2 | pass
3 |
4 |
5 | class B:
6 | pass
7 |
8 |
9 | class C(A, B):
10 | pass
11 |
12 |
13 | class X:
14 | pass
15 |
16 |
17 | class D(C, X, B):
18 | pass
19 |
20 |
21 | print(D.mro())
22 |
--------------------------------------------------------------------------------
/week05/README.md:
--------------------------------------------------------------------------------
1 | # Week 05
2 |
3 | - [@property](https://github.com/HackBulgaria/Programming-101-Python-2020-Spring/blob/master/playground/property.py)
4 | - [Packages and modules](https://github.com/HackBulgaria/Programming-101-Python-2020-Spring/tree/python-imports)
5 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/tasks/02.Vehicle-Repair-Manager/vehicle_repair_management_db_schema.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HackBulgaria/Programming-101-Python-2020-Spring/HEAD/week09/03.SQL-and-Python/tasks/02.Vehicle-Repair-Manager/vehicle_repair_management_db_schema.png
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/db.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | from .settings import DB_NAME
4 |
5 |
6 | class Database:
7 | def __init__(self):
8 | self.connection = sqlite3.connect(DB_NAME)
9 | self.cursor = self.connection.cursor()
10 |
--------------------------------------------------------------------------------
/playground/packages/pkg/__init__.py:
--------------------------------------------------------------------------------
1 | print('In pkg init...')
2 |
3 | import pkg.foo1
4 | print('foo1: ', foo1)
5 |
6 |
7 | from . import foo1
8 | print('foo1: ', foo1)
9 |
10 |
11 | from pkg import foo1
12 | print('foo1: ', foo1)
13 |
14 |
15 | # from pkg import foo3
16 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/db_schema/users.py:
--------------------------------------------------------------------------------
1 | # TODO: Hash the passwords
2 | CREATE_USERS = '''
3 | CREATE TABLE IF NOT EXISTS users (
4 | id integer PRIMARY KEY NOT NULL,
5 | email varchar(50) UNIQUE,
6 | password varchar(50)
7 | );
8 | '''
9 |
--------------------------------------------------------------------------------
/week03/README.md:
--------------------------------------------------------------------------------
1 | # Week 03 - OOP & git
2 |
3 | - [OOP Part 1 slides](https://slides.com/hackbulgaria/python101-9th-oop1#/)
4 | - [OOP Part 2 slides](https://slides.com/hackbulgaria/python101-9th-oop2#/)
5 | - [Git & GitHub slides](https://slides.com/hackbulgaria/python-101-9th-git#/)
6 |
--------------------------------------------------------------------------------
/week02/README.md:
--------------------------------------------------------------------------------
1 | # Week 02 - Introduction to course & Python
2 |
3 | - [Files and Commands slides](https://slides.com/hackbulgaria/python101-9th-files#/)
4 | - [Unit Tests & TDD](https://slides.com/hackbulgaria/python101-9th-tests/#/)
5 | - [Function Arguments](https://slides.com/hackbulgaria/function-arguments/#/)
6 |
--------------------------------------------------------------------------------
/week12/README.md:
--------------------------------------------------------------------------------
1 | # Week 12 - HTTP & Django
2 |
3 | Example with `netcat`:
4 |
5 | ```
6 | nc -l 8080
7 | curl http://localhost:8000
8 | ```
9 |
10 | Libraries:
11 |
12 | * [requests](https://requests.readthedocs.io/en/master/)
13 | * [BeautifulSoup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/)
14 |
--------------------------------------------------------------------------------
/week11/examples/setattr.py:
--------------------------------------------------------------------------------
1 | class Panda:
2 | pass
3 |
4 |
5 | def create_panda(attributes):
6 | panda = Panda()
7 |
8 | for key, value in attributes.items():
9 | setattr(panda, key, value)
10 |
11 | return panda
12 |
13 | p = create_panda({'name': 'Ivo', 'age': 23, 'weight': 80})
14 | print(p.__dict__)
15 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/db_schema/movies.py:
--------------------------------------------------------------------------------
1 | CREATE_MOVIES = '''
2 | CREATE TABLE IF NOT EXISTS movies(
3 | id INTEGER PRIMARY KEY,
4 | title VARCHAR(30) NOT NULL,
5 | year INTEGER NOT NULL CHECK(year BETWEEN 2019 and 2022),
6 | rating REAL CHECK(rating BETWEEN 0 and 10),
7 | UNIQUE(title, year)
8 | );
9 | '''
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/movies/queries.py:
--------------------------------------------------------------------------------
1 | GET_ALL_MOVIES = 'SELECT * FROM movies'
2 |
3 | CREATE_MOVIE = '''
4 | INSERT INTO movies (title, year, rating)
5 | VALUES (?, ?, ?);
6 | '''
7 |
8 | GET_MOVIE = '''
9 | SELECT *
10 | FROM movies
11 | WHERE id=?
12 | '''
13 |
14 | DELETE_MOVIE = 'DELETE FROM movies WHERE id=?'
15 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/users/models.py:
--------------------------------------------------------------------------------
1 | class UserModel:
2 | def __init__(self, *, id, email, password):
3 | self.id = id
4 | self.email = email
5 | self.password = password
6 |
7 | @staticmethod
8 | def validate(email, password):
9 | # TODO: Implement a validation -> Raise an error
10 | pass
11 |
--------------------------------------------------------------------------------
/week03/json_tutorial.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | my_object = {'name': 'Marto', 'age': 24, 'interests': ['sport', 'AI']}
4 |
5 | with open('example.json', 'w') as f:
6 | # Saves my_object in the example.json file
7 | json.dump(my_object, f)
8 |
9 | with open('example.json', 'r') as f:
10 | # Reads example.json file
11 | my_read_object = json.load(f)
12 | print(my_object)
13 | print(type(my_object))
14 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/demo/hash.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 |
4 | def make_it_secret_md5(password, count):
5 | for i in range(count):
6 | password = hashlib.md5(password.encode()).hexdigest()
7 |
8 | return password
9 |
10 |
11 |
12 | def make_it_secret_sha512(password, count):
13 | for i in range(count):
14 | password = hashlib.sha512(password.encode()).hexdigest()
15 |
16 | return password
17 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/users/controllers.py:
--------------------------------------------------------------------------------
1 | from .users_gateway import UserGateway
2 |
3 |
4 | class UserContoller:
5 | def __init__(self):
6 | self.users_gateway = UserGateway()
7 |
8 | def create_user(self, email, password):
9 | user = self.users_gateway.create(email=email, password=password)
10 |
11 | return user
12 |
13 | def login_user(self, email, password):
14 | pass
15 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/users/views.py:
--------------------------------------------------------------------------------
1 | from .controllers import UserContoller
2 |
3 |
4 | class UserViews:
5 | def __init__(self):
6 | self.controller = UserContoller()
7 |
8 | def login(self):
9 | pass
10 |
11 | def signup(self):
12 | email = input('Email: ')
13 | password = input('Password: ')
14 |
15 | self.controller.create_user(email=email, password=password)
16 |
--------------------------------------------------------------------------------
/playground/property.py:
--------------------------------------------------------------------------------
1 | class Temperature:
2 | def __init__(self, value):
3 | self.degrees = float(value)
4 |
5 | @property
6 | def farenheit(self):
7 | return (self.degrees * 1.8) + 32
8 |
9 | @farenheit.setter
10 | def farenheit(self, value):
11 | self.degrees = (value - 32) / 1.8
12 |
13 |
14 | t = Temperature(100)
15 | print(t.degrees, t.farenheit)
16 |
17 | t.farenheit = 100
18 | print(t.degrees, t.farenheit)
19 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/index_view.py:
--------------------------------------------------------------------------------
1 | from .users.views import UserViews
2 |
3 |
4 | def welcome():
5 | print('Welcome to HackCinema!')
6 | command = int(input('Choose a command:\n 1 - log in\n 2 - sign up\n Input: '))
7 | user_views = UserViews()
8 |
9 | if command == 1:
10 | return user_views.login()
11 |
12 | if command == 2:
13 | return user_views.signup()
14 |
15 | raise ValueError(f'Unknown command {command}.')
16 |
--------------------------------------------------------------------------------
/week13/hackademy/education/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from education.models import Course
4 |
5 |
6 | @admin.register(Course)
7 | class CourseAdmin(admin.ModelAdmin):
8 | list_display = ('name', 'get_duration', 'start_date', 'end_date')
9 |
10 | def get_duration(self, obj):
11 | if obj.duration:
12 | return f'{obj.duration.days // 7} weeks'
13 |
14 | return 'N/A'
15 |
16 | get_duration.short_description = 'Duration'
17 |
--------------------------------------------------------------------------------
/week13/hackademy/hackademy/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for hackademy project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hackademy.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/week13/hackademy/hackademy/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for hackademy 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/3.0/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', 'hackademy.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/week11/examples/class_deconstruction.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 |
4 | body = '''
5 | import abc
6 |
7 | def __init__(self, name):
8 | self.name = name
9 | print(collections)
10 |
11 | def be_panda(self):
12 | print('Bamboo & sleep')
13 | '''
14 |
15 | clsname = 'Panda'
16 | bases = (object, )
17 |
18 | clsdict = type.__prepare__(clsname, bases)
19 |
20 | exec(body, globals(), clsdict)
21 |
22 | Panda = type(clsname, bases, clsdict)
23 |
24 | p = Panda('Ivo')
25 | print(p.name)
26 | p.be_panda()
27 |
--------------------------------------------------------------------------------
/week13/hackademy/education/migrations/0002_auto_20200525_1459.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.6 on 2020-05-25 14:59
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('education', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='course',
15 | name='name',
16 | field=models.CharField(max_length=250, unique=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/week11/examples/ducktyping.py:
--------------------------------------------------------------------------------
1 | class Duck:
2 | def quack(self):
3 | print('Duck quacks!')
4 |
5 |
6 | class Panda:
7 | def quack(self):
8 | print('Panda quacks!')
9 |
10 |
11 | class Person:
12 | def __init__(self, name):
13 | self.quack = name
14 |
15 |
16 | def duck_duck_go(obj):
17 | if hasattr(obj, 'quack')\
18 | and callable(getattr(obj, 'quack')):
19 | obj.quack()
20 |
21 |
22 | duck_duck_go(Duck())
23 | duck_duck_go(Panda())
24 | duck_duck_go(1)
25 | duck_duck_go(Person('Ivo'))
26 |
--------------------------------------------------------------------------------
/playground/mocks/test_calendar.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from unittest.mock import patch
3 | from datetime import datetime
4 |
5 | from calendar import is_weekday
6 |
7 |
8 | class CalendarTests(unittest.TestCase):
9 | @patch('calendar.my_datetime', autospec=True)
10 | def test_sunday_is_not_weekday(self, datetime_mock):
11 | sunday = datetime(year=2020, month=4, day=26)
12 | datetime_mock.today.return_value = sunday
13 |
14 | self.assertFalse(is_weekday())
15 |
16 |
17 | if __name__ == '__main__':
18 | unittest.main()
19 |
--------------------------------------------------------------------------------
/week11/examples/base_tracking.py:
--------------------------------------------------------------------------------
1 | class basetracking(type):
2 | def __new__(cls, name, bases, clsdict):
3 | clsobj = super().__new__(cls, name, bases, clsdict)
4 |
5 | if not hasattr(cls, 'registry'):
6 | cls.registry = set()
7 |
8 | cls.registry.add(clsobj)
9 |
10 | return clsobj
11 |
12 |
13 | class Base(metaclass=basetracking):
14 | pass
15 |
16 |
17 | class A(Base):
18 | pass
19 |
20 |
21 | class B(Base):
22 | pass
23 |
24 |
25 | class C(A, B):
26 | pass
27 |
28 | print(Base.registry)
29 |
--------------------------------------------------------------------------------
/week13/hackademy/education/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils import timezone
3 |
4 |
5 | class Course(models.Model):
6 | name = models.CharField(max_length=250, unique=True)
7 | description = models.TextField()
8 | start_date = models.DateTimeField(default=timezone.now)
9 | end_date = models.DateTimeField(null=True)
10 |
11 | def __str__(self):
12 | return f'Course "{self.name}"'
13 |
14 | @property
15 | def duration(self):
16 | if self.end_date:
17 | return self.end_date - self.start_date
18 |
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/list.html:
--------------------------------------------------------------------------------
1 | {% extends "courses/base.html" %} {% block content %}
2 | Add new
3 |
4 | {% if courses %}
5 |
11 | {% else %}
12 | No courses yet.
13 | {% endif %} {% endblock %}
14 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/tasks/02.Ships/README.md:
--------------------------------------------------------------------------------
1 | ## За базата от данни SHIPS
2 | 
3 |
4 | •Напишете заявка, която за всеки кораб извежда името му, държавата, броя
5 | оръдия и годината на пускане (launched).
6 |
7 | •Повторете горната заявка като този път включите в резултата и класовете, които
8 | нямат кораби, но съществуват кораби със същото име като тяхното.
9 |
10 | • Напишете заявка, която извежда имената на корабите, участвали в битка от 1942г.
11 |
12 | • За всяка страна изведете имената на корабите, които никога не са участвали в битка.
13 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/users/users_gateway.py:
--------------------------------------------------------------------------------
1 | from ..db import Database
2 | from .models import UserModel
3 |
4 |
5 | class UserGateway:
6 | def __init__(self):
7 | self.model = UserModel
8 | self.db = Database()
9 |
10 | def create(self, *, email, password):
11 | self.model.validate(email, password)
12 |
13 | self.db.cursor.execute() # TODO: create user query
14 |
15 | # TODO: What whould I return?
16 |
17 | def all(self):
18 | raw_users = self.db.cursor.execute() # TODO: Select all users
19 |
20 | return [self.model(**row) for row in raw_users]
21 |
--------------------------------------------------------------------------------
/week07/02.PipAndVirtualenv/README.md:
--------------------------------------------------------------------------------
1 | # pip and virtualenv
2 |
3 | * [Slides & diagrams](https://whimsical.com/ETDiwVTKDvbkqWUNsBwY7X)
4 | * [https://pypi.org/](https://pypi.org/)
5 | * [https://packaging.python.org/tutorials/installing-packages/](https://packaging.python.org/tutorials/installing-packages/)
6 | * [https://virtualenv.pypa.io/en/stable/index.html](https://virtualenv.pypa.io/en/stable/index.html)
7 | * [https://github.com/pyenv/pyenv](https://github.com/pyenv/pyenv)
8 |
9 | **Your task is going to be:**
10 |
11 | 1. Setup a virtualenv for the course
12 | 2. Install `pytest`
13 | 3. Run some of the tests you have with `py.test`
14 |
--------------------------------------------------------------------------------
/week03/01.FractionsOOP/README.md:
--------------------------------------------------------------------------------
1 | # Fractions OOP
2 |
3 | Remember the Fractions problems you had to solve two weeks ago ([link](https://github.com/HackBulgaria/Programming-101-Python-2020-Spring/tree/master/week02/03.MoreTesting#simplify-fractions))? You will have to rewrite them today using your new OOP knowledge.
4 |
5 | 0. Preserve the functions from your previous solutions.
6 | 1. Reimplement them using TDD and OOP.
7 | 1. You will need to have a Fraction class.
8 | 1. Your old tests may need to be refactored.
9 | 1. You will have to override some dunders in order to collect and sort fractions.
10 | 1. We will expect to have "1/3" (for example) when we try to case our fraction to a string.
11 |
--------------------------------------------------------------------------------
/week11/examples/registry.py:
--------------------------------------------------------------------------------
1 | class RegistryMeta(type):
2 | def __new__(cls, name, bases, clsdict):
3 | for base in bases:
4 | print(vars(base))
5 | clsobj = super().__new__(cls, name, bases, clsdict)
6 | print(clsobj)
7 | print('===========')
8 |
9 | if not hasattr(clsobj, 'registry'):
10 | clsobj.registry = []
11 |
12 | clsobj.registry.append(clsobj)
13 |
14 | return clsobj
15 |
16 |
17 | class Base(metaclass=RegistryMeta):
18 | pass
19 |
20 |
21 | class A(Base):
22 | pass
23 |
24 |
25 | class B(Base):
26 | pass
27 |
28 |
29 | print(Base.registry)
30 | print(A.registry)
31 | print(B.registry)
32 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/README.md:
--------------------------------------------------------------------------------
1 | # SQL - Subqueries, JOINs, Groups
2 |
3 | Presentation: https://slides.com/hackbulgaria/sql-queries-joins-groups/
4 |
5 |
6 | ## Useful links
7 |
8 | ### JOINS
9 |
10 | 
11 |
12 |
13 | - http://www.sql-join.com
14 | - INNER vs OUTER JOIN - https://www.diffen.com/difference/Inner_Join_vs_Outer_Join
15 |
16 | ### GROUP BY & aggregations
17 |
18 | - https://www.w3schools.com/sql/sql_groupby.asp
19 | - https://www.youtube.com/watch?v=OM9ux-xiriU
20 |
21 | ## TOOLING
22 |
23 | - Sqlite3 - https://linuxhint.com/install_sqlite_browser_ubuntu_1804/
24 | - Nice wrapper with autocomplete3 - https://litecli.com/
25 |
--------------------------------------------------------------------------------
/week13/hackademy/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hackademy.settings')
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError as exc:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | ) from exc
17 | execute_from_command_line(sys.argv)
18 |
19 |
20 | if __name__ == '__main__':
21 | main()
22 |
--------------------------------------------------------------------------------
/week11/examples/serializer/main.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from serializers import Serializer
4 | from fields import CharField, EmailField, DateTimeField
5 |
6 |
7 | class Panda:
8 | def __init__(self, name, email):
9 | self.name = name
10 | self.email = email
11 | self.created_at = datetime.now()
12 |
13 |
14 | class PandaSerializer(Serializer):
15 | name = CharField()
16 | email = EmailField()
17 | created_at = DateTimeField()
18 |
19 |
20 | def main():
21 | panda = Panda('Ivo', 'ivo@hackbulgaria.com')
22 | serializer = PandaSerializer(instance=panda)
23 |
24 | serializer.is_valid()
25 | print(serializer.data)
26 |
27 |
28 | if __name__ == '__main__':
29 | main()
30 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/demo/register_form.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 |
4 | def create_user(*, username, password):
5 | connection = sqlite3.connect('users_with_plain_passwords.db')
6 | cursor = connection.cursor()
7 | insert_query = f'''
8 | INSERT INTO users (username, password)
9 | VALUES ( ? , ? )
10 | '''
11 | cursor.execute(insert_query, (username, password))
12 | connection.commit()
13 | connection.close()
14 |
15 |
16 | def main():
17 | while True:
18 | username = input('Username: ')
19 | password = input('Password: ')
20 |
21 | create_user(username=username, password=password)
22 |
23 | print('User registered successfully!!')
24 |
25 |
26 | if __name__ == '__main__':
27 | main()
28 |
--------------------------------------------------------------------------------
/week11/examples/serializer/fields.py:
--------------------------------------------------------------------------------
1 | import abc
2 | from validate_email import validate_email
3 |
4 |
5 | class Field(metaclass=abc.ABCMeta):
6 | def transform(self, value):
7 | return value
8 |
9 | @abc.abstractmethod
10 | def validate(self, value):
11 | return False
12 |
13 |
14 | class CharField(Field):
15 | def transform(self, value):
16 | return str(value)
17 |
18 | def validate(self, value):
19 | return True
20 |
21 |
22 | class EmailField(CharField):
23 | def validate(self, value):
24 | return validate_email(value)
25 |
26 |
27 | class DateTimeField(Field):
28 | def transform(self, value):
29 | return value.isoformat()
30 |
31 | def validate(self, value):
32 | return True
33 |
--------------------------------------------------------------------------------
/week07/01.ContextManagers/silence_exception.py:
--------------------------------------------------------------------------------
1 | from contextlib import contextmanager
2 |
3 |
4 | @contextmanager
5 | def silence_exception(exc_type, msg=None):
6 | try:
7 | yield
8 | except exc_type as exc:
9 | if msg is not None and str(exc) != msg:
10 | raise exc
11 |
12 |
13 | class SilenceException:
14 | def __init__(self, exc_type, msg=None):
15 | self.exc_type = exc_type
16 | self.msg = msg
17 |
18 | def __enter__(self):
19 | return self
20 |
21 | def __exit__(self, exc_type, exc_value, exc_traceback):
22 | same_exception_type = self.exc_type == exc_type
23 | correct_message = self.msg is None or str(exc_value) == self.msg
24 |
25 | return same_exception_type and correct_message
26 |
--------------------------------------------------------------------------------
/week11/examples/class_decorators.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 |
3 |
4 | def debug(func):
5 | fname = func.__qualname__
6 |
7 | @wraps(func)
8 | def wrapper(*arg, **kwargs):
9 | print('Calling {}'.format(fname))
10 |
11 | return func(*arg, **kwargs)
12 |
13 | return wrapper
14 |
15 |
16 | def debugmethods(cls):
17 | for attr, value in vars(cls).items():
18 | if callable(value):
19 | setattr(cls, attr, debug(value))
20 |
21 | return cls
22 |
23 |
24 | @debugmethods
25 | class Panda:
26 |
27 | def be_panda(self):
28 | print('Being panda')
29 |
30 | def awesome(self):
31 | print('Pandas are awesome!')
32 |
33 |
34 | if __name__ == '__main__':
35 | p = Panda()
36 | p.be_panda()
37 | p.awesome()
38 |
--------------------------------------------------------------------------------
/week11/examples/decorators.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 |
3 |
4 | def debug(func):
5 | fname = func.__qualname__
6 |
7 | @wraps(func)
8 | def wrapper(*arg, **kwargs):
9 | print('Calling {}'.format(fname))
10 |
11 | return func(*arg, **kwargs)
12 |
13 | return wrapper
14 |
15 |
16 | def debugmethods(cls):
17 | for attr, value in vars(cls).items():
18 | if callable(value):
19 | setattr(cls, attr, debug(value))
20 |
21 | return cls
22 |
23 | @debugmethods
24 | class Panda:
25 | def be_panda(self):
26 | print('Being panda')
27 |
28 | def awesome(self):
29 | print('Pandas are awesome')
30 |
31 |
32 | p = Panda()
33 | p.be_panda()
34 | p.awesome()
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/week13/hackademy/education/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path, include
2 |
3 | from education.views import index, courses
4 |
5 | app_name = 'education'
6 |
7 | courses_patterns = [
8 | path('', courses.list, name='list'),
9 | path('/', courses.detail, name='detail'),
10 | path('new/', courses.CourseCreateView.as_view(), name='create'),
11 | path('new-experiment/', courses.new_experiment, name='new-experiment'),
12 | path('update//', courses.edit_course, name='update'),
13 | path('delete//', courses.delete_course, name='delete'),
14 | path('session/', courses.session, name='session'),
15 | ]
16 |
17 | urlpatterns = [
18 | path('', index, name='index'),
19 | path('courses/', include((courses_patterns, 'courses')))
20 | ]
21 |
--------------------------------------------------------------------------------
/week06/01.Decorators/required.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 |
4 | def required(method):
5 | def required_method(instance, *args, **kwargs):
6 | method_name = method.__name__
7 |
8 | if method_name not in instance.__dict__:
9 | raise AttributeError(
10 | f'All classes that inherit from "{instance.__class__.__name__}" must provide "{method_name}" method.'
11 | )
12 |
13 | return method(instance, *args, **kwargs)
14 |
15 | return required_method
16 |
17 |
18 | class Animal(ABC):
19 | @abstractmethod
20 | def eat(self, food):
21 | pass
22 |
23 | @required
24 | def sleep(self, time=10):
25 | pass
26 |
27 |
28 | class Dog(Animal):
29 | pass
30 |
31 |
32 | sharo = Dog()
33 | # sharo.sleep()
34 |
--------------------------------------------------------------------------------
/week11/examples/metaclass.py:
--------------------------------------------------------------------------------
1 | from class_decorators import debugmethods
2 |
3 |
4 | class mytype(type):
5 | def __new__(cls, name, bases, clsdict):
6 | def m(self):
7 | print(self)
8 | print('in m')
9 |
10 | clsdict['m'] = m
11 | clsobj = super().__new__(cls, name, bases, clsdict)
12 |
13 | return clsobj
14 |
15 | class mytype2(type):
16 | def __new__(cls, name, bases, clsdict):
17 | print('mytype2')
18 | clsobj = super().__new__(cls, name, bases, clsdict)
19 |
20 | return clsobj
21 |
22 |
23 | class Base(metaclass=mytype):
24 | pass
25 |
26 |
27 | class Base2(metaclass=mytype2):
28 | pass
29 |
30 |
31 | class A(Base2, Base):
32 | pass
33 |
34 |
35 | class B(A):
36 | pass
37 |
38 |
39 | b = B()
40 | b.m()
41 |
--------------------------------------------------------------------------------
/week06/02.Generators/chain.py:
--------------------------------------------------------------------------------
1 | def chain1(iter1, iter2):
2 | res = []
3 |
4 | for x in iter1:
5 | res.append(x)
6 |
7 | for x in iter2:
8 | res.append(x)
9 |
10 | return res
11 |
12 |
13 | def chain2(iter1, iter2):
14 | res = []
15 |
16 | res.extend(iter1)
17 | res.extend(iter2)
18 |
19 | return res
20 |
21 |
22 | def chain3(iter1, iter2):
23 | return [*iter1, *iter2]
24 |
25 |
26 | def chain4(iter1, iter2):
27 | res = [*iter1, *iter2]
28 |
29 | for x in res:
30 | yield x
31 |
32 |
33 | def chain5(iter1, iter2):
34 | for x in iter1:
35 | yield x
36 |
37 | for x in iter2:
38 | yield x
39 |
40 |
41 | iter1 = range(4)
42 | iter2 = range(4, 10)
43 |
44 | result = chain5(iter1, iter2)
45 |
46 | print(result)
47 | print(list(result))
48 |
--------------------------------------------------------------------------------
/week13/hackademy/education/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.6 on 2020-05-25 14:58
2 |
3 | from django.db import migrations, models
4 | import django.utils.timezone
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Course',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('name', models.CharField(max_length=255, unique=True)),
20 | ('description', models.TextField()),
21 | ('start_date', models.DateTimeField(default=django.utils.timezone.now)),
22 | ('end_date', models.DateTimeField(null=True)),
23 | ],
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/week04/01.Mixins/solution/mixins.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 |
4 | class WithSetAttributes:
5 | def __init__(self, **kwargs):
6 | for name, value in kwargs.items():
7 | setattr(self, name, value)
8 |
9 |
10 | class WithEqualAttributes:
11 | def __eq__(self, other):
12 | return self.__dict__ == other.__dict__
13 |
14 |
15 | class Jsonable:
16 | def to_json(self, indent=4):
17 | name = self.__class__.__name__
18 |
19 | attributes = self.__dict__
20 |
21 | return json.dumps({'type': name, 'dict': attributes}, indent=indent)
22 |
23 | @classmethod
24 | def from_json(cls, json_string):
25 | data = json.loads(json_string)
26 |
27 | class_name = data['type']
28 |
29 | if class_name != cls.__name__:
30 | raise ValueError('Wrong type.')
31 |
32 | attributes = data['dict']
33 |
34 | return cls(**attributes)
35 |
--------------------------------------------------------------------------------
/week01/03.FinalRound/Anagrams/README.md:
--------------------------------------------------------------------------------
1 | # Are two words anagrams?
2 |
3 | For anagrams, check here - https://en.wikipedia.org/wiki/Anagram
4 |
5 | For example, `listen` and `silent` are anagrams.
6 |
7 | The program should read two words from the standard input and output:
8 |
9 | * `ANAGRAMS` if the words are anagrams of each other
10 | * `NOT ANAGRAMS` if the two words are not anagrams of each other
11 |
12 | **Consider lowering the case of the two words since the case does not matter. `SILENT` and `listen` are also anagrams.**
13 |
14 | ## Examples
15 | ---
16 |
17 | Input:
18 |
19 | ```
20 | TOP_CODER COTO_PRODE
21 | ```
22 |
23 | Output:
24 |
25 | ```
26 | NOT ANAGRAMS
27 | ```
28 |
29 | ---
30 |
31 | Input:
32 |
33 | ```
34 | kilata cvetelina_yaneva
35 | ```
36 |
37 | Output:
38 |
39 | ```
40 | NOT ANAGRAMS
41 | ```
42 |
43 | Also, should not make songs together.
44 |
45 | ---
46 |
47 | Input:
48 |
49 | ```
50 | BRADE BEARD
51 | ```
52 |
53 | Output:
54 |
55 | ```
56 | ANAGRAMS
57 | ```
58 |
--------------------------------------------------------------------------------
/week01/01.PythonProblems/week1_solutions.py:
--------------------------------------------------------------------------------
1 | def sum_of_digits(n):
2 | result = 0
3 |
4 | if n < 0:
5 | n = -1 * n
6 |
7 | while n > 0:
8 | result += n % 10
9 | n = n // 10
10 |
11 | return result
12 |
13 |
14 | def sum2_of_digits(n):
15 | n_str = str(n)
16 | if n_str[0] == '-':
17 | n_str = n_str[1::]
18 |
19 | return sum([int(i) for i in n_str])
20 |
21 |
22 | def sum3_of_digits(n):
23 | n_str = str(abs(n))
24 | return sum([int(i) for i in n_str])
25 |
26 |
27 | def to_digits(digits):
28 | return list(map(int, list(str(digits))))
29 |
30 |
31 | def to_digits2(digits):
32 | return [int(i) for i in str(digits)]
33 |
34 |
35 | def to_number(digits):
36 | number_str = "".join([str(d) for d in digits])
37 | return int(number_str)
38 |
39 |
40 | # print(sum_of_digits(1325132435356))
41 | # print(sum2_of_digits(1325132435356))
42 |
43 | # print(to_digits(123))
44 | # print(to_digits2(123))
45 |
46 | print(to_number([21, 2, 33]))
47 |
--------------------------------------------------------------------------------
/week01/03.FinalRound/GoldbachConjecture/README.md:
--------------------------------------------------------------------------------
1 | # Goldbach Conjecture
2 |
3 | Implement a function, called `goldbach(n)` which returns a list of tuples, that is the goldbach conjecture for the given number `n`.
4 |
5 | The Goldbach Conjecture states:
6 |
7 | > Every even integer greater than 2 can be expressed as the sum of two primes.
8 |
9 | Keep in mind that there can be more than one combination of two primes, that sums up to the given number.
10 |
11 | **The result should be sorted by the first item in the tuple.**
12 |
13 | For example:
14 |
15 | - `4 = 2 + 2`
16 | - `6 = 3 + 3`
17 | - `8 = 3 + 5`
18 | - `10 = 3 + 7 = 5 + 5`
19 | - `100 = 3 + 97 = 11 + 89 = 17 + 83 = 29 + 71 = 41 + 59 = 47 + 53`
20 |
21 | ## Signature
22 |
23 | ```python
24 | def goldbach(n):
25 | pass
26 | ```
27 |
28 | ## Test examples
29 |
30 | ```python
31 | >>> goldbach(4)
32 | [(2,2)]
33 | >>> goldbach(6)
34 | [(3,3)]
35 | >>> goldbach(8)
36 | [(3,5)]
37 | >>> goldbach(10)
38 | [(3,7), (5,5)]
39 | >>> goldbach(100)
40 | [(3, 97), (11, 89), (17, 83), (29, 71), (41, 59), (47, 53)]
41 | ```
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Hack Bulgaria
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/week01/03.FinalRound/CreditCard/README.md:
--------------------------------------------------------------------------------
1 | # Credit card validation
2 |
3 | Implement a function, called `is_credit_card_valid(number)`, which returns True/False based on the following algorithm:
4 |
5 | * Each credit card number must contain odd count of digits.
6 | * We transform the number with the following steps (based on the fact that we start from index 0)
7 | - All digits, read from right to left, at even positions (index), **remain the same.**
8 | - Every digit, read from right to left, at odd position is replaced by the result, that is obtained from doubling the given digit.
9 | * After the transformation, we find the sum of all digits in the transformed number.
10 | * The number is valid, if the sum is divisible by 10.
11 |
12 | For example: `79927398713` is valid, bacause:
13 |
14 | * When we double and replace all digits at odd position we get: `7 (18 = 2 * 9) 9 (4 = 2 * 2) 7 (6 = 2 * 3) 9 (16 = 2 * 8) 7 (2 = 2 * 1) 3`
15 | * The sum of all digits of the new number is 70, which is divisible by 10 => the card number is valid.
16 |
17 | More examples:
18 |
19 | * `79927398713` is a valid number
20 | * `79927398715` is invalid number
21 |
--------------------------------------------------------------------------------
/week13/hackademy/education/templates/courses/new_experiment.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Heading 1
6 | Heading 2
7 | Heading 3
8 |
9 |
14 |
15 |
16 |
17 | - Bullet 1
18 | - Bullet 2
19 | - Bullet 3
20 | - Bullet 4
21 |
22 |
23 |
24 |
25 | | Month |
26 | Savings |
27 |
28 |
29 | | January |
30 | $100 |
31 |
32 |
33 | | January |
34 | $100 |
35 |
36 |
37 | | January |
38 | $100 |
39 |
40 |
41 | | January |
42 | $100 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/week03/01.FractionsOOP/fraction.py:
--------------------------------------------------------------------------------
1 | from math import gcd
2 |
3 |
4 | class Fraction:
5 | def __init__(self, numerator, denominator):
6 | assert denominator >= 1, 'Zero or negative denominator.'
7 |
8 | self.numerator = numerator
9 | self.denominator = denominator
10 |
11 | def __str__(self):
12 | return f'{self.numerator}/{self.denominator}'
13 |
14 | def __repr__(self):
15 | return f'Fraction {self}'
16 |
17 | def __eq__(self, other):
18 | return self.numerator / self.denominator == other.numerator / other.denominator
19 |
20 | def __add__(self, other):
21 | numerator = (self.numerator * other.denominator) + (other.numerator * self.denominator)
22 | denominator = self.denominator * other.denominator
23 |
24 | return Fraction(numerator, denominator).simplify()
25 |
26 | def __lt__(self, other):
27 | return self.numerator / self.denominator < other.numerator / other.denominator
28 |
29 | def simplify(self):
30 | divider = gcd(self.numerator, self.denominator)
31 |
32 | return Fraction(self.numerator // divider, self.denominator // divider)
33 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/main.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from hack_cinema.db import Database
4 | from hack_cinema.db_schema import CREATE_USERS, CREATE_MOVIES
5 |
6 | from hack_cinema.index_view import welcome
7 |
8 |
9 | class Application:
10 | @classmethod
11 | def build(self):
12 | db = Database()
13 | db.cursor.execute(CREATE_USERS)
14 | db.cursor.execute(CREATE_MOVIES)
15 | # TODO: Build rest of the tables
16 |
17 | db.connection.commit()
18 | db.connection.close()
19 |
20 | print('Done.')
21 |
22 | @classmethod
23 | def seed(self):
24 | # TODO: Seed with inistial data - consider using another command for this
25 | pass
26 |
27 | @classmethod
28 | def start(self):
29 | welcome()
30 |
31 |
32 | if __name__ == '__main__':
33 | command = sys.argv[1]
34 |
35 | if command == 'build':
36 | Application.build()
37 | elif command == 'seed':
38 | Application.start()
39 | elif command == 'start':
40 | Application.start()
41 | else:
42 | raise ValueError(f'Unknown command {command}. Valid ones are "build", "seed" and "start"')
43 |
--------------------------------------------------------------------------------
/week11/examples/declarative.py:
--------------------------------------------------------------------------------
1 | class DeclarativeMeta(type):
2 | def __new__(cls, name, bases, clsdict):
3 | fields = {}
4 |
5 | for attr, value in clsdict.items():
6 | if not callable(value) and not attr.startswith('__'):
7 | fields[attr] = value
8 |
9 | for attr, _ in fields.items():
10 | clsdict.pop(attr)
11 |
12 | clsdict['_declared_fields'] = fields
13 |
14 | print(name, fields, clsdict, "=======================")
15 | clsobj = super().__new__(cls, name, bases, clsdict)
16 |
17 | return clsobj
18 |
19 | class Base(metaclass=DeclarativeMeta):
20 | def __init__(self):
21 | for attr, value in self._declared_fields.items():
22 | setattr(self, attr, value)
23 |
24 | # def __getattr__(self, name):
25 | # if name in self._declared_fields:
26 | # return self._declared_fields[name]
27 |
28 | # return object.__getattribute__(self, name)
29 |
30 |
31 | class Panda(Base):
32 | a = 5
33 | b = 6
34 |
35 |
36 | p1 = Panda()
37 | p2 = Panda()
38 |
39 | print(p1.a)
40 | print(vars(p1))
41 | print(p2.b)
42 | print(p1.panda)
43 |
--------------------------------------------------------------------------------
/week02/01.LinuxCommands/duhs.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | DIR_INIT_SIZE = 2 * 4096
5 |
6 |
7 | def get_file_disk_usage(filepath):
8 | blocks_used = (os.stat(filepath).st_size // 4096) + 1
9 | return blocks_used * 4 # KB
10 |
11 |
12 | def get_files_size(path):
13 | if os.path.isfile(path):
14 | return get_file_disk_usage(path)
15 |
16 | total = 0
17 |
18 | if os.path.isdir(path):
19 | for inner in os.scandir(path):
20 | if inner.is_file():
21 | total += os.stat(inner).st_size
22 | if inner.is_dir():
23 | total += get_files_size(inner)
24 |
25 | return total
26 |
27 |
28 | def get_dirs_size(path):
29 | counter = 0
30 | if os.path.isdir(path):
31 | counter = sum([1 for _ in os.walk(path)])
32 |
33 | return counter * DIR_INIT_SIZE
34 |
35 |
36 | def duhs(path):
37 | dirs_size = get_dirs_size(path)
38 | files_size = get_files_size(path)
39 |
40 | return dirs_size + files_size
41 |
42 |
43 | def main():
44 | size = duhs(sys.argv[1])
45 | print(f'{size // 1024}K')
46 |
47 | return size
48 |
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/demo/setup_users_database_with_plain_text_passwords.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 |
4 | def create_users_table():
5 | connection = sqlite3.connect('users_with_plain_passwords.db')
6 | cursor = connection.cursor()
7 | query = '''
8 | CREATE TABLE IF NOT EXISTS users (
9 | id INTEGER PRIMARY KEY AUTOINCREMENT,
10 | username VARCHAR(50),
11 | password VARCHAR(100)
12 | )
13 | '''
14 | cursor.execute(query)
15 | connection.commit()
16 | connection.close()
17 |
18 |
19 | def populate_users_table():
20 | users_data = []
21 |
22 | for i in range(100):
23 | users_data.append(
24 | f'("user_{i}", "password_{i}")'
25 | )
26 |
27 | connection = sqlite3.connect('users_with_plain_passwords.db')
28 | cursor = connection.cursor()
29 | query = f'''
30 | INSERT INTO users (username, password)
31 | VALUES {','.join(users_data)}
32 | '''
33 | cursor.execute(query)
34 | connection.commit()
35 | connection.close()
36 |
37 |
38 | def main():
39 | create_users_table()
40 | populate_users_table()
41 |
42 |
43 | if __name__ == '__main__':
44 | main()
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Programming 101 with Python - 2020
2 |
3 | Repo for the course "Programming 101 with Python", starting on 24th of February 2020
4 |
5 | This is going to be the 9th edition of the course.
6 |
7 | ## Partners
8 |
9 | The course is happening thanks to:
10 |
11 | | [](https://www.hedgeserv.com/career/) | | [](https://www.hacksoft.io/) |
12 | | -------------------------------------------------------------------------- | --- | --------------------------------------------------------------- |
13 |
14 |
15 | ## Agenda
16 |
17 | The course is about learning how to write software with Python. It's perfect for people that are going to start as junior developers afterwards.
18 |
19 | Key topics, that are going to be covered in the course:
20 |
21 | - Working with Linux-based operating system.
22 | - The Python programming language.
23 | - Testing.
24 | - Design patterns.
25 | - Using relational databases & SQL & ORMs.
26 | - Working in teams with git & GitHub.
27 | - Implementing client-server applications.
28 | - Communication between applications - formats, message queues, HTTP
29 | - Securing our applications from common vulnerabilities.
30 |
--------------------------------------------------------------------------------
/week02/02.CancellationPolicy/README.md:
--------------------------------------------------------------------------------
1 | # Cancellation Policy
2 |
3 | You are a co-working space and you have many floors, rooms and desks.
4 | Your customers can book rooms and desks for their needs.
5 | If a customer decides to cancel his or her booking just before it starts we may need to fee him.
6 |
7 | The cancellation policy conditions are in the given format:
8 |
9 | ```
10 | [
11 | { hours: 24, percent: 10 },
12 | { hours: 12, percent: 50 },
13 | { hours: 6, percent: 80 },
14 | { percent: 100 }
15 | ]
16 | ```
17 |
18 | The above example means: You will be feed with:
19 |
20 | - 10% if you cancel your booking more than 24 hours before it starts
21 | - 50% if you cancel it for less or equal than 24 hours and more than 12 hours before it starts
22 | - 80% if you cancel it for less or equal than 12 hours and more than 6 hours before it starts
23 | - 100% if you cancel it for less or equal than 6 hours before it starts.
24 |
25 | Notes:
26 |
27 | - Only the dictionary that has no `hours` key is required
28 | - The max `hours` value is 24
29 | - The cancellation conditions may not be ordered.
30 |
31 | Using TDD, implement a program that calculates the price that a customer needs to pay if he/she wants to cancel his/her booking **now**.
32 |
--------------------------------------------------------------------------------
/week09/02.SQL-Subqueries-JOINs-Groups/tasks/01.PC/README.md:
--------------------------------------------------------------------------------
1 | ## За базата от данни PC
2 | 
3 |
4 |
5 | • Напишете заявка, която извежда средната скорост на компютрите
6 |
7 | • Напишете заявка, която извежда средния размер на екраните на лаптопите за
8 | всеки производител.
9 |
10 | • Напишете заявка, която извежда средната скорост на лаптопите с цена над 1000.
11 |
12 | • Напишете заявка, която извежда средната цена на компютрите според различните им hd.
13 |
14 | • Напишете заявка, която извежда средната цена на компютрите за всяка скорост по-голяма от 500.
15 |
16 | • Напишете заявка, която извежда средната цена на компютрите произведени от производител ‘A’.
17 |
18 | • Напишете заявка, която извежда средната цена на компютрите и лаптопите за производител ‘B’
19 |
20 | • Напишете заявка, която извежда производителите, които са произвели поне по 3 различни модела компютъра. Помислете каква заявка можете да напишете за да сте сигурни в отговора, например да изведете за всеки производител, броя различни модели компютри.
21 |
22 | • Напишете заявка, която извежда производителите на компютрите с най-висока цена.
23 |
24 | •Напишете заявка, която извежда средния размер на диска на тези компютри произведени от производители, които произвеждат и принтери.
25 |
--------------------------------------------------------------------------------
/week01/03.FinalRound/MatrixBombing/README.md:
--------------------------------------------------------------------------------
1 | ## Matrix Bombing
2 |
3 | You are givn a `NxM` matrix of integer numbers.
4 |
5 | We can drop a bomb at any place in the matrix, which has the following effect:
6 |
7 | - All of the **3 to 8 neighbours** (depending on where you hit!) of the target are reduced by the value of the target.
8 | - Numbers can be reduced only to 0 - they cannot go to negative.
9 |
10 | For example, if we have the following matrix:
11 |
12 | ```
13 | 10 10 10
14 | 10 9 10
15 | 10 10 10
16 | ```
17 |
18 | and we drop bomb at `9`, this will result in the following matrix:
19 |
20 | ```
21 | 1 1 1
22 | 1 9 1
23 | 1 1 1
24 | ```
25 |
26 | Implement a function called `matrix_bombing_plan(m)`.
27 |
28 | 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.
29 |
30 | The positions are the standard indexes, starting from `(0, 0)`
31 |
32 | For example if we have the following matrix:
33 |
34 | ```
35 | 1 2 3
36 | 4 5 6
37 | 7 8 9
38 | ```
39 |
40 | and run the function, we will have:
41 |
42 | ```python
43 | {(0, 0): 42,
44 | (0, 1): 36,
45 | (0, 2): 37,
46 | (1, 0): 30,
47 | (1, 1): 15,
48 | (1, 2): 23,
49 | (2, 0): 29,
50 | (2, 1): 15,
51 | (2, 2): 26}
52 | ```
53 |
--------------------------------------------------------------------------------
/week11/examples/serializer/serializers.py:
--------------------------------------------------------------------------------
1 | from fields import Field
2 |
3 |
4 | class DeclarativeSerializerMeta(type):
5 | def __new__(cls, name, bases, clsdict):
6 | fields = {}
7 |
8 | for attr, value in clsdict.items():
9 | if isinstance(value, Field):
10 | fields[attr] = value
11 |
12 | for attr, _ in fields.items():
13 | clsdict.pop(attr)
14 |
15 | clsdict['_fields'] = fields
16 |
17 | return super().__new__(cls, name, bases, clsdict)
18 |
19 |
20 | class Serializer(metaclass=DeclarativeSerializerMeta):
21 | def __init__(self, instance):
22 | self._object = instance
23 | self._called_validation = False
24 |
25 | def is_valid(self):
26 | valid = True
27 |
28 | for field_name, field in self._fields.items():
29 | if not field.validate(getattr(self._object, field_name)):
30 | valid = False
31 | break
32 |
33 | self._called_validation = True
34 | return valid
35 |
36 | @property
37 | def data(self):
38 | if not self._called_validation:
39 | raise Exception('Call .is_valid() before .data')
40 |
41 | return { field_name: field.transform(getattr(self._object, field_name))
42 | for field_name, field in self._fields.items() }
43 |
--------------------------------------------------------------------------------
/week07/01.ContextManagers/measure_performance.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 |
4 |
5 | class MeasurePerformance:
6 | def __init__(self, filename):
7 | self.filename = filename
8 | self.file = None
9 | self._benchmarks = []
10 |
11 | def __enter__(self):
12 | self.file = open(self.filename, 'w')
13 |
14 | self.original_start = time.time()
15 | self.start = self.original_start
16 |
17 | return self
18 |
19 | def __exit__(self, exc_type, exc_value, exc_traceback):
20 | if self.file:
21 | if exc_type:
22 | self.file.close()
23 | os.remove(self.filename)
24 | else:
25 | self.file.write('\n'.join(self._benchmarks))
26 | self.file.write(f'\nFinished for: {int(time.time() - self.original_start)}s')
27 | self.file.close()
28 |
29 | def get_exceeded_time(self):
30 | return int(time.time() - self.start)
31 |
32 | def benchmark(self, msg=None, restart=False):
33 | exceeded_time = self.get_exceeded_time()
34 |
35 | if restart:
36 | self.start = time.time()
37 |
38 | if msg is None:
39 | msg = f'Benchmark No.{len(self._benchmarks) + 1}'
40 |
41 | benchmark = f'{msg}: {exceeded_time}s'
42 | self._benchmarks.append(benchmark)
43 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/hack_cinema/movies/movies_gateway.py:
--------------------------------------------------------------------------------
1 | from hack_cinema.db import Database
2 |
3 | from .models import Movie
4 | from .queries import (
5 | CREATE_MOVIE,
6 | GET_ALL_MOVIES,
7 | GET_MOVIE,
8 | DELETE_MOVIE
9 | )
10 |
11 |
12 | class MovieGateway:
13 | def __init__(self):
14 | self.db = Database()
15 | self.model = Movie
16 |
17 | def create(self, title, year, rating):
18 | self.db.cursor.execute(CREATE_MOVIE, (title, year, rating))
19 |
20 | movie_id = self.db.cursor.lastrowid
21 | return self.get(movie_id)
22 |
23 | def get(self, id):
24 | self.db.cursor.execute(GET_MOVIE, (id, ))
25 | movie_row = self.db.cursor.fetchone()
26 | self.db.connection.commit()
27 |
28 | if not movie_row:
29 | raise ValueError(f'Movie with {id} not found.')
30 |
31 | return self.model(*movie_row)
32 |
33 | def all(self):
34 | self.db.cursor.execute(GET_ALL_MOVIES)
35 | movie_rows = self.db.cursor.fetchall()
36 |
37 | self.db.connection.commit()
38 |
39 | return [self.model(*row) for row in movie_rows]
40 |
41 | def filter(self, **kwargs):
42 | # TODO: Build where clauses here
43 | pass
44 |
45 | def delete(self, id):
46 | movie = self.get(id) # make sure the movie exists before deleting it
47 |
48 | self.db.cursor.execute(DELETE_MOVIE, (id, ))
49 | self.db.connection.commit()
50 |
51 | return movie
52 |
--------------------------------------------------------------------------------
/playground/closures.py:
--------------------------------------------------------------------------------
1 | # def make_multiple(n):
2 | # def multiply(x):
3 | # print(f'{x} * {n} is ', x * n)
4 |
5 | # return multiply
6 |
7 |
8 | # times_three = make_multiple(3)
9 | # times_five = make_multiple(5)
10 |
11 | # del make_multiple
12 |
13 | # times_three(10) # == make_multiple(3)(10)
14 |
15 | # print('Closure of make_multiple', make_multiple.__closure__)
16 | # print('Closure of times_three', times_three.__closure__[0].cell_contents)
17 | # print('Closure of times_five', times_five.__closure__)
18 |
19 | # times_five(100)
20 | # times_five(10)
21 |
22 | # times_three(20)
23 |
24 |
25 | def add_discount(promo=100):
26 | print('In function that returns decorator')
27 |
28 | def inner(func):
29 | print('In decorator')
30 |
31 | def discounted(user, money):
32 | print('closure for: ', func.__name__)
33 | return func(user, money * (promo / 100)) # return value is the return value of `func`
34 | return discounted # return value is closure
35 | return inner # return value is decorator
36 |
37 |
38 | # decorator = add_discount(promo=20)
39 | # print('after add_discount return value')
40 | # pay_phone = decorator(pay_phone)
41 |
42 |
43 | # @shano
44 | @add_discount(promo=20)
45 | def pay_phone(user, money):
46 | print('phone', user, money)
47 |
48 |
49 | # @add_discount()
50 | # def pay_net(user, money):
51 | # print('net', user, money)
52 |
53 | print('Before pay_phone is called')
54 | pay_phone('marto', 100)
55 | # pay_net('marto', 100)
56 |
--------------------------------------------------------------------------------
/playground/context_managers.py:
--------------------------------------------------------------------------------
1 | import time
2 | from contextlib import contextmanager, ContextDecorator
3 |
4 |
5 | class FileManager:
6 | def __init__(self, filename, mode='r'):
7 | print('In __init__')
8 | self.filename = filename
9 | self.mode = mode
10 | self.file = None
11 |
12 | def __enter__(self):
13 | print('Entering')
14 | self.file = open(self.filename, self.mode)
15 | return self.file
16 |
17 | def __exit__(self, exc_type, exc_value, exc_traceback):
18 | print('Exiting')
19 | self.file.close()
20 |
21 |
22 | @contextmanager
23 | def file_manager(filename, mode='r'):
24 | try:
25 | file = open(filename, mode)
26 | # ========================
27 | yield file
28 | # ========================
29 | except Exception:
30 | pass
31 | finally:
32 | file.close()
33 |
34 |
35 | with FileManager('demo1.py', 'w') as f:
36 | f.write('print("Marto e bok")')
37 |
38 | with file_manager('demo2.py', 'w') as f:
39 | f.write('print("Marto e bok")')
40 |
41 |
42 | class stopwatch(ContextDecorator):
43 | def __enter__(self):
44 | self.start = time.time()
45 | return self
46 |
47 | def __exit__(self, *args):
48 | print('Finished in: ', time.time() - self.start)
49 |
50 |
51 | @stopwatch()
52 | def foo():
53 | time.sleep(1)
54 |
55 | with stopwatch():
56 | print('in with block')
57 | time.sleep(1)
58 |
59 | print('outside the with block')
60 |
61 |
62 | foo()
63 |
--------------------------------------------------------------------------------
/playground/generators.py:
--------------------------------------------------------------------------------
1 | # def fib():
2 | # print('Initializing generator')
3 | # a, b = 1, 1
4 |
5 | # while a < 100:
6 | # print('Current: ', a)
7 | # yield a
8 | # a, b = b, a + b
9 |
10 |
11 | # fib_generator = fib()
12 |
13 | # next(fib_generator)
14 | # next(fib_generator)
15 | # next(fib_generator)
16 | # next(fib_generator)
17 |
18 | # print('=========')
19 |
20 | # new_fib_generator = fib_generator
21 |
22 | # for _ in new_fib_generator:
23 | # pass
24 |
25 | # print('=========')
26 |
27 | # for _ in fib_generator:
28 | # pass
29 |
30 |
31 | def complex_generator():
32 | items = [1, 2, 3, 4, 5, 6, 7, 8]
33 | wtf = 1
34 |
35 | for item in items:
36 | print('Yielding: ', item)
37 | value = yield item
38 |
39 | # return 'Opa.'
40 |
41 | if (item == 8):
42 | print('No more ...')
43 | print(f'Last value is {value}')
44 |
45 | # value is None when the `next` is used a.k.a the first time the generator is triggered for sure.
46 | if value is not None:
47 | wtf += item + value
48 |
49 | print(wtf)
50 |
51 | return 'The end of the complex_generator'
52 |
53 |
54 | gen = complex_generator()
55 |
56 | next(gen)
57 | next(gen)
58 |
59 | gen.send(10) # Sends 10 to the generator and restarts it. Cannot be used if the generator is not started!!!
60 | gen.send(100)
61 |
62 | # gen.close() Will close the generator and the next time you try to use the generator
63 |
64 | # for i in range(4):
65 | for i in range(5):
66 | gen.send(i)
67 |
--------------------------------------------------------------------------------
/Application/3.RottingApples/README.md:
--------------------------------------------------------------------------------
1 | ## Гниещи ябълки
2 |
3 | В къщата, в която живеете, имате огромно ябълково дърво. Дървото е родило за поредна година и вие събирате реколтата от ябълки в касетки с различна големина. Налага ви се да заминете извън града за няколко дни малко след като сте събрали реколтата.
4 |
5 | ## Задачата
6 |
7 | Знаете, че ако една ябълка е изгнила, след 3 дни всички ябълки около нея ще са изгнили.
8 |
9 | На избран от вас език, напишете програма, която от стандарния вход чете:
10 |
11 | - големината на касетката - N x M (**ЗАБЕЛЕЖКА: Касетките нямат нулев ред или колона.**)
12 | - координатите на изгнилите ябълки
13 | - след колко дни ще се върнете в къщата си
14 |
15 | Изведете как изглежда касетката след този период.
16 |
17 | - O - свежа ябълка
18 | - X - изгнила ябълка
19 |
20 | ## Пример
21 |
22 | - :green_apple: - свежа ябълка
23 | - :apple: - изгнила ябълка
24 |
25 | В началото:
26 |
27 | 
28 |
29 | След 3 дни:
30 |
31 | 
32 |
33 | След 7 дни:
34 |
35 | 
36 |
37 | След 100 дни:
38 |
39 | 
40 |
41 | ## Примерен I/O
42 |
43 | Входни данни:
44 |
45 | ```
46 | Enter the size of the box: 20x10
47 | Еnter the coordinates of the rotten apples: (3,8) (16,4)
48 | After how many days will you come back: 10
49 | ```
50 |
51 | Изход:
52 |
53 | ```
54 | OOOOOOOOOOOOXXXXXXXO
55 | OOOOOOOOOOOOXXXXXXXO
56 | OOOOOOOOOOOOXXXXXXXO
57 | OOOOOOOOOOOOXXXXXXXO
58 | XXXXXXOOOOOOXXXXXXXO
59 | XXXXXXOOOOOOXXXXXXXO
60 | XXXXXXOOOOOOXXXXXXXO
61 | XXXXXXOOOOOOOOOOOOOO
62 | XXXXXXOOOOOOOOOOOOOO
63 | XXXXXXOOOOOOOOOOOOOO
64 | ```
65 |
--------------------------------------------------------------------------------
/week01/02.DiveIntoPython/biggest_palindrome_optimized.py:
--------------------------------------------------------------------------------
1 | def is_palindrome(num):
2 | return str(num) == str(num)[::-1]
3 |
4 |
5 | def to_number(lst):
6 | s = [str(x) for x in lst]
7 | s = ''.join(s)
8 | s = int(s)
9 | return s
10 |
11 |
12 | def to_list(num):
13 | return [int(x) for x in str(num)]
14 |
15 |
16 | def get_biggest_palindrome(num):
17 | if num <= 11:
18 | raise ValueError('There are no palindromes lower than 11.')
19 |
20 | if (is_palindrome(num)):
21 | num -= 1
22 |
23 | num_list = to_list(num)
24 | length = len(num_list)
25 |
26 | if num_list[0] == 1 and all(x == 0 for x in num_list[1:]):
27 | return num - 1
28 |
29 | pivot = []
30 | if length % 2 != 0:
31 | pivot = [num_list[length // 2]]
32 |
33 | left = num_list[:length // 2]
34 | right = num_list[(length // 2) + bool(pivot):]
35 |
36 | left_number = to_number(left)
37 | right_number = to_number(right)
38 |
39 | if (left_number < right_number):
40 | right = left[::-1]
41 | else:
42 | right = left[::-1]
43 |
44 | new = left + pivot + right
45 |
46 | if (to_number(new) > num):
47 | left_number -= 1
48 |
49 | if (pivot):
50 | pivot[0] -= 1
51 | if (pivot[0]) < 0:
52 | pivot[0] = 9
53 |
54 | new_left = to_list(left_number)
55 | if len(new_left) < len(left):
56 | pivot = [9]
57 |
58 | left = new_left[:]
59 | right = left[::-1]
60 |
61 | return to_number(left + pivot + right)
62 |
63 |
64 | print(get_biggest_palindrome(7550645))
65 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/demo/setup_users_database_with_hashed_passwords.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | import hashlib
3 |
4 |
5 | def make_it_secret(password):
6 | return hashlib.sha512(password.encode()).hexdigest()
7 |
8 |
9 | def create_users_table():
10 | connection = sqlite3.connect('users_with_hashed_passwords.db')
11 | cursor = connection.cursor()
12 | query = '''
13 | CREATE TABLE IF NOT EXISTS users (
14 | id INTEGER PRIMARY KEY AUTOINCREMENT,
15 | username VARCHAR(50),
16 | password VARCHAR(100),
17 | plain_text_password VARCHAR(100)
18 | )
19 | '''
20 | cursor.execute(query)
21 | connection.commit()
22 | connection.close()
23 |
24 |
25 | def populate_users_table():
26 | users_data = []
27 |
28 | for i in range(100):
29 | plain_password = f'password_{i}'
30 | password = make_it_secret(plain_password)
31 | users_data.append(
32 | f'("user_{i}", "{password}", "{plain_password}")'
33 | )
34 |
35 | for i in range(100, 130):
36 | plain_password = 'qwerty'
37 | password = make_it_secret(plain_password)
38 | users_data.append(
39 | f'("user_{i}", "{password}", "{plain_password}")'
40 | )
41 |
42 | connection = sqlite3.connect('users_with_hashed_passwords.db')
43 | cursor = connection.cursor()
44 | query = f'''
45 | INSERT INTO users (username, password, plain_text_password)
46 | VALUES {','.join(users_data)}
47 | '''
48 | cursor.execute(query)
49 | connection.commit()
50 | connection.close()
51 |
52 |
53 |
54 | def main():
55 | create_users_table()
56 | populate_users_table()
57 |
58 |
59 | if __name__ == '__main__':
60 | main()
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/week13/01.Models-and-Admin/README.md:
--------------------------------------------------------------------------------
1 | # Hackademy
2 |
3 | We are going to implement a simple course management system to hold courses & lectures.
4 |
5 | Make a fresh Django app.
6 |
7 | The model specification is as follows:
8 |
9 | ## Courses
10 |
11 | First of all, our system should know about courses.
12 |
13 | We are interested in the following attributes for one course:
14 |
15 | - Name - should be unique
16 | - Description
17 | - Start Date
18 | - End Date - can be nullable
19 |
20 | Here is how the admin page of the `Course` should look like:
21 |
22 | | Name | Description | Start Date | End Date | Duration |
23 | | --------------------------- | ----------- | ---------- | ---------- | -------- |
24 | | Programming 101 with Python | Python! | 01.01.2016 | 01.03.2016 | 3 months |
25 | | Programming 101 with Ruby | Ruby. | 10.01.2016 | 01.03.2016 | 2 months |
26 |
27 | The **Duration** should be an approximation. Figure it out ;)
28 |
29 | ## Lectures
30 |
31 | Having only courses is pointless. So lets add some lectures to the mix!
32 |
33 | Our lecture should have:
34 |
35 | - Unique Identifier
36 | - Name
37 | - Week - like the weeks in HackBulgaria
38 | - course - Foreign key
39 | - URL - where the real lecture or slides are located.
40 |
41 | ## Tasks
42 |
43 | We are interested in the following attributes for one task:
44 |
45 | - Name - should be unique
46 | - Description
47 | - Due date
48 | - Course - FK
49 | - Lecture - optional FK
50 |
51 | ## Solutions
52 |
53 | This is the interesting part of our system.
54 | Our solution should have:
55 |
56 | - Task - FK
57 | - Date
58 | - URL - link with solution in Github. **Validate the GitHub url and don't allow models with wrong urls to be saved!**
59 |
60 | ## Each model should have an admin page!
61 |
--------------------------------------------------------------------------------
/Application/1.Sensors/README.md:
--------------------------------------------------------------------------------
1 | # Сензори за мръсен въздух
2 |
3 | Имате компания, която мери нивата на лош въздух в София. Имате сензори в целия град. Понякога сензорите се чупят или започват да дават неверни резултати. Поради тази причина на повечето места сте сложили повече от 1 сензор.
4 |
5 | Можете да разберете дали даден сензор е в неизправност, ако има много голяма разлика между неговата стойност и стойностите на сензорите, които са му "съседни".
6 |
7 | ## Съседни сензори
8 |
9 | Търсим съседните сензори на (5, 8 ) в радиус от 2 километра. Спрямо схемата на картинката това е само сензор (7,8)
10 |
11 | 
12 |
13 | Ако обаче търсим съседните сензори на сензор (5, 8 ) с радиус 3, получаваме сензори (7, 8), (8, 8), (7, 7), (3, 6) и (5, 5)
14 |
15 | 
16 |
17 | ## Задачата
18 |
19 | Информацията за сензорите се намира във файл ([тук](sensors_data1.txt) има примерен). Всеки ред във файла репрезентира един сензор с неговите координати и нивото на мръсен въздух, който е измерил.
20 |
21 | На избран от вас език, напишете програма, която чете от файл и изкарва всички възможни неизправни сензори. Името на файла, максималната дистанция, която дефинира близки сензори и максималната позволена грешка между сензорите се получават от стандартния вход.
22 |
23 | Формат на файла:
24 |
25 | ```
26 | 3,4,100
27 | 3,3,100
28 | 4,3,20
29 | 10,9,200
30 | 9,9,180
31 | 7,6,50
32 | 7,5,300
33 | 1,1,100
34 | ```
35 |
36 | Всеки ред от файла репрезентира 1 сензор. Първите 2 колони са координатите на сензорите (X,Y), а в последната се намира стойността на сензора за мръсен въздух.
37 |
38 | > Ако няма проблемни сензори, изведете: "All sensors are OK."
39 |
40 | ## Пример
41 |
42 | Input:
43 |
44 | ```
45 | Filename: sensors_data.txt
46 | Neighbours distance: 1
47 | Max error: 50
48 | ```
49 |
50 | Output:
51 |
52 | ```
53 | Please check sensors at: (3,5), (3,6)
54 | ```
55 |
--------------------------------------------------------------------------------
/playground/teams_generator.py:
--------------------------------------------------------------------------------
1 | import random
2 | import time
3 | from pprint import pprint
4 |
5 | STUDENT_NAMES = [
6 | 'Tanya Budinova',
7 | 'Иван Георгиев Гочев',
8 | 'Ангелина Кръстанова',
9 | 'Мария-Магдалена Желязкова',
10 | 'Ралица Шиндарова',
11 | 'Антонио Георгиев',
12 | 'Silviya Vasileva Vasileva',
13 | 'Християна Малинова',
14 | 'Николай Колев Нешев',
15 | 'Цветомир Цветков',
16 | 'Петър Дамянов',
17 | 'Георги Къков',
18 | 'Николай Веселинов Късев',
19 | 'Антони Стоев',
20 | 'Златина Чолакова',
21 | 'Снежина Лъчезарова Цанкова',
22 | 'Димитър Николов',
23 | 'Емилиан Спасов',
24 | 'Йоанна Йотова',
25 | 'Боян Бонев',
26 | 'Георги Куклев',
27 | 'Йоан Джелекарски',
28 | 'Бойко Георгиев',
29 | 'Redjep ',
30 | 'Никола Методиев',
31 | ]
32 |
33 | TEAM_NAMES = [
34 | 'Panda',
35 | 'Dog',
36 | 'Cat',
37 | 'Bear',
38 | # 'Horse',
39 | 'Elephant',
40 | 'Tiger',
41 | 'Shark',
42 | 'Bat',
43 | 'Wolf',
44 | 'Crocodile',
45 | 'Lion'
46 | ]
47 |
48 |
49 | def generate_teams():
50 | student_names = STUDENT_NAMES[:]
51 |
52 | for _ in range(20):
53 | random.shuffle(student_names)
54 |
55 | teams = [student_names[i:i + 2] for i in range(0, len(student_names), 2)]
56 |
57 | # The last team is from 3 students
58 | teams[-2].extend(teams[-1])
59 | del teams[-1]
60 |
61 | return {
62 | team_name: teams[index]
63 | for index, team_name in enumerate(TEAM_NAMES)
64 | }
65 |
66 |
67 | def order_teams_for_final_projects():
68 | teams = TEAM_NAMES[:]
69 |
70 | for _ in range(50):
71 | random.shuffle(teams)
72 |
73 | return list(enumerate(teams))
74 |
75 |
76 | if __name__ == '__main__':
77 | while True:
78 | result = order_teams_for_final_projects()
79 | pprint(result)
80 | time.sleep(0.1)
81 |
--------------------------------------------------------------------------------
/week13/02.Views/README.md:
--------------------------------------------------------------------------------
1 | # Course Management System Views
2 |
3 | We'll now create views for the models from the last exercise.
4 |
5 | ## Index view
6 |
7 | - `GET /` There should be an index view with a navigation to all list views.
8 |
9 | ## Courses views
10 |
11 | For our courses, we should have the following HTML views:
12 |
13 | - `GET /courses` should display a nice table with all courses, sorted by their start date. The table should look like this:
14 |
15 | | Course Name | Description | Start Date | End Date | Duration |
16 | | --------------------------- | ----------- | ---------- | ---------- | -------- |
17 | | Programming 101 with Python | Python! | 01.01.2016 | 01.03.2016 | 3 months |
18 | | Programming 101 with Ruby | Ruby. | 10.01.2016 | 01.03.2016 | 2 months |
19 |
20 | The course name should be a link to the course detail page.
21 |
22 | - `GET /courses//` - this is the detailed course page. Add a list of all lectures for the given course with a link to the detail view of the lecture.
23 | - `GET /courses/new/` - this should display a form for creating new course.
24 | - `POST /courses/new/` - this should be handled by Django for creating new courses.
25 | - `GET /courses//edit/` - this should display a form to edit some of the course details.
26 | - `PUT /courses/edit//edit/` - this should be handled by Django for editing existing courses.
27 |
28 | ## Lectures views
29 |
30 | We should have a set of URLs for creating and editing lectures:
31 |
32 | - `GET /lectures` should display a list of all lectures grouped by their course
33 | - `GET /lectures/` should display (in a table) the information for our lecture. Should have a link to the related course.
34 | - `GET /lectures/new` - the form for creating a new lecture
35 | - `POST /lectures/new` - handled by Django for saving the lecture
36 | - `GET /lectures//edit` - the form for editing an existing lecture
37 | - `PUT /lectures//edit` - handled by Django for editing an existing lecture
38 |
--------------------------------------------------------------------------------
/week06/01.Decorators/README.md:
--------------------------------------------------------------------------------
1 | ## @accepts
2 |
3 | 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`
4 |
5 | ### Examples
6 |
7 | ```python
8 | @accepts(str)
9 | def say_hello(name):
10 | return "Hello, I am {}".format(name)
11 |
12 | say_hello(4)
13 |
14 | TypeError: Argument "name" of say_hello is not str!
15 | ```
16 |
17 | ```python
18 | @accepts(str, int)
19 | def deposit(name, money):
20 | print("{} sends {} $!".format(name, money))
21 |
22 | deposit("Marto", 10)
23 | ```
24 |
25 | Note that this is just a nice example. In real life you don't want use this!
26 |
27 | ## @performance(file_name)
28 |
29 | Make a decorator `@performance` that takes a `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.
30 |
31 | ### Example
32 |
33 | ```python
34 | @performance('log.txt')
35 | def something_heavy():
36 | sleep(2)
37 | return "I am done!"
38 |
39 | something_heavy()
40 |
41 | I am done!
42 | ```
43 |
44 | And the log file should look like this:
45 |
46 | ```
47 | get_low was called and took 2.00 seconds to complete
48 | get_low was called and took 2.10 seconds to complete
49 | ```
50 |
51 | ## @required
52 |
53 | Make a decorator `@required` that makes all decorated methods with throw exception if they are not overrided in the child classes.
54 |
55 | ### Example
56 |
57 | ```python
58 | class Animal:
59 | @required
60 | def eat(self, food):
61 | pass
62 |
63 |
64 | class Panda(Animal):
65 | pass
66 |
67 | p = Panda()
68 | p.eat('bamboo')
69 |
70 | Exception: All classes that inherit from "Animal" must provide "eat" method.
71 | ```
72 |
73 | ## @silence(file_name)
74 |
75 | Make a decorator `@silence` that accepts a `file_name` as an argument. All functions that are decorated with it should always run successfully. If an error is raised, it should be logged in the given file.
76 |
77 | ```python
78 | @silence('errors.txt')
79 | def foo(x)
80 | if x > 50:
81 | raise ValueError('Omg.')
82 |
83 | foo(10)
84 | foo(100)
85 |
86 | # in errors.txt
87 | Calling `foo` raised an error - ValueError: 'Omg.'. Provided arguments: (100, ).
88 | ```
89 |
--------------------------------------------------------------------------------
/week03/01.FractionsOOP/test_fraction.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from fraction import Fraction
3 |
4 |
5 | class TestFraction(unittest.TestCase):
6 | def test_cannot_instantiate_fraction_with_zero_denominator(self):
7 | exception = None
8 |
9 | try:
10 | Fraction(1, 0)
11 | except AssertionError as exc:
12 | exception = exc
13 |
14 | self.assertIsNotNone(exception)
15 |
16 | def test_fractions_string_representation_is_as_expected_one(self):
17 | fraction1 = Fraction(1, 3)
18 | fraction2 = Fraction(-1, 3)
19 | fraction3 = Fraction(2, 4)
20 |
21 | self.assertEqual(str(fraction1), '1/3')
22 | self.assertEqual(str(fraction2), '-1/3')
23 | self.assertEqual(str(fraction3), '2/4')
24 |
25 | def test_fractions_equalization_with_equal_fractions(self):
26 | fraction1 = Fraction(1, 5)
27 | fraction2 = Fraction(1, 5)
28 |
29 | self.assertTrue(fraction1 == fraction2, 'Fractions are not equal')
30 |
31 | def test_fractions_equalization_with_equal_nonsimpified_fractions(self):
32 | fraction1 = Fraction(1, 3)
33 | fraction2 = Fraction(3, 9)
34 |
35 | self.assertTrue(fraction1 == fraction2, 'Fractions are not equal')
36 |
37 | def test_simplified_fraction_is_preserved_after_simplification(self):
38 | fraction = Fraction(1, 5)
39 |
40 | expected = Fraction(1, 5)
41 |
42 | self.assertEqual(fraction.simplify(), expected)
43 |
44 | def test_fraction_is_simplified_as_expected(self):
45 | fraction = Fraction(10, 50)
46 |
47 | expected = Fraction(1, 5)
48 |
49 | self.assertEqual(fraction.simplify(), expected)
50 |
51 | def test_addition_fractions_works_correct_for_non_simplifiable_result_with_equal_denominator(self):
52 | fraction1 = Fraction(1, 5)
53 | fraction2 = Fraction(2, 5)
54 |
55 | result = fraction1 + fraction2
56 |
57 | self.assertEqual(result.numerator, 3)
58 | self.assertEqual(result.denominator, 5)
59 |
60 | def test_addition_fractions_works_correct_for_non_simplifiable_result_with_non_equal_denominator(self):
61 | fraction1 = Fraction(1, 7)
62 | fraction2 = Fraction(2, 6)
63 |
64 | result = fraction1 + fraction2
65 |
66 | self.assertEqual(result.numerator, 10)
67 | self.assertEqual(result.denominator, 21)
68 |
69 |
70 | if __name__ == '__main__':
71 | unittest.main()
72 |
--------------------------------------------------------------------------------
/week07/01.ContextManagers/README.md:
--------------------------------------------------------------------------------
1 | # Context Managers
2 |
3 | **NOTE: All tasks should be implemented using a class context managers and @contextmanager generator!**
4 |
5 | ## Silence errors
6 |
7 | Implement a context manager that silences specific exceptions. It can accept the message of the exception as non-required argument.
8 |
9 | ```python
10 | with silence_exception(ValueError):
11 | # nothing should happen
12 | raise ValueError('Test')
13 |
14 | with silence_exception(ValueError):
15 | # the error should be re-raised since it is not expected.
16 | raise TypeError('Test')
17 |
18 | with silence_exception(ValueError, 'Test'):
19 | # nothing should happen
20 | raise ValueError('Test')
21 |
22 | with silence_exception(ValueError, 'Testing.'):
23 | # the error should be re-raised since it is not expected.
24 | raise ValueError('Test')
25 | ```
26 |
27 | ## Change Decimal Precision
28 |
29 | Implement a context manager that sets the precision of all Decimals in the with-block. Outside the block, the Decimals must be preserved with their default precision. _Think for raised exceptions!_
30 |
31 | ```python
32 | with change_precision(2):
33 | print(Decimal('1.123132132') + Decimal('2.23232')) # 3.4
34 |
35 | print(Decimal('1.123132132') + Decimal('2.23232')) # 3.355452132
36 | ```
37 |
38 | Check this for help: https://docs.python.org/3/library/decimal.html#quick-start-tutorial
39 |
40 | ## Measure Performance
41 |
42 | Implement a context manager that tracks the performance of a code block using "benchmarks". The context manager should print how much time each code block took. When the with-block is finished, the context manager should print how much time the whole block took.
43 |
44 | The benchmarks can accept a string message so it's easier to know what is measured. If there is no message passed - use the index of the benchmark as shown below.
45 |
46 | Each benchmark can optionally restart the context manager time (show below).
47 |
48 | ```python
49 | with measure_performance() as p:
50 | time.sleep(1)
51 | p.benchmark('1st step')
52 |
53 | time.sleep(2)
54 | p.benchmark('2nd step', restart=True)
55 |
56 | time.sleep(3)
57 | p.benchmark()
58 | ```
59 |
60 | The output is:
61 |
62 | ```
63 | 1st step: 1.0011475086212158
64 | 2nd step: 3.0017237663269043
65 | Benchmark No.3: 3.003134250640869
66 | Finished for: 6.0052361488342285
67 | ```
68 |
69 | **NOTE: All tasks should be implemented using a class context managers and @contextmanager generator!**
70 |
--------------------------------------------------------------------------------
/week06/02.Generators/README.md:
--------------------------------------------------------------------------------
1 | # Generators
2 |
3 | ## chain
4 |
5 | Implement a function that takes two iterables and returns another one that concatenate the two iterables.
6 |
7 | ```python
8 | def chain(iterable_one, iterable_two):
9 | pass
10 | ```
11 |
12 | ### Example
13 |
14 | ```python
15 | >>> list(chain(range(0, 4), range(4, 8)))
16 | [0, 1, 2, 3, 4, 5, 6, 7]
17 | ```
18 |
19 | ## compress
20 |
21 | Implement a function that takes one iterables and one iterable mask. The mask is an collection that contains only `True` or `False`
22 |
23 | This function returns only this objects from the first collection that have `True` on their position in the mask.
24 |
25 | ```python
26 | def compress(iterable, mask):
27 | pass
28 | ```
29 |
30 | ### Example
31 |
32 | ```python
33 | >>> list(compress(["Ivo", "Rado", "Panda"], [False, False, True]))
34 | ["Panda"]
35 | ```
36 |
37 | ## cycle
38 |
39 | Implement a function that takes an iterable and returns endless concatenation of it.
40 |
41 | ```python
42 | def cycle(iterable):
43 | pass
44 | ```
45 |
46 | ```python
47 | >>> endless = cycle(range(0,10))
48 | for item in endless:
49 | print(item)
50 | ```
51 |
52 | ## Book Reader
53 |
54 | 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)
55 |
56 | Our book is made of many files. Each file has its number `001.txt, 002.txt, 003.txt`
57 |
58 | Each file may contain one or more chapters.
59 |
60 | [Link to the book](Book.zip)
61 |
62 | Write a program that displays on the console each chapter. You can only move forwards using the `space button`.
63 |
64 | Try not to load the whole book in the memory. Use generator!
65 |
66 | ## Book Generator
67 |
68 | Make a python program that generates books.
69 |
70 | Your program should take the following parameters.
71 |
72 | - Chapters count
73 | - Chapter length range (in words)
74 |
75 | 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.
76 |
77 | Try to generate bigger book. Like 1-2G, and try to pass it to the previous program.
78 |
79 | ## Extra Task - Mouse Beep
80 |
81 | Make a generator that returns the current position of your mouse pointer.
82 |
83 | 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.
84 |
85 | Ask Google for "How to get mouse cords" and "Python beep sound"
86 |
--------------------------------------------------------------------------------
/week09/01.SQL-Intro/README.md:
--------------------------------------------------------------------------------
1 | # Introduction to Databases and SQL
2 |
3 | Presentation: https://slides.com/hackbulgaria/python-101-9th-sql/
4 |
5 | ## Task 1: Tables, tables everywhere! SELECT, UPDATE, INSERT, DELETE
6 |
7 | Now, we know that our languages table looks like this:
8 |
9 | | id | language | answer | answered | guide |
10 | | ------------- |:-------------:| --- | --- |-----:|
11 | 1|Python|google|0|A folder named Python was created. Go there and fight with program.py!
12 | 2|Go|200 OK|0|A folder named Go was created. Go there and try to make Google Go run.
13 | 3|Java|object oriented programming|0|A folder named Java was created. Can you handle the class?
14 | 4|Haskell|Lambda|0|Something pure has landed. Go to Haskell folder and see it!
15 | 5|C#|NDI=|0|Do you see sharp? Go to the C# folder and check out.
16 | 6|Ruby|https://www.ruby-lang.org/bg/|0|Ruby, ruby, rubyyy, aaahaaaahaa! (music). Go to Ruby folder!
17 | 7|C++|header files|0|Here be dragons! It's C++ time. Go to the C++ folder.
18 | 8|JavaScript|Douglas Crockford|0|NodeJS time. Go to JavaScript folder and Node your way!
19 |
20 | Your task is to write queries for:
21 |
22 | * Create database
23 | * Create table `Languages` with columns and attributes with the correct types
24 | * Insert data
25 | * Add new column `rating` which is number from 1 to 9. Insert values for every language.
26 | * For few languages (`Python` and `Go`) update answered value from 0 to 1
27 | * Select languages which answer is `200 OK` or `Lambda`.
28 |
29 | ## Task 2: Database schema
30 | 
31 |
32 | ## First Queries
33 | 1. Напишете заявка, която извежда адреса на студио ‘MGM’
34 | 2. Напишете заявка, която извежда рождената дата на актрисата `Kim Basinger`
35 | 3. Напишете заявка, която извежда имената всички продуценти на филми с
36 | нетни активи (networth) над 10 000 000 долара
37 | 4. Напишете заявка, която извежда имената на всички актьори, които са
38 | мъже или живеят на Prefect Rd
39 | 5. Добавате нова филмова звезда 'Zahari Baharov', с адрес и рожденна дата по ваш избор.
40 | 6. Изтрийте всички студия, които имат в адреса си числото 5.
41 | 7. Променете студио да бъде "Fox" на тези филми, които в имената си имат 'star.
42 |
43 |
44 | ## Relations
45 |
46 | 1. Напишете заявка, която извежда имената на актьорите мъже участвали в ‘Terms
47 | 2. of Endearment’
48 | 3. Напишете заявка, която извежда имената на актьорите участвали във филми
49 | продуцирани от ‘MGM’през 1995 г.
50 | 4. Добавете колона "име на президент"на таблицата Студио и съответно и задайте стойности.Напишете заявка, която извежда името на президента на ‘MGM’
51 |
--------------------------------------------------------------------------------
/week13/hackademy/education/views/courses.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, get_object_or_404, redirect
2 | from django.views.generic import CreateView
3 | from django.urls import reverse_lazy, reverse
4 | from django.http import HttpResponse
5 | from django import forms
6 |
7 | from education.models import Course
8 |
9 |
10 | def list(request):
11 | return render(request, 'courses/list.html', {'courses': Course.objects.all()})
12 |
13 |
14 | def detail(request, course_id):
15 | course = get_object_or_404(Course, id=course_id)
16 | return render(request, 'courses/detail.html', {'course': course})
17 |
18 |
19 | class CrouseForm(forms.ModelForm):
20 | class Meta:
21 | model = Course
22 | fields = ('name', 'description', 'start_date', 'end_date')
23 |
24 |
25 | def session(request):
26 | if not request.session.get('counter'):
27 | request.session['counter'] = 1
28 | else:
29 | request.session['counter'] += 1
30 |
31 | return render(request, 'courses/session.html')
32 |
33 |
34 | def new_experiment(request):
35 | if request.method == "POST":
36 | data = request.POST
37 | form = CrouseForm(data=data)
38 | if form.is_valid():
39 | new_course = form.save()
40 | return redirect(reverse('education:courses:list'))
41 | else:
42 | return render(request, 'courses/new_experiment.html', {'form': form})
43 | else:
44 | form = CrouseForm()
45 | return render(request, 'courses/new_experiment.html', {'form': form})
46 |
47 |
48 | def edit_course(request, course_id):
49 | course = Course.objects.get(id=course_id)
50 |
51 | if request.method == 'POST':
52 | form = CrouseForm(data=request.POST, instance=course)
53 | if form.is_valid():
54 | form.save()
55 | return redirect(reverse('education:courses:list'))
56 | else:
57 | return render(request, 'courses/edit_course.html', {'course': course, 'form': form})
58 | else:
59 | form = CrouseForm(instance=course)
60 | return render(request, 'courses/edit_course.html', {'course': course, 'form': form})
61 |
62 |
63 | def delete_course(request, course_id):
64 | if request.method == 'POST':
65 | Course.objects.get(id=course_id).delete()
66 | return redirect(reverse('education:courses:list'))
67 |
68 |
69 | class CourseCreateView(CreateView):
70 | model = Course
71 | fields = ['name', 'description', 'start_date', 'end_date']
72 | template_name = 'courses/create.html'
73 |
74 | def get_success_url(self, **kwargs):
75 | return reverse_lazy('education:courses:detail', kwargs={'course_id': self.object.id})
76 |
--------------------------------------------------------------------------------
/week04/01.Mixins/README.md:
--------------------------------------------------------------------------------
1 | # Serializing & Deserializing from/to JSON and XML
2 |
3 | ## JSON & XML
4 |
5 | [JSON](https://en.wikipedia.org/wiki/JSON) and [XML](https://en.wikipedia.org/wiki/XML) are file formats. They serve for communication between systems and interfaces.
6 |
7 | ## Serialization & Deserialization
8 |
9 | Serialization is the process of translating an object into a primitive type. Deserialization is the opposite operation.
10 |
11 | ## Your task
12 |
13 | All the classes from today should now how to serailize & deserialize themselves into JSON and XML.
14 | You need to create two mixins called `Jsonable` and `Xmlable`.
15 | They should have the following methods:
16 |
17 | - `Jsonable`:
18 | - `to_json(indent)`. Default value `indent=4`. Returns JSON string representing the current object.
19 | - `from_json(json_string)`. Returns object instance of the current class with attributes from the passed argument.
20 | - `Xmlable` with methods:
21 | - `to_xml()`. Returns XML string representing the current object.
22 | - `from_xml(xml_string)`. Returns object instance of the current class with attributes from the passed argument.
23 |
24 | > NOTE: What should `from_json` and `from_xml` methods be?
25 |
26 | ### Example
27 |
28 | #### Serialization
29 |
30 | ```python
31 | p = Panda(name='Marto')
32 | p.to_json()
33 | '''
34 | {
35 | "dict": {
36 | "name": "Marto"
37 | },
38 | "type": "Panda"
39 | }
40 | '''
41 | p.to_xml()
42 | 'Ivo'
43 | ```
44 |
45 | #### Deserialization
46 |
47 | ```python
48 | p = Panda(name='marto')
49 | json_string = p.to_json()
50 | xml_string = p.to_xml()
51 |
52 | p1 = Panda.from_json(json_string)
53 | p2 = Panda.from_xml(xml_string)
54 |
55 | assert p == p1
56 | assert p == p2
57 | ```
58 |
59 | #### Validation
60 |
61 | Make sure you are `from_json` and `from_xml` return objects from the correct class. Raise `ValueError` instead.
62 |
63 | ```python
64 | person = Person('Pesho')
65 | Panda.from_json(person.to_json()) # ValueError
66 | ```
67 |
68 | ### Hints
69 |
70 | - Use TDD!
71 | - For XML, use the standard library -
72 | - For JSON, use the standard library -
73 |
74 | ### Bonus
75 |
76 | Make your program work with nested structures.
77 |
78 | ```python
79 | person = Person('Pesho', panda=Panda('Marto'))
80 | person.to_json()
81 | '''
82 | {
83 | "dict": {
84 | "name": "Pesho",
85 | "panda": {
86 | "dict": {
87 | "name": "Marto"
88 | },
89 | "type": "Panda"
90 | }
91 | },
92 | "type": "Person"
93 | }
94 | '''
95 | ```
96 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/tasks/01.Business-Card-Catalog/README.md:
--------------------------------------------------------------------------------
1 | # Business Card Catalog
2 |
3 |
4 | Your task is to create a Business Card Catalog
5 |
6 | You need to create one table: `User` with the following properties:
7 |
8 | - `id` - primary key (unique, not nullable, with autoincrement...)
9 | - `full_name` - unique string (not nullable)
10 | - `email` - unique string (not nullable)
11 | - `age` - integer (not nullable)
12 | - `phone` - string (not nullable)
13 | - `additional_info` - text
14 |
15 |
16 | ## Requirements
17 |
18 | ### 1. Supported actions
19 |
20 | - `add` - to insert new business card
21 | - `list` - to list all business cards
22 | - `delete` - to delete a certain business card
23 | - `get` - display full information for a certain business card
24 | - `help` - to list all available options
25 |
26 |
27 | ### 2. Interface
28 |
29 | ```
30 | $ python main.py
31 |
32 | Hello! This is your business card catalog. What would you like? (enter "help" to list all available options)
33 | >> Enter command: help
34 | #############
35 | ###Options###
36 | #############
37 |
38 | 1. `add` - insert new business card
39 | 2. `list` - list all business cards
40 | 3. `delete` - delete a certain business card (`ID` is required)
41 | 4. `get` - display full information for a certain business card (`ID` is required)
42 | 5. `help` - list all available options
43 | ```
44 |
45 | #### 2.1 `add`
46 |
47 | ```
48 | >>> Enter command: add
49 | Enter user name: ... # type here
50 | Enter email: ... # type here
51 | Enter age: ... # type here
52 | Enter phone: ... # type here
53 | Enter addional info (optional): ... # type here
54 | ```
55 |
56 | #### 2.2 `list`
57 |
58 | ```
59 | >>> Enter command: list
60 |
61 | #############
62 | ###Contacts###
63 | #############
64 |
65 | 1. ID: 1, Email: i.donchev@hacksoft.io, Full name: Ivaylo Donchev
66 |
67 | 2. ID: 2, Email: rositsa@hacksoft.io, Full name: Rositsa Zlateva
68 | ```
69 |
70 |
71 | #### 2.3 `get`
72 |
73 | ```
74 | >>> Enter command: get
75 |
76 | Enter id: ... # type here
77 |
78 | Contact info:
79 |
80 | ###############
81 | Id: 1,
82 | Full name: Ivaylo Donchev
83 | Email: i.donchev@hacksoft.io
84 | Age: 23
85 | Phone: 088......
86 | Additional info: ...
87 | ##############
88 | ```
89 |
90 |
91 | #### 2.4 `delete`
92 |
93 | ```
94 | >>> Enter command: delete
95 |
96 | Enter id: ... # type here
97 |
98 | Following contact is deleted successfully:
99 |
100 | ###############
101 | Id: 1,
102 | Full name: Ivaylo Donchev
103 | Email: i.donchev@hacksoft.io
104 | Age: 23
105 | Phone: 088......
106 | Additional info: ...
107 | ##############
108 | ```
109 |
--------------------------------------------------------------------------------
/Application/2.CorrectBrackets/README.md:
--------------------------------------------------------------------------------
1 | ## Правилни скоби
2 |
3 | Дефинираме "Правилни скоби" по следния начин:
4 |
5 | Правилни скоби са:
6 |
7 | а) `()` - лява скоба `(` до дясна скоба `)`
8 |
9 | б) `(< правилни скоби >)` - лява скоба `(` до правилни скоби, до дясна скоба `)`
10 |
11 | в) `< правилни скоби >< правилни скоби >` - правилни скоби до правилни скоби
12 |
13 | ## Задача
14 |
15 | На избран от вас език, напишете програма, която чете от стандартния вход подаден низ и връща дали този низ е "Правилни скоби."
16 |
17 | ## Пример 1
18 |
19 | Input:
20 | ```
21 | ()(())
22 | ```
23 | Output:
24 | ```
25 | True
26 | ```
27 |
28 | ### Описание
29 |
30 | Това са правилни скоби защото:
31 | 1. Спрямо дефиниция `в)`, разделяме въведеният израз `()(())` на две части: `()` и `(())`. Това означава, че ако първата и втората част са "правилни скоби" - то и целия израз е "правилни скоби"
32 | 2. Спрямо дефиниция `a)` разглеждаме първият израз от разделените в точка 1. Изразът `()` е вярно, че е "правилни скоби" спрямо дефиниция `a)`.
33 | 3. Спрямо дефиниция `б)` разгеждаме втората част от изразът от точка 1. Изразът `(())` е възможно да отговаря на `б)` защото започва с лява скоба `(` и завършва на дясна скоба `)`. Тоест ако изразът между първата и последната скоба (именно този "()"), отговаря на дефиницията на "правилни скоби" - то и целия израз отговаря на дефиницията на "правилни скоби".
34 | 4. Спрямо дефиниция `a)` разглеждаме последната отделена част в точка 3. Изразът `()` отговаря на `a)` от дефиницията на "правилни скоби", тоест - И изразът в точка 3 също отговаря на дефиницията.
35 |
36 | С горните съображения можем да кажем, че въведеният израз "()(())" отговаря на дефиницията на "правилни скоби"
37 |
38 | ## Пример 2
39 |
40 | Input:
41 | ```
42 | ()))
43 | ```
44 | Output:
45 | ```
46 | False
47 | ```
48 |
49 | ### Описание
50 |
51 | 1. Разглеждаме изразът `()))` спрямо дефиниция `б)`, защото започва с лява скоба и завършва на дясна скоба. Това означава, че ако изразът между първата и последната скоба, отговаря на дефиницията на "правилни скоби", то целият израз отговаря.
52 | 2. Разглеждаме изразът `))` останал от точка 1. Той не отговаря на нито една от дефинициите `а)` `б)` или `в)`. Това означава, че все още не сме доказали, дали изразът е "правилни скоби".
53 | 3. Разглеждаме изразът `()))` спрямо дефиниция `в)` - това означава, че го разделяме на 2 части. Ако лявата част и дясната част са "правилни скоби", то и целият израз е "правилни скоби"
54 | 4. Разглеждаме лявата част от точка 3 `()` спрямо дефинициите и виждаме, че отговаря на дефиниция `a)`
55 | 5. Разглеждаме дясната част от точка 3 `))` тя не отговаря на нито една от дефинициите `a)`, `б)` или `в)`. Следователно целият израз не е "правилни скоби" спрямо дефиниция `в)`
56 |
--------------------------------------------------------------------------------
/week08/02.Mocks/README.md:
--------------------------------------------------------------------------------
1 | ## Presentation
2 |
3 | https://slides.com/hackbulgaria/python-101-9th-mocking/
4 |
5 | ## You should write tests for the following functions
6 |
7 | ### 1
8 |
9 | Remember the [Cancellation policy task](https://github.com/HackBulgaria/Programming-101-Python-2020-Spring/tree/master/week02/02.CancellationPolicy)? Rework the solution and the tests and stop expecting `now` as argument!
10 |
11 | ```
12 | def get_cancellation_policy(conditions, price, start):
13 | now = datetime.now()
14 |
15 | assert now < start, 'Invalid booking start.'
16 | validate_conditions(conditions)
17 |
18 | ensured_conditions = ensure_conditions(conditions)
19 |
20 | if (len(ensured_conditions)) == 1:
21 | return get_cancellation_fee(price, ensured_conditions[0]['percent'])
22 |
23 | sorted_conditions = sort_conditions(ensured_conditions)
24 | paired_conditions = pair_conditions(sorted_conditions)
25 |
26 | current = get_current_condition(paired_conditions, start, now)
27 |
28 | return get_cancellation_fee(price, current)
29 | ```
30 |
31 | ### 2
32 |
33 | Remember [Measure Performance task](https://github.com/HackBulgaria/Programming-101-Python-2020-Spring/tree/master/week07/01.ContextManagers#measure-performance)? Rework the tests to test if the context manager **prints the correct values**.
34 |
35 | ### 3
36 |
37 | ```
38 | from datetime import datetime
39 |
40 | def get_booking_status(booking):
41 | now = datetime.now()
42 |
43 | if booking.cancelled:
44 | return 'Cancelled'
45 |
46 | if booking.is_fully_paid():
47 | return 'Paid'
48 |
49 | if now < booking.start:
50 | return 'Upcoming'
51 |
52 | return 'Waiting taxes'
53 | ```
54 |
55 | ### 4
56 |
57 | ```
58 | from random import randint
59 |
60 | def big_possitive_pow():
61 | x = randint(100, 10000)
62 | y = randint(-10, 100)
63 |
64 | if y < 1:
65 | raise ValueError('Try again.')
66 |
67 | return x ** y
68 | ```
69 |
70 | ### 5
71 |
72 | You don't need to install the libraries!
73 |
74 | ```
75 | import requests
76 | from bs4 import BeautifulSoup
77 |
78 |
79 | def get_url_title(*, url):
80 | try:
81 | # Pretend to be a bot.
82 | headers = {'User-Agent': 'Testbot'}
83 | response = requests.get(url, headers=headers, timeout=10)
84 | except RequestException as request_exception:
85 | raise Exception(f'Couldn\'t request {url}. This was the exception {request_exception}')
86 |
87 | if not response.ok:
88 | raise Exception(f'Response for {url} was not ok.')
89 |
90 | soup = BeautifulSoup(response.content, 'html.parser')
91 | title_tag = soup.title
92 |
93 | if title_tag
94 | return title_tag.string
95 |
96 | return ''
97 | ```
98 |
--------------------------------------------------------------------------------
/week03/03.Polynomials/README.md:
--------------------------------------------------------------------------------
1 | # Polynomials and Derivates
2 |
3 | ## Polynomials
4 |
5 | In math, [a polynomial](https://en.wikipedia.org/wiki/Polynomial) is a function, defined like that:
6 |
7 | ```
8 | f(x) = Cn*x^n + Cn-1*x^n-1 + ... + c1*x^1 + c0
9 | ```
10 |
11 | where:
12 |
13 | - `Cn` to `C0` are coefficients. **We are going to work with positive integers for coefficients.**
14 | - `x` is the variable and `x^n` means x to the power of `n`
15 | - If the given coefficient is equal to `1`, it can be omitted.
16 |
17 | Here are few examples:
18 |
19 | ```
20 | f(x) = 2x^3 + 3x + 1
21 | f'(x) = 6x + 3
22 | ```
23 |
24 | ## Derivatives
25 |
26 | A derivative of a polynomial function is easily calculated. The only thing that we need to know is how to take derivative from each member of the polynomial function.
27 |
28 | Here is the general rule for taking derivative of a function in the following form:
29 |
30 | ```
31 | f(x) = c * x^n
32 | f'(x) = n * c * x^(n - 1)
33 | ```
34 |
35 | Where `c` and `n` are integers and `f'(x)` denotes the derivative of `f(x)`
36 |
37 | There are two corner cases:
38 |
39 | Taking derivative of `x` to the power of 1.
40 |
41 | ```
42 | f(x) = c * x
43 | f'(x) = c
44 | ```
45 |
46 | and taking derivatives of constants:
47 |
48 | ```
49 | f(x) = c
50 | f'(x) = 0
51 | ```
52 |
53 | So if we want to take the derivative of a polynomial function, we just apply that rule to every member of the polynom:
54 |
55 | ```
56 | f(x) = 2x^3 + 3x + 1
57 | f'(x) = 6*x^2 + 3
58 | ```
59 |
60 | ## Your task
61 |
62 | Using your OO knowledge, implement a program that takes a string, representing a polynomial function and returns / prints the derivative of that polynomial function.
63 |
64 | Few examples:
65 |
66 | ```
67 | $ python3 solution.py '2*x^3+x'
68 | Derivative of f(x) = 2*x^3 + x is:
69 | f'(x) = 6*x^2 + 1
70 | ```
71 |
72 | ```
73 | $ python3 solution.py '1'
74 | The derivative of f(x) = 1 is:
75 | f'(x) = 0
76 | ```
77 |
78 | ```
79 | $ python3 solution.py 'x^4+10*x^3'
80 | The derivative of f(x) = x^4 + 10*x^3 is:
81 | f'(x) = 4*x^3 + 30*x^2
82 | ```
83 |
84 | Few things to keep in mind:
85 |
86 | ```
87 | $ python3 solution.py '1+x^2'
88 | The derivative of f(x) = x^2 + 1 is:
89 | f'(x) = 2x
90 | ```
91 |
92 | And
93 |
94 | ```
95 | $ python3 solution.py '3x^2'
96 | The derivative of f(x) = 3x^2 is:
97 | f'(x) = 6*x
98 | ```
99 |
100 | Don't bother checking if the polynomial is correct for it's variable. It's always going to be the same ( for example `x`)
101 |
102 | ## Hints
103 |
104 | Do not forget to test your implementation.
105 | Of course, think wisely and create your program in a way that is independent from the console input/output.
106 |
--------------------------------------------------------------------------------
/week02/02.CancellationPolicy/solution.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timedelta
2 |
3 |
4 | def validate_conditions(conditions):
5 | counter = 0
6 |
7 | for condition in conditions:
8 | if not condition.get('hours'):
9 | counter += 1
10 | if condition.get('hours', 0) > 24:
11 | raise ValueError('Hours cannot be > 24.')
12 |
13 | if counter != 1:
14 | raise ValueError('Invalid conditions.')
15 |
16 |
17 | def ensure_conditions(conditions):
18 | for condition in conditions:
19 | if 'hours' not in condition.keys():
20 | condition['hours'] = 0
21 |
22 | return conditions
23 |
24 |
25 | def pair_conditions(conditions):
26 | result = []
27 |
28 | for index in range(len(conditions) - 1):
29 | left = conditions[index]
30 | right = conditions[index + 1]
31 |
32 | result.append((left, right))
33 |
34 | return result
35 |
36 |
37 | def get_current_condition(pairs, start, now):
38 | for pair in pairs:
39 | lower_condition, higher_condition = pair
40 |
41 | lower_date = start - timedelta(hours=lower_condition['hours'])
42 | upper_date = start - timedelta(hours=higher_condition['hours'])
43 |
44 | if (now >= lower_date and now < upper_date):
45 | return higher_condition['percent']
46 |
47 | return pairs[0][0]['percent']
48 |
49 |
50 | def get_cancellation_fee(price, percent):
51 | return price * (percent / 100)
52 |
53 |
54 | def sort_conditions(conditions):
55 | return sorted(conditions, key=lambda c: c['hours'], reverse=True)
56 |
57 |
58 | def get_cancellation_policy(
59 | conditions,
60 | price,
61 | start,
62 | now
63 | ):
64 | assert now < start, 'Invalid booking start.'
65 | validate_conditions(conditions)
66 |
67 | ensured_conditions = ensure_conditions(conditions)
68 |
69 | if (len(ensured_conditions)) == 1:
70 | return get_cancellation_fee(price, ensured_conditions[0]['percent'])
71 |
72 | sorted_conditions = sort_conditions(ensured_conditions)
73 | paired_conditions = pair_conditions(sorted_conditions)
74 |
75 | current = get_current_condition(paired_conditions, start, now)
76 |
77 | return get_cancellation_fee(price, current)
78 |
79 |
80 | def main():
81 | now = datetime.now()
82 | booking_start = now + timedelta(hours=10)
83 | price = 1000
84 | conditions = [
85 | {'hours': 24, 'percent': 10},
86 | {'hours': 12, 'percent': 50},
87 | {'hours': 6, 'percent': 80},
88 | {'percent': 100}
89 | ]
90 |
91 | result = get_cancellation_policy(
92 | conditions,
93 | price,
94 | booking_start,
95 | now
96 | )
97 | print(result)
98 |
99 |
100 | if __name__ == '__main__':
101 | main()
102 |
--------------------------------------------------------------------------------
/week02/03.MoreTesting/README.md:
--------------------------------------------------------------------------------
1 | ## Python sort
2 |
3 | Implement a function, called `my_sort` that takes 3 arguments. The arguments are:
4 |
5 | - `iterable` - list or tuple that has to be sorted. Preserve the type in the return value. **Must have default value**
6 | - `ascending` - boolean that controls the sorting order. **Must have default value**
7 | - `key` - string that serves as a lookup key if the `iterable` argument is a list of dictionaries. **Must have default value**
8 |
9 | The function should return the given iterable sorted in order that is controlled by the `ascending` value.
10 |
11 | **NOTE: You should NOT use Python's builtin list.sort() or sorted()**
12 |
13 | ### Test examples
14 |
15 | ```
16 | >>> my_sort([])
17 | []
18 | >>> my_sort((10,8,9,10,100))
19 | (8, 9, 10, 10, 100)
20 | >>> my_sort([10, 8, 9, 10, 100], False)
21 | [100, 10, 10, 9, 8]
22 | >>> my_sort(iterable=[{'name': 'Marto', 'age': 24}, {'name': 'Ivo', 'age': 27}, {'name': 'Sashko', 'age': 25}], key='age')
23 | [{'name': 'Marto', 'age': 24}, {'name': 'Sashko', 'age': 25}, {'name': 'Ivo', 'age': 27}]
24 | ```
25 |
26 | ## Simplify fractions
27 |
28 | Implement a function, called `simplify_fraction(fraction)` that takes a tuple of the form `(nominator, denominator)` and simplifies the fraction.
29 |
30 | The function should return the fraction in it's irreducible form.
31 |
32 | For example, a fraction `3/9` can be reduced by dividing both the nominator and the denominator by 3. We end up with `1/3` which is irreducible.
33 |
34 | ### Test examples
35 |
36 | ```
37 | >>> simplify_fraction((3,9))
38 | (1,3)
39 | >>> simplify_fraction((1,7))
40 | (1,7)
41 | >>> simplify_fraction((4,10))
42 | (2,5)
43 | >>> simplify_fraction((462,63))
44 | (22,3)
45 | ```
46 |
47 | ## Collect fractions
48 |
49 | Implement a function, called `collect_fractions(fractions)` where `fractions` is a list of tuples of the form `(nominator, denominator)`.
50 |
51 | Both the nominator and the denominator are integers.
52 | The function should return the sum of the fractions.
53 |
54 | ### Test examples
55 |
56 | ```
57 | >>> collect_fractions([(1, 4), (1, 2)])
58 | (3, 4)
59 | >>> collect_fractions([(1, 7), (2, 6)])
60 | (10,21)
61 | ```
62 |
63 | ## Sort array of fractions
64 |
65 | Implement a function, called `sort_fractions(fractions, ascending = True)` where `fractions` is a list of tuples of the form `(nominator, denominator)` and `ascending` is boolean that controls the sorting direction.
66 |
67 | Both the nominator and the denominator are integers.
68 |
69 | The function should return the list, sorted depending on the `ascending` value.
70 |
71 | ### Test examples
72 |
73 | ```
74 | >>> sort_fractions([(2, 3), (1, 2)])
75 | [(1, 2), (2, 3)]
76 | >>> sort_fractions([(2, 3), (1, 2), (1, 3)])
77 | [(1, 3), (1, 2), (2, 3)]
78 | >>> sort_fractions([(5, 6), (22, 78), (22, 7), (7, 8), (9, 6), (15, 32)])
79 | [(22, 78), (15, 32), (5, 6), (7, 8), (9, 6), (22, 7)]
80 | ```
81 |
--------------------------------------------------------------------------------
/week03/05.BowlingGame/README.md:
--------------------------------------------------------------------------------
1 | ## Bowling Game
2 |
3 | ### Game Basics
4 |
5 | One game of bowling consists of 10 frames. The minimum score for a game is 0 and a maximum is 300.
6 | Each frame consists of two chances to knock down ten pins. In the bowling game we use "pins", instead of "points".
7 |
8 | - Strikes -
9 | Knocking down all 10 pins on your first ball is called a strike, denoted by an "X" on the score sheet. A strike is worth 10, plus the value of your next 2 rolls.
10 | At minimum, your score for a frame in which you throw a strike will be 10 (10+0+0).
11 | At best, your next two shots will be strikes, and the frame will be worth 30 (10+10+10).
12 | Say you throw a strike in the first frame. Technically, you don't have a score yet.
13 | You need to throw two more balls to figure out your total score for the frame.
14 | In the second frame, you throw a 6 on your first ball and a 2 on your second ball. Your score for the first frame will be 18 (10+6+2).
15 | - Spares -
16 | If it takes two shots to knock down all ten pins, it's called a spare, denoted by "N /". A spare is worth 10, plus the value of your next roll.
17 | Say you throw a spare in your first frame. Then, in your first ball of the second frame, you throw a 7.
18 | Your score for the first frame will be 17 (10+7).
19 | The maximum score for a frame in which you get a spare is 20 (a spare followed by a strike), and the minimum is 10.
20 | - Open Frames -
21 | If, after 2 shots, at least 1 pin is still standing, it's called an open frame. If you don't get a strike or a spare in a frame, your score is the total number of pins you knock down. If you knock down 3 pins on your first ball and 2 on your second, your score for that frame is 5.
22 | - The Tenth Frame -
23 | In the sample score, three shots were thrown in the tenth frame. This is because of the bonuses awarded for strikes and spares. If you throw a strike on your first ball in the tenth frame, you need two more shots to determine the total value of the strike. If you throw a spare on your first two balls in the tenth frame, you need one more shot to determine the total value of the spare. This is called a fill ball.
24 | If you throw an open frame in the tenth frame, you won't get a third shot. The only reason the third shot exists is to determine the full value of a strike or spare.
25 | **Use OOP and follow the TDD to implement the Bowling game**
26 | - Help -
27 | You can use this [calculator](https://www.bowlinggenius.com/) to understand the rules better.
28 |
29 | ```python3.6
30 | game = BowlingGame([1, 4, 4, 5, 6, 3, 5, 1, 1, 0, 1, 7, 3, 6, 4, 3, 2, 1, 6, 2])
31 | game.result # 65
32 | game = BowlingGame([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] )
33 | game.result # 0
34 | game = BowlingGame([10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10])
35 | game.result # 300
36 | game = BowlingGame([5, 1, 1, 0, 1, 7, 3, 6, 4, 3, 2, 1, 6])
37 | game.result # invalid number of frames
38 | ```
39 |
--------------------------------------------------------------------------------
/week04/01.Mixins/solution/test_mixins.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import json
3 |
4 | from mixins import Jsonable, WithSetAttributes, WithEqualAttributes
5 |
6 |
7 | class Panda(Jsonable, WithSetAttributes, WithEqualAttributes):
8 | pass
9 |
10 |
11 | class Car(Jsonable, WithSetAttributes, WithEqualAttributes):
12 | pass
13 |
14 |
15 | class TestJsonable(unittest.TestCase):
16 | def test_to_json_returns_empty_json_for_objects_with_no_arguments(self):
17 | panda = Panda()
18 |
19 | result = panda.to_json(indent=4)
20 | expexted = {
21 | 'type': Panda.__name__,
22 | 'dict': {}
23 | }
24 |
25 | self.assertEqual(result, json.dumps(expexted, indent=4))
26 |
27 | def test_to_json_returns_correct_json_with_arguments(self):
28 | panda = Panda(
29 | name='Marto',
30 | age=20,
31 | weight=100.10,
32 | food=['bamboo', 'grass'],
33 | skills={'eat': 100, 'sleep': 200}
34 | )
35 |
36 | panda_result = panda.to_json(indent=4)
37 | panda_expexted = {
38 | 'type': Panda.__name__,
39 | 'dict': {
40 | 'name': 'Marto',
41 | 'age': 20,
42 | 'weight': 100.10,
43 | 'food': ['bamboo', 'grass'],
44 | 'skills': {'eat': 100, 'sleep': 200}
45 | }
46 | }
47 |
48 | self.assertEqual(panda_result, json.dumps(panda_expexted, indent=4))
49 |
50 | def test_to_json_returns_correct_json_with_arguments_of_jsonable_type(self):
51 | panda = Panda(name='Marto', friend=Panda(name='Ivo'))
52 |
53 | panda_result = panda.to_json(indent=4)
54 | panda_expexted = {
55 | 'type': Panda.__name__,
56 | 'dict': {
57 | 'name': 'Marto',
58 | 'friend': {
59 | 'type': Panda.__name__,
60 | 'dict': {
61 | 'name': 'Ivo'
62 | }
63 | }
64 | }
65 | }
66 |
67 | self.assertEqual(panda_result, json.dumps(panda_expexted, indent=4))
68 |
69 | def test_from_json_with_wrong_class_type(self):
70 | car = Car()
71 | car_json = car.to_json()
72 |
73 | with self.assertRaises(ValueError):
74 | Panda.from_json(car_json)
75 |
76 | def test_from_json_with_no_arguments(self):
77 | car = Car()
78 | car_json = car.to_json()
79 |
80 | result = Car.from_json(car_json)
81 |
82 | self.assertEqual(car, result)
83 |
84 | def test_from_json_with_arguments(self):
85 | panda = Panda(
86 | name='Marto',
87 | age=20,
88 | weight=100.10,
89 | food=['bamboo', 'grass'],
90 | skills={'eat': 100, 'sleep': 200}
91 | )
92 | panda_json = panda.to_json()
93 |
94 | result = Panda.from_json(panda_json)
95 |
96 | self.assertEqual(panda, result)
97 |
98 |
99 | if __name__ == '__main__':
100 | unittest.main()
101 |
--------------------------------------------------------------------------------
/week08/01.Graphs/README.md:
--------------------------------------------------------------------------------
1 | ## Tasks
2 |
3 | Every dictionary in the following tasks can have another dictionary as value or a iterable of dictionaries.
4 |
5 | ### Task 1
6 |
7 | Implement `deep_find(data, key)` which finds the given `key` in the `data` and returns it's value.
8 |
9 | **NOTE:** Add 2 solutions - one using DFS and one with BFS.
10 |
11 | ### Task 2
12 |
13 | Implement `deep_find_all(data, key)` which finds the given `key` in the `data` and returns array of the found values.
14 |
15 | **NOTE:** Add 2 solutions - one using DFS and one with BFS.
16 |
17 | ### Task 3
18 |
19 | Implement `deep_update(data, key, val)` which updates every occurance of the given `key` in the `data` with `val`.
20 |
21 | ### Task 4
22 |
23 | Implement `deep_apply(func, data)` which applies the given `func` to all **keys** from the given `data`.
24 |
25 | ### Task 5
26 |
27 | Implement `deep_compare(obj1, obj2)` where obj1 and obj2 can be `dict` or `iterable` and compares the given objects.
28 |
29 | ### Task 6
30 |
31 | Implement `schema_validator(schema: List, data: Dict)` which should assert that the given `data` keys are as the given `schema`.
32 | **Notes**
33 |
34 | - `data` is valid only if the given keys from the `schema` are found in the `data`.
35 | - If the `schema` has more or less keys, `data` is invalid.
36 | - If there is a missmatch in the `schema` and the `data` keys, `data` is invalid.
37 | - `schema_validator` should work for N levels of nesting.
38 |
39 | Example `schema`:
40 |
41 | ```
42 | schema = [
43 | 'key1',
44 | 'key2',
45 | [
46 | 'key3',
47 | ['inner_key1', 'inner_key2']
48 | ]
49 | ]
50 | ```
51 |
52 | Valid `data`:
53 |
54 | ```
55 | data = {
56 | 'key1': 'val1',
57 | 'key2': 'val2',
58 | 'key3': {
59 | 'inner_key1': 'val1',
60 | 'inner_key2': 'val2'
61 | }
62 | }
63 | ```
64 |
65 | Invalid `data`:
66 |
67 | ```
68 | data = {
69 | 'key1': 'val1',
70 | 'key2': 'val2',
71 | 'key3': {
72 | 'inner_key1': 'val1',
73 | 'inner_key2': 'val2'
74 | },
75 | 'key4': 'not expected'
76 | }
77 | ```
78 |
79 | ### Extra task: Frogs
80 |
81 | There's a lake. N lily-pads are in a series on the lake. On the first half pads (starting from the left) are sitting brown frogs (facing right). Then there is an empty pad. The second half pads have 3 green frogs on them (facing left). The frogs want to pass each other, but they can only jump on the empty lilly pad in the direction they are facing or jump over 1 frog on the empty lilly pad behind that frog.
82 |
83 | The configuration can be depicted as such:
84 |
85 | ```
86 | > > > _ < < <
87 | ```
88 |
89 | The frogs can only jump to an empty lily-pad to the direction they are facing.
90 |
91 | ```
92 | > > _ > < < <
93 | ```
94 |
95 | The frogs can also jump up to 2 lily-pads away.
96 |
97 | ```
98 | > > < > _ < <
99 | ```
100 |
101 | In the end, the frogs must come to this configuration:
102 |
103 | ```
104 | < < < _ > > >
105 | ```
106 |
107 | Write a program that will print all of the jumps that the frogs must do in order to pass each other
108 |
--------------------------------------------------------------------------------
/week01/01.PythonProblems/word_counter.py:
--------------------------------------------------------------------------------
1 | def is_palindrome(word):
2 | return word == word[::-1]
3 |
4 |
5 | def get_word_occurences(word, text):
6 | if is_palindrome(word):
7 | return text.count(word)
8 |
9 | return text.count(word) + text[::-1].count(word)
10 |
11 |
12 | def build_matrix(rows, cols):
13 | matrix = []
14 | rows_inputted = 0
15 | print('Enter matrix: ')
16 |
17 | while rows_inputted < rows:
18 | row_input = input()
19 |
20 | row = row_input.strip().split(' ')
21 | if len(row) != cols:
22 | return 'Wrong input.'
23 |
24 | matrix.append(row)
25 | rows_inputted += 1
26 |
27 | return matrix
28 |
29 |
30 | def word_occurances_in_rows(matrix, word):
31 | word_occurances = 0
32 |
33 | for row in matrix:
34 | word_occurances += get_word_occurences(word, ''.join(row))
35 |
36 | return word_occurances
37 |
38 |
39 | def word_occurances_in_cols(matrix, word):
40 | word_occurances = 0
41 | cols_count = len(matrix[0])
42 |
43 | for i in range(cols_count):
44 | column = []
45 | for row in matrix:
46 | column.append(row[i])
47 |
48 | word_occurances += get_word_occurences(word, ''.join(column))
49 |
50 | return word_occurances
51 |
52 |
53 | def word_occurances_in_right_diagonals(matrix, word):
54 | word_occurances = 0
55 | rows_count = len(matrix)
56 | cols_count = len(matrix[0])
57 |
58 | for c in range(rows_count + cols_count - 1):
59 | diagonal = []
60 | for i in range(rows_count):
61 | for j in range(cols_count):
62 | if i + j == c:
63 | diagonal.append(matrix[i][j])
64 |
65 | word_occurances += get_word_occurences(word, ''.join(diagonal))
66 |
67 | return word_occurances
68 |
69 |
70 | def word_occurances_in_left_diagonals(matrix, word):
71 | word_occurances = 0
72 | rows_count = len(matrix)
73 | cols_count = len(matrix[0])
74 |
75 | for c in range(1 - cols_count, rows_count):
76 | diagonal = []
77 | for i in range(rows_count):
78 | for j in reversed(range(cols_count)):
79 | if j - i == c:
80 | diagonal.append(matrix[i][j])
81 |
82 | word_occurances += get_word_occurences(word, ''.join(diagonal))
83 |
84 | return word_occurances
85 |
86 |
87 | def word_counter():
88 | word = input('Enter word: ')
89 | size = input('Enter matrix size (format: N M): ')
90 |
91 | n = int(size.split(' ')[0])
92 | m = int(size.split(' ')[1])
93 |
94 | if len(word) > min([n, m]):
95 | return 'Invalid number of rows or columns!'
96 |
97 | matrix = build_matrix(n, m)
98 |
99 | word_occurances = word_occurances_in_rows(matrix, word)
100 | word_occurances += word_occurances_in_cols(matrix, word)
101 | word_occurances += word_occurances_in_right_diagonals(matrix, word)
102 | word_occurances += word_occurances_in_left_diagonals(matrix, word)
103 |
104 | return word_occurances
105 |
106 |
107 | if __name__ == '__main__':
108 | print(word_counter())
109 |
--------------------------------------------------------------------------------
/week03/04.MusicLibrary/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 | The methods we want for our `Song` are:
21 |
22 | - `__str__` - should be able to turn the song into the following string: `"{artist} - {title} from {album} - {length}"`
23 | - Our `Song` should be hashabe! Implement `__eq__` and `__hash__`
24 | - `length(seconds=True)` should return the length in seconds.
25 | - `length(minutes=True)` should return the length in minutes (omit the seconds)
26 | - `length(hours=True)` should return the length in hours (omit minutes and seconds that does not add up to a full hour)
27 | - `length()` should return the string representation of the length
28 |
29 | ## Playlist class
30 |
31 | We are going to implement a collection for our songs.
32 |
33 | The playlist should be initialized with a name:
34 |
35 | ```python
36 | code_songs = Playlist(name="Code", repeat=True, shuffle=True)
37 | ```
38 |
39 | - `repeat` should be defaulted to `False`
40 | - `shuffle` should be defaulted to `False`
41 |
42 | The `Playlist` should behave like that:
43 |
44 | - `add_song(song)` and `remove_song(song)` are self explanatory.
45 | - `add_songs(songs)` should add a list of `songs`.
46 | - `total_length()` should return a string representation of tha total length of all songs in the playlist
47 | - `artists()` - returns a histogram of all artists in the playlist. For each artist, we must have the count of songs in the playlist.
48 | - `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!**
49 |
50 | ## Saving and Loading the playlist
51 |
52 | > JSON is a format which is really handy if we want to represent objects. It looks like Python dictionaties. You can check this [tutorial](https://github.com/HackBulgaria/Programming-101-Python-2020-Spring/blob/master/week03/json_tutorial.py) for working JSON files.
53 |
54 | We should be able to save and load a playlist to a JSON file. Use the [json](https://docs.python.org/3/library/json.html) module from python.
55 |
56 | - `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.
57 | - `load(path)` should return a new `Playlist` instance with all songs from the file.
58 |
59 | Example usage:
60 |
61 | ```python
62 | code = Playlist("For Code")
63 | # ... adding songs ...
64 |
65 | # Saves to For-Code.json
66 | code.save()
67 | ```
68 |
69 | Later on:
70 |
71 | ```python
72 | code = Playlist.load("For-Code.json")
73 | code.name == "For Code" # True
74 | ```
75 |
76 | Save the `*.json` files in a specified folder, for instance, called `playlist-data`. Make the `load` function look there too.
77 |
--------------------------------------------------------------------------------
/week03/02.CashDesk/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 | - If amount is negative number, raise an `ValueError` error.
24 | - If type of amount isn't `int`, raise an `TypeError` error.
25 |
26 | Here is an example usage:
27 |
28 | ```python
29 | from cashdesk import Bill
30 |
31 | a = Bill(10)
32 | b = Bill(5)
33 | c = Bill(10)
34 |
35 | int(a) # 10
36 | str(a) # "A 10$ bill"
37 | print(a) # A 10$ bill
38 |
39 | a == b # False
40 | a == c # True
41 |
42 | money_holder = {}
43 |
44 | money_holder[a] = 1 # We have one 10% bill
45 |
46 | if c in money_holder:
47 | money_holder[c] += 1
48 |
49 | print(money_holder) # { "A 10$ bill": 2 }
50 | ```
51 |
52 | ## The BatchBill class
53 |
54 | We are going to implement a class, which represents more than one bill. A `BatchBill`!
55 |
56 | The class takes a list of `Bills` as the single constructor argument.
57 |
58 | The class should have the following methods:
59 |
60 | - `__len__(self)` - returns the number of `Bills` in the batch
61 | - `total(self)` - returns the total amount of all `Bills` in the batch
62 |
63 | We should be able to iterate the `BatchBill` class with a for-loop.
64 |
65 | Here is an example:
66 |
67 | ```python
68 | from cashdesk import Bill, BillBatch
69 |
70 | values = [10, 20, 50, 100]
71 | bills = [Bill(value) for value in values]
72 |
73 | batch = BillBatch(bills)
74 |
75 | for bill in batch:
76 | print(bill)
77 |
78 | # A 10$ bill
79 | # A 20$ bill
80 | # A 50$ bill
81 | # A 100$ bill
82 | ```
83 |
84 | In order to do that, you need to implement the following method:
85 |
86 | ```python
87 | def __getitem__(self, index):
88 | pass
89 | ```
90 |
91 | ## The CashDesk classs
92 |
93 | Finally, implement a `CashDesk` class, which has the following methods:
94 |
95 | - `take_money(money)`, where `money` can be either `Bill` or `BatchBill` class
96 | - `total()` - returns the total amount of money currenly in the desk
97 | - `inspect()` - prints a table representation of the money - for each bill, how many copies of it we have.
98 |
99 | For example:
100 |
101 | ```python
102 | from cashdesk import Bill, BillBatch, CashDesk
103 |
104 | values = [10, 20, 50, 100, 100, 100]
105 | bills = [Bill(value) for value in values]
106 |
107 | batch = BillBatch(bills)
108 |
109 | desk = CashDesk()
110 |
111 | desk.take_money(batch)
112 | desk.take_money(Bill(10))
113 |
114 | print(desk.total()) # 390
115 | desk.inspect()
116 |
117 | # We have a total of 390$ in the desk
118 | # We have the following count of bills, sorted in ascending order:
119 | # 10$ bills - 2
120 | # 20$ bills - 1
121 | # 50$ bills - 1
122 | # 100$ bills - 3
123 |
124 | ```
125 |
--------------------------------------------------------------------------------
/week01/README.md:
--------------------------------------------------------------------------------
1 | # Week 01 - Introduction to course & Python
2 |
3 | - [Intro slides](https://slides.com/hackbulgaria/python101-9th-opening)
4 | - [Exceptions slides](https://slides.com/hackbulgaria/python101-9th-exceptions)
5 |
6 | ## Data Structures
7 |
8 | Here are the basic data structures, which will help you to solve our problems. Test them in the **REPL**.
9 |
10 | All of the information you need can be found in the [docs](https://docs.python.org/3.8/tutorial/datastructures.html#data-structures)!
11 |
12 | ### List
13 |
14 | It does not work like a traditional array. It is **heterogeneous**! This means it can store elements with different types in one list.
15 |
16 | ```python
17 | things = [1 , 2, 'Ivo', [8, 'Rado']]
18 | ```
19 |
20 | We can iterate:
21 |
22 | ```python
23 | for thing in things:
24 | print(thing)
25 | ```
26 |
27 | This outputs:
28 |
29 | ```
30 | 1
31 | 2
32 | Ivo
33 | [8, 'Rado']
34 | ```
35 |
36 | ### Dictionaries
37 |
38 | Dictionaries are also known as hash tables / associative arrays.
39 |
40 | They are used for key-value mapping.
41 |
42 | **That's one of the most used & powerful data structures in Python.**
43 |
44 | ```python
45 | youtube_views = {
46 | 'Gangnam Style': 2096709806,
47 | 'Baby': 1091538504,
48 | 'Waka Waka': 746709408,
49 | }
50 |
51 | print(youtube_views['Waka Waka']) # 746709408
52 | ```
53 |
54 | Values are added by assigning them to `keys`.
55 |
56 | ```python
57 | youtube_views['Wrecking Ball'] = 709604432
58 | ```
59 |
60 | If that `key` already exists, the value held by that key will be replaced.
61 |
62 | ```python
63 | youtube_views['Wrecking Ball'] = 85
64 |
65 | print(youtube_views)
66 | # {'Wrecking Ball': 85, 'Gangnam Style': 2096709806, 'Waka Waka': 746709408, 'Baby': 1091538504}
67 | ```
68 |
69 | ### Set
70 |
71 | A set is an unordered collection with no duplicate elements.
72 |
73 | Basic usage includes membership testing and eliminating duplicate entries.
74 |
75 | Set objects also support mathematical operations like `union`, `intersection`, `difference`, and `symmetric difference`.
76 |
77 | ```python
78 | sports = set()
79 | sports.add('volleyball')
80 | sports.add('football')
81 | sports.add('basketball')
82 |
83 | print('volleyball' in sports) # True
84 | print('tenis' in sports) # False
85 | ```
86 |
87 | Sets are perfect for searching elements with `in` since they can find them in constant time, in comparison to `O(n)` for `lists`.
88 |
89 | ### Tuple
90 |
91 | Tuple are fixed-sized lists, which are **immutable**.
92 |
93 | ```python
94 | super_heroes = ('Hackman', 'Spiderman', 'Hulk')
95 |
96 | super_heroes[1] = 'Spindi'
97 | ```
98 |
99 | This will result in the following error:
100 |
101 | ```
102 | Traceback (most recent call last):
103 | File "", line 1, in
104 | TypeError: 'tuple' object does not support item assignment
105 | ```
106 |
107 | ### List Comprehension
108 |
109 | Python has a nice syntax for list / dict & set operations, called "comprehensions".
110 |
111 | For example, if we have the following cycle:
112 |
113 | ```python
114 | numbers = []
115 |
116 | for x in range(4, 10):
117 | numbers.append(x)
118 | ```
119 |
120 | We can rewrite this as follows:
121 |
122 | ```python
123 | numbers = [x for x in range(4, 10)]
124 | ```
125 |
126 | We can also make different operations with the element, we append in list:
127 |
128 | ```python
129 | squares = [x**2 for x in range(10)]
130 | print(squares)
131 | # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
132 | ```
133 |
--------------------------------------------------------------------------------
/week13/hackademy/hackademy/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for hackademy project.
3 |
4 | Generated by 'django-admin startproject' using Django 3.0.6.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.0/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/3.0/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
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/3.0/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'y@a!*t%=z_v#0-$fan7f70**d(1+1977uz60%76jf44pg#m'
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 | 'education.apps.EducationConfig',
35 |
36 | 'django.contrib.admin',
37 | 'django.contrib.auth',
38 | 'django.contrib.contenttypes',
39 | 'django.contrib.sessions',
40 | 'django.contrib.messages',
41 | 'django.contrib.staticfiles',
42 | ]
43 |
44 | MIDDLEWARE = [
45 | 'django.middleware.security.SecurityMiddleware',
46 | 'django.contrib.sessions.middleware.SessionMiddleware',
47 | 'django.middleware.common.CommonMiddleware',
48 | 'django.middleware.csrf.CsrfViewMiddleware',
49 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
50 | 'django.contrib.messages.middleware.MessageMiddleware',
51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
52 | ]
53 |
54 | ROOT_URLCONF = 'hackademy.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 = 'hackademy.wsgi.application'
73 |
74 |
75 | # Database
76 | # https://docs.djangoproject.com/en/3.0/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 | # Password validation
87 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
88 |
89 | AUTH_PASSWORD_VALIDATORS = [
90 | {
91 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
92 | },
93 | {
94 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
95 | },
96 | {
97 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
98 | },
99 | {
100 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
101 | },
102 | ]
103 |
104 |
105 | # Internationalization
106 | # https://docs.djangoproject.com/en/3.0/topics/i18n/
107 |
108 | LANGUAGE_CODE = 'en-us'
109 |
110 | TIME_ZONE = 'UTC'
111 |
112 | USE_I18N = True
113 |
114 | USE_L10N = True
115 |
116 | USE_TZ = True
117 |
118 |
119 | # Static files (CSS, JavaScript, Images)
120 | # https://docs.djangoproject.com/en/3.0/howto/static-files/
121 |
122 | STATIC_URL = '/static/'
123 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/tasks/03.Break-the-Cipher/README.md:
--------------------------------------------------------------------------------
1 | # Break the Cipher (bonus task)
2 |
3 |
4 | The `secret_keeper.py` file has 1 public function - `make_it_secret('publicmessage')`
5 | It accepts a public message and gives a secret one.
6 |
7 | So in order to decrypt some message you need to generate all combinations and encrypt them with this function and then compare the encrypted values. If they are equal you've successfully decrypted the message.
8 |
9 | Example:
10 |
11 | ```
12 | >>> make_it_secret('publicword')
13 | '0da537d4cad7299cbbd9905932463537'
14 |
15 | # Our encrypted message is not '0da537d4cad7299cbbd9905932463537'
16 |
17 | >>> make_it_secret('not_successful_attempt') == '0da537d4cad7299cbbd9905932463537'
18 | False
19 |
20 |
21 | >>> make_it_secret('publicword') == '0da537d4cad7299cbbd9905932463537'
22 | True
23 |
24 | # You decrypted the message
25 | ```
26 |
27 | ## Task
28 |
29 | Your task is simple - *decrypt the following message*:
30 |
31 | ```
32 | f0aa25cd443edb4cacf75bb24c5ad303 e641386ea86d75a37a79d7a8ca142103 e902668782c8ff35c741a60abb2ee751 b88cb6ce3803814cbe6b4f621210693c
33 | ef9fcdb53e4e10b12bfcd5e9e78135dc cae8d14edd025e72c59dbab6f378c95a 60b36cd3c72aa7c0ddbc69436d7eca96 8fc42c6ddf9966db3b09e84365034357
34 | 74459ca3cf85a81df90da95ff6e7a207 2cb9df9898e55fd0ad829dc202ddbd1c c13367945d5d4c91047b3b50234aa7ab be5d5d37542d75f93a87094459f76678
35 | caf98268abd13bb8ed384da0313e2dd6 8534a9e46af4ac17812152f8b21e3ffd 0cc175b9c0f1b6a831c399e269772661 23678db5efde9ab46bce8c23a6d91b50
36 | 1822266030c65a00f35ba3836cd61158 aab9e1de16f38176f86d7a92ba337a8d ebdacc3da0a7c89897c9433855c6cd1d 4d643b1bd384922ca968749b93b81db5
37 | a2a551a6458a8de22446cc76d639a9e9 1ced92c2ce3df0ae7a634022e7455469 cc5c0452c1308f585de22b4afc7723f7 6e57d6c47d23024e41f4a1aac73a3ea9
38 | a170c4cb1ae103745ec02e015cb86727 dd7536794b63bf90eccfd37f9b147d7f 92159805cf28ee78e13c41ebbbb1aeb4 639bae9ac6b3e1a84cebb7b403297b79
39 | 0cc175b9c0f1b6a831c399e269772661 efa35b82d8622819a31a8bc9208a076f 9942d8472ee432bb4179199f5beae701 9e925e9341b490bfd3b4c4ca3b0c1ef2
40 | cebd65946444e5cd3e861a2dbf69e221 030c76e4f0ed31202379e6df29def1d6 326577fbe6d73973bd67437829bf9301 bb90e57a80b6817ef63ea3b42f948a0a
41 | f9da66cdef69f6ffdc642a29a29eae93 18218139eec55d83cf82679934e5cd75 7f021a1415b86f2d013b2618fb31ae53 46c48bec0d282018b9d167eef7711b2c
42 | b2700999997b51194434005c3d95e619 9f31730024c592d8aa6c3e567e9895dd c67fd61e3b7d35f07e3ca92c8a84a5ab be5d5d37542d75f93a87094459f76678
43 | 99be1ee67a0137092d3d112c0620c552 9dfc8dce7280fd49fc6e7bf0436ed325 e7cbf67460e47dea4b13e81304850d5f
44 | ```
45 |
46 | ## Limitations:
47 |
48 | 1. The maximum length of each word is **5 symbols**.
49 | 2. Allowed characters
50 | - lowercased letters - `a-z`
51 | - uppercased letters - `A-Z`
52 | - digits - `0-9`
53 | - only following special charachters - `!`, `.`, `<`
54 |
55 |
56 | ## Requirements:
57 |
58 | 1. Create a database table `CipherBreaker` with the following columns:
59 |
60 | - `id` - primary key, not null, unique, auto increment
61 | - `message` - text (this is the public message)
62 | - `encrypted_message` - text (this is the public message **after** you call `make_it_secret`)
63 |
64 | 2. After you try a new string you should store it into the `CipherBreaker` table
65 |
66 | 3. When you try to decrypt a new value (like '0da537d4cad7299cbbd9905932463537') you should first check in `CipherBreaker` if you've already decrypted it ;). That's why you need **step 2**
67 |
68 | 4. If you stop the program while running(kill the process) and then run it again it should continue decrypting from the same place (and not from the beginning)
69 |
70 |
71 | **Here's the deal:** If you send the decrypted message with a working solution(see the requirements), you'll get a beer at the end of the course. Deal ?
72 |
73 | NOTE: The encryption will take some time. If you make a working solution (if it starts decrypting the message and meets the requirements), you'll get a beer anyway :)
74 |
75 | Additional NOTE: Due the Covid-19 pandemic, you'll get the beer as we meet at the end of the course (hopefully) :(
76 |
--------------------------------------------------------------------------------
/week07/01.ContextManagers/test_silence_exception.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from silence_exception import silence_exception, SilenceException
4 |
5 |
6 | class SilenceExceptionTests(unittest.TestCase):
7 | def test_silences_passed_exception(self):
8 | exception = None
9 |
10 | try:
11 | with silence_exception(ValueError):
12 | raise ValueError('Testing.')
13 | except Exception as exc:
14 | exception = exc
15 |
16 | self.assertIsNone(exception)
17 |
18 | def test_not_silences_different_exception_from_passed_one(self):
19 | with self.assertRaises(ValueError):
20 | with silence_exception(TypeError):
21 | raise ValueError('Testing.')
22 |
23 | def test_not_silences_passed_exception_outside_context_manager(self):
24 | with self.assertRaises(ValueError, msg='Testing outside with-block'):
25 | with silence_exception(ValueError):
26 | raise ValueError('Testing inside with-block')
27 |
28 | raise ValueError('Testing outside with-block')
29 |
30 | def test_silences_passed_exception_with_correct_message(self):
31 | exception = None
32 | exc_message = 'Testing with msg argument.'
33 |
34 | try:
35 | with silence_exception(ValueError, msg=exc_message):
36 | raise ValueError(exc_message)
37 | except Exception as exc:
38 | exception = exc
39 |
40 | self.assertIsNone(exception)
41 |
42 | def test_not_silences_passed_exception_with_different_message(self):
43 | exc_message = 'Testing with msg argument.'
44 |
45 | with self.assertRaises(ValueError):
46 | with silence_exception(ValueError, msg=exc_message):
47 | raise ValueError(f'{exc_message} - different.')
48 |
49 | def test_not_silences_different_exception_with_same_message(self):
50 | exc_message = 'Testing with msg argument.'
51 |
52 | with self.assertRaises(TypeError):
53 | with silence_exception(ValueError, msg=exc_message):
54 | raise TypeError(exc_message)
55 |
56 |
57 | class SilenceExceptionClassTests(unittest.TestCase):
58 | def test_silences_passed_exception(self):
59 | exception = None
60 |
61 | try:
62 | with SilenceException(ValueError):
63 | raise ValueError('Testing.')
64 | except Exception as exc:
65 | exception = exc
66 |
67 | self.assertIsNone(exception)
68 |
69 | def test_not_silences_different_exception_from_passed_one(self):
70 | with self.assertRaises(ValueError):
71 | with SilenceException(TypeError):
72 | raise ValueError('Testing.')
73 |
74 | def test_not_silences_passed_exception_outside_context_manager(self):
75 | with self.assertRaises(ValueError, msg='Testing outside with-block'):
76 | with SilenceException(ValueError):
77 | raise ValueError('Testing inside with-block')
78 |
79 | raise ValueError('Testing outside with-block')
80 |
81 | def test_silences_passed_exception_with_correct_message(self):
82 | exception = None
83 | exc_message = 'Testing with msg argument.'
84 |
85 | try:
86 | with SilenceException(ValueError, msg=exc_message):
87 | raise ValueError(exc_message)
88 | except Exception as exc:
89 | exception = exc
90 |
91 | self.assertIsNone(exception)
92 |
93 | def test_not_silences_passed_exception_with_different_message(self):
94 | exc_message = 'Testing with msg argument.'
95 |
96 | with self.assertRaises(ValueError):
97 | with SilenceException(ValueError, msg=exc_message):
98 | raise ValueError(f'{exc_message} - different.')
99 |
100 | def test_not_silences_different_exception_with_same_message(self):
101 | exc_message = 'Testing with msg argument.'
102 |
103 | with self.assertRaises(TypeError):
104 | with SilenceException(ValueError, msg=exc_message):
105 | raise TypeError(exc_message)
106 |
107 |
108 | if __name__ == '__main__':
109 | unittest.main()
110 |
--------------------------------------------------------------------------------
/week07/01.ContextManagers/test_measure_performance.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | import time
4 |
5 | from measure_performance import MeasurePerformance
6 |
7 |
8 | class MeasurePerformanceTests(unittest.TestCase):
9 | def setUp(self):
10 | self.filename = 'test_measure_performance.txt'
11 |
12 | def test_get_exceeded_time_returns_correct_value(self):
13 | meter = MeasurePerformance(self.filename)
14 |
15 | meter.start = time.time() - 1
16 |
17 | self.assertEqual(meter.get_exceeded_time(), 1)
18 |
19 | def test_benchmark_measures_correct_time_from_init_to_first_call(self):
20 | with MeasurePerformance(self.filename) as meter:
21 | time.sleep(1)
22 |
23 | self.assertEqual(meter.get_exceeded_time(), 1)
24 |
25 | def test_benchmark_appends_benchmark_using_passed_message(self):
26 | with MeasurePerformance(self.filename) as meter:
27 | time.sleep(1)
28 |
29 | meter.benchmark('test')
30 |
31 | expected = ['test: 1s']
32 |
33 | self.assertEqual(meter._benchmarks, expected)
34 |
35 | def test_benchmark_appends_benchmark_with_no_message_using_benchmarks_count(self):
36 | with MeasurePerformance(self.filename) as meter:
37 | time.sleep(1)
38 |
39 | meter.benchmark('test')
40 |
41 | time.sleep(1)
42 | meter.benchmark()
43 |
44 | expected = ['test: 1s', 'Benchmark No.2: 2s']
45 |
46 | self.assertEqual(meter._benchmarks, expected)
47 |
48 | def test_benchmark_does_not_restart_the_start_of_measure_performance_if_not_passed(self):
49 | with MeasurePerformance(self.filename) as meter:
50 | original_start = meter.original_start
51 |
52 | time.sleep(1)
53 | meter.benchmark('test')
54 |
55 | self.assertEqual(meter.original_start, original_start)
56 |
57 | def test_benchmark_restarts_the_start_of_the_measure_performance_if_passed(self):
58 | with MeasurePerformance(self.filename) as meter:
59 | original_start = meter.original_start
60 |
61 | time.sleep(1)
62 | meter.benchmark('test', restart=True)
63 |
64 | self.assertEqual(meter.original_start, original_start)
65 |
66 | def test_benchmark_preserves_correct_execution_times_if_not_restarted(self):
67 | msg = 'test'
68 |
69 | with MeasurePerformance(self.filename) as meter:
70 | time.sleep(1)
71 |
72 | meter.benchmark(msg)
73 |
74 | time.sleep(1)
75 | meter.benchmark(msg, restart=False)
76 |
77 | expected = ['test: 1s', 'test: 2s']
78 |
79 | self.assertEqual(meter._benchmarks, expected)
80 |
81 | def test_benchmark_preserves_correct_execution_times_if_restarted(self):
82 | msg = 'test'
83 |
84 | with MeasurePerformance(self.filename) as meter:
85 | time.sleep(1)
86 |
87 | meter.benchmark(msg, restart=True)
88 |
89 | time.sleep(1)
90 | meter.benchmark(msg)
91 |
92 | expected = ['test: 1s', 'test: 1s']
93 |
94 | self.assertEqual(meter._benchmarks, expected)
95 |
96 | def test_context_manager_does_not_create_the_file_if_exception_is_raised(self):
97 | with self.assertRaises(ValueError):
98 | with MeasurePerformance(self.filename):
99 | raise ValueError('Testing')
100 |
101 | self.assertFalse(os.path.exists(self.filename))
102 |
103 | def test_context_manager_closes_the_file_if_there_is_no_exception(self):
104 | with MeasurePerformance(self.filename):
105 | x = 111
106 | x += 10
107 |
108 | self.assertTrue(os.path.exists(self.filename))
109 |
110 | # we can open only closed files
111 | test_file = open(self.filename, 'r')
112 | test_file.close()
113 |
114 | def test_context_manager_dumps_correct_info_into_file_when_closed(self):
115 | with MeasurePerformance(self.filename) as meter:
116 | time.sleep(1)
117 |
118 | meter.benchmark('test')
119 |
120 | time.sleep(1)
121 | meter.benchmark()
122 |
123 | expected = 'test: 1s\nBenchmark No.2: 2s\nFinished for: 2s'
124 |
125 | with open(self.filename, 'r') as test_file:
126 | self.assertEqual(expected, test_file.read())
127 |
128 | def tearDown(self):
129 | try:
130 | os.remove(self.filename)
131 | except FileNotFoundError:
132 | pass
133 |
134 |
135 | if __name__ == '__main__':
136 | unittest.main()
137 |
--------------------------------------------------------------------------------
/week09/01.SQL-Intro/movies.sql:
--------------------------------------------------------------------------------
1 | ----- Tables -----
2 |
3 | DROP TABLE IF EXISTS MOVIEEXEC;
4 | DROP TABLE IF EXISTS MOVIE;
5 | DROP TABLE IF EXISTS MOVIESTAR;
6 | DROP TABLE IF EXISTS STARSIN;
7 | DROP TABLE IF EXISTS STUDIO;
8 |
9 | CREATE TABLE MOVIEEXEC (
10 | CERT INTEGER NOT NULL PRIMARY KEY,
11 | NAME CHAR(30),
12 | ADDRESS VARCHAR(255),
13 | NETWORTH INTEGER
14 | );
15 |
16 | CREATE TABLE STUDIO (
17 | NAME CHAR(50) NOT NULL PRIMARY KEY,
18 | ADDRESS VARCHAR(255),
19 | PRESC INTEGER
20 | );
21 |
22 | CREATE TABLE MOVIE (
23 | TITLE VARCHAR(255) NOT NULL,
24 | YEAR INTEGER NOT NULL,
25 | LENGTH INTEGER,
26 | INCOLOR CHAR(1),
27 | STUDIONAME CHAR(50),
28 | PRODUCER INTEGER,
29 | PRIMARY KEY( TITLE, YEAR),
30 | FOREIGN KEY(PRODUCER) REFERENCES MOVIEEXEC(CERT),
31 | FOREIGN KEY(STUDIONAME) REFERENCES STUDIO(NAME)
32 | );
33 |
34 | CREATE TABLE MOVIESTAR (
35 | NAME CHAR(30) NOT NULL PRIMARY KEY,
36 | ADDRESS VARCHAR(255),
37 | GENDER CHAR(1),
38 | BIRTHDATE DATE
39 | );
40 |
41 | CREATE TABLE STARSIN (
42 | MOVIETITLE VARCHAR(255) NOT NULL,
43 | MOVIEYEAR INTEGER NOT NULL,
44 | STARNAME CHAR(30) NOT NULL,
45 | FOREIGN KEY(MOVIETITLE) REFERENCES MOVIE(TITLE),
46 | FOREIGN KEY(STARNAME) REFERENCES MOVIESTAR(NAME),
47 | PRIMARY KEY(MOVIETITLE, MOVIEYEAR, STARNAME)
48 | );
49 |
50 | ----- Constraints -----
51 | INSERT INTO STUDIO (NAME, ADDRESS, PRESC)
52 | VALUES ('Disney','USA, 234534 CF, World',4);
53 |
54 | INSERT INTO STUDIO (NAME, ADDRESS, PRESC)
55 | VALUES ('Fox','USA, 234576 CF, Fox Str',3);
56 |
57 | INSERT INTO STUDIO (NAME, ADDRESS, PRESC)
58 | VALUES ('MGM','USA, 127823 NY, 8th Str',1);
59 |
60 | INSERT INTO STUDIO (NAME, ADDRESS, PRESC)
61 | VALUES ('Paramount','USA, 986745 CF, Para Str',1);
62 |
63 | INSERT INTO STUDIO (NAME, ADDRESS, PRESC)
64 | VALUES ('USA Entertainm.','USA, 213243 CF, Uni Str',3);
65 |
66 | INSERT INTO STUDIO (NAME, ADDRESS, PRESC)
67 | VALUES ('Warner Bros','USA, 324235 NY, Bacon Str',2);
68 |
69 | INSERT INTO MOVIEEXEC (NAME, ADDRESS, CERT, NETWORTH)
70 | VALUES ('George Lucas', 'Oak Rd.', 555, 200000000);
71 |
72 | INSERT INTO MOVIEEXEC (NAME, ADDRESS, CERT, NETWORTH)
73 | VALUES ('Ted Turner', 'Turner Av.', 333, 125000000);
74 |
75 | INSERT INTO MOVIEEXEC (NAME, ADDRESS, CERT, NETWORTH)
76 | VALUES ('Stephen Spielberg', '123 ET road', 222, 100000000);
77 |
78 | INSERT INTO MOVIEEXEC (NAME, ADDRESS, CERT, NETWORTH)
79 | VALUES ('Merv Griffin', 'Riot Rd.', 199, 112000000);
80 |
81 | INSERT INTO MOVIEEXEC (NAME, ADDRESS, CERT, NETWORTH)
82 | VALUES ('Calvin Coolidge', 'Fast Lane', 123, 20000000);
83 |
84 | INSERT INTO MOVIESTAR
85 | VALUES ('Jane Fonda', 'Turner Av.', 'F', '1977-07-07');
86 |
87 | INSERT INTO MOVIESTAR
88 | VALUES ('Alec Baldwin', 'Baldwin Av.', 'M', '1977-07-06');
89 |
90 | INSERT INTO MOVIESTAR
91 | VALUES ('Kim Basinger', 'Baldwin Av.', 'F', '1979-07-05');
92 |
93 | INSERT INTO MOVIESTAR
94 | VALUES ('Harrison Ford', 'Prefect Rd.', 'M', '1955-05-05');
95 |
96 | INSERT INTO MOVIESTAR
97 | VALUES ('Debra Winger', 'A way', 'F', '1978-06-05');
98 |
99 | INSERT INTO MOVIESTAR
100 | VALUES ('Jack Nicholson', 'X path', 'M', '1949-05-05');
101 |
102 | INSERT INTO MOVIE
103 | VALUES ('Pretty Woman', 1990, 119, 'y', 'Disney', 199);
104 |
105 | INSERT INTO MOVIE
106 | VALUES ('The Man Who Wasn''t There', 2001, 116, 'N', 'USA Entertainm.',
107 | 555);
108 |
109 | INSERT INTO MOVIE
110 | VALUES ('Logan''s run', 1976, NULL, 'Y', 'Fox', 333);
111 |
112 | INSERT INTO MOVIE
113 | VALUES ('Star Wars', 1977, 124, 'Y', 'Fox', 555);
114 |
115 | INSERT INTO MOVIE
116 | VALUES ('Empire Strikes Back', 1980, 111, 'Y', 'Fox', 555);
117 |
118 | INSERT INTO MOVIE
119 | VALUES ('Star Trek', 1979, 132, 'Y', 'Paramount', 222);
120 |
121 | INSERT INTO MOVIE
122 | VALUES ('Star Trek: Nemesis', 2002, 116, 'Y', 'Paramount', 123);
123 |
124 | INSERT INTO MOVIE
125 | VALUES ('Terms of Endearment', 1983, 132, 'Y', 'MGM', 123);
126 |
127 | INSERT INTO MOVIE
128 | VALUES ('The Usual Suspects', 1995, 106, 'Y', 'MGM', 199);
129 |
130 | INSERT INTO MOVIE
131 | VALUES ('Gone With the Wind', 1938, 238, 'Y', 'MGM', 123);
132 |
133 | INSERT INTO STARSIN
134 | VALUES ('Star Wars', 1977, 'Kim Basinger');
135 |
136 | INSERT INTO STARSIN
137 | VALUES ('Star Wars', 1977, 'Alec Baldwin');
138 |
139 | INSERT INTO STARSIN
140 | VALUES ('Star Wars', 1977, 'Harrison Ford');
141 |
142 | INSERT INTO STARSIN
143 | VALUES ('Empire Strikes Back', 1980, 'Harrison Ford');
144 |
145 | INSERT INTO STARSIN
146 | VALUES ('The Usual Suspects', 1995, 'Jack Nicholson');
147 |
148 | INSERT INTO STARSIN
149 | VALUES ('Terms of Endearment', 1983, 'Jane Fonda');
150 |
151 | INSERT INTO STARSIN
152 | VALUES ('Terms of Endearment', 1983, 'Jack Nicholson');
153 |
--------------------------------------------------------------------------------
/week09/03.SQL-and-Python/tasks/02.Vehicle-Repair-Manager/README.md:
--------------------------------------------------------------------------------
1 | # Vehicle Repair Manager
2 |
3 | Your task is to create a Vehicle Management System.
4 | For this application you need to create `vehicle_management.db` and several tables.
5 |
6 | - BaseUser
7 | - Client
8 | - Mechanic
9 | - Vehicle
10 | - Service
11 | - Mechanic_services
12 | - Vehicle_repair
13 |
14 | Take a look at the database schema to understand the structure and recreate it locally on your machines.
15 |
16 | ### Example GUI for Customer
17 |
18 | ```python
19 | $ python vehicle_management.py
20 | Hello!
21 | Provide user name:
22 | >>> Roza
23 | Unknown user!
24 | Would you like to create new user?
25 | >>> yes
26 | Are you a client or Mechanic?
27 | >>> Client
28 | Provide user_name:
29 | >>> Roza
30 | Provide phone_number:
31 | >>> 0888 88 88 88
32 | Provide email:
33 | >>> roza@roza.com
34 | Provide address:
35 | >>> Sofia, Hack Bulgaria
36 |
37 | Thank you, Roza!
38 | Welcome to Vehicle Services!
39 | Next time you try to login, provide your user_name!
40 |
41 | You can choose from the following commands:
42 | list_all_free_hours
43 | list_free_hours
44 | save_repair_hour
45 | update_repair_hour
46 | delete_repair_hour
47 | add_vehicle
48 | update_vehicle
49 | delete_vehicle
50 | exit
51 | ```
52 |
53 | ### Example GUI for Customer
54 |
55 | ```python
56 | $ python vehicle_management.py
57 | Hello!
58 | Provide user name:
59 | >>> Roza
60 |
61 | Hello, Roza!
62 | You can choose from the following commands:
63 | list_all_free_hours
64 | list_free_hours
65 | save_repair_hour
66 | update_repair_hour
67 | delete_repair_hour
68 | add_vehicle
69 | update_vehicle
70 | delete_vehicle
71 | exit
72 |
73 | command> :list_all_free_hours
74 | +----+-------------+-------+
75 | | id | date | start_hour |
76 | +----+---------------------+
77 | | 1 | 24-05-2018 | 10:00 |
78 | | 2 | 24-05-2018 | 10:40 |
79 | | 3 | 25-05-2018 | 16:00 |
80 | | 4 | 27-05-2018 | 11:20 |
81 | +----+-------------+-------+
82 |
83 | Hello, Roza!
84 | You can choose from the following commands:
85 | list_all_free_hours
86 | list_free_hours
87 | save_repair_hour
88 | update_repair_hour
89 | delete_repair_hour
90 | list_personal_vehicles
91 | add_vehicle
92 | update_vehicle
93 | delete_vehicle
94 | exit
95 |
96 | command> :add_vehicle
97 | Vehicle category:
98 | >>> Automobile
99 | Vehicle make:
100 | >>> Audi
101 | Vehicle model:
102 | >>> A3
103 | Vehicle register number:
104 | >>> X 8888 XX
105 | Vehicle gear box:
106 | >>> Manual
107 |
108 | Thank you! You added new personal vehicle!
109 |
110 | Hello, Roza!
111 | You can choose from the following commands:
112 | list_all_free_hours
113 | list_free_hours
114 | save_repair_hour
115 | update_repair_hour
116 | delete_repair_hour
117 | list_personal_vehicles
118 | add_vehicle
119 | update_vehicle
120 | delete_vehicle
121 | exit
122 |
123 | command> :save_repair_hour 1
124 | Choose Vehicle to repair:
125 | +----+------------------------------------+
126 | | id | Vehicle |
127 | +----+------------------------------------+
128 | | 1 | Audi A3 with RegNumber: X 8888 XX |
129 | +----+------------------------------------+
130 | >>> 1
131 | Choose Service:
132 | +----+------------------------------------+
133 | | id | Service |
134 | +----+------------------------------------+
135 | | 1 | Oil Change |
136 | | 2 | Tire Change |
137 | +----+------------------------------------+
138 | >>> 2
139 |
140 | Thank you! You saved an hour on 24-05-2018 at 10:00 for Tire Change!
141 | Vehicle: Audi A3 with RegNumber: X 8888 XX
142 | ```
143 |
144 | ### Example GUI for Mechanic
145 |
146 | ```python
147 | $ python vehicle_management.py
148 | Hello!
149 | Provide user name:
150 | >>> Mechanic Panda
151 |
152 | Hello, Mechanic Panda!
153 | You can choose from the following commands:
154 | list_all_free_hours
155 | list_free_hours
156 | list_all_busy_hours
157 | list_busy_hours
158 | add_new_repair_hour
159 | add_new_service
160 | update_repair_hour
161 | exit
162 |
163 | command> :update_repair_hour 4
164 |
165 | On 23-05-2018 at 10:30:
166 | Client: Carol Danvers
167 | Vehicle: Audi A6 Saloon
168 | Current Bill: 20$
169 | Choose one of the following:
170 | 1 - change start hour
171 | 2 - change bill
172 | 3 - return to main menu
173 |
174 | >>> 2
175 | Current Bill is: 20$
176 | New Bill:
177 | >>> 27.5$
178 |
179 | On 23-05-2018 at 10:30:
180 | Client: Carol Danvers
181 | Vehicle: Audi A6 Saloon
182 | Current Bill: 27.5$
183 | Choose one of the following:
184 | 1 - change start hour
185 | 2 - change bill
186 | 3 - return to main menu
187 |
188 | >>> 3
189 |
190 | Hello, Mechanic Panda!
191 | You can choose from the following commands:
192 | list_all_free_hours
193 | list_free_hours
194 | list_all_busy_hours
195 | list_busy_hours
196 | add_new_repair_hour
197 | add_new_service
198 | update_repair_hour
199 | exit
200 |
201 | command> :add_new_repair_hour
202 | Repair hour date:
203 | >>> 26-05-2018
204 | Start Hour:
205 | >>> 13:20
206 |
207 | Your created new repair_hour!
208 | +----+-------------+-------+
209 | | id | date | start_hour |
210 | +----+---------------------+
211 | | 1 | 24-05-2018 | 10:00 |
212 | | 2 | 24-05-2018 | 10:40 |
213 | | 3 | 25-05-2018 | 16:00 |
214 | | 5 | 26-05-2018 | 13:20 |
215 | | 4 | 27-05-2018 | 11:20 |
216 | +----+-------------+-------+
217 |
218 | Hello, Mechanic Panda!
219 | You can choose from the following commands:
220 | list_all_free_hours
221 | list_free_hours
222 | list_all_busy_hours
223 | list_busy_hours
224 | add_new_repair_hour
225 | add_new_service
226 | update_repair_hour
227 | exit
228 |
229 | command> :add_new_service
230 | Provide New service name:
231 | >>> Oil Change
232 | ```
233 |
--------------------------------------------------------------------------------
/week12/Crawler/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 | ## What serves that website?
16 |
17 | 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.
18 |
19 | **There is a `Server` header which holds a string with the information.**
20 |
21 | For example:
22 |
23 | ```
24 | $ curl -I https://hackbulgaria.com
25 | HTTP/2 200
26 | content-type: text/html
27 | content-length: 21670
28 | date: Mon, 18 May 2020 13:40:00 GMT
29 | last-modified: Fri, 14 Feb 2020 09:29:22 GMT
30 | etag: "cd86f2b4b40d1e458e1fa8667d855100"
31 | accept-ranges: bytes
32 | server: AmazonS3
33 | x-cache: Miss from cloudfront
34 | via: 1.1 b2721dd2c0bbd4046fd80941e54642eb.cloudfront.net (CloudFront)
35 | x-amz-cf-pop: BUD50-C1
36 | x-amz-cf-id: 6cAXWK0JG8bbBQziSVh89uVAKrwOi1Wujw4kKpIHVZpbM-5Wd0L7iA==
37 | ```
38 |
39 | As you can see, the `Server: AmazonS3` header gives us the information that `hackbulgaria.com` is hosted on S3.
40 |
41 | That's great.
42 |
43 | How about Python?
44 |
45 | ```python
46 | >>> import requests
47 | >>> r = requests.get("https://hackbulgaria.com")
48 | >>> print(r.headers["Server"])
49 | AmazonS3
50 | ```
51 |
52 | Easy as a pie!
53 |
54 | ## Crawling & Information
55 |
56 | 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.
57 |
58 | We can a big list, around 10 000 pages, from here - http://register.start.bg/
59 |
60 | The catch is - there is no API. We will have to crawl our information. **Find all links in the HTML of the page.**
61 |
62 | 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.
63 |
64 | If you plan on using **regular expressions**, be sure to check
65 |
66 | 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.
67 |
68 | ## Histogram of the results
69 |
70 | While you crawl all the 10 000 pages, create a histogram of the different web servers that run them.
71 |
72 | When you are done, save the result in a database.
73 |
74 | You are going to have results which look like that:
75 |
76 | ```
77 | ...
78 | Apache/2.4.7 (Unix) PHP/5.4.25: 1
79 | Apache/2.4.7 (Win64) OpenSSL/1.0.1g mod_fcgid/2.3.9: 1
80 | Apache/2.4.9 (Unix) OpenSSL/1.0.1e-fips mod_bwlimited/1.4: 1
81 | Apache/2: 7
82 | Apache: 384
83 | Apache-Coyote/1.1: 1
84 | Apache mod_fcgid/2.3.7 mod_auth_pgsql/2.0.3: 2
85 | Apashi 1.1 mod_fcgid/2.3.5: 1
86 | cloudflare-nginx: 5
87 | Host.bg redirect server based on Apache/1.3.31 (Unix): 2
88 | IBM_HTTP_Server: 2
89 | lighttpd/1.4.22: 1
90 | Microsoft-IIS/5.0: 5
91 | Microsoft-IIS/6.0: 15
92 | Microsoft-IIS/7.0: 3
93 | Microsoft-IIS/7.5: 23
94 | Microsoft-IIS/8.0: 2
95 | Microsoft-IIS/8.5: 3
96 | nginx/0.7.62: 1
97 | nginx/0.7.65: 1
98 | nginx/0.7.67: 1
99 | nginx/0.8.53: 1
100 | nginx/1.0.10: 3
101 | ...
102 | ```
103 |
104 | ## Consolidate Results & Plot Them
105 |
106 | 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.
107 |
108 | Do so and plot the resulted histogram in a histogram chart that looks something like that:
109 |
110 | 
111 |
112 |
113 | You can use [matplotlib](http://matplotlib.org/) for ploting.
114 |
115 | ## Extra Credit
116 |
117 | Think about the design of the problem. Can you split the problem in smaller parts / systems?
118 |
119 | After you are done, make a simple **Flask** HTTP serves, that handles one URL:
120 |
121 | ```
122 | GET /results
123 | ```
124 |
125 | This should return the resulting histogram, so far, in a JSON format.
126 |
127 | After this, you can hook the plotter to this server.
128 |
129 | ## Hints
130 |
131 | It is a good idea to create a [spike solution](http://www.extremeprogramming.org/rules/spike.html) first. This will give you more understanding about the problem domain & the libraries that we are going to use.
132 |
133 | ## SQLAlchemy
134 |
135 | Use it. It will make your life easier.
136 |
137 | ### User Agent
138 |
139 | If you send request from python's `requests` it looks like that:
140 |
141 | ```
142 | GET / HTTP/1.1
143 | Host: localhost:8000
144 | User-Agent: python-requests/2.3.0 CPython/3.4.2 Linux/3.16.0-34-generic
145 | Accept-Encoding: gzip, deflate
146 | Accept: */*
147 | ```
148 |
149 | As you can see, the `User-Agent` header says python-requests. This is not good.
150 |
151 | If we want to send a request that looks like google chrome, we can check here - http://www.useragentstring.com/pages/Chrome/
152 |
153 | In order to do this, we will use the `headers=` keyword for `requests.get`
154 |
155 | ```python
156 | our_headers = {
157 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"
158 | }
159 |
160 | requests.get("http://localhost:8000", headers=our_headers)
161 | ```
162 |
163 | This will look like a Google Chrome request, which is good when we are crawling.
164 |
165 | ### HEAD instead of GET
166 |
167 | When we make a `GET` request, we download the entire HTML. This can be slow, especially on slow internet connections.
168 |
169 | There is another HTTP verb that we can use. It is called `HEAD`.
170 |
171 | This request asks only for the server headers and not for the content - exactly what we need.
172 |
173 | Read the [requests documentation](http://docs.python-requests.org/en/latest/user/quickstart/) to see how to make a `HEAD` request.
174 |
175 | ### Ploting from dictionary
176 |
177 | When you are ready and you have a dictionary with results, you can plot the histogram like that: (it is ugly!)
178 |
179 | ```python
180 | import matplotlib.pyplot as plt
181 |
182 | h = { .. dict with results }
183 | keys = list(h.keys())
184 | values = list(h.values())
185 |
186 | X = list(range(len(keys)))
187 |
188 | plt.bar(X, list(h.values()), align="center")
189 | plt.xticks(X, keys)
190 |
191 | plt.title(".bg servers")
192 | plt.xlabel("Server")
193 | plt.ylabel("Count")
194 |
195 | plt.savefig("histogram.png")
196 | ```
197 |
--------------------------------------------------------------------------------
/week10/CinemaReservation/README.md:
--------------------------------------------------------------------------------
1 | # Hack Bulgaria Cinema Reservation System
2 |
3 | We are going to the cinema! But the reservation systems are down and the cinema officials don't let people enter without reservations. So we offered to make them a new reservation system that will allow us to go and watch the newest movies.
4 |
5 | ## Problem 0 - The database
6 |
7 | No complex stuff here. Just a few simple tables:
8 |
9 | ### Movies
10 |
11 | | id | name | rating |
12 | | --- | :------------------------------ | :----: |
13 | | 1 | The Hunger Games: Catching Fire | 7.9 |
14 | | 2 | Wreck-It Ralph | 7.8 |
15 | | 3 | Her | 8.3 |
16 |
17 | ### Projections
18 |
19 | | id | movie_id | type | date | time |
20 | | --- | -------- | :--: | :--------: | :---: |
21 | | 1 | 1 | 3D | 2020-04-01 | 19:10 |
22 | | 2 | 1 | 2D | 2020-04-01 | 19:00 |
23 | | 3 | 1 | 4DX | 2020-04-02 | 21:00 |
24 | | 4 | 3 | 2D | 2020-04-05 | 20:20 |
25 | | 5 | 2 | 3D | 2020-04-02 | 22:00 |
26 | | 6 | 2 | 2D | 2020-04-02 | 19:30 |
27 |
28 | ### Users
29 |
30 | | id | username | password |
31 | | --- | ----------------- | :----------: |
32 | | 1 | Martin Angelov | **\*\*\*\*** |
33 | | 2 | Ivo Donchev | **\*\*\*\*** |
34 | | 3 | Radoslav Georgiev | **\*\*\*\*** |
35 | | 4 | Rositza Zlateva | **\*\*\*\*** |
36 |
37 | Passwords should be with length **at least 8 symbols, 1 capital letter and a special symbol**. Also, don't forget to **hash** the passwords!
38 |
39 | ### Reservations
40 |
41 | | id | user_id | projection_id | row | col |
42 | | --- | ------- | ------------- | :-: | :-: |
43 | | 1 | 3 | 1 | 2 | 1 |
44 | | 2 | 3 | 1 | 3 | 5 |
45 | | 3 | 3 | 1 | 7 | 8 |
46 | | 4 | 2 | 3 | 1 | 1 |
47 | | 5 | 2 | 3 | 1 | 2 |
48 | | 6 | 5 | 5 | 2 | 3 |
49 | | 7 | 6 | 5 | 2 | 4 |
50 |
51 | ### Things to note
52 |
53 | - For each projection we assume the hall to be a 10x10 matrix.
54 | - All data presented here is just an example. If you want, you can make up your own data. For example, implement a ticketing system after you have the reservations in place.
55 |
56 | ## Problem 1 - The CLI (Command-Line Interface)
57 |
58 | Our cinema reservation system will be very simple. We will use the console for user interactions:
59 |
60 | - `show movies` - print all movies ORDERed BY rating
61 | - `show movie projections []` - print all projections of a given movie for the given date (date is optional).
62 |
63 | 1. ORDER the results BY date
64 | 2. For each projection, show the total number of spots available.
65 |
66 | - `make reservation` - It's showtime!
67 |
68 | 1. Check if user is logged
69 | 2. If not, first action is to log the user into the system.
70 | 3. If already logged, the user can make a reservation
71 | 4. Make him choose how many seats he want to reserve
72 | 5. Call `show movies` and make the user choose a movie by id
73 | 6. Call `show movie projections` for the chosen `` and make the user choose a projection
74 |
75 | - _If the available spots for a projection are less than the number of reservations needed, print an appropriate message and stay at step 6_;
76 |
77 | 7. Show all available seats
78 | 8. Make the user choose his seats - validate the input
79 | 9. Format the reservation appropriately and show it to the user
80 | 10. On `finalize`, save all the info and wish a happy cinema!
81 | 11. **At each step, allow for `cancel` option.**
82 |
83 | - On `cancel reservation ` - disintegrate given person's reservation (**NOTE**: reservations cannot be so easily removed)
84 | - On `exit` - close Pandora's Box before it's too late.
85 | - On `help` - show a list of valid options
86 |
87 | ### Things to note
88 |
89 | - Reuse code and write tests
90 | - Try not to build everything in one place.
91 | - Make use of the following techniques : **OOP, TDD, SQL, MVC**.
92 |
93 | ## Problem 2 - Decorators
94 |
95 | We want you to implement a decorator for atomic transactions on each of the query you execute with the cinema application!
96 |
97 | ```python
98 | @atomic
99 | def show_movies():
100 | pass
101 | ```
102 |
103 | We want to make sure the user is logged in in order to use the system.
104 |
105 | ```python
106 | @login_required
107 | def show_movies():
108 | pass
109 | ```
110 |
111 | Log each reservation in the system in a file.
112 |
113 | ```python
114 |
115 | @log_info
116 | def finalize():
117 | pass
118 | ```
119 |
120 | ## Examples
121 |
122 | ### Show movies
123 |
124 | ```
125 | > show movies
126 | Current movies:
127 | [1] - The Hunger Games: Catching Fire (7.9)
128 | [2] - Wreck-It Ralph (7.8)
129 | [3] - Her (8.3)
130 | ```
131 |
132 | ### Show movie projections
133 |
134 | ```
135 | > show movie projections 2
136 | Projections for movie 'Wreck-It Ralph':
137 | [5] - 2020-04-02 19:30 (2D)
138 | [6] - 2020-04-02 22:00 (3D)
139 | > show movie projections 1 2020-04-01
140 | Projections for movie 'The Hunger Games: Catching Fire' on date 2020-04-01:
141 | [1] - 19:00 (3D)
142 | [2] - 19:10 (2D)
143 | ```
144 |
145 | ### Make a reservation
146 |
147 | ```
148 | > make reservation
149 | You need to a user in the system to make reservations!
150 | Username: Rositsa Zlateva
151 | Password:
152 | Hello, Rositsa Zlateva
153 | ............
154 | ```
155 |
156 | ### Make a reservation
157 |
158 | ```
159 | > make reservation
160 | Hello, Rositsa Zlateva
161 | Step 1 (User): Choose number of tickets> 2
162 | Current movies:
163 | [1] - The Hunger Games: Catching Fire (7.9)
164 | [2] - Wreck-It Ralph (7.8)
165 | [3] - Her (8.3)
166 | Step 2 (Movie): Choose a movie> 2
167 | Projections for movie 'Wreck-It Ralph':
168 | [5] - 2020-04-02 19:30 (2D) - 98 spots available
169 | [6] - 2020-04-02 22:00 (3D) - 100 spots availabe
170 | Step 3 (Projection): Choose a projection> 5
171 | Available seats (marked with a dot):
172 | 1 2 3 4 5 6 7 8 9 10
173 | 1 . . . . . . . . . .
174 | 2 . . X X . . . . . .
175 | 3 . . . . . . . . . .
176 | 4 . . . . . . . . . .
177 | 5 . . . . . . . . . .
178 | 6 . . . . . . . . . .
179 | 7 . . . . . . . . . .
180 | 8 . . . . . . . . . .
181 | 9 . . . . . . . . . .
182 | 10 . . . . . . . . . .
183 | Step 4 (Seats): Choose seat 1> (2,3)
184 | This seat is already taken!
185 | Step 4 (Seats): Choose seat 1> (15, 16)
186 | Lol...NO!
187 | Step 4 (Seats): Choose seat 1> (7,8)
188 | Step 4 (Seats): Choose seat 2> (7,7)
189 | This is your reservation:
190 | Movie: Wreck-It Ralph (7.8)
191 | Date and Time: 2020-04-02 19:30 (2D)
192 | Seats: (7,7), (7.8)
193 | Step 5 (Confirm - type 'finalize') > finalize
194 | Thanks.
195 | ```
196 |
197 | ## DISCLAIMER
198 |
199 | Customize it! Think of some add-ons and extras and add it to the task!
200 |
--------------------------------------------------------------------------------
/week01/02.DiveIntoPython/README.md:
--------------------------------------------------------------------------------
1 | # Python Problems
2 |
3 | - [Gas Stations](#gas-stations)
4 | - [Signature ](#signature-)
5 | - [Test examples](#test-examples)
6 | - [Is Number Balanced](#is-number-balanced)
7 | - [Signature ](#signature--1)
8 | - [Test examples](#test-examples-1)
9 | - [Increasing and Decreasing Sequences](#increasing-and-decreasing-dequences)
10 | - [Signature ](#signature--2)
11 | - [Test examples](#test-examples-2)
12 | - [Largest Palindrome](#largest-palindrome)
13 | - [Signature ](#signature--3)
14 | - [Test examples](#test-examples-3)
15 | - [Sum all numbers in a given string](#sum-all-numbers-in-a-given-string)
16 | - [Signature ](#signature--4)
17 | - [Test examples](#test-examples-4)
18 | - [Birthday Ranges](#birthday-ranges)
19 | - [Signature ](#signature--5)
20 | - [Test examples](#test-examples-5)
21 | - [100 SMS](#100-sms)
22 | - [Signature ](#signature--6)
23 | - [Test examples](#test-examples-6)
24 |
25 | In a file called `week2_solutions.py`, solve the following problems:
26 |
27 | ## Gas Stations
28 |
29 | ---
30 |
31 | We are implementing a smart GPS software.
32 |
33 | - We are taking a long trip from Sofia to Bourgas and we know the distance between the two cities. It is a positive integer and we mark it as `distance`.
34 |
35 | - We know how much our car can ride with a full tank of gas. It is a positive integer in kilometers. We mark it as `tank_size`.
36 |
37 | - We have a list of gas stations. We know the distance between Sofia and the current gas station. `stations = [50, 80, 110, 180, 220, 290]` Notice, the list is sorted!
38 |
39 | By using this information we will implement a function that returns the shortest `list` of gas stations that we have to visit in order to travel from Sofia to Bourgas. We allways start with a full tank_size!
40 |
41 | ### Signature
42 |
43 | ```python
44 | def gas_stations(distance, tank_size, stations):
45 | pass
46 | ```
47 |
48 | ### Test Example
49 |
50 | ```python
51 | >>> gas_stations(320, 90, [50, 80, 140, 180, 220, 290])
52 | [80, 140, 220, 290]
53 | >>> gas_stations(390, 80, [70, 90, 140, 210, 240, 280, 350])
54 | [70, 140, 210, 280, 350]
55 | ```
56 |
57 | ## Is Number Balanced
58 |
59 | A number is called balanced, if we take the middle of it and the sums of the left and right parts are equal.
60 |
61 | For example, the number `1238033` is balanced, because it's left part is `123` and right part is `033`.
62 |
63 | We have: `1 + 2 + 3 = 0 + 3 + 3 = 6`.
64 |
65 | - A number with only one digit is always balanced!
66 |
67 | ### Signature
68 |
69 | ```python
70 | def is_number_balanced(number):
71 | pass
72 | ```
73 |
74 | ### Test Examples
75 |
76 | ```python
77 | >>> is_number_balanced(9)
78 | True
79 | >>> is_number_balanced(4518)
80 | True
81 | >>> is_number_balanced(28471)
82 | False
83 | >>> is_number_balanced(1238033)
84 | True
85 | ```
86 |
87 | ## Increasing and Decreasing Sequences
88 |
89 | Implement a function, called `increasing_or_decreasing(seq)` where the `seq` parameter is a `list` of integers.
90 |
91 | ### Signature
92 |
93 | ```python
94 | def increasing_or_decreasing(seq):
95 | pass
96 | ```
97 |
98 | The function should return `Up!`, if the given sequence is monotonously increasing.
99 | If monotonously decreasing return `Down!` .
100 | If both of the conditions are not satisfied, then return `False`.
101 |
102 | And before you skip this problem, because of the math terminology, let me explain:
103 |
104 | **A sequence is monotonously increasing if for every two elements `a` and `b`, that are next to each other (`a` is before `b`), we have `a` < `b`.**
105 |
106 | For example, `[1,2,3,4,5]` is monotonously increasing, but `[1,2,3,4,5,1]` is not.
107 |
108 | ### Test Examples
109 |
110 | ```python
111 | >>> increasing_or_decreasing([1,2,3,4,5])
112 | Up!
113 | >>> increasing_or_decreasing([5,6,-10])
114 | False
115 | >>> increasing_or_decreasing([1,1,1,1])
116 | False
117 | >>> increasing_or_decreasing([9,8,7,6])
118 | Down!
119 | ```
120 |
121 | ## Largest Palindrome
122 |
123 | Implement a function `get_largest_palindrome`, which returns the largest palindrome smaller than `n`. Given number `n` can also be a palindrome.
124 |
125 | ### Signature
126 |
127 | ```python
128 | def get_largest_palindrome(n):
129 | pass
130 | ```
131 |
132 | ### Test Examples
133 |
134 | ```python
135 | >>> get_largest_palindrome(99)
136 | 88
137 | >>> get_largest_palindrome(252)
138 | 242
139 | >>> get_largest_palindrome(994687)
140 | 994499
141 | >>> get_largest_palindrome(754649)
142 | 754457
143 | ```
144 |
145 | ## Sum all numbers in a given string
146 |
147 | You are given a string, where there can be numbers. Return the sum of all numbers in that string(**not digits, numbers**).
148 |
149 | ### Signature
150 |
151 | ```python
152 | def sum_of_numbers(input_string):
153 | pass
154 | ```
155 |
156 | ### Test Examples
157 |
158 | ```python
159 | >>> sum_of_numbers("ab125cd3")
160 | 128
161 | >>> sum_of_numbers("ab12")
162 | 12
163 | >>> sum_of_numbers("ab")
164 | 0
165 | >>> sum_of_numbers("1101")
166 | 1101
167 | >>> sum_of_numbers("1111O")
168 | 1111
169 | >>> sum_of_numbers("1abc33xyz22")
170 | 56
171 | >>> sum_of_numbers("0hfabnek")
172 | 0
173 | ```
174 |
175 | ## Birthday Ranges
176 |
177 | Implement a function that calculates how many people are born in a range of `start` and `end` date(`end` is included in the range). The input parameters are:
178 |
179 | - `birthdays` - a list of integers, which are in the range from 1 to 365 inclusive.
180 | - `ranges` - a list of tuples, where each tuple has only two integer values(the first one represents the `start` date and the second - `end` date). All values are in the range from 1 to 365 inclusive.
181 |
182 | ### Signature
183 |
184 | ```python
185 | def birthday_ranges(birthdays, ranges):
186 | pass
187 | ```
188 |
189 | Calculate, for each tuple, how many people are born in that between the `start` and `end` date.
190 |
191 | ### Test Examples
192 |
193 | ```python
194 | >>> birthday_ranges([1, 2, 3, 4, 5], [(1, 2), (1, 3), (1, 4), (1, 5), (4, 6)])
195 | [2, 3, 4, 5, 2]
196 | >>> birthday_ranges([5, 10, 6, 7, 3, 4, 5, 11, 21, 300, 15], [(4, 9), (6, 7), (200, 225), (300, 365)])
197 | [5, 2, 0, 1]
198 | ```
199 |
200 | ## 100 SMS
201 |
202 | A long time ago, before the smartphones, when you had to write some messages, the keypads looked like that:
203 |
204 | 
205 |
206 | For example, on such keypad, if you want to write **Ruby**, you had to press the following sequence of numbers:
207 |
208 | ```
209 | 7778822999
210 | ```
211 |
212 | Each key contains some letters from the alphabet. And by pressing that key, you rotate the letters until you get to your desired one.
213 |
214 | It's time to implement some encode / decode functions for the old keypads!
215 |
216 | First, implement a function that takes a list of integers - the sequence of numbers that have been pressed. The returned value should be the corresponding string of the message.
217 |
218 | ### Signature:
219 |
220 | ```python
221 | def numbers_to_message(pressed_sequence):
222 | pass
223 | ```
224 |
225 | There are some special rules:
226 |
227 | - If you press `1`, the next letter is going to be capitalized
228 | - If you press `0`, this will insert a single white-space
229 | - If you press a number and wait for a few seconds, the special breaking number `-1` enters the sequence. This is the way to write different letters from only one keypad!
230 |
231 | ### Test examples:
232 |
233 | ```python
234 | >>> numbers_to_message([2, -1, 2, 2, -1, 2, 2, 2])
235 | "abc"
236 | >>> numbers_to_message([2, 2, 2, 2])
237 | "a"
238 | >>> numbers_to_message([1, 4, 4, 4, 8, 8, 8, 6, 6, 6, 0, 3, 3, 0, 1, 7, 7, 7, 7, 7, 2, 6, 6, 3, 2])
239 | "Ivo e Panda"
240 | ```
241 |
242 | Now it is time to convert the message to a sequence of numbers. This function takes a string - the `message` and returns the **minimal** keystrokes that you need to write that `message`
243 |
244 | ### Signature:
245 |
246 | ```python
247 | def message_to_numbers(message):
248 | pass
249 | ```
250 |
251 | ### Test examples:
252 |
253 | ```python
254 | >>> message_to_numbers("abc")
255 | [2, -1, 2, 2, -1, 2, 2, 2]
256 | >>> message_to_numbers("a")
257 | [2]
258 | >>> message_to_numbers("Ivo e Panda")
259 | [1, 4, 4, 4, 8, 8, 8, 6, 6, 6, 0, 3, 3, 0, 1, 7, 2, 6, 6, 3, 2]
260 | >>> message_to_numbers("aabbcc")
261 | [2, -1, 2, -1, 2, 2, -1, 2, 2, -1, 2, 2, 2, -1, 2, 2, 2]
262 | ```
263 |
--------------------------------------------------------------------------------
/week02/01.LinuxCommands/README.md:
--------------------------------------------------------------------------------
1 | ## 1. Implement the `cat` command - Print file contents
2 |
3 | In Linux, there is a very useful command, called `cat`:
4 |
5 | ```
6 | $ cat file.txt
7 | This is some file
8 | And cat is printing it's contents
9 | ```
10 |
11 | Implement a Python script, called `cat.py` that takes one argument - a filename and prints the contents of that file to the console.
12 |
13 | ### Boilerplate:
14 |
15 | ```python
16 | # cat.py
17 | import sys
18 |
19 | def cat(arguments):
20 | pass
21 |
22 |
23 | def main():
24 | pass
25 |
26 | if __name__ == '__main__':
27 | main()
28 | ```
29 |
30 | ### Test Examples
31 |
32 | If we have `file.txt` in the same directory with `cat.py`, and `file.txt` content is:
33 |
34 | ```
35 | Python is an awesome language!
36 | You should try it.
37 | ```
38 |
39 | This is the result:
40 |
41 | ```
42 | $ python cat.py file.txt
43 | Python is an awesome language!
44 | You should try it.
45 | ```
46 |
47 | ## 2. Cat multiple files
48 |
49 | Implement a Python script, called `cat2.py` that takes multiple arguments - filenames and prints the contents of all files to the console, in the order of the arguments.
50 |
51 | **The number of the files that are given as arguments is unknown.**
52 |
53 | There should be a newline between every two files that are printed.
54 |
55 | ### Boilerplate
56 |
57 | ```python
58 | # cat2.py
59 | import sys
60 |
61 |
62 | def cat2(arguments):
63 | pass
64 |
65 |
66 | def main():
67 | pass
68 |
69 | if __name__ == '__main__':
70 | main()
71 | ```
72 |
73 | ### Examples
74 |
75 | If we have two files - `file1.txt` and `file2.txt` in the same directory with `cat2.py` and:
76 |
77 | **file1.txt:**
78 |
79 | ```
80 | Python is an awesome language!
81 | You should try it.
82 | ```
83 |
84 | **file2.txt:**
85 |
86 | ```
87 | Also, you can use Python at a lot of different places!
88 | ```
89 |
90 | This has to be the result:
91 |
92 | ```
93 | $ python cat2.py file1.txt file2.txt
94 | Python is an awesome language!
95 | You should try it.
96 |
97 | Also, you can use Python at a lot of different places!
98 | ```
99 |
100 | ## 3. Generate file with random integers
101 |
102 | Implement a Python script, called `generate_numbers.py` that takes two arguments - a `filename` and a positive integer `n`.
103 |
104 | The script should create a file with the `filename` and writes `n` random integers in it, separated by `" "`.
105 |
106 | For random integers, you can use:
107 |
108 | ```python
109 | from random import randint
110 | print(randint(1, 1000))
111 | ```
112 |
113 | ### Boilerplate
114 |
115 | ```python
116 | # generate_numbers.py
117 | import sys
118 | from random import randint
119 |
120 |
121 | def generate_numbers(filename, numbers):
122 | pass
123 |
124 |
125 | def main():
126 | pass
127 |
128 | if __name__ == '__main__':
129 | main()
130 | ```
131 |
132 | ### Examples
133 |
134 | ```
135 | $ python generate_numbers.py numbers.txt 100
136 | $ cat numbers.txt
137 | 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
138 | ```
139 |
140 | ## 4. Sum integers from file
141 |
142 | Implement a Python script, called `sum_numbers.py` that takes one argument - a `filename` which has integers, separated by `" "`.
143 |
144 | The script should print the sum of all integers in that file.
145 |
146 | ### Examples
147 |
148 | If we use the generated file Problem 3:
149 |
150 | ```
151 | $ python sum_numbers.py numbers.txt
152 | 47372
153 | ```
154 |
155 | ## 5. Implement an alternative to du -h command
156 |
157 | In Linux, if we want to know the size of a directory, we use the `du` command. For example:
158 |
159 | ```
160 | $ du -hs /home/rositsazz/code
161 | 2,3G /home/rositsazz/code
162 | ```
163 |
164 | - `-h` flag is for "human readable" which means we get the size in gigabytes, not bytes.
165 | - `-s` flag is for silent. We don't want to print every file that we go through.
166 |
167 | 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.
168 |
169 | Example usage:
170 |
171 | ```
172 | $ python duhs.py /home/rositsazz/code
173 | /home/rositsazz/code size is: 2.3G
174 | ```
175 |
176 | **THIS IS NOT THE SOLUTION WE WANT:**
177 |
178 | ```python
179 | from subprocess import call
180 | import sys
181 |
182 | path = sys.argv[1]
183 |
184 | call(["du", "-s", "-h", path])
185 | ```
186 |
187 | ### Hints
188 |
189 | - Check the [`os`](https://docs.python.org/3.7/library/os.html) python module.
190 | - Many of the methods raise errors. Handle them properly!:
191 |
192 | ## 6. Count characters, words or lines
193 |
194 | Implement a Python script, called `wc.py` that takes two arguments:
195 |
196 | - A command, that can be one of the following : `chars`, `words`, `lines`
197 | - A filename
198 |
199 | The script should output, according to the command, the following:
200 |
201 | - For the command `chars`, the number of characters in the file
202 | - For the command `words`, the number of words in the file
203 | - For the command `lines`, the number of lines in the file
204 |
205 | ### Examples
206 |
207 | Lets have the following text:
208 |
209 | **story.txt:**
210 |
211 | ```
212 | Now indulgence dissimilar for his thoroughly has terminated. Agreement offending commanded my an. Change wholly say why eldest period. Are projection put celebrated particular unreserved joy unsatiable its. In then dare good am rose bred or. On am in nearer square wanted.
213 |
214 | Of resolve to gravity thought my prepare chamber so. Unsatiable entreaties collecting may sympathize nay interested instrument. If continue building numerous of at relation in margaret. Lasted engage roused mother an am at. Other early while if by do to. Missed living excuse as be. Cause heard fat above first shall for. My smiling to he removal weather on anxious.
215 |
216 | Ferrars all spirits his imagine effects amongst neither. It bachelor cheerful of mistaken. Tore has sons put upon wife use bred seen. Its dissimilar invitation ten has discretion unreserved. Had you him humoured jointure ask expenses learning. Blush on in jokes sense do do. Brother hundred he assured reached on up no. On am nearer missed lovers. To it mother extent temper figure better.
217 |
218 | ```
219 |
220 | **Print the chars:**
221 |
222 | ```
223 | $ python wc.py chars story.txt
224 | 1032
225 | ```
226 |
227 | **Print the words:**
228 |
229 | ```
230 | $ python wc.py words story.txt
231 | 166
232 | ```
233 |
234 | **Print the lines:**
235 |
236 | ```
237 | $ python wc.py lines story.txt
238 | 5
239 | ```
240 |
241 | ## Reduce file path
242 |
243 | A file path in a Unix OS looks like this - `/home/rositsazz/courses/Programming101-Python/week01`
244 |
245 | We start from the root - `/` and we navigate to the destination fodler.
246 |
247 | But there is a problem - if we have `..` and `.` in our file path, it's not clear where we are going to end up.
248 |
249 | - `..` means to go back one directory
250 | - `.` means to stay in the same directory
251 | - we can have more then one `/` between the directories - `/home//code`
252 |
253 | So for example : `/home//rositsazz/courses/./Programming101-Python/week01/../` reduces to `/home/rositsazz/courses/Programming101-Python/`.
254 |
255 | Implement a function, called `reduce_file_path(path)` which takes a string and returns the reduced version of the path.
256 |
257 | - Every `..` means that we have to go one directory back
258 | - Every `.` means that we are staying in the same directory
259 | - Every extra `/` is unnecessary
260 | - Always remove the last `/`
261 |
262 | ### Signature
263 |
264 | ```python
265 | def reduce_file_path(path):
266 | pass
267 | ```
268 |
269 | ### Test examples
270 |
271 | ```python
272 | >>> reduce_file_path("/")
273 | "/"
274 | >>> reduce_file_path("/srv/../")
275 | "/"
276 | >>> reduce_file_path("/srv/www/htdocs/wtf/")
277 | "/srv/www/htdocs/wtf"
278 | >>> reduce_file_path("/srv/www/htdocs/wtf")
279 | "/srv/www/htdocs/wtf"
280 | >>> reduce_file_path("/srv/./././././")
281 | "/srv"
282 | >>> reduce_file_path("/etc//wtf/")
283 | "/etc/wtf"
284 | >>> reduce_file_path("/etc/../etc/../etc/../")
285 | "/"
286 | >>> reduce_file_path("//////////////")
287 | "/"
288 | >>> reduce_file_path("/../")
289 | "/"
290 | ```
291 |
--------------------------------------------------------------------------------
/week04/02.BeloteDeclarations/README.md:
--------------------------------------------------------------------------------
1 | # Belote Declarations
2 |
3 | [Belote](https://en.wikipedia.org/wiki/Belote) is one of the most famous card games in Bulgaria. Your task is to implement just part of it - the possible declarations (announcements) in the first trick.
4 |
5 | ## Explanations
6 |
7 | > TL;DR: If you are familiar with the game of Belote this paragraph may be skipped.
8 |
9 | The Belote game is explained well in the above link from Wikipedia. There are a lot of rules but most of them do not apply for the declarations which are our target for this task. Here, I will try to summarize the rules:
10 |
11 | - The game is played with all 4 ranks `(Clubs, Diamonds, Hearts, Spades)` and the suites from 7 to A `(7, 8, 9, 10, J, Q, K, A)`.
12 | - The game is played by 2 teams -> Team X = Player 1 + Player 3; Team Y = Player 2 + Player 4
13 | - Each player has 8 cards
14 | - The announcements are made in consecutive order by the players.
15 |
16 | ### Belote
17 |
18 | - A player can announce "belote" if he has a K and Q from the same rank.
19 | - You can have a "belote" only from the rank that is the same with the game that is played (Ks + Qs is a belote only in a game of Spades)
20 | - In a game of "All trumps" -> you can have belote from all of the ranks
21 | - In a game of "No trumps" -> **there are no belotes**
22 |
23 | ### Consecutive cards
24 |
25 | - A player can announce "tierce" (20 points) if he has 3 consecutive cards from the same rank ( e.g. 7c, 8c, 9c ).
26 | - A player can announce "quarte" (50 points) if he has 4 consecutive cards from the same rank ( e.g. 10h, Jh, Qh, Kh ).
27 | - A player can announce "quinte" (100 points) if he has 5 (or more) consecutive cards from the same rank ( e.g. 8s, 9s, 10s, Js, Qs ).
28 | - In a game of "No trumps" -> **there are no tierces, quartes or quintes**
29 |
30 | ### Carre
31 |
32 | - A player can announce "carre" if he has 4 cards with the same suite ( e.g. Js, Jh, Jd, Jc ).
33 | - **4 7's or 4 8's are not considered carre**
34 | - In a game of "No trumps" -> **there are no carres**
35 | - Pointing:
36 | - carre of 10, Q, K, A = 100 points
37 | - carre of 9's = 150 points
38 | - carre of J's = 200 points
39 | - **NOTE: One card cannot be part from a carre and tierce/quarte/quinte in the same time. It can be part of a belote though.**. Let's say you have the following cards: `['7s', '8s', '9s', '9c', '9d', '9h', 'Kc', 'As']`. In this case the program is expected to output carre of 9's. You can still announce only tierce, but it gives you less points.
40 |
41 | ## Scoring
42 |
43 | The game is won by the team who first make > 150 points (If you have 150 points, the game is not over!)
44 |
45 | For this task, the points of one team will be calculated by summing their points from their announces.
46 |
47 | There are multiple rules connected to the priority of the announces. They apply only to between the teams:
48 |
49 | - The general rule: tierce < quarte < quinte. This means that if Team 1 has tierce and Teams 2 has quinte, the tierce of Team 1 should be excluded from the points.
50 | - If two enemy players have same sequences (e.g. player 1 and player 2 both have tierce) the one that is to a higher rank wins. If the sequences are equal, both of them should be excluded.
51 | - All belotes hold good!
52 | - All carres hold good!
53 |
54 | ### Example rounds
55 |
56 | ```
57 | Team 1: 7s 8s 9s - tierce
58 | Team 2: 7c 8c 9c 10c - quarte
59 |
60 | Result: Team 1 has no tierce, because it's killed by the quarte of the other team => 0 points. Team 2 has quarte => 50 points
61 | ```
62 |
63 | ```
64 | Team 1: Jd Qd Kd - tierce and belote
65 | Team 2: 7c 8c 9c - tierce
66 |
67 | Result: Team 1 has no tierce and belote => 40 points. Team 2 has no tierce, because it's lower than the tierce of the other team => 0 points
68 |
69 | *NOTE: Rules for the belotes and the current played game should be applied*
70 | ```
71 |
72 | ```
73 | Team 1: Jd Qd Kd - tierce and belote
74 | Team 2: Js Qs Ks - tierce and belote
75 |
76 | Result: Team 1 has only belote, the tierce is killed as it's equal to the tierce of the other team => 20 points. Team 2 has only belote, the tierce is killed as it's equal to the tierce of the other team => 20 points.
77 |
78 | *NOTE: Rules for the belotes and the current played game should be applied*
79 | ```
80 |
81 | ```
82 | Team 1: 10s 10d 10h 10c - carre of 10's
83 | Team 2: 9s 9d 9h 9c - carre of 9's
84 |
85 | Result: Team 1 has carre of 10's => 100 points. Team 2 has care of 9's => 150 points
86 | ```
87 |
88 | ```
89 | Team 1: 10s 10d 10h 10c - carre of 10's
90 | Team 2: 7s 8s 9s - tierce
91 |
92 | Result: Team 1 has carre of 10's => 100 points. Team 2 has tierce => 20 points
93 | ```
94 |
95 | ```
96 | Team 1: 7s 8s 9s + 9d 10d Jd - 2 tierces
97 | Team 2: 7h 8h 9h - tierce
98 |
99 | Result: Team 1 has 2 tierces => 40 points. Team 2 has no tierce, becase one of the tierces of the other team is higher => 0 points
100 | ```
101 |
102 | ```
103 | Team 1: 7s 8s 9s + 9d 10d Jd Qd - tierce & quarte
104 | Team 2: 9c 10c Jc Qc - quarte
105 |
106 | Result: Team 1 has nothing because the quartes are equal and the quarte of the other team kills the tierce => 0 points. Team 2 has no quarte, because the quartes are equal => 0 points
107 | ```
108 |
109 | ## The task
110 |
111 | You should imitate a game of Belote where the scores are generated only from the declarations of the players.
112 |
113 | Team and player names should inputted from the user.
114 | Example:
115 |
116 | ```
117 | python3 belote.py
118 |
119 | Team 1 name: Mecheta
120 | Team 2 name: Koteta
121 |
122 | "Mecheta" players: Marto, Rado
123 | "Koteta" players: Gosho, Pesho
124 | ```
125 |
126 | The two teams should alternate when declaring. When the program is started, the first player is player 1 from team 1 ('Marto'). In other words, the order of the players after the above input is: 'Marto' -> 'Gosho' -> 'Rado' -> 'Pesho'.
127 | After each round, the first player from the last round becomes last: 'Gosho' -> 'Rado' -> 'Pesho' -> 'Marto'.
128 | When a game is won by a team, a random player from this team starts the first round of the next game.
129 |
130 | The points from one round are calculated by summing the points from the declarations of each team. For example, if 'Marto' has tierce (20) and 'Rado' has quarte (50), their result from the round is 70 points.
131 | **NOTE: In the real game of Belote, these points are divided by 10. Don't do this for this task!!!**
132 |
133 | The program should finish when one of the teams win 2 games.
134 |
135 | One game is won by the first team that scores more than 150 points. If both of the teams have more than 150 points, the game is won by the team who has more points. If the points are equal, the game should continue until one of the teams shoot ahead.
136 |
137 | After each round, the points from the current round should be written into the `results.txt` file.
138 | In the following example we will see the expected format of the file:
139 |
140 | ```
141 | Mecheta | Koteta
142 | =================================
143 | 20 | 100
144 | 20 + 50 | 100 + 0
145 | ```
146 |
147 | This is how you should mark when a game is won by a team:
148 |
149 | ```
150 | Mecheta | Koteta
151 | =================================
152 | 20 | 100
153 | 20 + 20 | 100 + 20
154 | 40 + 50 | 120 + 0
155 | 90 + 100 | 120 + 50
156 | 190 | 170
157 | =================================
158 | (1) | (0)
159 | =================================
160 | ```
161 |
162 | After each round, the cards and the announcements of each player should be written in `data.json` file.
163 | Let's say we have this round:
164 |
165 | ```
166 | Marto: ["7s", "8s", "9s", "10c", "Jd", "Qd", "Kh", "As"] # team Mecheta
167 | Gosho: ["7c", "8d", "9c", "10s", "Jh", "Qc", "Kc", "Ad"] # team Koteta
168 | Rado: ["7d", "8c", "9d", "10d", "Js", "Qs", "Kd", "Ac"] # team Mecheta
169 | Pesho: ["7h", "8h", "9h", "10h", "Jc", "Qh", "Ks", "Ah"] # team Koteta
170 | ```
171 |
172 | ```json
173 | {
174 | "game 1": {
175 | "round 1": {
176 | "contract": "Hearts",
177 | "Mecheta": {
178 | "Marto": {
179 | "cards:": ["7s", "8s", "9s", "10c", "Jd", "Qh", "Kh", "As"],
180 | "announcements": ["belote"],
181 | "points": 20
182 | },
183 | "Rado": {
184 | "cards:": ["7d", "8c", "9d", "10d", "Js", "Qs", "Kd", "Ac"],
185 | "announcements": [],
186 | "points": 0
187 | }
188 | },
189 | "Koteta": {
190 | "Gosho": {
191 | "cards:": ["7c", "8d", "9c", "10s", "Jh", "Qc", "Kc", "Ad"],
192 | "announcements": [],
193 | "points": 0
194 | },
195 | "Pesho": {
196 | "cards:": ["7h", "8h", "9h", "10h", "Jc", "Qd", "Ks", "Ah"],
197 | "announcements": ["quarte"],
198 | "points": 50
199 | }
200 | }
201 | }
202 | // rest of the rounds here ....
203 | }
204 | }
205 | ```
206 |
--------------------------------------------------------------------------------
/week02/02.CancellationPolicy/tests.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from datetime import datetime, timedelta
3 | from solution_interface import (
4 | validate_conditions,
5 | ensure_conditions,
6 | pair_conditions,
7 | get_cancellation_policy,
8 | get_current_condition,
9 | sort_conditions
10 | )
11 |
12 |
13 | class TestValidateConditions(unittest.TestCase):
14 | def test_validation_passes_with_valid_conditions(self):
15 | conditions = [
16 | {'hours': 10, 'percent': 10},
17 | {'percent': 100}
18 | ]
19 |
20 | validate_conditions(conditions)
21 |
22 | def test_raises_exception_if_all_conditions_have_hours(self):
23 | conditions = [
24 | {'hours': 10, 'percent': 10}
25 | ]
26 | exc = None
27 |
28 | # ACT
29 | try:
30 | validate_conditions(conditions)
31 | except Exception as err:
32 | exc = err
33 |
34 | # ASSERTS
35 | self.assertIsNotNone(exc)
36 | self.assertEqual(str(exc), 'Invalid conditions.')
37 |
38 | def test_raises_exception_if_more_than_one_condition_with_no_hours(self):
39 | conditions = [
40 | {'hours': 10, 'percent': 10000},
41 | {'percent': 10},
42 | {'percent': 100}
43 | ]
44 | exc = None
45 |
46 | # ACT
47 | try:
48 | validate_conditions(conditions)
49 | except Exception as err:
50 | exc = err
51 |
52 | # ASSERTS
53 | self.assertIsNotNone(exc)
54 | self.assertEqual(str(exc), 'Invalid conditions.')
55 |
56 | def test_raises_exception_if_hours_bigger_than_24(self):
57 | # ARRANGE
58 | conditions = [
59 | {'hours': 72, 'percent': 10000},
60 | {'percent': 10},
61 | ]
62 | exc = None
63 |
64 | # ACT
65 | try:
66 | validate_conditions(conditions)
67 | except Exception as err:
68 | exc = err
69 |
70 | # ASSERTS
71 | self.assertIsNotNone(exc)
72 | self.assertEqual(str(exc), 'Hours cannot be > 24.')
73 |
74 |
75 | class TestEnsureConditions(unittest.TestCase):
76 | def test_all_conditions_have_hours_after_ensuring(self):
77 | cond1 = {'hours': 10, 'percent': 10}
78 | cond2 = {'percent': 100}
79 | conditions = [cond1, cond2]
80 |
81 | ensure_conditions(conditions)
82 |
83 | self.assertEqual(cond1['hours'], 10)
84 | self.assertEqual(cond2['hours'], 0)
85 |
86 |
87 | class TestPairConditions(unittest.TestCase):
88 | def test_pair_conditions_with_two_elements(self):
89 | conditions = [{'hours': 10, 'percent': 20}, {'hours': 0, 'percent': 50}]
90 |
91 | result = pair_conditions(conditions)
92 |
93 | expected = [({'hours': 10, 'percent': 20}, {'hours': 0, 'percent': 50})]
94 | self.assertEqual(result, expected)
95 |
96 | def test_pair_conditions_with_even_number_of_elements(self):
97 | conditions = [
98 | {'hours': 24, 'percent': 0},
99 | {'hours': 18, 'percent': 20},
100 | {'hours': 12, 'percent': 50},
101 | {'hours': 0, 'percent': 100}
102 | ]
103 |
104 | result = pair_conditions(conditions)
105 |
106 | expected = [
107 | ({'hours': 24, 'percent': 0}, {'hours': 18, 'percent': 20}),
108 | ({'hours': 18, 'percent': 20}, {'hours': 12, 'percent': 50}),
109 | ({'hours': 12, 'percent': 50}, {'hours': 0, 'percent': 100})
110 | ]
111 |
112 | self.assertEqual(result, expected)
113 |
114 | def test_pair_conditions_with_odd_number_of_elements(self):
115 | conditions = [
116 | {'hours': 24, 'percent': 0},
117 | {'hours': 18, 'percent': 20},
118 | {'hours': 12, 'percent': 50},
119 | {'hours': 6, 'percent': 80},
120 | {'hours': 0, 'percent': 100}
121 | ]
122 |
123 | result = pair_conditions(conditions)
124 |
125 | expected = [
126 | ({'hours': 24, 'percent': 0}, {'hours': 18, 'percent': 20}),
127 | ({'hours': 18, 'percent': 20}, {'hours': 12, 'percent': 50}),
128 | ({'hours': 12, 'percent': 50}, {'hours': 6, 'percent': 80}),
129 | ({'hours': 6, 'percent': 80}, {'hours': 0, 'percent': 100})
130 | ]
131 |
132 | self.assertEqual(result, expected)
133 |
134 |
135 | class TestGetCurrentCondition(unittest.TestCase):
136 | def test_with_current_date_before_min_condition_date_should_return_min_condition_percent(self):
137 | conditions = [
138 | ({'hours': 24, 'percent': 0}, {'hours': 18, 'percent': 20}),
139 | ({'hours': 18, 'percent': 20}, {'hours': 12, 'percent': 50}),
140 | ({'hours': 12, 'percent': 50}, {'hours': 6, 'percent': 80}),
141 | ({'hours': 6, 'percent': 80}, {'hours': 0, 'percent': 100})
142 | ]
143 | booking_start = datetime.now()
144 | now = booking_start - timedelta(hours=100)
145 |
146 | result = get_current_condition(conditions, booking_start, now)
147 |
148 | self.assertEqual(result, 0)
149 |
150 | def test_with_current_date_in_condition_interval_should_return_higher_condition_percent(self):
151 | conditions = [
152 | ({'hours': 24, 'percent': 0}, {'hours': 18, 'percent': 20}),
153 | ({'hours': 18, 'percent': 20}, {'hours': 12, 'percent': 50}),
154 | ({'hours': 12, 'percent': 50}, {'hours': 6, 'percent': 80}),
155 | ({'hours': 6, 'percent': 80}, {'hours': 0, 'percent': 100})
156 | ]
157 | booking_start = datetime.now()
158 | now = booking_start - timedelta(hours=10)
159 |
160 | result = get_current_condition(conditions, booking_start, now)
161 |
162 | self.assertEqual(result, 80)
163 |
164 | def test_with_current_date_equal_to_condition_hours_should_return_interval_upper_boundary_percent(self):
165 | conditions = [
166 | ({'hours': 24, 'percent': 0}, {'hours': 18, 'percent': 20}),
167 | ({'hours': 18, 'percent': 20}, {'hours': 12, 'percent': 50}),
168 | ({'hours': 12, 'percent': 50}, {'hours': 6, 'percent': 80}),
169 | ({'hours': 6, 'percent': 80}, {'hours': 0, 'percent': 100})
170 | ]
171 | booking_start = datetime.now()
172 | now = booking_start - timedelta(hours=6)
173 |
174 | result = get_current_condition(conditions, booking_start, now)
175 |
176 | self.assertEqual(result, 100)
177 |
178 |
179 | class TestGetCancellationPolicy(unittest.TestCase):
180 | def test_with_now_equal_to_booking_start_should_raise_error(self):
181 | conditions = [{'percent': 50}]
182 | booking_start = datetime.now()
183 | now = booking_start
184 | exc = None
185 |
186 | try:
187 | get_cancellation_policy(conditions, 10, booking_start, now)
188 | except Exception as err:
189 | exc = err
190 |
191 | self.assertIsNotNone(exc)
192 | self.assertEqual(str(exc), 'Invalid booking start.')
193 |
194 | def test_with_now_later_than_booking_start_should_raise_error(self):
195 | conditions = [{'percent': 50}]
196 | booking_start = datetime.now()
197 | now = booking_start + timedelta(hours=1)
198 | exc = None
199 |
200 | try:
201 | get_cancellation_policy(conditions, 10, booking_start, now)
202 | except Exception as err:
203 | exc = err
204 |
205 | self.assertIsNotNone(exc)
206 | self.assertEqual(str(exc), 'Invalid booking start.')
207 |
208 | def test_cancellation_fee_with_only_one_condition(self):
209 | conditions = [{'percent': 50}]
210 | price = 100
211 | booking_start = datetime.now()
212 | now = booking_start - timedelta(hours=100)
213 |
214 | result = get_cancellation_policy(conditions, price, booking_start, now)
215 |
216 | self.assertEqual(result, 50.0)
217 |
218 | def test_cancellation_fee_with_several_conditions_and_now_in_them(self):
219 | conditions = [
220 | {'hours': 24, 'percent': 10},
221 | {'hours': 12, 'percent': 50},
222 | {'hours': 6, 'percent': 80},
223 | {'percent': 100}
224 | ]
225 | price = 100
226 | booking_start = datetime.now()
227 | now = booking_start - timedelta(hours=10)
228 |
229 | result = get_cancellation_policy(conditions, price, booking_start, now)
230 |
231 | self.assertEqual(result, 80.0)
232 |
233 | def test_cancellation_fee_with_several_conditions_and_now_in_them_and_decimal_percent(self):
234 | conditions = [
235 | {'hours': 24, 'percent': 10},
236 | {'hours': 12, 'percent': 50},
237 | {'hours': 6, 'percent': 65.5},
238 | {'percent': 100}
239 | ]
240 | price = 200
241 | booking_start = datetime.now()
242 | now = booking_start - timedelta(hours=10)
243 |
244 | result = get_cancellation_policy(conditions, price, booking_start, now)
245 |
246 | self.assertEqual(result, price * (65.5 / 100))
247 |
248 |
249 | class TestSortConditions(unittest.TestCase):
250 | def test_conditions_are_sorted_in_descending_order(self):
251 | conditions = [
252 | {'hours': 12, 'percent': 50},
253 | {'hours': 0, 'percent': 100},
254 | {'hours': 6, 'percent': 80},
255 | {'hours': 24, 'percent': 10},
256 | ]
257 |
258 | result = sort_conditions(conditions)
259 |
260 | expected = [
261 | {'hours': 24, 'percent': 10},
262 | {'hours': 12, 'percent': 50},
263 | {'hours': 6, 'percent': 80},
264 | {'hours': 0, 'percent': 100},
265 | ]
266 |
267 | self.assertEqual(result, expected)
268 |
269 |
270 | if __name__ == '__main__':
271 | unittest.main()
272 |
--------------------------------------------------------------------------------