├── .flake8 ├── .github └── workflows │ └── algolia-scraper.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .templates ├── lecture.md └── section.md ├── CONTRIBUTING.md ├── README.md ├── algolia.config.json ├── assets ├── course-image.png └── download-repo-zip.png ├── babel.config.js ├── course_contents ├── 10_advanced_python │ ├── README.md │ └── lectures │ │ ├── 01_mutability │ │ └── code.py │ │ ├── 02_argument_mutability │ │ └── code.py │ │ ├── 03_default_parameter_values │ │ └── code.py │ │ ├── 04_mutable_default_arguments │ │ └── code.py │ │ ├── 05_argument_unpacking │ │ └── code.py │ │ ├── 07_interesting_python_collections │ │ └── code.py │ │ ├── 09_dates_and_times_python │ │ ├── Datetime cheatsheet.pdf │ │ └── code.py │ │ ├── 10_timing_your_code │ │ └── code.py │ │ ├── 11_regex │ │ └── code.py │ │ ├── 12_regex_in_python │ │ └── code.py │ │ ├── 14_logging_to_file │ │ └── code.py │ │ └── 16_higher_order_functions │ │ └── code.py ├── 11_web_scraping │ ├── README.md │ └── projects │ │ ├── scraping-books │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── all_books_page.py │ │ │ └── book_locators.py │ │ ├── menu.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── all_books_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── book.py │ │ ├── scraping-quotes │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ └── understanding-html │ │ ├── class_html_parsing.py │ │ ├── middle_html_parsing.py │ │ ├── scraping_example.py │ │ ├── separating_locators_in_classes.py │ │ └── simple_html_parsing.py ├── 12_browser_automation_selenium │ ├── README.md │ ├── code │ │ ├── Pipfile │ │ ├── Pipfile.lock │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ └── lectures │ │ ├── 10_implicit_and_explicit_waits │ │ └── slide.html │ │ ├── 11_adding_waits_to_our_code │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ ├── 1_our_scraping_code │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ ├── 3_using_chrome_in_scraping_code │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ ├── 4_our_new_page_locators │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ ├── 5_interacting_with_dropdowns │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ ├── 6_selecting_tags │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ ├── 7_searching_for_quotes │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ ├── 8_encapsulating_logic_simply │ │ ├── app.py │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── quote_locators.py │ │ │ └── quotes_page_locators.py │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── quotes_page.py │ │ └── parsers │ │ │ ├── __init__.py │ │ │ └── quote.py │ │ └── 9_adding_some_error_handling │ │ ├── app.py │ │ ├── locators │ │ ├── __init__.py │ │ ├── quote_locators.py │ │ └── quotes_page_locators.py │ │ ├── pages │ │ ├── __init__.py │ │ └── quotes_page.py │ │ └── parsers │ │ ├── __init__.py │ │ └── quote.py ├── 13_async_development │ ├── README.md │ ├── lectures │ │ ├── 01_code_samples │ │ │ └── README.md │ │ └── 02_async_terms_glossary │ │ │ └── README.md │ ├── projects │ │ └── async_scraping │ │ │ ├── Pipfile │ │ │ ├── Pipfile.lock │ │ │ ├── app.py │ │ │ ├── locators │ │ │ ├── __init__.py │ │ │ ├── all_books_page.py │ │ │ └── book_locators.py │ │ │ ├── menu.py │ │ │ ├── old_app.py │ │ │ ├── pages │ │ │ ├── __init__.py │ │ │ └── all_books_page.py │ │ │ ├── parsers │ │ │ ├── __init__.py │ │ │ └── book.py │ │ │ ├── requirements.txt │ │ │ └── samples │ │ │ ├── 0_first_async_request.py │ │ │ ├── 1_seemingly_async.py │ │ │ ├── 2_actually_async.py │ │ │ ├── 3_with_async_timeout.py │ │ │ └── 4_a_note_on_https.py │ └── sample_code │ │ ├── 10_generators_two_way.py │ │ ├── 11_receiving_through_yield.py │ │ ├── 12_async_await.py │ │ ├── 1_threads.py │ │ ├── 2_threads_with_pool.py │ │ ├── 3_processes.py │ │ ├── 4_processes_with_pool.py │ │ ├── 5_non_locked_threads.py │ │ ├── 6_queued_threads.py │ │ ├── 7_queued_threads_executor.py │ │ ├── 8_queued_no_waits.py │ │ └── 9_generators.py ├── 14_managing_projects_pipenv │ ├── README.md │ └── using_pipenv.pdf ├── 15_flask │ ├── README.md │ └── projects │ │ └── first-flask-app-lectures │ │ ├── 1-first-flask-endpoint │ │ └── app.py │ │ ├── 2-returning-information │ │ └── app.py │ │ ├── 3-rendering-html │ │ ├── app.py │ │ └── templates │ │ │ └── post.jinja2 │ │ ├── 4-error-pages │ │ ├── app.py │ │ └── templates │ │ │ ├── 404.jinja2 │ │ │ ├── base.jinja2 │ │ │ └── post.jinja2 │ │ ├── 5-rendering-forms │ │ ├── app.py │ │ └── templates │ │ │ ├── 404.jinja2 │ │ │ ├── base.jinja2 │ │ │ ├── create.jinja2 │ │ │ └── post.jinja2 │ │ ├── 6-forms-with-post │ │ ├── app.py │ │ └── templates │ │ │ ├── 404.jinja2 │ │ │ ├── base.jinja2 │ │ │ ├── create.jinja2 │ │ │ └── post.jinja2 │ │ ├── 7-single-endpoint-for-form │ │ ├── app.py │ │ └── templates │ │ │ ├── 404.jinja2 │ │ │ ├── base.jinja2 │ │ │ ├── create.jinja2 │ │ │ └── post.jinja2 │ │ ├── 8-jinja-for-loops │ │ ├── app.py │ │ └── templates │ │ │ ├── 404.jinja2 │ │ │ ├── base.jinja2 │ │ │ ├── create.jinja2 │ │ │ ├── home.jinja2 │ │ │ └── post.jinja2 │ │ ├── 9-adding-home-link │ │ ├── app.py │ │ └── templates │ │ │ ├── 404.jinja2 │ │ │ ├── base.jinja2 │ │ │ ├── create.jinja2 │ │ │ ├── home.jinja2 │ │ │ └── post.jinja2 │ │ ├── Pipfile │ │ ├── Pipfile.lock │ │ └── README.md ├── 16_interacting_with_apis │ ├── README.md │ ├── code │ │ ├── 1_simple_interaction │ │ │ └── app.py │ │ ├── 2_creating_a_library │ │ │ ├── app.py │ │ │ └── libs │ │ │ │ ├── __init__.py │ │ │ │ └── openexchange.py │ │ └── 3_speed_up_with_cache │ │ │ ├── app.py │ │ │ └── libs │ │ │ ├── __init__.py │ │ │ └── openexchange.py │ └── lectures │ │ ├── 3_getting_all_exchange_rates │ │ └── code.py │ │ ├── 4_creating_a_currency_exchange_library │ │ ├── app.py │ │ └── libs │ │ │ ├── __init__.py │ │ │ └── openexchange.py │ │ ├── 5_caching_with_cachetools │ │ ├── app.py │ │ └── libs │ │ │ ├── __init__.py │ │ │ └── openexchange.py │ │ └── 6_functools_lru_cache │ │ └── code.py ├── 17_decorators │ ├── README.md │ └── lectures │ │ ├── 01_simple_decorators │ │ └── app.py │ │ ├── 02_the_at_syntax │ │ └── app.py │ │ ├── 03_functools_wraps │ │ └── app.py │ │ ├── 04_decorating_functions_with_parameters │ │ └── app.py │ │ ├── 05_decorators_with_parameters │ │ └── app.py │ │ ├── 06_functions_that_accept_multiple_arguments │ │ └── app.py │ │ ├── 07_generic_decorator_for_any_function │ │ └── app.py │ │ └── 08_multiple_decorators_one_function │ │ └── app.py ├── 18_advanced_oop │ ├── README.md │ └── sample_code │ │ ├── 1-multiple-inheritance │ │ ├── admin.py │ │ ├── app.py │ │ ├── database.py │ │ ├── saveable.py │ │ └── user.py │ │ ├── 2-abc │ │ ├── animal.py │ │ ├── app.py │ │ └── dog.py │ │ ├── 3-abc-2 │ │ ├── animal.py │ │ ├── app.py │ │ ├── dog.py │ │ └── monkey.py │ │ ├── 4-abc-3-and-interfaces │ │ ├── admin.py │ │ ├── app.py │ │ ├── database.py │ │ ├── saveable.py │ │ └── user.py │ │ └── 5-property-setters │ │ ├── app.py │ │ └── flight.py ├── 19_gui_development_tkinter │ ├── README.md │ └── lectures │ │ ├── 01_how_to_get_tkinter │ │ └── README.md │ │ ├── 03_tkinter_hello_world │ │ └── app.py │ │ ├── 04_greetings │ │ └── app.py │ │ ├── 05_packing_components │ │ └── code.py │ │ ├── 10_saving_files │ │ └── app.py │ │ ├── 11_opening_files │ │ └── app.py │ │ ├── 12_binding_shortcuts │ │ └── app.py │ │ ├── 13_checking_unsaved_changes │ │ └── app.py │ │ ├── 14_confirm_exit_with_unsaved_changes │ │ └── app.py │ │ ├── 15_closing_individual_tabs │ │ └── app.py │ │ ├── 16_adding_another_menu │ │ └── app.py │ │ ├── 17_adding_permanent_scrollbar │ │ └── app.py │ │ ├── 6_packing_with_frames │ │ └── code.py │ │ ├── 7_starting_our_text_editor │ │ └── README.md │ │ ├── 8_tkinter_notebook_and_creating_files │ │ └── app.py │ │ └── 9_adding_a_menu │ │ └── app.py ├── 1_intro │ ├── README.md │ ├── lectures │ │ ├── 10_and_or │ │ │ ├── README.md │ │ │ └── code.py │ │ ├── 11_lists │ │ │ └── code.py │ │ ├── 12_tuples │ │ │ └── code.py │ │ ├── 13_sets │ │ │ └── code.py │ │ ├── 14_advanced_set_operations │ │ │ └── code.py │ │ ├── 15_dictionaries │ │ │ └── code.py │ │ ├── 16_length_and_sum │ │ │ └── code.py │ │ ├── 17_joining_a_list │ │ │ └── code.py │ │ ├── 3_variables_printing │ │ │ └── code.py │ │ ├── 4_numbers │ │ │ └── code.py │ │ ├── 5_remainder │ │ │ └── code.py │ │ ├── 6_strings │ │ │ ├── README.md │ │ │ └── code.py │ │ ├── 7_string_formatting │ │ │ └── code.py │ │ ├── 8_user_input │ │ │ └── code.py │ │ └── 9_booleans │ │ │ └── code.py │ └── notes │ │ ├── 01. Data Types (cheatsheet).pdf │ │ ├── 01. Data Types.md │ │ ├── 02. Arithmetic Operators.md │ │ ├── 02. Operators (cheatsheet).pdf │ │ ├── 03. Comparison Operators.md │ │ ├── 04. Assignment Operators.md │ │ ├── 05. Logical Operators.md │ │ ├── 06. Identity Operators.md │ │ └── 07. Membership Operators.md ├── 20_unit_testing │ ├── README.md │ ├── code │ │ ├── 1_functions │ │ │ ├── functions.py │ │ │ └── test_functions.py │ │ ├── 2_classes │ │ │ ├── printer.py │ │ │ └── test_printer.py │ │ └── 3_external_libraries │ │ │ ├── page.py │ │ │ └── test_page.py │ └── lectures │ │ ├── 1_testing_equality │ │ ├── functions.py │ │ └── test_functions.py │ │ ├── 2_testing_errors │ │ ├── functions.py │ │ └── test_functions.py │ │ ├── 3_boundary_value_analysis │ │ └── README.md │ │ ├── 4_testing_multiplication │ │ ├── functions.py │ │ └── test_functions.py │ │ ├── 5_creating_our_printer_class │ │ └── printer.py │ │ ├── 6_setup_method │ │ ├── README.md │ │ ├── printer.py │ │ └── test_printer.py │ │ ├── 7_more_printer_tests │ │ ├── printer.py │ │ └── test_printer.py │ │ ├── 8_testing_external_libraries_with_mocks │ │ ├── page.py │ │ └── test_page.py │ │ └── 9_simple_testing_guidelines │ │ └── README.md ├── 21_algorithms_data_structures │ ├── README.md │ └── projects │ │ ├── binary_tree │ │ ├── app.py │ │ ├── binary_tree.py │ │ └── node.py │ │ └── samples │ │ ├── queue.py │ │ └── stack.py ├── 22_popular_libraries │ ├── README.md │ ├── projects │ │ └── sending_email │ │ │ ├── __init__.py │ │ │ ├── email_mailgun.py │ │ │ ├── email_smtplib.py │ │ │ ├── libs │ │ │ ├── __init__.py │ │ │ └── mailgun.py │ │ │ └── using_mailgun_lib.py │ └── video_code │ │ ├── 1_pylint │ │ └── end │ │ │ └── app.py │ │ ├── 2_yapf │ │ ├── end │ │ │ └── app.py │ │ └── start │ │ │ └── app.py │ │ ├── 3_sending_email │ │ └── end │ │ │ └── email_smtplib.py │ │ ├── 4_sending_email_mailgun │ │ └── end │ │ │ └── email_mailgun.py │ │ └── 5_creating_a_mailgun_library │ │ ├── end │ │ ├── email_mailgun.py │ │ ├── libs │ │ │ ├── __init__.py │ │ │ └── mailgun.py │ │ └── using_mailgun_lib.py │ │ └── start │ │ └── email_mailgun.py ├── 2_intro_to_python │ ├── README.md │ ├── lectures │ │ ├── 10_list_comprehensions │ │ │ └── code.py │ │ ├── 11_comprehensions_with_conditionals │ │ │ └── code.py │ │ ├── 12_set_dictionary_comprehensions │ │ │ └── code.py │ │ ├── 13_zip │ │ │ └── code.py │ │ ├── 14_enumerate │ │ │ └── code.py │ │ ├── 15_functions │ │ │ └── code.py │ │ ├── 16_arguments_and_parameters │ │ │ └── code.py │ │ ├── 17_return_values │ │ │ └── code.py │ │ ├── 18_default_parameter_values │ │ │ └── code.py │ │ ├── 19_lambda_functions │ │ │ └── code.py │ │ ├── 1_if_statements │ │ │ └── code.py │ │ ├── 20_first_class_functions │ │ │ └── code.py │ │ ├── 2_while_loops │ │ │ └── code.py │ │ ├── 3_for_loops │ │ │ └── code.py │ │ ├── 4_destructuring │ │ │ └── code.py │ │ ├── 5_iterating_over_dictionaries │ │ │ └── code.py │ │ ├── 6_break_continue │ │ │ └── code.py │ │ ├── 7_else_with_loops │ │ │ └── code.py │ │ ├── 8_finding_prime_numbers │ │ │ └── code.py │ │ └── 9_slicing │ │ │ └── code.py │ └── notes │ │ ├── 01. Conditions.md │ │ ├── 02. Functions.md │ │ ├── 02. Loops.md │ │ ├── 03. Comprehensions.md │ │ ├── 04. Destructuring.md │ │ ├── Comprehensions.pdf │ │ ├── Conditional.pdf │ │ ├── Destructuring.pdf │ │ ├── Functions.pdf │ │ └── Loops.pdf ├── 3_first_milestone_project │ ├── README.md │ ├── milestone_1 │ │ ├── app.py │ │ └── incomplete_app.py │ └── milestone_project_brief.pdf ├── 4_object_oriented_programming │ ├── README.md │ └── lectures │ │ ├── 01_dictionaries_and_objects │ │ └── app.py │ │ ├── 02_more_on_classes │ │ └── app.py │ │ ├── 03_magic_methods │ │ └── app.py │ │ ├── 04_inheritance │ │ └── app.py │ │ ├── 05_classmethod_staticmethod │ │ └── app.py │ │ └── 06_more_class_static_examples │ │ └── app.py ├── 5_errors │ ├── README.md │ ├── errors_project │ │ ├── app.py │ │ ├── errors.py │ │ └── user_score.py │ ├── lectures │ │ ├── 01_intro_to_errors │ │ │ └── app.py │ │ ├── 02_built_in_errors │ │ │ └── README.md │ │ ├── 03_raising_errors │ │ │ └── app.py │ │ ├── 04_creating_our_own_errors │ │ │ └── app.py │ │ ├── 05_dealing_with_python_errors │ │ │ └── app.py │ │ ├── 06_on_success_and_reraise │ │ │ └── app.py │ │ └── 07_handling_user_errors_exercise │ │ │ └── README.md │ └── side_projects │ │ └── handling_errors_in_user_input │ │ └── errors.py ├── 6_files │ ├── README.md │ ├── files_project │ │ ├── app.py │ │ ├── cars_json.txt │ │ ├── csv_data.txt │ │ ├── csv_read.py │ │ ├── data.txt │ │ ├── friends.py │ │ ├── friends_json.txt │ │ ├── json_context_managers.py │ │ ├── json_imports.py │ │ ├── nearby_friends.txt │ │ └── people.txt │ └── imports_project │ │ ├── app.py │ │ ├── test.txt │ │ └── utils │ │ ├── __init__.py │ │ ├── common │ │ ├── __init__.py │ │ └── file_operations.py │ │ ├── json_operations.py │ │ └── utils.py ├── 7_second_milestone_project │ ├── Milestone Project 2 Brief.pdf │ ├── README.md │ ├── milestone_2_files │ │ ├── app.py │ │ └── utils │ │ │ ├── __init__.py │ │ │ └── database.py │ ├── milestone_2_json │ │ ├── app.py │ │ ├── books.json │ │ └── utils │ │ │ ├── __init__.py │ │ │ └── database.py │ ├── milestone_2_lists │ │ ├── app.py │ │ └── utils │ │ │ ├── __init__.py │ │ │ └── database.py │ └── milestone_2_sql │ │ ├── app.py │ │ └── utils │ │ ├── __init__.py │ │ ├── database.py │ │ └── database_connection.py ├── 8_type_hinting │ ├── README.md │ └── lectures │ │ └── 01_typing_in_python │ │ └── README.md ├── 9_advanced_built_in_functions │ ├── README.md │ └── lectures │ │ ├── 01_generators │ │ └── app.py │ │ ├── 02_generator_classes_and_iterators │ │ └── app.py │ │ ├── 03_iterables_in_python │ │ └── app.py │ │ ├── 04_filter_function │ │ └── app.py │ │ ├── 05_map_function │ │ └── app.py │ │ ├── 06_any_and_all │ │ └── app.py │ │ └── 07_enumerate_function │ │ └── app.py ├── assets │ ├── images │ │ └── favicon.png │ └── logo.png └── index-page-contents.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src ├── components │ ├── HomepageFeatures │ │ ├── index.js │ │ └── styles.module.css │ ├── LockedVideoEmbed │ │ └── index.js │ └── VideoEmbed │ │ └── index.js ├── css │ └── custom.css └── pages │ ├── index.js │ └── index.module.css └── static └── img ├── cloud-download.svg ├── favicon.ico ├── folder-closed.svg ├── folder-open.svg ├── product-dev.svg ├── robot-coding.svg ├── undraw_docusaurus_mountain.svg ├── undraw_docusaurus_react.svg └── undraw_docusaurus_tree.svg /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | exclude = .git,__pycache__ 4 | max-complexity = 10 -------------------------------------------------------------------------------- /.github/workflows/algolia-scraper.yml: -------------------------------------------------------------------------------- 1 | name: Run Algolia Scraper 2 | 3 | on: 4 | push: 5 | branches: ["master", "develop"] 6 | pull_request: 7 | branches: ["master", "develop"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: darrenjennings/algolia-docsearch-action@master 18 | with: 19 | algolia_application_id: ${{ secrets.ALGOLIA_APP_ID }} 20 | algolia_api_key: ${{ secrets.ALGOLIA_API_KEY }} 21 | file: "algolia.config.json" 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rebrand-subs/ 2 | subs/ 3 | promo/ 4 | cpc-questions/ 5 | captions/ 6 | page_*.jpeg 7 | exercises/ 8 | Subtitles 9 | documents/ 10 | .env/ 11 | logs.txt 12 | data.db 13 | *.key 14 | *.indd 15 | *.png 16 | .DS_Store 17 | .idea/ 18 | .vscode/ 19 | __pycache__/ 20 | *.scriv 21 | *.pyc 22 | *.screenflow 23 | *.mp4 24 | *.mov 25 | *.cmproj 26 | exports/ 27 | videos/ 28 | *.numbers 29 | *.sketch 30 | *.pptx 31 | *.vtt 32 | *.zip 33 | **/old 34 | .venv 35 | .python-version 36 | 37 | # Dependencies 38 | /node_modules 39 | 40 | # Production 41 | /build 42 | /site 43 | 44 | # Generated files 45 | .docusaurus 46 | .cache-loader 47 | .cache 48 | 49 | # Misc 50 | .DS_Store 51 | .env.local 52 | .env.development.local 53 | .env.test.local 54 | .env.production.local 55 | 56 | npm-debug.log* 57 | yarn-debug.log* 58 | yarn-error.log* 59 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.3.0 4 | hooks: 5 | - id: check-yaml 6 | - repo: https://github.com/ambv/black 7 | rev: stable 8 | hooks: 9 | - id: black 10 | language_version: python3.10 11 | files: "projects/**" 12 | -------------------------------------------------------------------------------- /.templates/lecture.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The lecture title goes here 3 | description: A brief description of the lecture goes here. 4 | --- 5 | 6 | - [ ] Set metadata above 7 | - [ ] Start writing! 8 | - [ ] Create `start` folder 9 | - [ ] Create `end` folder 10 | - [ ] Write TL;DR 11 | - [ ] Create per-file diff between `end` and `start` (use "Compare Folders") 12 | 13 | 14 | 15 | # Lecture Title 16 | 17 | 18 | -------------------------------------------------------------------------------- /.templates/section.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Section name here" 3 | --- 4 | 5 | # Section name here 6 | 7 | Description of the section goes here. -------------------------------------------------------------------------------- /assets/course-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/assets/course-image.png -------------------------------------------------------------------------------- /assets/download-repo-zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/assets/download-repo-zip.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /course_contents/10_advanced_python/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Intermediate 3 | hidden: true 4 | --- 5 | # Advanced Python Development 6 | 7 | In this section of the course we look at some advanced Python features, such as: 8 | 9 | - Mutability 10 | - Default parameter values and how mutability plays into that 11 | - Argument unpacking 12 | - Python collections 13 | - Working with dates and times 14 | - Timing your code 15 | - Regular Expressions 16 | - Higher order functions and decorators -------------------------------------------------------------------------------- /course_contents/10_advanced_python/lectures/09_dates_and_times_python/Datetime cheatsheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/10_advanced_python/lectures/09_dates_and_times_python/Datetime cheatsheet.pdf -------------------------------------------------------------------------------- /course_contents/10_advanced_python/lectures/16_higher_order_functions/code.py: -------------------------------------------------------------------------------- 1 | def greet(): 2 | print("Hello!") 3 | 4 | # `before_and_after` is a higher-order function. That just means it's a function which has another function as a parameter. 5 | def before_and_after(func): # func is a function passed 6 | print("Before...") 7 | func() 8 | print("After...") 9 | 10 | 11 | # greet, not greet(). That's because we're passing the function, not the result of calling the function. 12 | before_and_after(greet) 13 | 14 | 15 | # Another example 16 | 17 | 18 | movies = [ 19 | {"name": "The Matrix", "director": "Wachowski"}, 20 | {"name": "A Beautiful Day in the Neighborhood", "director": "Heller"}, 21 | {"name": "The Irishman", "director": "Scorsese"}, 22 | {"name": "Klaus", "director": "Pablos"}, 23 | {"name": "1917", "director": "Mendes"}, 24 | ] 25 | 26 | 27 | def find_movie(expected, finder): 28 | found = [] 29 | for movie in movies: 30 | if finder(movie) == expected: 31 | found.append(movie) 32 | return found 33 | 34 | 35 | find_by = input("What property are we searching by? ") 36 | looking_for = input("What are you looking for? ") 37 | movie = find_movie(looking_for, lambda x: x[find_by]) 38 | print(movie or 'No movies found.') 39 | 40 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Practical Python 3 | hidden: true 4 | --- 5 | # Web Scraping 6 | 7 | In this section we look at web scraping using Python and the `requests` library. 8 | 9 | First we learn about HTML and its structure, and how we can let Python understand it. 10 | 11 | Then we build two scraper projects using `BeautifulSoup4`. -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-books/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/11_web_scraping/projects/scraping-books/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-books/locators/all_books_page.py: -------------------------------------------------------------------------------- 1 | class AllBooksPageLocators: 2 | BOOKS = 'div.page_inner section li.col-xs-6.col-sm-4.col-md-3.col-lg-3' 3 | PAGER = 'div.page_inner section ul.pager li.current' 4 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-books/locators/book_locators.py: -------------------------------------------------------------------------------- 1 | class BookLocators: 2 | """ 3 | Locators for an item in the HTML page. 4 | 5 | This allows us to easily see what our code will be looking at 6 | as well as change it quickly if we notice it is now different. 7 | """ 8 | NAME_LOCATOR = 'article.product_pod h3 a' 9 | LINK_LOCATOR = 'article.product_pod h3 a' 10 | PRICE_LOCATOR = 'article.product_pod p.price_color' 11 | RATING_LOCATOR = 'article.product_pod p.star-rating' 12 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-books/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/11_web_scraping/projects/scraping-books/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-books/pages/all_books_page.py: -------------------------------------------------------------------------------- 1 | import re 2 | import logging 3 | 4 | from locators.all_books_page import AllBooksPageLocators 5 | from parsers.book import BookParser 6 | from bs4 import BeautifulSoup 7 | 8 | logger = logging.getLogger('scraping.all_books_page') 9 | 10 | 11 | class AllBooksPage: 12 | def __init__(self, page): 13 | logger.debug('Parsing page content with BeautifulSoup HTML parser.') 14 | self.soup = BeautifulSoup(page, 'html.parser') 15 | 16 | @property 17 | def books(self): 18 | logger.debug(f'Finding all books in the page using `{AllBooksPageLocators.BOOKS}`') 19 | return [BookParser(e) for e in self.soup.select(AllBooksPageLocators.BOOKS)] 20 | 21 | @property 22 | def page_count(self): 23 | logger.debug('Finding all number of catalogue pages available...') 24 | content = self.soup.select_one(AllBooksPageLocators.PAGER).string 25 | logger.info(f'Found number of catalogue pages available: `{content}`') 26 | pattern = 'Page [0-9]+ of ([0-9]+)' 27 | matcher = re.search(pattern, content) 28 | pages = int(matcher.group(1)) 29 | logger.info(f'Extracted number of pages as integer: `{pages}`.') 30 | return pages 31 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-books/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/11_web_scraping/projects/scraping-books/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/app.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from pages.quotes_page import QuotesPage 4 | 5 | page_content = requests.get('http://quotes.toscrape.com').content 6 | page = QuotesPage(page_content) 7 | 8 | for quote in page.quotes: 9 | print(quote) 10 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/11_web_scraping/projects/scraping-quotes/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = 'span.text' 3 | AUTHOR_LOCATOR = 'small.author' 4 | TAGS_LOCATOR = 'div.tags a.tag' 5 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = 'div.quote' 3 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/11_web_scraping/projects/scraping-quotes/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/pages/quotes_page.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | 3 | from locators.quotes_page_locators import QuotesPageLocators 4 | from parsers.quote import QuoteParser 5 | 6 | 7 | class QuotesPage: 8 | def __init__(self, page): 9 | self.soup = BeautifulSoup(page, 'html.parser') 10 | 11 | @property 12 | def quotes(self): 13 | return [QuoteParser(e) for e in self.soup.select(QuotesPageLocators.QUOTE)] 14 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/11_web_scraping/projects/scraping-quotes/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/scraping-quotes/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from locators.quote_locators import QuoteLocators 2 | 3 | 4 | class QuoteParser: 5 | def __init__(self, parent): 6 | self.parent = parent 7 | 8 | def __repr__(self): 9 | return f'' 10 | 11 | @property 12 | def content(self): 13 | locator = QuoteLocators.CONTENT_LOCATOR 14 | return self.parent.select_one(locator).string 15 | 16 | @property 17 | def author(self): 18 | locator = QuoteLocators.AUTHOR_LOCATOR 19 | return self.parent.select_one(locator).string 20 | 21 | @property 22 | def tags(self): 23 | locator = QuoteLocators.TAGS_LOCATOR 24 | return self.parent.select(locator) 25 | -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/understanding-html/scraping_example.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | 4 | 5 | page = requests.get('http://www.example.com') 6 | soup = BeautifulSoup(page.content, 'html.parser') 7 | 8 | print(soup.find('h1').string) 9 | print(soup.select_one('p a').attrs['href']) -------------------------------------------------------------------------------- /course_contents/11_web_scraping/projects/understanding-html/simple_html_parsing.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | 3 | SIMPLE_HTML = ''' 4 | 5 | 6 |

This is a title

7 |

Lorem ipsum dolor sit amet. Consectetur edipiscim elit.

8 |

Here's another p without a class

9 | 15 | 16 | ''' 17 | 18 | simple_soup = BeautifulSoup(SIMPLE_HTML, 'html.parser') 19 | 20 | 21 | def find_title(): 22 | print(simple_soup.find('h1').string) 23 | 24 | 25 | def find_list_items(): 26 | list_items = simple_soup.find_all('li') 27 | list_content = [e.string for e in list_items] 28 | print(list_content) 29 | 30 | 31 | def find_paragraph(): 32 | print(simple_soup.find('p', {'class': 'subtitle'}).string) 33 | 34 | 35 | def find_other_paragraph(): 36 | paragraphs = simple_soup.find_all('p') 37 | other_paragraph = [p for p in paragraphs if 'subtitle' not in p.attrs.get('class', [])] 38 | print(other_paragraph[0].string) 39 | 40 | 41 | find_title() 42 | find_list_items() 43 | find_paragraph() 44 | find_other_paragraph() 45 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Practical Python 3 | hidden: true 4 | --- 5 | # Browser automation with Selenium 6 | 7 | The code is very similar to last section, but now we're launching a browser instead of requesting the page with Python. We will be controlling the browser, instead of just getting HTML. 8 | 9 | The browser will work like a normal browser: it will run JavaScript, it will have cookies, etc... 10 | 11 | ## Requirements 12 | 13 | - Selenium library 14 | - A webdriver 15 | 16 | ### Selenium 17 | 18 | Install `selenium` using PyCharm's preferences panel or in your virtual environment. 19 | 20 | ### Webdriver 21 | 22 | Each of the major browsers releases a webdriver for their browser. It essentially allows other applications to interact with the browser. In this course we use the Chrome webdriver. 23 | 24 | Download it from http://chromedriver.chromium.org/. Make sure to download the version for your browser (e.g. v74 if you're using Chrome v74). 25 | 26 | Place the de-compressed executable, `chromedriver`, into a folder. Remember the folder's path, as you'll need it after. 27 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | selenium = "*" 10 | 11 | [requires] 12 | python_version = "3.7" 13 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "726cf9b35c20af30d6b256dadd9e5a33f26e119f077d009d0a9d6a1e472bb13d" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "selenium": { 20 | "hashes": [ 21 | "sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c", 22 | "sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d" 23 | ], 24 | "index": "pypi", 25 | "version": "==3.141.0" 26 | }, 27 | "urllib3": { 28 | "hashes": [ 29 | "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c", 30 | "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098" 31 | ], 32 | "index": "pypi", 33 | "version": "==1.26.5" 34 | } 35 | }, 36 | "develop": {} 37 | } 38 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/app.py: -------------------------------------------------------------------------------- 1 | from pages.quotes_page import QuotesPage, InvalidTagForAuthorError 2 | from selenium import webdriver 3 | from selenium.webdriver.chrome.service import Service 4 | 5 | # chrome = webdriver.Chrome(executable_path='/usr/local/bin/chromedriver') 6 | # chrome.get('http://quotes.toscrape.com/search.aspx') 7 | # page = QuotesPage(chrome) 8 | 9 | # author = input("Enter the author you'd like quotes from: ") 10 | # page.select_author(author) 11 | 12 | # tags = page.get_available_tags() 13 | # print("Select one of these tags: [{}]".format(" | ".join(tags))) 14 | # selected_tag = input("Enter your tag: ") 15 | 16 | # page.select_tag(selected_tag) 17 | # page.search_button.click() 18 | # print(page.quotes) 19 | 20 | # -- 21 | 22 | try: 23 | author = input("Enter the author you'd like quotes from: ") 24 | tag = input("Enter your tag: ") 25 | 26 | service = Service("/usr/local/bin/chromedriver") 27 | chrome = webdriver.Chrome(service=service) 28 | chrome.get("http://quotes.toscrape.com/search.aspx") 29 | page = QuotesPage(chrome) 30 | 31 | print(page.search_for_quotes(author, tag)) 32 | except InvalidTagForAuthorError as e: 33 | print(e) 34 | except Exception: 35 | print("An unknown error occurred. Please try again.") 36 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/code/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = 'span.content' 3 | AUTHOR_LOCATOR = 'span.author' 4 | TAGS_LOCATOR = 'span.tag' 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = 'div.quote' 3 | AUTHOR_DROPDOWN = 'select#author' 4 | TAG_DROPDOWN = 'select#tag' 5 | SEARCH_BUTTON = 'input[name="submit_button"]' 6 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/code/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/code/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/code/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from locators.quote_locators import QuoteLocators 2 | 3 | 4 | class QuoteParser: 5 | def __init__(self, parent): 6 | self.parent = parent 7 | 8 | def __repr__(self): 9 | return f"" 10 | 11 | @property 12 | def content(self): 13 | locator = QuoteLocators.CONTENT_LOCATOR 14 | return self.parent.find_element_by_css_selector(locator).text 15 | 16 | @property 17 | def author(self): 18 | locator = QuoteLocators.AUTHOR_LOCATOR 19 | return self.parent.find_element_by_css_selector(locator).text 20 | 21 | @property 22 | def tag(self): 23 | locator = QuoteLocators.TAGS_LOCATOR 24 | return self.parent.find_element_by_css_selector(locator) 25 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | from pages.quotes_page import QuotesPage, InvalidTagForAuthorError 4 | 5 | 6 | try: 7 | author = input("Enter the author you'd like quotes from: ") 8 | tag = input("Enter your tag: ") 9 | 10 | service = Service("/usr/local/bin/chromedriver") 11 | chrome = webdriver.Chrome(service=service) 12 | chrome.get("http://quotes.toscrape.com/search.aspx") 13 | page = QuotesPage(chrome) 14 | 15 | print(page.search_for_quotes(author, tag)) 16 | except InvalidTagForAuthorError as e: 17 | print(e) 18 | except Exception as e: 19 | print(e) 20 | print("An unknown error occurred. Please try again.") 21 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = "span.content" 3 | AUTHOR_LOCATOR = "span.author" 4 | TAGS_LOCATOR = "span.tag" 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | AUTHOR_DROPDOWN = "select#author" 4 | TAG_DROPDOWN = "select#tag" 5 | TAG_DROPDOWN_VALUE_OPTION = "select#tag option[value]" 6 | SEARCH_BUTTON = 'input[name="submit_button"]' 7 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/11_adding_waits_to_our_code/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/app.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from pages.quotes_page import QuotesPage 4 | 5 | page_content = requests.get('http://quotes.toscrape.com').content 6 | page = QuotesPage(page_content) 7 | 8 | for quote in page.quotes: 9 | print(quote) 10 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = 'span.text' 3 | AUTHOR_LOCATOR = 'small.author' 4 | TAGS_LOCATOR = 'div.tags a.tag' 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/pages/quotes_page.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | 3 | from locators.quotes_page_locators import QuotesPageLocators 4 | from parsers.quote import QuoteParser 5 | 6 | 7 | class QuotesPage: 8 | def __init__(self, page): 9 | self.soup = BeautifulSoup(page, 'html.parser') 10 | 11 | @property 12 | def quotes(self): 13 | return [QuoteParser(e) for e in self.soup.select(QuotesPageLocators.QUOTE)] 14 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/1_our_scraping_code/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from locators.quote_locators import QuoteLocators 2 | 3 | 4 | class QuoteParser: 5 | def __init__(self, parent): 6 | self.parent = parent 7 | 8 | def __repr__(self): 9 | return f'' 10 | 11 | @property 12 | def content(self): 13 | locator = QuoteLocators.CONTENT_LOCATOR 14 | return self.parent.select_one(locator).string 15 | 16 | @property 17 | def author(self): 18 | locator = QuoteLocators.AUTHOR_LOCATOR 19 | return self.parent.select_one(locator).string 20 | 21 | @property 22 | def tags(self): 23 | locator = QuoteLocators.TAGS_LOCATOR 24 | return self.parent.select(locator) 25 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | 4 | from pages.quotes_page import QuotesPage 5 | 6 | service = Service("/usr/local/bin/chromedriver") 7 | chrome = webdriver.Chrome(service=service) 8 | chrome.get("http://quotes.toscrape.com") 9 | page = QuotesPage(chrome) 10 | 11 | for quote in page.quotes: 12 | print(quote) 13 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = 'span.text' 3 | AUTHOR_LOCATOR = 'small.author' 4 | TAGS_LOCATOR = 'div.tags a.tag' 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/pages/quotes_page.py: -------------------------------------------------------------------------------- 1 | from locators.quotes_page_locators import QuotesPageLocators 2 | from parsers.quote import QuoteParser 3 | 4 | 5 | class QuotesPage: 6 | def __init__(self, browser): 7 | self.browser = browser 8 | 9 | @property 10 | def quotes(self): 11 | return [ 12 | QuoteParser(e) 13 | for e in self.browser.find_elements( 14 | By.CSS_SELECTOR, QuotesPageLocators.QUOTE 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/3_using_chrome_in_scraping_code/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | 4 | from pages.quotes_page import QuotesPage 5 | 6 | service = Service("/usr/local/bin/chromedriver") 7 | chrome = webdriver.Chrome(service=service) 8 | chrome.get("http://quotes.toscrape.com") 9 | page = QuotesPage(chrome) 10 | 11 | for quote in page.quotes: 12 | print(quote) 13 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = "span.content" 3 | AUTHOR_LOCATOR = "span.author" 4 | TAGS_LOCATOR = "span.tag" 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | AUTHOR_DROPDOWN = "select#author" 4 | TAG_DROPDOWN = "select#tag" 5 | SEARCH_BUTTON = 'input[name="submit_button"]' 6 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/pages/quotes_page.py: -------------------------------------------------------------------------------- 1 | from locators.quotes_page_locators import QuotesPageLocators 2 | from parsers.quote import QuoteParser 3 | 4 | 5 | class QuotesPage: 6 | def __init__(self, browser): 7 | self.browser = browser 8 | 9 | @property 10 | def quotes(self): 11 | return [ 12 | QuoteParser(e) 13 | for e in self.browser.find_elements( 14 | By.CSS_SELECTOR, QuotesPageLocators.QUOTE 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/4_our_new_page_locators/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | from pages.quotes_page import QuotesPage 4 | 5 | service = Service("/usr/local/bin/chromedriver") 6 | chrome = webdriver.Chrome(service=service) 7 | chrome.get("http://quotes.toscrape.com/search.aspx") 8 | page = QuotesPage(chrome) 9 | 10 | author = input("Enter the author you'd like quotes from: ") 11 | page.select_author(author) 12 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = "span.content" 3 | AUTHOR_LOCATOR = "span.author" 4 | TAGS_LOCATOR = "span.tag" 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | AUTHOR_DROPDOWN = "select#author" 4 | TAG_DROPDOWN = "select#tag" 5 | SEARCH_BUTTON = 'input[name="submit_button"]' 6 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/pages/quotes_page.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from selenium.webdriver.common.by import By 3 | from selenium.webdriver.support.ui import Select 4 | 5 | from locators.quotes_page_locators import QuotesPageLocators 6 | from parsers.quote import QuoteParser 7 | 8 | 9 | class QuotesPage: 10 | def __init__(self, browser): 11 | self.browser = browser 12 | 13 | @property 14 | def quotes(self) -> List[QuoteParser]: 15 | return [ 16 | QuoteParser(e) 17 | for e in self.browser.find_elements( 18 | By.CSS_SELECTOR, QuotesPageLocators.QUOTE 19 | ) 20 | ] 21 | 22 | @property 23 | def author_dropdown(self) -> Select: 24 | element = self.browser.find_element( 25 | By.CSS_SELECTOR, QuotesPageLocators.AUTHOR_DROPDOWN 26 | ) 27 | return Select(element) 28 | 29 | def select_author(self, author_name: str): 30 | self.author_dropdown.select_by_visible_text(author_name) 31 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/5_interacting_with_dropdowns/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | from pages.quotes_page import QuotesPage 4 | 5 | service = Service("/usr/local/bin/chromedriver") 6 | chrome = webdriver.Chrome(service=service) 7 | chrome.get("http://quotes.toscrape.com/search.aspx") 8 | page = QuotesPage(chrome) 9 | 10 | author = input("Enter the author you'd like quotes from: ") 11 | page.select_author(author) 12 | 13 | tags = page.get_available_tags() 14 | print("Select one of these tags: [{}]".format(" | ".join(tags))) 15 | selected_tag = input("Enter your tag: ") 16 | 17 | page.select_tag(selected_tag) 18 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = "span.content" 3 | AUTHOR_LOCATOR = "span.author" 4 | TAGS_LOCATOR = "span.tag" 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | AUTHOR_DROPDOWN = "select#author" 4 | TAG_DROPDOWN = "select#tag" 5 | SEARCH_BUTTON = 'input[name="submit_button"]' 6 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/6_selecting_tags/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | from pages.quotes_page import QuotesPage 4 | 5 | service = Service("/usr/local/bin/chromedriver") 6 | chrome = webdriver.Chrome(service=service) 7 | chrome.get("http://quotes.toscrape.com/search.aspx") 8 | page = QuotesPage(chrome) 9 | 10 | author = input("Enter the author you'd like quotes from: ") 11 | page.select_author(author) 12 | 13 | tags = page.get_available_tags() 14 | print("Select one of these tags: [{}]".format(" | ".join(tags))) 15 | selected_tag = input("Enter your tag: ") 16 | 17 | page.select_tag(selected_tag) 18 | page.search_button.click() 19 | 20 | print(page.quotes) 21 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = "span.content" 3 | AUTHOR_LOCATOR = "span.author" 4 | TAGS_LOCATOR = "span.tag" 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | AUTHOR_DROPDOWN = "select#author" 4 | TAG_DROPDOWN = "select#tag" 5 | SEARCH_BUTTON = 'input[name="submit_button"]' 6 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/7_searching_for_quotes/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | from pages.quotes_page import QuotesPage 4 | 5 | author = input("Enter the author you'd like quotes from: ") 6 | tag = input("Enter your tag: ") 7 | 8 | service = Service("/usr/local/bin/chromedriver") 9 | chrome = webdriver.Chrome(service=service) 10 | chrome.get("http://quotes.toscrape.com/search.aspx") 11 | page = QuotesPage(chrome) 12 | 13 | print(page.search_for_quotes(author, tag)) 14 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = "span.content" 3 | AUTHOR_LOCATOR = "span.author" 4 | TAGS_LOCATOR = "span.tag" 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | AUTHOR_DROPDOWN = "select#author" 4 | TAG_DROPDOWN = "select#tag" 5 | SEARCH_BUTTON = 'input[name="submit_button"]' 6 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/8_encapsulating_logic_simply/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/app.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.chrome.service import Service 3 | from pages.quotes_page import QuotesPage, InvalidTagForAuthorError 4 | 5 | 6 | try: 7 | author = input("Enter the author you'd like quotes from: ") 8 | tag = input("Enter your tag: ") 9 | 10 | service = Service("/usr/local/bin/chromedriver") 11 | chrome = webdriver.Chrome(service=service) 12 | chrome.get("http://quotes.toscrape.com/search.aspx") 13 | page = QuotesPage(chrome) 14 | 15 | print(page.search_for_quotes(author, tag)) 16 | except InvalidTagForAuthorError as e: 17 | print(e) 18 | except Exception as e: 19 | print(e) 20 | print("An unknown error occurred. Please try again.") 21 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/locators/quote_locators.py: -------------------------------------------------------------------------------- 1 | class QuoteLocators: 2 | CONTENT_LOCATOR = "span.content" 3 | AUTHOR_LOCATOR = "span.author" 4 | TAGS_LOCATOR = "span.tag" 5 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/locators/quotes_page_locators.py: -------------------------------------------------------------------------------- 1 | class QuotesPageLocators: 2 | QUOTE = "div.quote" 3 | AUTHOR_DROPDOWN = "select#author" 4 | TAG_DROPDOWN = "select#tag" 5 | SEARCH_BUTTON = 'input[name="submit_button"]' 6 | -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/12_browser_automation_selenium/lectures/9_adding_some_error_handling/parsers/quote.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.common.by import By 2 | from locators.quote_locators import QuoteLocators 3 | 4 | 5 | class QuoteParser: 6 | def __init__(self, parent): 7 | self.parent = parent 8 | 9 | def __repr__(self): 10 | return f"" 11 | 12 | @property 13 | def content(self): 14 | locator = QuoteLocators.CONTENT_LOCATOR 15 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 16 | 17 | @property 18 | def author(self): 19 | locator = QuoteLocators.AUTHOR_LOCATOR 20 | return self.parent.find_element(By.CSS_SELECTOR, locator).text 21 | 22 | @property 23 | def tags(self): 24 | locator = QuoteLocators.TAGS_LOCATOR 25 | return self.parent.find_elements(By.CSS_SELECTOR, locator) 26 | -------------------------------------------------------------------------------- /course_contents/13_async_development/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Practical Python 3 | hidden: true 4 | --- 5 | # Async Development with Python 6 | 7 | Python is a single-threaded language, which means that asynchronous development can sometimes be tricky. 8 | 9 | In this section we learn about multi-processing, multi-threading, and async development using coroutines and the `async` and `await` keywords. 10 | 11 | These three different types of asynchronous development each have their strengths and weaknesses, so it's important to understand them all. -------------------------------------------------------------------------------- /course_contents/13_async_development/lectures/01_code_samples/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | hidden: false 3 | --- 4 | 5 | # Code samples for this section 6 | 7 | Hey, welcome back! 8 | 9 | This section won't have code available on our online editor, but instead all the code we'll write here is available on the GitHub page. 10 | 11 | Again, don't read through the code before going through some of the videos. The code is here for you to check as you progress through the section. Otherwise, it could get a bit confusing! 12 | 13 | Here's a link to the code in this section: https://github.com/tecladocode/complete-python-course/tree/master/course_contents/13_async_development/sample_code 14 | 15 | As always, if you have any questions please ask away in the Course Q&A. 16 | 17 | Happy coding! 18 | 19 | Jose—your instructor -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | 3 | url = "https://pypi.python.org/simple" 4 | verify_ssl = true 5 | name = "pypi" 6 | 7 | 8 | [packages] 9 | 10 | aiohttp = "==3.7.4" 11 | requests = "==2.20.0" 12 | beautifulsoup4 = "==4.6.0" 13 | 14 | 15 | [dev-packages] 16 | 17 | -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/locators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/13_async_development/projects/async_scraping/locators/__init__.py -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/locators/all_books_page.py: -------------------------------------------------------------------------------- 1 | class AllBooksPageLocators: 2 | BOOKS = 'div.page_inner section li.col-xs-6.col-sm-4.col-md-3.col-lg-3' 3 | PAGER = 'div.page_inner section ul.pager li.current' 4 | -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/locators/book_locators.py: -------------------------------------------------------------------------------- 1 | class BookLocators: 2 | """ 3 | Locators for an item in the HTML page. 4 | 5 | This allows us to easily see what our code will be looking at 6 | as well as change it quickly if we notice it is now different. 7 | """ 8 | NAME_LOCATOR = 'article.product_pod h3 a' 9 | LINK_LOCATOR = 'article.product_pod h3 a' 10 | PRICE_LOCATOR = 'article.product_pod p.price_color' 11 | RATING_LOCATOR = 'article.product_pod p.star-rating' 12 | -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/13_async_development/projects/async_scraping/pages/__init__.py -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/pages/all_books_page.py: -------------------------------------------------------------------------------- 1 | import re 2 | import logging 3 | 4 | from locators.all_books_page import AllBooksPageLocators 5 | from parsers.book import BookParser 6 | from bs4 import BeautifulSoup 7 | 8 | logger = logging.getLogger('scraping.all_books_page') 9 | 10 | 11 | class AllBooksPage: 12 | def __init__(self, page): 13 | logger.debug('Parsing page content with BeautifulSoup HTML parser.') 14 | self.soup = BeautifulSoup(page, 'html.parser') 15 | 16 | @property 17 | def books(self): 18 | logger.debug(f'Finding all books in the page using `{AllBooksPageLocators.BOOKS}`') 19 | return [BookParser(e) for e in self.soup.select(AllBooksPageLocators.BOOKS)] 20 | 21 | @property 22 | def page_count(self): 23 | logger.debug('Finding all number of catalogue pages available...') 24 | content = self.soup.select_one(AllBooksPageLocators.PAGER).string 25 | logger.info(f'Found number of catalogue pages available: `{content}`') 26 | pattern = 'Page [0-9]+ of ([0-9]+)' 27 | matcher = re.search(pattern, content) 28 | pages = int(matcher.group(1)) 29 | logger.info(f'Extracted number of pages as integer: `{pages}`.') 30 | return pages 31 | -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/13_async_development/projects/async_scraping/parsers/__init__.py -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.0.5 2 | requests==2.18.4 3 | beautifulsoup4==4.6.0 -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/samples/0_first_async_request.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | import asyncio 3 | 4 | async def fetch_page(url): 5 | async with aiohttp.ClientSession() as session: 6 | async with session.get(url) as response: 7 | print(response.status) 8 | return response.status 9 | 10 | loop = asyncio.get_event_loop() 11 | loop.run_until_complete(fetch_page('http://google.com')) -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/samples/1_seemingly_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import time 4 | 5 | async def fetch_page(session, url): 6 | start = time.time() 7 | async with session.get(url) as response: 8 | print(f'{url} took {time.time() - start}') 9 | return response.status 10 | 11 | 12 | async def get_multiple_pages(loop, *urls): 13 | pages = [] 14 | async with aiohttp.ClientSession(loop=loop) as session: 15 | for url in urls: 16 | pages.append(await fetch_page(session, url)) 17 | return pages 18 | 19 | 20 | if __name__ == '__main__': 21 | 22 | def main(): 23 | loop = asyncio.get_event_loop() 24 | urls = [ 25 | 'http://google.com', 26 | 'http://example.com', 27 | 'http://tecladocode.com/blog' 28 | ] 29 | start = time.time() 30 | pages = loop.run_until_complete(get_multiple_pages(loop, *urls)) 31 | print(f'Total took {time.time() - start}') 32 | for page in pages: 33 | print(page) 34 | 35 | main() -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/samples/2_actually_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import time 4 | 5 | async def fetch_page(session, url): 6 | start = time.time() 7 | async with session.get(url) as response: 8 | print(f'{url} took {time.time() - start}') 9 | return response.status 10 | 11 | 12 | async def get_multiple_pages(loop, *urls): 13 | tasks = [] 14 | async with aiohttp.ClientSession(loop=loop) as session: 15 | for url in urls: 16 | tasks.append(fetch_page(session, url)) 17 | return await asyncio.gather(*tasks) 18 | 19 | 20 | if __name__ == '__main__': 21 | 22 | def main(): 23 | loop = asyncio.get_event_loop() 24 | urls = [ 25 | 'http://google.com', 26 | 'http://example.com', 27 | 'http://tecladocode.com/blog' 28 | ] 29 | start = time.time() 30 | pages = loop.run_until_complete(get_multiple_pages(loop, *urls)) 31 | print(f'Total took {time.time() - start}') 32 | for page in pages: 33 | print(page) 34 | 35 | main() -------------------------------------------------------------------------------- /course_contents/13_async_development/projects/async_scraping/samples/3_with_async_timeout.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import async_timeout 4 | import time 5 | 6 | async def fetch_page(session, url): 7 | async with async_timeout.timeout(10): 8 | start = time.time() 9 | async with session.get(url) as response: 10 | print(f'{url} took {time.time() - start}') 11 | return response.status 12 | 13 | 14 | async def get_multiple_pages(loop, *urls): 15 | tasks = [] 16 | async with aiohttp.ClientSession(loop=loop) as session: 17 | for url in urls: 18 | tasks.append(fetch_page(session, url)) 19 | return await asyncio.gather(*tasks) 20 | 21 | 22 | if __name__ == '__main__': 23 | 24 | def main(): 25 | loop = asyncio.get_event_loop() 26 | urls = [ 27 | 'http://google.com', 28 | 'http://example.com', 29 | 'http://tecladocode.com/blog' 30 | ] 31 | start = time.time() 32 | pages = loop.run_until_complete(get_multiple_pages(loop, *urls)) 33 | print(f'Total took {time.time() - start}') 34 | for page in pages: 35 | print(page) 36 | 37 | main() -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/10_generators_two_way.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | friends = deque(('Rolf', 'Jose', 'Charlie', 'Jen', 'Anna')) 4 | 5 | 6 | def get_friend(): 7 | yield from friends 8 | 9 | 10 | def greet(g): 11 | while True: 12 | try: 13 | friend = next(g) 14 | yield f'HELLO {friend}' 15 | except StopIteration: 16 | pass 17 | 18 | 19 | friends_generator = get_friend() 20 | g = greet(friends_generator) 21 | print(next(g)) 22 | print(next(g)) 23 | 24 | -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/11_receiving_through_yield.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | friends = deque(('Rolf', 'Jose', 'Charlie', 'Jen', 'Anna')) 4 | 5 | 6 | def friend_upper(): 7 | while friends: 8 | friend = friends.popleft().upper() 9 | greeting = yield 10 | print(f'{greeting} {friend}') 11 | 12 | 13 | def greet(g): 14 | yield from g 15 | 16 | 17 | greeter = greet(friend_upper()) 18 | greeter.send(None) 19 | greeter.send('Hello') 20 | print('Hello, world! Multitasking...') 21 | greeter.send('How are you,') 22 | -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/12_async_await.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from types import coroutine 3 | 4 | friends = deque(('Rolf', 'Jose', 'Charlie', 'Jen', 'Anna')) 5 | 6 | 7 | @coroutine 8 | def friend_upper(): 9 | while friends: 10 | friend = friends.popleft().upper() 11 | greeting = yield 12 | print(f'{greeting} {friend}') 13 | 14 | 15 | async def greet(g): 16 | print('Starting...') 17 | await g 18 | print('Ending...') 19 | 20 | 21 | greeter = greet(friend_upper()) 22 | greeter.send(None) 23 | greeter.send('Hello') 24 | 25 | greeting = input('Enter a greeting: ') 26 | greeter.send(greeting) 27 | 28 | greeting = input('Enter a greeting: ') 29 | greeter.send(greeting) 30 | -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/1_threads.py: -------------------------------------------------------------------------------- 1 | import time 2 | from threading import Thread 3 | 4 | ####### SINGLE THREAD 5 | 6 | def ask_user(): 7 | start = time.time() 8 | user_input = input('Enter your name: ') 9 | greet = f'Hello, {user_input}' 10 | print(greet) 11 | print('ask_user: ', time.time() - start) 12 | 13 | def complex_calculation(): 14 | print('Started calculating...') 15 | start = time.time() 16 | [x**2 for x in range(20000000)] 17 | print('complex_calculation: ', time.time() - start) 18 | 19 | 20 | # With a single thread, we can do one at a time—e.g. 21 | start = time.time() 22 | ask_user() 23 | complex_calculation() 24 | print('Single thread total time: ', time.time() - start, '\n\n') 25 | 26 | 27 | ####### TWO THREADS 28 | 29 | 30 | # With two threads, we can do them both at once... 31 | thread = Thread(target=complex_calculation) 32 | thread2 = Thread(target=ask_user) 33 | 34 | start = time.time() 35 | 36 | thread.start() 37 | thread2.start() 38 | 39 | thread.join() 40 | thread2.join() 41 | 42 | print('Two thread total time: ', time.time() - start) 43 | 44 | # Run this and see what happens! 45 | -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/2_threads_with_pool.py: -------------------------------------------------------------------------------- 1 | import time 2 | from concurrent.futures import ThreadPoolExecutor 3 | 4 | ####### SINGLE THREAD 5 | 6 | def ask_user(): 7 | start = time.time() 8 | user_input = input('Enter your name: ') 9 | greet = f'Hello, {user_input}' 10 | print(greet) 11 | print('ask_user: ', time.time() - start) 12 | 13 | def complex_calculation(): 14 | print('Started calculating...') 15 | start = time.time() 16 | [x**2 for x in range(20000000)] 17 | print('complex_calculation: ', time.time() - start) 18 | 19 | 20 | # With a single thread, we can do one at a time—e.g. 21 | start = time.time() 22 | ask_user() 23 | complex_calculation() 24 | print('Single thread total time: ', time.time() - start, '\n\n') 25 | 26 | 27 | ####### TWO THREADS 28 | 29 | 30 | # With two threads, we can do them both at once... 31 | start = time.time() 32 | 33 | with ThreadPoolExecutor(max_workers=2) as pool: 34 | pool.submit(complex_calculation) 35 | pool.submit(ask_user) 36 | 37 | # All this does is we don't have to call pool.shutdown() 38 | 39 | print('Two thread total time: ', time.time() - start) 40 | 41 | # Run this and see what happens! 42 | -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/3_processes.py: -------------------------------------------------------------------------------- 1 | from multiprocessing import Process 2 | import time 3 | 4 | ####### SINGLE PROCESS 5 | 6 | def ask_user(): 7 | start = time.time() 8 | user_input = input('Enter your name: ') 9 | greet = f'Hello, {user_input}' 10 | print(greet) 11 | print('ask_user: ', time.time() - start) 12 | 13 | def complex_calculation(): 14 | print('Started calculating...') 15 | start = time.time() 16 | [x**2 for x in range(20000000)] 17 | print('complex_calculation: ', time.time() - start) 18 | 19 | 20 | # With a single thread, we can do one at a time—e.g. 21 | start = time.time() 22 | ask_user() 23 | complex_calculation() 24 | print('Single thread total time: ', time.time() - start, '\n\n') 25 | 26 | 27 | ####### TWO PROCESSES 28 | 29 | 30 | # With two processes, we can do them both at once... 31 | process = Process(target=complex_calculation) 32 | process.start() 33 | 34 | start = time.time() 35 | 36 | ask_user() 37 | 38 | process.join() # this waits for the process to finish 39 | 40 | print('Two process total time: ', time.time() - start) 41 | 42 | # Run this and see what happens! 43 | 44 | -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/4_processes_with_pool.py: -------------------------------------------------------------------------------- 1 | import time 2 | from concurrent.futures import ProcessPoolExecutor 3 | 4 | ####### SINGLE PROCESS 5 | 6 | def ask_user(): 7 | start = time.time() 8 | user_input = input('Enter your name: ') 9 | greet = f'Hello, {user_input}' 10 | print(greet) 11 | print('ask_user: ', time.time() - start) 12 | 13 | def complex_calculation(): 14 | print('Started calculating...') 15 | start = time.time() 16 | [x**2 for x in range(20000000)] 17 | print('complex_calculation: ', time.time() - start) 18 | 19 | 20 | # With a single thread, we can do one at a time—e.g. 21 | start = time.time() 22 | ask_user() 23 | complex_calculation() 24 | print('Single thread total time: ', time.time() - start, '\n\n') 25 | 26 | 27 | ####### TWO PROCESSES 28 | 29 | 30 | # With two processes, we can do them both at once... 31 | start = time.time() 32 | 33 | with ProcessPoolExecutor(max_workers=2) as pool: 34 | pool.submit(complex_calculation) 35 | pool.submit(complex_calculation) 36 | 37 | print('Two process total time: ', time.time() - start) 38 | 39 | # Run this and see what happens! 40 | 41 | -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/5_non_locked_threads.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | import time 3 | import random 4 | 5 | counter = 0 6 | 7 | def increment_counter(): 8 | global counter 9 | time.sleep(random.randint(0, 2)) 10 | counter += 1 11 | time.sleep(random.randint(0, 2)) 12 | print(f'New counter value: {counter}') 13 | time.sleep(random.randint(0, 2)) 14 | print('-----------') 15 | 16 | 17 | 18 | for x in range(10): 19 | t = Thread(target=increment_counter) 20 | time.sleep(random.randint(0, 2)) 21 | t.start() -------------------------------------------------------------------------------- /course_contents/13_async_development/sample_code/9_generators.py: -------------------------------------------------------------------------------- 1 | def countdown(n): 2 | while n > 0: 3 | yield n 4 | n -= 1 5 | 6 | 7 | tasks = [countdown(10), countdown(5), countdown(20)] 8 | 9 | while tasks: 10 | task = tasks[0] 11 | tasks.remove(task) 12 | try: 13 | x = next(task) 14 | print(x) 15 | tasks.append(task) 16 | except StopIteration: 17 | print('Task finished') 18 | -------------------------------------------------------------------------------- /course_contents/14_managing_projects_pipenv/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Practical Python 3 | hidden: true 4 | --- 5 | # Managing projects with Pipenv 6 | 7 | In this section we briefly look at managing your project dependencies using Pipenv. 8 | 9 | Please read the [Pipenv PDF](using_pipenv.pdf) for more information. -------------------------------------------------------------------------------- /course_contents/14_managing_projects_pipenv/using_pipenv.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/14_managing_projects_pipenv/using_pipenv.pdf -------------------------------------------------------------------------------- /course_contents/15_flask/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Practical Python 3 | hidden: true 4 | --- 5 | # Web Development with Flask 6 | 7 | In this section we learn about making websites using the Flask framework, HTML, and Jinja. 8 | 9 | We build a project, which you can see broken down into steps inside [the project folder](projects/first-flask-app-lectures/). -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/1-first-flask-endpoint/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | # We create a new Flask app with a unique name (normally we use '__main__' since that's always unique across all files imported in the project). 4 | app = Flask(__name__) 5 | 6 | 7 | # This decorator registers the '/' route with our app. 8 | # When a user visits the '/' route (which is the homepage), the home() function will execute. 9 | @app.route('/') 10 | def home(): 11 | """ 12 | This function will run when the user visits the '/' route (the homepage). 13 | It just returns 'Hello, world!' as any good first application should! 14 | """ 15 | return 'Hello, world!' 16 | 17 | 18 | # Only run the code inside the if statement if we've execute this file. 19 | # We don't want to run our app if we have imported this file, as running the app 20 | # would block and prevent importing this file, since it runs until the app is shut down. 21 | if __name__ == '__main__': 22 | # Run the app. 23 | # The debug=True flag is just here for development purposes. 24 | # It gives us more information if an error happens. 25 | app.run(debug=True) -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/2-returning-information/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | 4 | app = Flask(__name__) 5 | posts = { 6 | 0: { 7 | 'title': 'Hello, world', 8 | 'content': 'This is my first blog post!' 9 | } 10 | } 11 | 12 | 13 | @app.route('/') 14 | def home(): 15 | return 'Hello, world!' 16 | 17 | 18 | # This route expects to be in the format of /post/0 (for example). 19 | # Then it will pass 0 as argument to the post() function. 20 | @app.route('/post/') 21 | def post(post_id): 22 | """ 23 | This function runs when a user visits route such as: 24 | 25 | - /post/0 26 | - /post/2 27 | - /post/99 28 | 29 | But not: 30 | 31 | - /post/a 32 | - /post/something/else 33 | - /posts/1 34 | 35 | Then we get the 0 as a number (not a string!) as argument, so we can use it. 36 | """ 37 | post = posts.get(post_id) # Retrieve the post from our global posts dictionary by the ID passed in as argument. 38 | return f"Post {post['title']}, content:\n\n{post['content']}" # Return the title and content formatted a bit nicer. 39 | 40 | 41 | if __name__ == '__main__': 42 | app.run(debug=True) 43 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/3-rendering-html/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | 4 | app = Flask(__name__) 5 | posts = { 6 | 0: { 7 | 'title': 'Hello, world', 8 | 'content': 'This is my first blog post!' 9 | } 10 | } 11 | 12 | 13 | @app.route('/') 14 | def home(): 15 | return 'Hello, world!' 16 | 17 | 18 | @app.route('/post/') 19 | def post(post_id): 20 | """ 21 | This function renders a template from the `templates` folder in the directory of app.py. 22 | It will find the `post.jinja2` template and render it with the data passed in. 23 | 24 | Look at `post.jinja2` for information on how the variable `post` gets used there. 25 | """ 26 | return render_template('post.jinja2', post=posts.get(post_id)) 27 | 28 | 29 | if __name__ == '__main__': 30 | app.run(debug=True) 31 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/3-rendering-html/templates/post.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 |

{{ post['title'] }}

10 |

{{ post['content'] }}

11 | 12 | 13 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/4-error-pages/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | 4 | app = Flask(__name__) 5 | posts = { 6 | 0: { 7 | 'title': 'Hello, world', 8 | 'content': 'This is my first blog post!' 9 | } 10 | } 11 | 12 | 13 | @app.route('/') 14 | def home(): 15 | return 'Hello, world!' 16 | 17 | 18 | @app.route('/post/') 19 | def post(post_id): 20 | post = posts.get(post_id) 21 | # If the post was not found, then we can render a different page instead—the user will see an error page. 22 | if not post: 23 | # Here we pass another variable, the string `message`. 24 | # It can be used inside the jinja template. 25 | return render_template('404.jinja2', message=f'A post with id {post_id} was not found.') 26 | return render_template('post.jinja2', post=post) 27 | 28 | 29 | if __name__ == '__main__': 30 | app.run(debug=True) 31 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/4-error-pages/templates/404.jinja2: -------------------------------------------------------------------------------- 1 | 2 | {% extends 'base.jinja2' %} 3 | 4 | 6 | 7 | {% block content %} 8 |

404 — Post not found

9 |

{{ message }}

10 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/4-error-pages/templates/base.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | {% block content %} 9 | 10 | {% endblock %} 11 | 12 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/4-error-pages/templates/post.jinja2: -------------------------------------------------------------------------------- 1 | 2 | {% extends 'base.jinja2' %} 3 | 4 | 6 | 7 | {% block content %} 8 |

{{ post['title'] }}

9 |

{{ post['content'] }}

10 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/5-rendering-forms/templates/404.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

404 — Post not found

5 |

{{ message }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/5-rendering-forms/templates/base.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block content %} 5 | 6 | {% endblock %} 7 | 8 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/5-rendering-forms/templates/create.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 | 5 |

Create post

6 | 7 |
8 | 9 |
10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 |
26 |
27 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/5-rendering-forms/templates/post.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

{{ post['title'] }}

5 |

{{ post['content'] }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/6-forms-with-post/templates/404.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

404 — Post not found

5 |

{{ message }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/6-forms-with-post/templates/base.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block content %} 5 | 6 | {% endblock %} 7 | 8 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/6-forms-with-post/templates/create.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

Create post

5 |
6 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 |
21 |
22 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/6-forms-with-post/templates/post.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

{{ post['title'] }}

5 |

{{ post['content'] }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/7-single-endpoint-for-form/templates/404.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

404 — Post not found

5 |

{{ message }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/7-single-endpoint-for-form/templates/base.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block content %} 5 | 6 | {% endblock %} 7 | 8 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/7-single-endpoint-for-form/templates/create.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

Create post

5 |
6 | 9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/7-single-endpoint-for-form/templates/post.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

{{ post['title'] }}

5 |

{{ post['content'] }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/8-jinja-for-loops/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, redirect, url_for 2 | 3 | 4 | app = Flask(__name__) 5 | posts = { 6 | 0: { 7 | 'title': 'Hello, world', 8 | 'content': 'This is my first blog post!' 9 | } 10 | } 11 | 12 | 13 | @app.route('/') 14 | def home(): 15 | return render_template('home.jinja2', posts=posts) 16 | 17 | 18 | @app.route('/post/') 19 | def post(post_id): 20 | post = posts.get(post_id) 21 | if not post: 22 | return render_template('404.jinja2', message=f'A post with id {post_id} was not found.') 23 | return render_template('post.jinja2', post=post) 24 | 25 | 26 | @app.route('/post/create', methods=['GET', 'POST']) 27 | def create(): 28 | if request.method == 'POST': 29 | title = request.form.get('title') 30 | content = request.form.get('content') 31 | post_id = len(posts) 32 | posts[post_id] = {'id': post_id, 'title': title, 'content': content} 33 | 34 | return redirect(url_for('post', post_id=post_id)) 35 | return render_template('create.jinja2') 36 | 37 | 38 | if __name__ == '__main__': 39 | app.run(debug=True) 40 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/8-jinja-for-loops/templates/404.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

404 — Post not found

5 |

{{ message }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/8-jinja-for-loops/templates/base.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block content %} 5 | 6 | {% endblock %} 7 | 8 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/8-jinja-for-loops/templates/create.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

Create post

5 |
6 | 7 | 8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 |
21 |
22 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/8-jinja-for-loops/templates/home.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

Welcome to your blog.

5 | 6 |

Posts

7 | 8 |
    9 | 10 | {% for post_id, post in posts.items() %} 11 | 12 |
  • {{ post['title'] }}
  • 13 | {% endfor %} 14 |
15 | {% endblock %} 16 | 17 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/8-jinja-for-loops/templates/post.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

{{ post['title'] }}

5 |

{{ post['content'] }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/9-adding-home-link/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, redirect, url_for 2 | 3 | 4 | app = Flask(__name__) 5 | posts = { 6 | 0: { 7 | 'title': 'Hello, world', 8 | 'content': 'This is my first blog post!' 9 | } 10 | } 11 | 12 | 13 | @app.route('/') 14 | def home(): 15 | return render_template('home.jinja2', posts=posts) 16 | 17 | 18 | @app.route('/post/') 19 | def post(post_id): 20 | post = posts.get(post_id) 21 | if not post: 22 | return render_template('404.jinja2', message=f'A post with id {post_id} was not found.') 23 | return render_template('post.jinja2', post=post) 24 | 25 | 26 | @app.route('/post/create', methods=['GET', 'POST']) 27 | def create(): 28 | if request.method == 'POST': 29 | title = request.form.get('title') 30 | content = request.form.get('content') 31 | post_id = len(posts) 32 | posts[post_id] = {'id': post_id, 'title': title, 'content': content} 33 | 34 | return redirect(url_for('post', post_id=post_id)) 35 | return render_template('create.jinja2') 36 | 37 | 38 | if __name__ == '__main__': 39 | app.run(debug=True) 40 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/9-adding-home-link/templates/404.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

404 — Post not found

5 |

{{ message }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/9-adding-home-link/templates/base.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Home 6 | {% block content %} 7 | 8 | {% endblock %} 9 | 10 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/9-adding-home-link/templates/create.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

Create post

5 |
6 | 9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/9-adding-home-link/templates/home.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

Welcome to your blog.

5 |

Posts

6 | 7 | 12 | 13 | Create new post 14 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/9-adding-home-link/templates/post.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block content %} 4 |

{{ post['title'] }}

5 |

{{ post['content'] }}

6 | {% endblock %} -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | 3 | url = "https://pypi.python.org/simple" 4 | verify_ssl = true 5 | name = "pypi" 6 | 7 | 8 | [packages] 9 | 10 | flask = "*" 11 | 12 | 13 | [dev-packages] 14 | 15 | -------------------------------------------------------------------------------- /course_contents/15_flask/projects/first-flask-app-lectures/README.md: -------------------------------------------------------------------------------- 1 | Hey there! Welcome to your first Flask app. 2 | 3 | The completed project can be seen at https://github.com/tecladocode/first-flask-app. 4 | 5 | The various files in this project, numbered from 1 onwards, represent different steps in implementing this first Flask app, as covered in our comprehensive Python course: [The Complete Python Course](https://go.tecla.do/cpc). -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Practical Python 3 | hidden: true 4 | --- 5 | # Interacting with APIs 6 | 7 | ## Requirements 8 | 9 | - requests 10 | - cachetools (for part 3) 11 | - An account with OpenExchangeRates (free) 12 | - Generate an **App ID** on their website, you need this to use their API. 13 | 14 | ## Recap 15 | 16 | First two parts recapped here: https://blog.tecladocode.com/how-to-interact-with-apis-using-python/ 17 | 18 | ## Caching 19 | 20 | Can use something like `functools.lru_cache` for caching function calls. That is, if you apply this decorator to a function and then you call the function with the same arguments 10 times, 9 of them will be really quick and the function won't evaluate. 21 | 22 | Can use `cachetools.TTLCache` to cache a function call for up to a certain amount of time. When interacting with APIs it can be useful as sometimes we won't be interested in repeating the same call over and over. -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/code/1_simple_interaction/app.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | APP_ID = "72dba35060b54cf9ad3ffbdc68de9174" 4 | ENDPOINT = "https://openexchangerates.org/api/latest.json" 5 | 6 | response = requests.get(f"{ENDPOINT}?app_id={APP_ID}") 7 | exchange_rates = response.json() 8 | 9 | usd_amount = 1000 10 | gbp_amount = usd_amount * exchange_rates['rates']['GBP'] 11 | 12 | print(f"USD{usd_amount} is GBP{gbp_amount}") -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/code/2_creating_a_library/app.py: -------------------------------------------------------------------------------- 1 | from libs.openexchange import OpenExchangeClient 2 | 3 | APP_ID = "72dba35060b54cf9ad3ffbdc68de9174" 4 | 5 | client = OpenExchangeClient(APP_ID) 6 | 7 | usd_amount = 1000 8 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 9 | 10 | print(f"USD{usd_amount} is GBP{gbp_amount}") -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/code/2_creating_a_library/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/16_interacting_with_apis/code/2_creating_a_library/libs/__init__.py -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/code/2_creating_a_library/libs/openexchange.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import functools 3 | 4 | 5 | class OpenExchangeClient: 6 | BASE_URL = "https://openexchangerates.org/api/" 7 | 8 | def __init__(self, app_id): 9 | self.app_id = app_id 10 | 11 | @property 12 | def latest(self): 13 | return requests.get(f"{self.BASE_URL}/latest.json?app_id={self.app_id}").json() 14 | 15 | def convert(self, from_amount, from_currency, to_currency): 16 | rates = self.latest['rates'] 17 | to_rate = rates[to_currency] 18 | 19 | if from_currency == 'USD': 20 | return from_amount * to_rate 21 | else: 22 | from_in_usd = from_amount / rates[from_currency] 23 | return from_in_usd * to_rate 24 | 25 | -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/code/3_speed_up_with_cache/app.py: -------------------------------------------------------------------------------- 1 | from libs.openexchange import OpenExchangeClient 2 | import time 3 | 4 | 5 | 6 | APP_ID = "72dba35060b54cf9ad3ffbdc68de9174" 7 | 8 | client = OpenExchangeClient(APP_ID) 9 | 10 | usd_amount = 1000 11 | start = time.time() 12 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 13 | print(f'First call took {time.time() - start} seconds.') 14 | 15 | start = time.time() 16 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 17 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 18 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 19 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 20 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 21 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 22 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 23 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 24 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 25 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 26 | print(f'After, 10 calls took {time.time() - start} seconds.') 27 | 28 | print(f"USD{usd_amount} is GBP{gbp_amount}") -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/code/3_speed_up_with_cache/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/16_interacting_with_apis/code/3_speed_up_with_cache/libs/__init__.py -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/code/3_speed_up_with_cache/libs/openexchange.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from cachetools import cached, TTLCache 3 | 4 | 5 | class OpenExchangeClient: 6 | BASE_URL = "https://openexchangerates.org/api/" 7 | 8 | def __init__(self, app_id): 9 | self.app_id = app_id 10 | 11 | @property 12 | @cached(cache=TTLCache(maxsize=2, ttl=900)) 13 | def latest(self): 14 | return requests.get(f"{self.BASE_URL}/latest.json?app_id={self.app_id}").json() 15 | 16 | def convert(self, from_amount, from_currency, to_currency): 17 | rates = self.latest['rates'] 18 | to_rate = rates[to_currency] 19 | 20 | if from_currency == 'USD': 21 | return from_amount * to_rate 22 | else: 23 | from_in_usd = from_amount / rates[from_currency] 24 | return from_in_usd * to_rate 25 | 26 | -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/3_getting_all_exchange_rates/code.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | APP_ID = "c640347fae594fc5add36046c807b282" 4 | ENDPOINT = "https://openexchangerates.org/api/latest.json" 5 | 6 | response = requests.get(f"{ENDPOINT}?app_id={APP_ID}") 7 | exchange_rates = response.json() 8 | 9 | usd_amount = 1000 10 | gbp_amount = usd_amount * exchange_rates["rates"]["GBP"] 11 | 12 | print(f"USD{usd_amount} is GBP{gbp_amount}") 13 | -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/4_creating_a_currency_exchange_library/app.py: -------------------------------------------------------------------------------- 1 | from libs.openexchange import OpenExchangeClient 2 | 3 | APP_ID = "72dba35060b54cf9ad3ffbdc68de9174" 4 | 5 | client = OpenExchangeClient(APP_ID) 6 | 7 | usd_amount = 1000 8 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 9 | 10 | print(f"USD{usd_amount} is GBP{gbp_amount}") -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/4_creating_a_currency_exchange_library/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/16_interacting_with_apis/lectures/4_creating_a_currency_exchange_library/libs/__init__.py -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/4_creating_a_currency_exchange_library/libs/openexchange.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import functools 3 | 4 | 5 | class OpenExchangeClient: 6 | BASE_URL = "https://openexchangerates.org/api/" 7 | 8 | def __init__(self, app_id): 9 | self.app_id = app_id 10 | 11 | @property 12 | def latest(self): 13 | return requests.get(f"{self.BASE_URL}/latest.json?app_id={self.app_id}").json() 14 | 15 | def convert(self, from_amount, from_currency, to_currency): 16 | rates = self.latest['rates'] 17 | to_rate = rates[to_currency] 18 | 19 | if from_currency == 'USD': 20 | return from_amount * to_rate 21 | else: 22 | from_in_usd = from_amount / rates[from_currency] 23 | return from_in_usd * to_rate 24 | 25 | -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/5_caching_with_cachetools/app.py: -------------------------------------------------------------------------------- 1 | from libs.openexchange import OpenExchangeClient 2 | import time 3 | 4 | 5 | 6 | APP_ID = "72dba35060b54cf9ad3ffbdc68de9174" 7 | 8 | client = OpenExchangeClient(APP_ID) 9 | 10 | usd_amount = 1000 11 | start = time.time() 12 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 13 | print(f'First call took {time.time() - start} seconds.') 14 | 15 | start = time.time() 16 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 17 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 18 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 19 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 20 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 21 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 22 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 23 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 24 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 25 | gbp_amount = client.convert(usd_amount, 'USD', 'GBP') 26 | print(f'After, 10 calls took {time.time() - start} seconds.') 27 | 28 | print(f"USD{usd_amount} is GBP{gbp_amount}") -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/5_caching_with_cachetools/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/16_interacting_with_apis/lectures/5_caching_with_cachetools/libs/__init__.py -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/5_caching_with_cachetools/libs/openexchange.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from cachetools import cached, TTLCache 3 | 4 | 5 | class OpenExchangeClient: 6 | BASE_URL = "https://openexchangerates.org/api/" 7 | 8 | def __init__(self, app_id): 9 | self.app_id = app_id 10 | 11 | @property 12 | @cached(cache=TTLCache(maxsize=2, ttl=900)) 13 | def latest(self): 14 | return requests.get(f"{self.BASE_URL}/latest.json?app_id={self.app_id}").json() 15 | 16 | def convert(self, from_amount, from_currency, to_currency): 17 | rates = self.latest['rates'] 18 | to_rate = rates[to_currency] 19 | 20 | if from_currency == 'USD': 21 | return from_amount * to_rate 22 | else: 23 | from_in_usd = from_amount / rates[from_currency] 24 | return from_in_usd * to_rate 25 | 26 | -------------------------------------------------------------------------------- /course_contents/16_interacting_with_apis/lectures/6_functools_lru_cache/code.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import time 3 | 4 | 5 | @functools.lru_cache(2) 6 | def cached_function(value): 7 | for i in range(value): 8 | i ** value 9 | 10 | 11 | def timed(): 12 | start = time.time() 13 | cached_function(4647) 14 | print(time.time() - start) 15 | 16 | 17 | timed() 18 | timed() 19 | -------------------------------------------------------------------------------- /course_contents/17_decorators/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Advanced 3 | hidden: true 4 | --- 5 | # Decorators in Python 6 | 7 | Decorators allow us to extend another function without modifying it directly. 8 | 9 | The video course goes into a lot of detail. You can see this in the [lectures](https://github.com/tecladocode/complete-python-course/tree/master/course_contents/17_decorators/lectures/). 10 | 11 | We've also made a [full free mini-course on decorators](https://blog.teclado.com/decorators-in-python/)! -------------------------------------------------------------------------------- /course_contents/17_decorators/lectures/01_simple_decorators/app.py: -------------------------------------------------------------------------------- 1 | user = {"username": "jose123", "access_level": "admin"} 2 | 3 | 4 | def user_has_permission(func): 5 | def secure_func(): 6 | if user.get("access_level") == "admin": 7 | return func() 8 | 9 | return secure_func 10 | 11 | 12 | def my_function(): 13 | return "Password for admin panel is 1234." 14 | 15 | 16 | my_secure_function = user_has_permission(my_function) 17 | 18 | print(my_secure_function()) 19 | -------------------------------------------------------------------------------- /course_contents/17_decorators/lectures/02_the_at_syntax/app.py: -------------------------------------------------------------------------------- 1 | user = {"username": "jose123", "access_level": "guest"} 2 | 3 | 4 | def user_has_permission(func): 5 | def secure_func(): 6 | if user.get("access_level") == "admin": 7 | return func() 8 | 9 | return secure_func 10 | 11 | 12 | @user_has_permission 13 | def my_function(): 14 | """ 15 | Allows us to retrieve the password for the admin panel. 16 | """ 17 | return "Password for admin panel is 1234." 18 | 19 | 20 | @user_has_permission 21 | def another(): 22 | pass 23 | 24 | 25 | print(my_function.__name__) 26 | print(another.__name__) 27 | -------------------------------------------------------------------------------- /course_contents/17_decorators/lectures/03_functools_wraps/app.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | user = {"username": "jose123", "access_level": "guest"} 4 | 5 | 6 | def user_has_permission(func): 7 | @functools.wraps(func) 8 | def secure_func(): 9 | if user.get("access_level") == "admin": 10 | return func() 11 | 12 | return secure_func 13 | 14 | 15 | @user_has_permission 16 | def my_function(): 17 | """ 18 | Allows us to retrieve the password for the admin panel. 19 | """ 20 | return "Password for admin panel is 1234." 21 | 22 | 23 | @user_has_permission 24 | def another(): 25 | pass 26 | 27 | 28 | print(my_function.__name__) 29 | print(another.__name__) 30 | -------------------------------------------------------------------------------- /course_contents/17_decorators/lectures/04_decorating_functions_with_parameters/app.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | user = {"username": "jose123", "access_level": "admin"} 4 | 5 | 6 | def user_has_permission(func): 7 | @functools.wraps(func) 8 | def secure_func(panel): 9 | if user.get("access_level") == "admin": 10 | return func(panel) 11 | 12 | return secure_func 13 | 14 | 15 | @user_has_permission 16 | def my_function(panel): 17 | """ 18 | Allows us to retrieve the password for the admin panel. 19 | """ 20 | return f"Password for {panel} panel is 1234." 21 | 22 | 23 | print(my_function.__name__) 24 | 25 | print(my_function("movies")) 26 | -------------------------------------------------------------------------------- /course_contents/17_decorators/lectures/05_decorators_with_parameters/app.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | user = {"username": "jose123", "access_level": "user"} 4 | 5 | 6 | def user_has_permission(access_level): 7 | def my_decorator(func): 8 | @functools.wraps(func) 9 | def secure_func(panel): 10 | if user.get("access_level") == access_level: 11 | return func(panel) 12 | 13 | return secure_func 14 | 15 | return my_decorator 16 | 17 | 18 | @user_has_permission("user") 19 | def my_function(panel): 20 | """ 21 | Allows us to retrieve the password for the admin panel. 22 | """ 23 | return f"Password for {panel} panel is 1234." 24 | 25 | 26 | print(my_function.__name__) 27 | print(my_function("movies")) 28 | -------------------------------------------------------------------------------- /course_contents/17_decorators/lectures/06_functions_that_accept_multiple_arguments/app.py: -------------------------------------------------------------------------------- 1 | def add_all(*args): 2 | return sum(args) 3 | 4 | 5 | def pretty_print(**kwargs): 6 | for k, v in kwargs.items(): 7 | print(f"For {k} we have {v}.") 8 | 9 | 10 | pretty_print(**{"username": "jose123", "access_level": "admin"}) 11 | -------------------------------------------------------------------------------- /course_contents/17_decorators/lectures/07_generic_decorator_for_any_function/app.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | user = {"username": "jose123", "access_level": "admin"} 4 | 5 | 6 | def user_has_permission(func): 7 | @functools.wraps(func) 8 | def secure_func(*args, **kwargs): 9 | if user.get("access_level") == "admin": 10 | return func(*args, **kwargs) 11 | 12 | return secure_func 13 | 14 | 15 | @user_has_permission 16 | def my_function(panel): 17 | """ 18 | Allows us to retrieve the password for the admin panel. 19 | """ 20 | return f"Password for {panel} panel is 1234." 21 | 22 | 23 | @user_has_permission 24 | def another(): 25 | pass 26 | 27 | 28 | print(my_function.__name__) 29 | 30 | print(my_function("movies")) 31 | print(another()) 32 | -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Advanced 3 | hidden: true 4 | --- 5 | # Advanced Object-Oriented Programming with Python 6 | 7 | In this section we look at some more advanced OOP concepts, such as multiple inheritance, abstract classes, interfaces, and properties. 8 | 9 | These are not things you'll use very often, but they are tools in your arsenal which can come in handy! -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/1-multiple-inheritance/admin.py: -------------------------------------------------------------------------------- 1 | from user import User 2 | from saveable import Saveable 3 | 4 | class Admin(User, Saveable): 5 | def __init__(self, username, password, access): 6 | super(Admin, self).__init__(username, password) 7 | self.access = access 8 | 9 | def __repr__(self): 10 | return f'' 11 | 12 | def to_dict(self): 13 | return { 14 | 'username': self.username, 15 | 'password': self.password, 16 | 'access': self.access 17 | } 18 | -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/1-multiple-inheritance/app.py: -------------------------------------------------------------------------------- 1 | from database import Database 2 | from admin import Admin 3 | 4 | a = Admin('paco', 'perez', 2) 5 | b = Admin('rolf', 'smith', 1) 6 | 7 | a.save() 8 | b.save() 9 | 10 | user = Database.find(lambda x: x['username'] == 'paco')[0] 11 | user_obj = Admin(**user) 12 | print(user_obj.username) -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/1-multiple-inheritance/database.py: -------------------------------------------------------------------------------- 1 | class Database: 2 | content = {'users': []} 3 | 4 | @classmethod 5 | def insert(cls, data): 6 | cls.content['users'].append(data) 7 | 8 | @classmethod 9 | def remove(cls, finder): 10 | cls.content['users'] = [user for user in cls.content['users'] if not finder(user)] 11 | 12 | @classmethod 13 | def find(cls, finder): 14 | return [user for user in cls.content['users'] if finder(user)] 15 | -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/1-multiple-inheritance/saveable.py: -------------------------------------------------------------------------------- 1 | from database import Database 2 | 3 | class Saveable: 4 | def save(self): 5 | Database.insert(self.to_dict()) 6 | -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/1-multiple-inheritance/user.py: -------------------------------------------------------------------------------- 1 | class User: 2 | def __init__(self, username, password): 3 | self.username = username 4 | self.password = password 5 | 6 | def login(self): 7 | return 'Logged in!' 8 | 9 | def __repr__(self): 10 | return f'' -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/2-abc/animal.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | class Animal(metaclass=ABCMeta): 4 | def walk(self): 5 | print('Walking...') 6 | 7 | def eat(self): 8 | print('Eating...') 9 | 10 | @abstractmethod 11 | def num_legs(): 12 | pass -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/2-abc/app.py: -------------------------------------------------------------------------------- 1 | from animal import Animal 2 | from dog import Dog 3 | 4 | d = Dog('Bob') 5 | # a = Animal() 6 | 7 | d.walk() -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/2-abc/dog.py: -------------------------------------------------------------------------------- 1 | from animal import Animal 2 | 3 | 4 | class Dog(Animal): 5 | def __init__(self, name): 6 | self.name = name 7 | 8 | def num_legs(self): 9 | return 4 -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/3-abc-2/animal.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | class Animal(metaclass=ABCMeta): 4 | def __init__(self, name): 5 | self.name = name 6 | 7 | def walk(self): 8 | print('Walking...') 9 | 10 | def eat(self): 11 | print('Eating...') 12 | 13 | @abstractmethod 14 | def num_legs(): 15 | pass -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/3-abc-2/app.py: -------------------------------------------------------------------------------- 1 | from dog import Dog 2 | from monkey import Monkey 3 | 4 | d = Dog('Bob') 5 | m = Monkey('Rolf') 6 | 7 | d.walk() 8 | m.eat() -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/3-abc-2/dog.py: -------------------------------------------------------------------------------- 1 | from animal import Animal 2 | 3 | 4 | class Dog(Animal): 5 | def num_legs(self): 6 | return 4 -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/3-abc-2/monkey.py: -------------------------------------------------------------------------------- 1 | from animal import Animal 2 | 3 | 4 | class Monkey(Animal): 5 | def num_legs(self): 6 | return 2 -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/4-abc-3-and-interfaces/admin.py: -------------------------------------------------------------------------------- 1 | from user import User 2 | 3 | class Admin(User): 4 | def __init__(self, username, password, access): 5 | super(Admin, self).__init__(username, password) 6 | self.access = access 7 | 8 | def __repr__(self): 9 | return f'' 10 | 11 | def to_dict(self): 12 | return { 13 | 'username': self.username, 14 | 'password': self.password, 15 | 'access': self.access 16 | } 17 | -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/4-abc-3-and-interfaces/app.py: -------------------------------------------------------------------------------- 1 | from database import Database 2 | from admin import Admin 3 | from user import User 4 | from saveable import Saveable 5 | 6 | a = Admin('paco', 'perez', 2) 7 | b = Admin('rolf', 'smith', 1) 8 | 9 | a.save() 10 | b.save() 11 | 12 | user = Database.find(lambda x: x['username'] == 'paco')[0] 13 | user_obj = Admin(**user) 14 | print(user_obj.username) 15 | 16 | print(isinstance(user_obj, Saveable)) # This is True because it's a subclass 17 | 18 | # You can do things like these without worry: 19 | 20 | users_to_save = [a, b, User('jose', '1234')] 21 | for u in users_to_save: 22 | u.save() # This is fine, because all users (Admin and User) implement the Saveable interface so we know they have a .save() method -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/4-abc-3-and-interfaces/database.py: -------------------------------------------------------------------------------- 1 | class Database: 2 | content = {'users': []} 3 | 4 | @classmethod 5 | def insert(cls, data): 6 | cls.content['users'].append(data) 7 | 8 | @classmethod 9 | def remove(cls, finder): 10 | cls.content['users'] = [user for user in cls.content['users'] if not finder(user)] 11 | 12 | @classmethod 13 | def find(cls, finder): 14 | return [user for user in cls.content['users'] if finder(user)] 15 | -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/4-abc-3-and-interfaces/saveable.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from database import Database 4 | 5 | class Saveable(metaclass=ABCMeta): 6 | def save(self): 7 | Database.insert(self.to_dict()) 8 | 9 | # @classmethod (or @staticmethod, or @property) 10 | @abstractmethod # @abstractmethod must always be the innermost decorator if used in conjunction with other decorators. 11 | def to_dict(): 12 | pass 13 | -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/4-abc-3-and-interfaces/user.py: -------------------------------------------------------------------------------- 1 | from saveable import Saveable 2 | 3 | class User(Saveable): 4 | def __init__(self, username, password): 5 | self.username = username 6 | self.password = password 7 | 8 | def login(self): 9 | return 'Logged in!' 10 | 11 | def __repr__(self): 12 | return f'' 13 | 14 | def to_dict(self): 15 | return { 16 | 'username': self.username, 17 | 'password': self.password 18 | } -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/5-property-setters/app.py: -------------------------------------------------------------------------------- 1 | from flight import Segment, Flight 2 | 3 | 4 | seg = [Segment('EDI', 'LHR'), Segment('LHR', 'CAN')] 5 | flight = Flight(seg) 6 | 7 | print(flight.departure_point) 8 | print(flight) 9 | 10 | flight.departure_point = 'GLA' 11 | print('...Set departure to GLA...') 12 | 13 | print(flight.departure_point) 14 | print(flight) -------------------------------------------------------------------------------- /course_contents/18_advanced_oop/sample_code/5-property-setters/flight.py: -------------------------------------------------------------------------------- 1 | class Flight: 2 | def __init__(self, segments): 3 | """ 4 | Creates a new Flight wrapper object from an arbitrary number of segments. 5 | 6 | :param segments: a list of segments in this flight—normally just one. 7 | """ 8 | self.segments = segments 9 | 10 | def __repr__(self): 11 | stops = [self.segments[0].departure, self.segments[0].destination] 12 | for seg in self.segments[1:]: 13 | stops.append(seg.destination) 14 | 15 | return ' -> '.join(stops) 16 | 17 | @property 18 | def departure_point(self): 19 | return self.segments[0].departure 20 | 21 | @departure_point.setter 22 | def departure_point(self, val): 23 | dest = self.segments[0].destination 24 | self.segments[0] = Segment(departure=val, destination=dest) 25 | 26 | 27 | class Segment: 28 | def __init__(self, departure, destination): 29 | self.departure = departure 30 | self.destination = destination 31 | -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Advanced 3 | hidden: true 4 | --- 5 | 6 | # GUI Development with Tkinter 7 | 8 | In this section we learn about making desktop applications using Python's `tkinter` library. 9 | 10 | You can see a breakdown of all the [lectures](https://github.com/tecladocode/complete-python-course/tree/master/course_contents/19_gui_development_tkinter/lectures/), which build a Tkinter project incrementally. -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/lectures/01_how_to_get_tkinter/README.md: -------------------------------------------------------------------------------- 1 | # Getting Tkinter 2 | 3 | Tkinter should be installed already. Try to run this in IDLE: 4 | 5 | ``` 6 | import tkinter 7 | tkinter._test() 8 | ``` 9 | 10 | If that fails, read on... 11 | 12 | ## Installing Tkinter on Windows 13 | 14 | Tkinter comes with Python on Windows, if you selected the appropriate option in the official Windows installer. 15 | 16 | If you have IDLE on Windows, you have Tkinter. 17 | 18 | If not, run the installer again making sure that Tcl/Tk is enabled in the installation. 19 | 20 | Instructions: https://stackoverflow.com/a/50027385/1587271 21 | 22 | ## Installing Tkinter on Mac 23 | 24 | I recommend installing Python via the official installer, which comes with Tkinter. 25 | 26 | ## Installing Tkinter on Ubuntu 27 | 28 | Enable your virtual environment, and then run: 29 | 30 | ``` 31 | sudo apt-get install python3-tk 32 | ``` -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/lectures/03_tkinter_hello_world/app.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | 5 | def greet(): 6 | print("Hello, World!") 7 | 8 | 9 | root = tk.Tk() 10 | root.title("Hello") 11 | 12 | greet_button = ttk.Button(root, text="Greet", command=greet) 13 | greet_button.pack(side="left", fill="x", expand=True) 14 | 15 | quit_button = ttk.Button(root, text="Quit", command=root.destroy) 16 | quit_button.pack(side="left", fill="x", expand=True) # could use side="right" 17 | 18 | root.mainloop() 19 | -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/lectures/04_greetings/app.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | 5 | def greet(): 6 | # The get() method is used to fetch the value of a StringVar() instance. 7 | # If user_name is empty, print Hello, World! 8 | print(f"Hello, {user_name.get() or 'World'}!") 9 | 10 | 11 | root = tk.Tk() 12 | root.title("Greeter") 13 | 14 | # Here we create an instances of the StringVar() class, which is to track the content of widgets 15 | user_name = tk.StringVar() 16 | 17 | 18 | name_label = ttk.Label(root, text="Name: ") 19 | name_label.pack(side="left", padx=(0, 10)) 20 | name_entry = ttk.Entry(root, width=15, textvariable=user_name) 21 | name_entry.pack(side="left") 22 | name_entry.focus() 23 | 24 | greet_button = ttk.Button(root, text="Greet", command=greet) 25 | greet_button.pack(side="left", fill="x", expand=True) 26 | 27 | root.mainloop() 28 | -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/lectures/6_packing_with_frames/code.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | root = tk.Tk() 5 | 6 | main = ttk.Frame(root) 7 | main.pack(side="left", fill="both", expand=True) 8 | 9 | tk.Label(main, text="Label top", bg="red").pack(side="top", expand=True, fill="both") 10 | tk.Label(main, text="Label top", bg="red").pack(side="top", expand=True, fill="both") 11 | tk.Label(root, text="Label left", bg="green").pack( 12 | side="left", expand=True, fill="both" 13 | ) 14 | 15 | root.mainloop() 16 | -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/lectures/7_starting_our_text_editor/README.md: -------------------------------------------------------------------------------- 1 | # Starting our Text Editor 2 | 3 | Brief description of project. 4 | 5 | Image of final project that we want to create. 6 | -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/lectures/8_tkinter_notebook_and_creating_files/app.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | 5 | def create_file(): 6 | text_area = tk.Text(notebook) 7 | text_area.pack(fill="both", expand=True) 8 | 9 | notebook.add(text_area, text="Untitled") 10 | notebook.select(text_area) 11 | 12 | 13 | root = tk.Tk() 14 | root.title("Teclado Text Editor") 15 | 16 | main = ttk.Frame(root) 17 | main.pack(fill="both", expand=True, padx=(1), pady=(4, 0)) 18 | 19 | notebook = ttk.Notebook(main) 20 | notebook.pack(fill="both", expand=True) 21 | 22 | create_file() 23 | 24 | root.mainloop() 25 | -------------------------------------------------------------------------------- /course_contents/19_gui_development_tkinter/lectures/9_adding_a_menu/app.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | 5 | def create_file(): 6 | text_area = tk.Text(notebook) 7 | text_area.pack(fill="both", expand=True) 8 | 9 | notebook.add(text_area, text="Untitled") 10 | notebook.select(text_area) 11 | 12 | 13 | root = tk.Tk() 14 | root.title("Teclado Text Editor") 15 | root.option_add("*tearOff", False) 16 | 17 | main = ttk.Frame(root) 18 | main.pack(fill="both", expand=True, padx=(1), pady=(4, 0)) 19 | 20 | menubar = tk.Menu(root) 21 | root.config(menu=menubar) 22 | 23 | file_menu = tk.Menu(menubar) 24 | 25 | menubar.add_cascade(menu=file_menu, label="File") 26 | 27 | file_menu.add_command(label="New", command=create_file) 28 | 29 | notebook = ttk.Notebook(main) 30 | notebook.pack(fill="both", expand=True) 31 | 32 | create_file() 33 | 34 | root.mainloop() 35 | -------------------------------------------------------------------------------- /course_contents/1_intro/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Introduction 3 | id: intro 4 | --- 5 | 6 | # Course Introduction 7 | 8 | Welcome to the very first section of the **[Complete Python Course](https://go.tecla.do/complete-python-sale)**! This section is for complete beginners to Python and coding. 9 | 10 | We'll cover: 11 | 12 | - Variables, numbers 13 | - String formatting 14 | - User input 15 | - Booleans 16 | - Lists, tuples, sets 17 | - Dictionaries 18 | 19 | On the left hand side you'll see a table of contents with all the lectures for which we have written an e-book page. At the moment, since this e-book is new, that's just the [`and` and `or` lecture](/intro/lectures/and_or/). More coming soon! 20 | 21 | You can see the [lectures](https://github.com/tecladocode/complete-python-course/tree/master/course_contents/1_intro/lectures) folder for a break down of all the topics and the code in each. 22 | 23 | In the [notes](https://github.com/tecladocode/complete-python-course/tree/master/course_contents/1_intro/notes) folder you can download Markdown or PDF cheatsheets to help you remember some key points. -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/12_tuples/code.py: -------------------------------------------------------------------------------- 1 | # -- Defining tuples -- 2 | 3 | short_tuple = "Rolf", "Bob" 4 | a_bit_clearer = ("Rolf", "Bob") 5 | not_a_tuple = "Rolf" 6 | 7 | # -- Adding to a tuple -- 8 | 9 | friends = ("Rolf", "Bob", "Anne") 10 | friends.append("Jen") # ERROR! 11 | 12 | print(friends) # ["Rolf", "Bob", "Anne", "Jen"] 13 | 14 | # -- Removing from a tuple -- 15 | 16 | friends.remove("Bob") # ERROR! 17 | 18 | print(friends) # ["Rolf", "Anne", "Jen"] 19 | 20 | # Tuples are useful for when you want to keep it unchanged forever. 21 | # Most of the time I'd recommend using tuples over lists, and only use lists when you specifically want to allow changes. 22 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/13_sets/code.py: -------------------------------------------------------------------------------- 1 | # -- Defining sets -- 2 | 3 | art_friends = {"Rolf", "Anne"} 4 | science_friends = {"Jen", "Charlie"} 5 | 6 | # -- Adding to a set -- 7 | 8 | art_friends.add("Jen") 9 | 10 | print(art_friends) 11 | 12 | # -- No duplicate items -- 13 | 14 | art_friends.add("Jen") 15 | 16 | print(art_friends) # Same as before, "Jen" was not added twice 17 | 18 | # -- Removing from a set -- 19 | 20 | science_friends.remove("Charlie") 21 | 22 | print(science_friends) 23 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/14_advanced_set_operations/code.py: -------------------------------------------------------------------------------- 1 | art_friends = {"Rolf", "Anne", "Jen"} 2 | science_friends = {"Jen", "Charlie"} 3 | 4 | # -- Difference -- 5 | # Gives you members that are in one set but not the other. 6 | 7 | art_but_not_science = art_friends.difference(science_friends) 8 | science_but_not_art = science_friends.difference(art_friends) 9 | 10 | print(art_but_not_science) 11 | print(science_but_not_art) 12 | 13 | # -- Symmetric difference -- 14 | # Gives you those members that aren't in both sets 15 | # Order doesn't matter with symmetric_difference 16 | 17 | not_in_both = art_friends.symmetric_difference(science_friends) 18 | 19 | print(not_in_both) 20 | 21 | # -- Intersection -- 22 | # Gives you members of both sets 23 | 24 | art_and_science = art_friends.intersection(science_friends) 25 | print(art_and_science) 26 | 27 | # -- Union -- 28 | # Gives you all members of all sets, but of course without duplicates 29 | 30 | all_friends = art_friends.union(science_friends) 31 | print(all_friends) 32 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/15_dictionaries/code.py: -------------------------------------------------------------------------------- 1 | friend_ages = {"Rolf": 24, "Adam": 30, "Anne": 27} 2 | 3 | print(friend_ages["Rolf"]) # 24 4 | # friend_ages["Bob"] ERROR 5 | 6 | # -- Adding a new key to the dictionary -- 7 | 8 | friend_ages["Bob"] = 20 9 | print(friend_ages) # {'Rolf': 24, 'Adam': 30, 'Anne': 27, 'Bob': 20} 10 | 11 | # -- Modifying existing keys -- 12 | 13 | friend_ages["Rolf"] = 25 14 | 15 | print(friend_ages) # {'Rolf': 25, 'Adam': 30, 'Anne': 27, 'Bob': 20} 16 | 17 | # -- Lists of dictionaries -- 18 | # Imagine you have a program that stores information about your friends. 19 | # This is the perfect place to use a list of dictionaries. 20 | # That way you can store multiple pieces of data about each friend, all in a single variable. 21 | 22 | friends = [ 23 | {"name": "Rolf Smith", "age": 24}, 24 | {"name": "Adam Wool", "age": 30}, 25 | {"name": "Anne Pun", "age": 27}, 26 | ] 27 | 28 | # You can turn a list of lists or tuples into a dictionary: 29 | 30 | friends = [("Rolf", 24), ("Adam", 30), ("Anne", 27)] 31 | friend_ages = dict(friends) 32 | print(friend_ages) 33 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/16_length_and_sum/code.py: -------------------------------------------------------------------------------- 1 | # Imagine you're wanting a variable that stores the grades attained by a student in their class. 2 | # Which of these is probably not going to be a good data structure? 3 | 4 | grades = [80, 75, 90, 100] 5 | grades = (80, 75, 90, 100) 6 | grades = {80, 75, 90, 100} # This one, because of no duplicates 7 | 8 | 9 | total = sum(grades) 10 | length = len(grades) 11 | 12 | average = total / length 13 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/17_joining_a_list/code.py: -------------------------------------------------------------------------------- 1 | # Imagine you've got all your friends in a list, and you want to print it out. 2 | friends = ["Rolf", "Anne", "Charlie"] 3 | print(f"My friends are {friends}.") 4 | 5 | # Not the prettiest, so instead you can join your friends using a ",": 6 | friends = ["Rolf", "Anne", "Charlie"] 7 | comma_separated = ", ".join(friends) 8 | print(f"My friends are {comma_separated}.") 9 | 10 | # Want the last one to say ", and" ? 11 | # You'll have to wait until we cover list slicing in the next section! 12 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/3_variables_printing/code.py: -------------------------------------------------------------------------------- 1 | # Define variables by giving them a name and a value 2 | 3 | age = 30 4 | 5 | # Print their values out by using the print() function 6 | 7 | print(age) 8 | 9 | # You can print values directly if you prefer 10 | 11 | print(30) 12 | 13 | # But having variables means you can change them after the fact 14 | 15 | age = 30 16 | print(age) 17 | age = 40 18 | print(age) 19 | 20 | # Variable names can contain letters, numbers, and underscores. However, they cannot start with a number. 21 | # There are some reserved names because Python uses them for other things. 22 | # For example, don't call your variable print, because print is something else! 23 | 24 | # Longer variable names are written in snake_case in Python: 25 | 26 | friend_age = 23 27 | countries_visited = 90 28 | 29 | # Variable names you will never change are written in all uppercase 30 | 31 | PI = 3.14159 32 | RADIANS_TO_DEGREES = 180 / PI 33 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/4_numbers/code.py: -------------------------------------------------------------------------------- 1 | # Two main types of number: whole numbers and floating point numbers 2 | # Also called integers and floats 3 | 4 | age = 35 # integer 5 | PI = 3.14159 # float 6 | 7 | # Maths works just as normal: 8 | 9 | maths_operation = 1 + 3 * 4 / 2 - 2 10 | print(maths_operation) 11 | 12 | # Division always results in a float 13 | 14 | float_division = 12 / 3 15 | print(float_division) 16 | 17 | # But you can drop everything after the decimal, doing 18 | # "integer division" 19 | 20 | integer_division = 12 // 3 # drops anything after the decimal (no rounding!) 21 | print(integer_division) 22 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/5_remainder/code.py: -------------------------------------------------------------------------------- 1 | integer_division = 13 // 5 # should be 2.6 2 | print(integer_division) # prints 2 3 | 4 | # 5 goes into 13 two times. (5 * 2 is 10). The remainder is 3. 5 | # Getting the remainder of a division is such a popular operation, that Python gives us a way to do it really easily. 6 | 7 | remainder = 13 % 5 8 | print(remainder) # prints 3 9 | 10 | # Why is it so popular? 11 | # What would the remainder be in these divisions? 12 | # 13 | # 10 / 2 14 | # 14 / 2 15 | # 6 / 2 16 | # 340 / 2 17 | # 18 | # What about these? 19 | # 20 | # 11 / 2 21 | # 27 / 2 22 | # 3 / 2 23 | 24 | # For every even number, the remainder when divided by 2 is always 0. 25 | # For every odd number, the remainder when divided by 2 is always 1. 26 | 27 | # We can check whether a number is odd or even just by checking the remainder! 28 | 29 | x = 37 30 | remainder = x % 2 31 | print(remainder) # should print 1, therefore it is odd 32 | 33 | # We'll look at doing things depending on whether a number is odd or even soon in the course! 34 | -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/6_strings/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | hidden: true 3 | --- 4 | 5 | # Strings and string formatting 6 | 7 | * Strings 8 | * f-strings 9 | * The `.format()` method 10 | * Multi-line strings -------------------------------------------------------------------------------- /course_contents/1_intro/lectures/9_booleans/code.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Boolean is a true/false, yes/no, one/zero value. 3 | We can use it to make decisions. 4 | In Python, True and False are keywords to represent these values. 5 | """ 6 | 7 | truthy = True 8 | falsy = False 9 | 10 | # ---- 11 | 12 | age = 20 13 | is_over_age = age >= 18 14 | is_under_age = age < 18 15 | is_twenty = age == 20 16 | 17 | """ 18 | Other symbols are > and <=. 19 | 20 | We can of course compare two variables, as below. We ask the user for a number, and check that it matches our 'secret number'. 21 | """ 22 | 23 | my_number = 5 24 | user_number = int(input("Enter a number: ")) 25 | 26 | print(my_number == user_number) 27 | print(my_number != user_number) 28 | -------------------------------------------------------------------------------- /course_contents/1_intro/notes/01. Data Types (cheatsheet).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/1_intro/notes/01. Data Types (cheatsheet).pdf -------------------------------------------------------------------------------- /course_contents/1_intro/notes/02. Operators (cheatsheet).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/1_intro/notes/02. Operators (cheatsheet).pdf -------------------------------------------------------------------------------- /course_contents/1_intro/notes/03. Comparison Operators.md: -------------------------------------------------------------------------------- 1 | Each comparison operator returns True or False 2 | ### Equal `==` 3 | 4 | `4 == 4 # True` 5 | 6 | `4 == 5 # False` 7 | 8 | - Can be used to check the truth value of an operand: 9 | 10 | `4 is True # True` 11 | 12 | `4 is False # False` 13 | 14 | `0 is False # True` 15 | 16 | - Can be used to compare two strings or any other data type: 17 | 18 | `"Hello" == "Hello" # True` 19 | 20 | `"Hello" == "HELLO" # False` 21 | 22 | `(100, 50) == 50 # False` 23 | 24 | `(100, 50) == (500, 219) # False` 25 | 26 | ### Not equal `!=` 27 | - The opposite of `==` 28 | 29 | `4 != 4 # False` 30 | 31 | `4 != 5 # True` 32 | 33 | ### Greater than `>` 34 | `10 > 10 # False` 35 | 36 | ### Less than `<` 37 | - Opposite of higher than 38 | `10 < 10 # False` 39 | 40 | ### Greater than or equal to `>=` 41 | `10 >= 10 # True` 42 | 43 | ### Less than or equal to `<=` 44 | `10 <= 10 # True` 45 | 46 | ### Extra resources 47 | - [Conditionals and Booleans](https://www.teclado.com/30-days-of-python/python-30-day-5-conditionals-booleans) -------------------------------------------------------------------------------- /course_contents/1_intro/notes/04. Assignment Operators.md: -------------------------------------------------------------------------------- 1 | | Operator | Example | 2 | | ------- | ---------- | 3 | | `=` | `var = 2` | 4 | | `+=` | `var += 2` | 5 | | `-=` | `var -= 2` | 6 | | `*=` | `var *= 2` | 7 | | `**=` | `var **= 2`| 8 | | `/=` | `var /= 2` | 9 | | `//=` | `var //= 2`| 10 | | `%=` | `var %= 2` | 11 | -------------------------------------------------------------------------------- /course_contents/1_intro/notes/05. Logical Operators.md: -------------------------------------------------------------------------------- 1 | ## AND 2 | - `and` returns the first value if it evaluates to false, otherwise it returns the second value. 3 | 4 | Example: 5 | 6 | | Statement | Result | 7 | | ------------- | ---------- | 8 | | `True and True` | `True` | 9 | | `True and False` | `False` | 10 | | `False and False` | `False` | 11 | | `False and True` | `False` | 12 | 13 | 14 | ## OR 15 | - `or` returns the first value if it evaluates to true, otherwise it returns the second value. 16 | 17 | Example: 18 | 19 | | Statement | Result | 20 | | ------------- | ---------- | 21 | | `True or True` | `True` | 22 | | `True or False` | `True` | 23 | | `False or False` | `False` | 24 | | `False or True` | `True` | 25 | 26 | ## NOT 27 | - `not` returns True if the operand is False 28 | 29 | Example: 30 | 31 | | Statement | Result | 32 | | ------------- | ---------- | 33 | | `not True` | `False` | 34 | | `not False` | `True` | 35 | 36 | 37 | ### Extra resources 38 | - [Logical comparisons in Python: and & or](https://blog.teclado.com/logical-comparisons-in-python-and-or/) 39 | -------------------------------------------------------------------------------- /course_contents/1_intro/notes/06. Identity Operators.md: -------------------------------------------------------------------------------- 1 | ## IS 2 | - Return True if the operands have the same identity 3 | 4 | Example: 5 | ``` 6 | users = ['James', 'Charlie', 'Ana'] 7 | print(id(users)) # 2425142160952 8 | 9 | users_copy = users 10 | print(id(users_copy)) # 2425142160952 11 | 12 | print(users_copy is users) # True 13 | 14 | users_copy = ['James', 'Charlie', 'Ana'] 15 | print(id(users_copy)) # 2425142558551 16 | 17 | print(users_copy is users) # False 18 | ``` 19 | 20 | ## IS NOT 21 | - Returns True if the operands don't have the same identity 22 | 23 | Example: 24 | ``` 25 | users = ['James', 'Charlie', 'Ana'] 26 | print(id(users)) # 2425142160952 27 | 28 | users_copy = users 29 | print(id(users_copy)) # 2425142160952 30 | 31 | print(users_copy is users) # False 32 | 33 | users_copy = ['James', 'Charlie', 'Ana'] 34 | print(id(users_copy)) # 2425142558551 35 | 36 | print(users_copy is users) # True 37 | ``` 38 | -------------------------------------------------------------------------------- /course_contents/1_intro/notes/07. Membership Operators.md: -------------------------------------------------------------------------------- 1 | ## IN 2 | - Returns True if an element exists inside an object 3 | 4 | Example: 5 | ```python 6 | users = ['James', 'Charlie', 'Ana'] 7 | print("Johnny" in users) 8 | ``` 9 | 10 | ## NOT IN 11 | - Returns True if an element does not exist inside an object 12 | 13 | Example: 14 | ```python 15 | users = ['James', 'Charlie', 'Ana'] 16 | print("Johnny" in users) 17 | ``` 18 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Advanced 3 | hidden: true 4 | --- 5 | 6 | # Unit testing with Python 7 | 8 | Unit testing means writing some code that checks other code you've written, to make sure it works as intended. 9 | 10 | In this section we learn about the `unittest` framework, and how to write unit tests using it. -------------------------------------------------------------------------------- /course_contents/20_unit_testing/code/1_functions/functions.py: -------------------------------------------------------------------------------- 1 | from typing import Union, Tuple 2 | 3 | def divide(dividend: Union[int, float], divisor: Union[int, float]): 4 | if divisor == 0: 5 | raise ValueError('The divisor cannot be zero.') 6 | 7 | return dividend / divisor 8 | 9 | 10 | def multiply(*args: Tuple[Union[int, float]]): 11 | if len(args) == 0: 12 | raise ValueError('At least one value to multiply must be passed.') 13 | total = 1 14 | for arg in args: 15 | total *= arg 16 | 17 | return total 18 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/code/2_classes/printer.py: -------------------------------------------------------------------------------- 1 | class PrinterError(RuntimeError): 2 | pass 3 | 4 | 5 | class Printer: 6 | def __init__(self, pages_per_s: int, capacity: int): 7 | self.pages_per_s = pages_per_s 8 | self._capacity = capacity 9 | 10 | def print(self, pages): 11 | if pages > self._capacity: 12 | raise PrinterError('Printer does not have enough capacity for all these pages.') 13 | 14 | self._capacity -= pages 15 | 16 | return f'Printed {pages} pages in {pages/self.pages_per_s:.2f} seconds.' -------------------------------------------------------------------------------- /course_contents/20_unit_testing/code/3_external_libraries/page.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | class PageRequester: 4 | def __init__(self, url): 5 | self.url = url 6 | 7 | def get(self): 8 | return requests.get(self.url).content 9 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/code/3_external_libraries/test_page.py: -------------------------------------------------------------------------------- 1 | from page import PageRequester 2 | from unittest import TestCase 3 | from unittest.mock import patch 4 | 5 | 6 | class TestPageRequester(TestCase): 7 | def setUp(self): 8 | self.page = PageRequester('https://google.com') 9 | 10 | def test_make_request(self): 11 | with patch('requests.get') as mocked_get: 12 | self.page.get() 13 | mocked_get.assert_called() 14 | 15 | def test_content_returned(self): 16 | class FakeResponse: 17 | def __init__(self): 18 | self.content = 'Hello' 19 | 20 | with patch('requests.get', return_value=FakeResponse()) as mocked_get: 21 | result = self.page.get() 22 | self.assertEqual(result, 'Hello') 23 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/1_testing_equality/functions.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | 4 | def divide(dividend: Union[int, float], divisor: Union[int, float]): 5 | return dividend / divisor 6 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/1_testing_equality/test_functions.py: -------------------------------------------------------------------------------- 1 | from functions import divide 2 | from unittest import TestCase 3 | 4 | 5 | class TestFunctions(TestCase): 6 | def test_divide_result(self): 7 | dividend = 15 8 | divisor = 3 9 | expected_result = 5.0 10 | self.assertAlmostEqual(divide(dividend, divisor), expected_result, delta=0.0001) 11 | 12 | def test_divide_negative(self): 13 | dividend = 15 14 | divisor = -3 15 | expected_result = -5.0 16 | self.assertAlmostEqual(divide(dividend, divisor), expected_result, delta=0.0001) 17 | 18 | def test_divide_dividend_zero(self): 19 | dividend = 0 20 | divisor = 5 21 | expected_result = 0 22 | self.assertEqual(divide(dividend, divisor), expected_result) 23 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/2_testing_errors/functions.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | 4 | def divide(dividend: Union[int, float], divisor: Union[int, float]): 5 | if divisor == 0: 6 | raise ValueError("The divisor cannot be zero.") 7 | 8 | return dividend / divisor 9 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/2_testing_errors/test_functions.py: -------------------------------------------------------------------------------- 1 | from functions import divide 2 | from unittest import TestCase 3 | 4 | 5 | class TestFunctions(TestCase): 6 | def test_divide_result(self): 7 | dividend = 15 8 | divisor = 3 9 | expected_result = 5.0 10 | self.assertAlmostEqual(divide(dividend, divisor), expected_result, delta=0.0001) 11 | 12 | def test_divide_negative(self): 13 | dividend = 15 14 | divisor = -3 15 | expected_result = -5.0 16 | self.assertAlmostEqual(divide(dividend, divisor), expected_result, delta=0.0001) 17 | 18 | def test_divide_dividend_zero(self): 19 | dividend = 0 20 | divisor = 5 21 | expected_result = 0 22 | self.assertEqual(divide(dividend, divisor), expected_result) 23 | 24 | def test_divide_error_on_zero(self): 25 | with self.assertRaises(ValueError): 26 | divide(25, 0) 27 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/4_testing_multiplication/functions.py: -------------------------------------------------------------------------------- 1 | from typing import Union, Tuple 2 | 3 | def divide(dividend: Union[int, float], divisor: Union[int, float]): 4 | if divisor == 0: 5 | raise ValueError('The divisor cannot be zero.') 6 | 7 | return dividend / divisor 8 | 9 | 10 | def multiply(*args: Tuple[Union[int, float]]): 11 | if len(args) == 0: 12 | raise ValueError('At least one value to multiply must be passed.') 13 | total = 1 14 | for arg in args: 15 | total *= arg 16 | 17 | return total 18 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/5_creating_our_printer_class/printer.py: -------------------------------------------------------------------------------- 1 | class PrinterError(RuntimeError): 2 | pass 3 | 4 | 5 | class Printer: 6 | def __init__(self, pages_per_s: int, capacity: int): 7 | self.pages_per_s = pages_per_s 8 | self._capacity = capacity 9 | 10 | def print(self, pages): 11 | if pages > self._capacity: 12 | raise PrinterError('Printer does not have enough capacity for all these pages.') 13 | 14 | self._capacity -= pages 15 | 16 | return f'Printed {pages} pages in {pages/self.pages_per_s:.2f} seconds.' -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/6_setup_method/README.md: -------------------------------------------------------------------------------- 1 | # The `setUp` method 2 | 3 | Interestingly, this method in the `unittest` library is called `setUp()`, and **not** `set_up()` as you would expect in Python. 4 | 5 | This method runs for each test—so before each test you run the `setUp()` method. 6 | 7 | After each test, a `tearDown()` method runs. We have not defined such method in this code. 8 | 9 | ## Class-level setUp 10 | 11 | If you'd like a method to run once for the entire `TestCase`, you can use `setUpClass()` instead. That only runs once for all the test methods within the test case. 12 | 13 | Similarly, a `tearDownClass()` method is also available. -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/6_setup_method/printer.py: -------------------------------------------------------------------------------- 1 | class PrinterError(RuntimeError): 2 | pass 3 | 4 | 5 | class Printer: 6 | def __init__(self, pages_per_s: int, capacity: int): 7 | self.pages_per_s = pages_per_s 8 | self._capacity = capacity 9 | 10 | def print(self, pages): 11 | if pages > self._capacity: 12 | raise PrinterError('Printer does not have enough capacity for all these pages.') 13 | 14 | self._capacity -= pages 15 | 16 | return f'Printed {pages} pages in {pages/self.pages_per_s:.2f} seconds.' -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/6_setup_method/test_printer.py: -------------------------------------------------------------------------------- 1 | from printer import Printer, PrinterError 2 | from unittest import TestCase 3 | 4 | 5 | class TestPrinter(TestCase): 6 | def setUp(self): 7 | self.printer = Printer(pages_per_s=2.0, capacity=300) 8 | 9 | def test_print_within_capacity(self): 10 | self.printer.print(25) 11 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/7_more_printer_tests/printer.py: -------------------------------------------------------------------------------- 1 | class PrinterError(RuntimeError): 2 | pass 3 | 4 | 5 | class Printer: 6 | def __init__(self, pages_per_s: int, capacity: int): 7 | self.pages_per_s = pages_per_s 8 | self._capacity = capacity 9 | 10 | def print(self, pages): 11 | if pages > self._capacity: 12 | raise PrinterError('Printer does not have enough capacity for all these pages.') 13 | 14 | self._capacity -= pages 15 | 16 | return f'Printed {pages} pages in {pages/self.pages_per_s:.2f} seconds.' -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/8_testing_external_libraries_with_mocks/page.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | class PageRequester: 4 | def __init__(self, url): 5 | self.url = url 6 | 7 | def get(self): 8 | return requests.get(self.url).content 9 | -------------------------------------------------------------------------------- /course_contents/20_unit_testing/lectures/8_testing_external_libraries_with_mocks/test_page.py: -------------------------------------------------------------------------------- 1 | from page import PageRequester 2 | from unittest import TestCase 3 | from unittest.mock import patch 4 | 5 | 6 | class TestPageRequester(TestCase): 7 | def setUp(self): 8 | self.page = PageRequester('https://google.com') 9 | 10 | def test_make_request(self): 11 | with patch('requests.get') as mocked_get: 12 | self.page.get() 13 | mocked_get.assert_called() 14 | 15 | def test_content_returned(self): 16 | class FakeResponse: 17 | def __init__(self): 18 | self.content = 'Hello' 19 | 20 | with patch('requests.get', return_value=FakeResponse()) as mocked_get: 21 | result = self.page.get() 22 | self.assertEqual(result, 'Hello') 23 | -------------------------------------------------------------------------------- /course_contents/21_algorithms_data_structures/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Advanced 3 | hidden: true 4 | --- 5 | 6 | # Algorithms and Data Structures with Python 7 | 8 | In this section we learn about algorithms and data structures, two topics that are essential when finding a software development job. 9 | 10 | Specifically in this section we learn about: 11 | 12 | - Queues 13 | - Stacks 14 | - Complexity and Big-O notation 15 | - Binary Trees 16 | -------------------------------------------------------------------------------- /course_contents/21_algorithms_data_structures/projects/binary_tree/app.py: -------------------------------------------------------------------------------- 1 | from binary_tree import BinaryTree 2 | from node import Node 3 | 4 | tree = BinaryTree(Node(6)) 5 | 6 | nodes = [5, 3, 9, 7, 8, 7.5, 12, 11] 7 | 8 | for n in nodes: 9 | tree.add(Node(n)) 10 | 11 | tree.delete(9) 12 | tree.inorder() -------------------------------------------------------------------------------- /course_contents/21_algorithms_data_structures/projects/binary_tree/node.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, value, left=None, right=None): 3 | self.value = value 4 | self.left = left 5 | self.right = right 6 | 7 | def __repr__(self): 8 | return f'' 9 | -------------------------------------------------------------------------------- /course_contents/21_algorithms_data_structures/projects/samples/queue.py: -------------------------------------------------------------------------------- 1 | class Queue: 2 | def __init__(self): 3 | self.items = [] 4 | 5 | def push(self, e): 6 | self.items.append(e) 7 | 8 | def pop(self): 9 | head = self.items[0] 10 | self.items = self.items[1:] 11 | return head 12 | 13 | q = Queue() 14 | q.push(5) # [5] 15 | q.push(7) # [5, 7] 16 | q.push(11) # [5, 7, 11] 17 | print(q.pop()) # returns 5, left is [7, 11] 18 | print(q.pop()) # returns 7, left is [11] 19 | -------------------------------------------------------------------------------- /course_contents/21_algorithms_data_structures/projects/samples/stack.py: -------------------------------------------------------------------------------- 1 | class Stack: 2 | def __init__(self): 3 | self.items = [] 4 | 5 | def push(self, e): 6 | self.items = [e] + self.items 7 | 8 | def pop(self): 9 | return self.items.pop(0) 10 | 11 | 12 | s = Stack() 13 | s.push(5) # [5] 14 | s.push(7) # [5, 7] 15 | s.push(11) # [5, 7, 11] 16 | print(s.pop()) # returns 11, left is [5, 7] 17 | print(s.pop()) # returns 7, left is [5] 18 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Advanced 3 | hidden: true 4 | --- 5 | 6 | # Python Libraries and Tools 7 | 8 | In this section I'll show you some popular Python libraries and tools that you can use to make your project development easier, or to extend your projects. 9 | 10 | `pylint` is a **linter**: a tool that checks your code for potential problems before you run it. Another popular linter is `flake8`. 11 | 12 | `yapf` is a **formatter**: a tool that takes your Python code and re-writes it so it looks nicer and so that it always has a consistent style. Another popular formatter is `black`. 13 | 14 | `mailgun` is a service used to send e-mails to users of your application. In some lectures of this section I cover how to interact with Mailgun to send e-mails when you need to. -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/projects/sending_email/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/22_popular_libraries/projects/sending_email/__init__.py -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/projects/sending_email/email_mailgun.py: -------------------------------------------------------------------------------- 1 | """ 2 | To send e-mails via Mailgun, you first need to create an account: 3 | 4 | https://signup.mailgun.com/new/signup 5 | 6 | Create a "Pay As You Go" account to get 100,000 free e-mails every month. 7 | For learning, you won't need any more than this! 8 | 9 | Once you've created your account, you can send e-mails to yourself using 10 | the sandbox account and API key. 11 | 12 | If you want to send e-mails from an address that uses your domain, you must 13 | link your domain name with Mailgun (something outside the scope of this course). 14 | """ 15 | 16 | import requests 17 | 18 | MAILGUN_DOMAIN = '' 19 | MAILGUN_API_KEY = '' 20 | FROM_NAME = '' 21 | FROM_EMAIL = '' 22 | 23 | TO_EMAILS = ['test@email.com'] 24 | SUBJECT = 'Test e-mail' 25 | CONTENT = 'Hello, this is a test e-mail.' 26 | 27 | requests.post( 28 | "https://api.mailgun.net/v3/{}/messages".format(MAILGUN_DOMAIN), 29 | auth=("api", MAILGUN_API_KEY), 30 | data={ 31 | "from": "{} <{}>".format(FROM_NAME, FROM_EMAIL), 32 | "to": TO_EMAILS, 33 | "subject": SUBJECT, 34 | "text": CONTENT 35 | }) 36 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/projects/sending_email/email_smtplib.py: -------------------------------------------------------------------------------- 1 | """ 2 | If you get an SMTPAuthenticationError even when your password is correct, 3 | it's possible that you have 2-factor authentication enabled. 4 | You'll need to use an App Password to log in instead of your normal password. 5 | 6 | If you don't have 2-FA enabled, you'll have to allow access by 7 | less secure apps in your Gmail security preferences—though remember to deactivate 8 | it once you've finished learning about sending e-mails with Python! 9 | """ 10 | 11 | import smtplib 12 | 13 | from email.message import EmailMessage 14 | 15 | email = EmailMessage() 16 | 17 | email['Subject'] = 'Test email' 18 | email['From'] = 'email@gmail.com' 19 | email['To'] = 'john@gmail.com' 20 | 21 | email.set_content('Hello, John') 22 | 23 | s = smtplib.SMTP(host='smtp.gmail.com', port=587) 24 | s.starttls() 25 | s.login('you@gmail.com', 'password') 26 | 27 | s.send_message(email) 28 | s.quit() 29 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/projects/sending_email/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/22_popular_libraries/projects/sending_email/libs/__init__.py -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/projects/sending_email/libs/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | class Mailgun: 4 | MAILGUN_DOMAIN = '' 5 | MAILGUN_API_KEY = '' 6 | FROM_NAME = '' 7 | FROM_EMAIL = '' 8 | 9 | @classmethod 10 | def send(cls, to_emails, subject, content): 11 | requests.post("https://api.mailgun.net/v3/{}/messages".format(cls.MAILGUN_DOMAIN), 12 | auth=("api", cls.MAILGUN_API_KEY), 13 | data={"from": "{} <{}>".format(cls.FROM_NAME, cls.FROM_EMAIL), 14 | "to": to_emails, 15 | "subject": subject, 16 | "text": content}) 17 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/projects/sending_email/using_mailgun_lib.py: -------------------------------------------------------------------------------- 1 | from libs.mailgun import Mailgun 2 | 3 | Mailgun.send(['test@email.com'], 'Test e-mail', 'This is a test e-mail') 4 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/1_pylint/end/app.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file contains the definition of our Flask application. 3 | 4 | It also runs the app if the file is executed. 5 | """ 6 | from flask import Flask 7 | 8 | APP = Flask(__name__) 9 | 10 | if __name__ == '__main__': 11 | APP.run() 12 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/2_yapf/end/app.py: -------------------------------------------------------------------------------- 1 | movies = ['The Matrix', 'Fast & Furious', 'Frozen', 'The life of Pi'] 2 | 3 | for movie in movies: 4 | print(movie) 5 | 6 | print('We\'re done here!') 7 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/2_yapf/start/app.py: -------------------------------------------------------------------------------- 1 | movies = ['The Matrix', 'Fast & Furious', 2 | 'Frozen', 3 | 'The life of Pi'] 4 | 5 | for movie in movies: print(movie) 6 | 7 | print('We\'re done here!') 8 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/3_sending_email/end/email_smtplib.py: -------------------------------------------------------------------------------- 1 | """ 2 | If you get an SMTPAuthenticationError even when your password is correct, 3 | it's possible that you have 2-factor authentication enabled. 4 | You'll need to use an App Password to log in instead of your normal password. 5 | 6 | If you don't have 2-FA enabled, you'll have to allow access by 7 | less secure apps in your Gmail security preferences—though remember to deactivate 8 | it once you've finished learning about sending e-mails with Python! 9 | """ 10 | 11 | import smtplib 12 | from email.message import EmailMessage 13 | 14 | email_content = '''Dear Sir/Madam, 15 | 16 | I am sending you an e-mail with Python. I hope you like it. 17 | 18 | Kind regards, 19 | Jose 20 | ''' 21 | 22 | email = EmailMessage() 23 | 24 | email['Subject'] = 'Test email' 25 | email['From'] = 'you@gmail.com' 26 | email['To'] = 'someonelse@gmail.com' 27 | 28 | email.set_content(email_content) 29 | 30 | 31 | smtp_connector = smtplib.SMTP(host='smtp.gmail.com', port=587) 32 | smtp_connector.starttls() 33 | smtp_connector.login('you@gmail.com', 'password') 34 | 35 | smtp_connector.send_message(email) 36 | smtp_connector.quit() 37 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/4_sending_email_mailgun/end/email_mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | MAILGUN_API_URL = 'https://api.mailgun.net/v3/sandbox0fd1d065f521484b8af277034648e756.mailgun.org' 5 | MAILGUN_API_KEY = 'key-798b9585aedd35d87f1bf506cadc221e' 6 | 7 | FROM_NAME = 'Jose' 8 | FROM_EMAIL = 'jose@schoolofcode.me' 9 | 10 | TO_EMAILS = ['jslvtr@gmail.com'] 11 | SUBJECT = 'Test e-mail' 12 | CONTENT = 'Hello, this is a test e-mail' 13 | 14 | print(requests.post( 15 | MAILGUN_API_URL, 16 | auth=('api', MAILGUN_API_KEY), # This is Basic Auth 17 | data={ 18 | 'from': f'{FROM_NAME} <{FROM_EMAIL}>', 19 | 'to': TO_EMAILS, 20 | 'subject': SUBJECT, 21 | 'text': CONTENT 22 | })) 23 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/5_creating_a_mailgun_library/end/email_mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | MAILGUN_API_URL = 'your_url' 5 | MAILGUN_API_KEY = 'your_api_key' 6 | 7 | FROM_NAME = 'Jose' 8 | FROM_EMAIL = 'jose@schoolofcode.me' 9 | 10 | TO_EMAILS = ['jslvtr@gmail.com'] 11 | SUBJECT = 'Test e-mail' 12 | CONTENT = 'Hello, this is a test e-mail' 13 | 14 | requests.post( 15 | MAILGUN_API_URL, 16 | auth=('api', MAILGUN_API_KEY), # This is Basic Auth 17 | data={ 18 | 'from': f'{FROM_NAME} <{FROM_EMAIL}>', 19 | 'to': TO_EMAILS, 20 | 'subject': SUBJECT, 21 | 'text': CONTENT 22 | }) 23 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/5_creating_a_mailgun_library/end/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/22_popular_libraries/video_code/5_creating_a_mailgun_library/end/libs/__init__.py -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/5_creating_a_mailgun_library/end/libs/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | class Mailgun: 4 | MAILGUN_API_URL = 'your_url' 5 | MAILGUN_API_KEY = 'your_api_key' 6 | 7 | FROM_NAME = 'Jose' 8 | FROM_EMAIL = 'jose@schoolofcode.me' 9 | 10 | @classmethod 11 | def send_email(cls, to_emails, subject, content): 12 | requests.post( 13 | cls.MAILGUN_API_URL, 14 | auth=('api', cls.MAILGUN_API_KEY), 15 | data={ 16 | 'from': f'{cls.FROM_NAME} <{cls.FROM_EMAIL}>', 17 | 'to': to_emails, 18 | 'subject': subject, 19 | 'text': content 20 | }) 21 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/5_creating_a_mailgun_library/end/using_mailgun_lib.py: -------------------------------------------------------------------------------- 1 | from libs.mailgun import Mailgun 2 | 3 | Mailgun.send_email(['test@gmail.com'], 4 | subject='Test e-mail', 5 | content='This is a test e-mail') 6 | -------------------------------------------------------------------------------- /course_contents/22_popular_libraries/video_code/5_creating_a_mailgun_library/start/email_mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | MAILGUN_API_URL = 'https://api.mailgun.net/v3/sandbox0fd1d065f521484b8af277034648e756.mailgun.org' 5 | MAILGUN_API_KEY = 'key-798b9585aedd35d87f1bf506cadc221e' 6 | 7 | FROM_NAME = 'Jose' 8 | FROM_EMAIL = 'jose@schoolofcode.me' 9 | 10 | TO_EMAILS = ['jslvtr@gmail.com'] 11 | SUBJECT = 'Test e-mail' 12 | CONTENT = 'Hello, this is a test e-mail' 13 | 14 | print(requests.post( 15 | MAILGUN_API_URL, 16 | auth=('api', MAILGUN_API_KEY), # This is Basic Auth 17 | data={ 18 | 'from': f'{FROM_NAME} <{FROM_EMAIL}>', 19 | 'to': TO_EMAILS, 20 | 'subject': SUBJECT, 21 | 'text': CONTENT 22 | })) 23 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Introduction 3 | hidden: true 4 | --- 5 | # Python Fundamentals 6 | 7 | This section goes deeper into Python, covering many essential features. 8 | 9 | We'll cover: 10 | 11 | - If statements 12 | - Loops 13 | - Destructuring 14 | - Iteration 15 | - `Break` and `continue` 16 | - Slicing 17 | - List, set, and dictionary comprehensions 18 | - `zip` and `enumerate` 19 | - `map` and `filter` 20 | - Functions 21 | - Arguments, parameters, and return values 22 | - Default parameter values 23 | - Lambda functions 24 | 25 | You can see the [lectures](https://github.com/tecladocode/complete-python-course/tree/master/course_contents/2_intro_to_python/lectures/) folder for a break down of all the topics and the code in each. 26 | 27 | In the [notes](https://github.com/tecladocode/complete-python-course/tree/master/course_contents/2_intro_to_python/notes/) folder you can download Markdown or PDF cheatsheets to help you remember some key points. -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/10_list_comprehensions/code.py: -------------------------------------------------------------------------------- 1 | numbers = [0, 1, 2, 3, 4] 2 | doubled_numbers = [] 3 | 4 | for num in numbers: 5 | doubled_numbers.append(num * 2) 6 | 7 | print(doubled_numbers) 8 | 9 | # -- List comprehension -- 10 | 11 | numbers = [0, 1, 2, 3, 4] # list(range(5)) is better 12 | doubled_numbers = [num * 2 for num in numbers] 13 | # [num * 2 for num in range(5)] would be even better. 14 | 15 | print(doubled_numbers) 16 | 17 | # -- You can add anything to the new list -- 18 | 19 | friend_ages = [22, 31, 35, 37] 20 | age_strings = [f"My friend is {age} years old." for age in friend_ages] 21 | 22 | print(age_strings) 23 | 24 | 25 | # -- This includes things like -- 26 | names = ["Rolf", "Bob", "Jen"] 27 | lower = [name.lower() for name in names] 28 | 29 | # That is particularly useful for working with user input. 30 | # By turning everything to lowercase, it's less likely we'll miss a match. 31 | 32 | friend = input("Enter your friend name: ") 33 | friends = ["Rolf", "Bob", "Jen", "Charlie", "Anne"] 34 | friends_lower = [name.lower() for name in friends] 35 | 36 | if friend.lower() in friends_lower: 37 | print(f"I know {friend}!") 38 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/11_comprehensions_with_conditionals/code.py: -------------------------------------------------------------------------------- 1 | ages = [22, 35, 27, 21, 20] 2 | odds = [n for n in ages if n % 2 == 1] 3 | 4 | # -- with strings -- 5 | 6 | friends = ["Rolf", "ruth", "charlie", "Jen"] 7 | guests = ["jose", "Bob", "Rolf", "Charlie", "michael"] 8 | 9 | friends_lower = [f.lower() for f in friends] 10 | 11 | present_friends = [ 12 | name.capitalize() for name in guests if name.lower() in friends_lower 13 | ] 14 | 15 | # -- nested list comprehensions -- 16 | # Don't do this, because it's almost completely unreadable. 17 | # Splitting things out into variables is better. 18 | 19 | friends = ["Rolf", "ruth", "charlie", "Jen"] 20 | guests = ["jose", "Bob", "Rolf", "Charlie", "michael"] 21 | 22 | present_friends = [ 23 | name.capitalize() for name in guests if name.lower() in [f.lower() for f in friends] 24 | ] 25 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/12_set_dictionary_comprehensions/code.py: -------------------------------------------------------------------------------- 1 | friends = ["Rolf", "ruth", "charlie", "Jen"] 2 | guests = ["jose", "Bob", "Rolf", "Charlie", "michael"] 3 | 4 | friends_lower = {n.lower() for n in friends} 5 | guests_lower = {n.lower() for n in guests} 6 | 7 | present_friends = friends_lower.intersection(guests_lower) 8 | present_friends = {name.capitalize() for name in friends_lower & guests_lower} 9 | 10 | print(present_friends) 11 | 12 | # Transforming data for easier consumption and processing is a very common task. 13 | # Working with homogeneous data is really nice, but often you can't (e.g. when working with user input!). 14 | 15 | # -- Dictionary comprehension -- 16 | # Works just like set comprehension, but you need to do key-value pairs. 17 | 18 | friends = ["Rolf", "Bob", "Jen", "Anne"] 19 | time_since_seen = [3, 7, 15, 11] 20 | 21 | long_timers = { 22 | friends[i]: time_since_seen[i] 23 | for i in range(len(friends)) 24 | if time_since_seen[i] > 5 25 | } 26 | 27 | print(long_timers) 28 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/13_zip/code.py: -------------------------------------------------------------------------------- 1 | """ 2 | friends = ["Rolf", "Bob", "Jen", "Anne"] 3 | time_since_seen = [3, 7, 15, 11] 4 | 5 | long_timers = { 6 | friends[i]: time_since_seen[i] 7 | for i in range(len(friends)) 8 | if time_since_seen[i] > 5 9 | } 10 | 11 | print(long_timers) 12 | """ 13 | 14 | # While that is extremely useful when we have conditionals, sometimes we 15 | # just want to create a dictionary out of two lists or tuples. 16 | # That's when `zip` comes in handy! 17 | 18 | friends = ["Rolf", "Bob", "Jen", "Anne"] 19 | time_since_seen = [3, 7, 15, 11] 20 | 21 | # Remember how we can turn a list of lists or tuples into a dictionary? 22 | # `zip(friends, time_since_seen)` returns something like [("Rolf", 3), ("Bob", 7)...] 23 | # We then use `dict()` on that to get a dictionary. 24 | 25 | friends_last_seen = dict(zip(friends, time_since_seen)) 26 | print(friends_last_seen) 27 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/14_enumerate/code.py: -------------------------------------------------------------------------------- 1 | # https://blog.tecladocode.com/python-enumerate/ 2 | 3 | friends = ["Rolf", "John", "Anna"] 4 | 5 | for counter, friend in enumerate(friends, start=1): 6 | print(counter, friend) 7 | 8 | # 1 Rolf 9 | # 2 John 10 | # 3 Anna 11 | 12 | 13 | friends = ["Rolf", "John", "Anna"] 14 | print(list(enumerate(friends))) # [(0, 'Rolf'), (1, 'John'), (2, 'Anna')] 15 | print(dict(enumerate(friends))) # {0: 'Rolf', 1: 'John', 2: 'Anna'} 16 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/15_functions/code.py: -------------------------------------------------------------------------------- 1 | # So far we've been using functions such as `print`, `len`, and `zip`. 2 | # But we haven't learned how to create our own functions, or even how they really work. 3 | 4 | # Let's create our own function. The building blocks are: 5 | # def 6 | # the name 7 | # brackets 8 | # colon 9 | # any code you want, but it must be indented if you want it to run as part of the function. 10 | 11 | 12 | def greet(): 13 | name = input("Enter your name: ") 14 | print(f"Hello, {name}!") 15 | 16 | 17 | # Running this does nothing, because although we have defined a function, we haven't executed it. 18 | # We must execute the function in order for its contents to run. 19 | 20 | greet() 21 | 22 | # You can put as much or as little code as you want inside a function, but prefer shorter functions over longer ones. 23 | # You'll usually be putting code that you want to reuse inside functions. 24 | 25 | # Any variables declared inside the function are not accessible outside it. 26 | print(name) # ERROR! 27 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/17_return_values/code.py: -------------------------------------------------------------------------------- 1 | def calculate_mpg(car): 2 | mpg = car["mileage"] / car["fuel_consumed"] 3 | return mpg # Ends the function, gives back the value 4 | 5 | 6 | def car_name(car): 7 | return f"{car['make']} {car['model']}" 8 | 9 | 10 | def print_car_info(car): 11 | name = car_name(car) 12 | mpg = calculate_mpg(car) 13 | 14 | print(f"{name} does {mpg} miles per gallon.") 15 | # Returns None by default, as all functions do 16 | 17 | 18 | cars = [ 19 | {"make": "Ford", "model": "Fiesta", "mileage": 23000, "fuel_consumed": 460}, 20 | {"make": "Ford", "model": "Focus", "mileage": 17000, "fuel_consumed": 350}, 21 | {"make": "Mazda", "model": "MX-5", "mileage": 49000, "fuel_consumed": 900}, 22 | {"make": "Mini", "model": "Cooper", "mileage": 31000, "fuel_consumed": 235}, 23 | ] 24 | 25 | for car in cars: 26 | print_car_info(car) 27 | # try print(print_car_info(car)), you'll see None 28 | 29 | 30 | # -- Multiple returns -- 31 | 32 | 33 | def divide(x, y): 34 | if y == 0: 35 | return "You tried to divide by zero!" 36 | else: 37 | return x / y 38 | 39 | 40 | print(divide(10, 2)) # 5 41 | print(divide(6, 0)) # You tried to divide by zero! 42 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/18_default_parameter_values/code.py: -------------------------------------------------------------------------------- 1 | def add(x, y=3): # x=2, y is not OK 2 | total = x + y 3 | print(total) 4 | 5 | 6 | add(5) 7 | add(2, 6) 8 | add(x=3) 9 | add(x=5, y=2) 10 | 11 | # add(y=2) # ERROR! 12 | # add(x=2, 5) # ERROR! 13 | 14 | 15 | # -- More named arguments -- 16 | 17 | print(1, 2, 3, 4, 5, sep=" - ") # default is " " 18 | 19 | # You can use almost anything as a default parameter value. 20 | # But using variables as default parameter values is discouraged, as that can introduce difficult to spot bugs 21 | 22 | default_y = 3 23 | 24 | 25 | def add(x, y=default_y): 26 | sum = x + y 27 | print(sum) 28 | 29 | 30 | add(2) # 5 31 | 32 | default_y = 4 33 | print(default_y) # 4 34 | 35 | add(2) # 5 36 | 37 | # Be careful when using lists or dictionaries as default parameter values. Unlike integers or strings, these will update if you modify the original list or dictionary. 38 | 39 | # This is due to a language feature called mutability. It's not important to understand this now, but just know that they behave differently to integers and strings behind the scenes when you change them. 40 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/1_if_statements/code.py: -------------------------------------------------------------------------------- 1 | friend = "Rolf" 2 | user_name = input("Enter your name: ") 3 | 4 | if user_name == friend: 5 | print("Hello, friend!") 6 | else: 7 | print("Hello, stranger!") 8 | 9 | 10 | # -- Checking whether the if statement will run -- 11 | 12 | print(bool(user_name == friend)) # if this is True, the if statement will run 13 | 14 | # -- Using the `in` keyword -- 15 | 16 | friends = ["Rolf", "Bob", "Anne"] 17 | family = ["Jen", "Charlie"] 18 | 19 | user_name = input("Enter your name: ") 20 | 21 | if user_name in friends: 22 | print("Hello, friend!") 23 | elif user_name in family: 24 | print("Hello, family!") 25 | else: 26 | print("I don't know you.") 27 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/2_while_loops/code.py: -------------------------------------------------------------------------------- 1 | # -- Infinite loop -- 2 | 3 | is_learning = True 4 | 5 | while is_learning: 6 | print("You're learning!") 7 | 8 | 9 | # -- Ending a loop with user input -- 10 | 11 | user_input = input("Do you wish to run the program? (yes/no): ") 12 | 13 | while user_input == "yes": 14 | print("We're running!") 15 | user_input = input("Do you wish to run the program? (yes/no): ") 16 | 17 | print("We stopped running.") 18 | 19 | 20 | # When to use? 21 | # When you want to repeat something an undefined number of times. 22 | # For example, until a user tells you to stop or some other condition becomes False. 23 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/4_destructuring/code.py: -------------------------------------------------------------------------------- 1 | # Given a tuple or list: 2 | currencies = 0.8, 1.2 3 | usd, eur = currencies 4 | 5 | # -- Destructuring in a loop -- 6 | # If you've got a list of lists, such as friend names and ages, you can destructure 7 | # in a loop like this: 8 | 9 | friends = [("Rolf", 25), ("Anne", 37), ("Charlie", 31), ("Bob", 22)] 10 | for name, age in friends: # for friend in friends first 11 | print(f"{name} is {age} years old.") 12 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/5_iterating_over_dictionaries/code.py: -------------------------------------------------------------------------------- 1 | friend_ages = {"Rolf": 25, "Anne": 37, "Charlie": 31, "Bob": 22} 2 | 3 | for name in friend_ages: 4 | print(name) 5 | 6 | 7 | for age in friend_ages.values(): 8 | print(age) 9 | 10 | 11 | for name, age in friend_ages.items(): 12 | print(f"{name} is {age} years old.") 13 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/6_break_continue/code.py: -------------------------------------------------------------------------------- 1 | # -- break -- 2 | # Exits out of the loop, so that no more iterations occur. 3 | 4 | cars = ["ok", "ok", "ok", "faulty", "ok", "ok"] 5 | 6 | for status in cars: 7 | if status == "faulty": 8 | print("Stopping the production line!") 9 | break 10 | 11 | print(f"This car is {status}.") 12 | 13 | # -- continue -- 14 | # Terminates the current iteration and moves onto the next one. 15 | 16 | cars = ["ok", "ok", "ok", "faulty", "ok", "ok"] 17 | 18 | for status in cars: 19 | if status == "faulty": 20 | print("Found faulty car, skipping...") 21 | continue 22 | 23 | print(f"This car is {status}.") 24 | print("Shipping new car to customer!") 25 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/7_else_with_loops/code.py: -------------------------------------------------------------------------------- 1 | # On loops, you can add an `else` clause. This only runs if the loop does not encounter a `break` or an error. 2 | # That means, if the loop completes successfully, the `else` part will run. 3 | 4 | cars = ["ok", "ok", "ok", "faulty", "ok", "ok"] 5 | 6 | for status in cars: 7 | if status == "faulty": 8 | print("Stopping the production line!") 9 | break 10 | 11 | print(f"This car is {status}.") 12 | else: 13 | print("All cars built successfully. No faulty cars!") 14 | 15 | 16 | # Remove the "faulty" and you'll see the `else` part starts running. 17 | # Link: https://blog.tecladocode.com/python-snippet-1-more-uses-for-else/ 18 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/8_finding_prime_numbers/code.py: -------------------------------------------------------------------------------- 1 | for n in range(2, 10): 2 | for x in range(2, n): 3 | if n % x == 0: # if n is divisible by x, it means it's not a prime number. 4 | print(f"{n} equals {x} * {n//x}") 5 | break 6 | else: # if n was not divisible by any x, it means it is a prime number. 7 | print(f"{n} is a prime number.") 8 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/lectures/9_slicing/code.py: -------------------------------------------------------------------------------- 1 | friends = ["Rolf", "Charlie", "Anna", "Bob", "Jen"] 2 | 3 | print(friends[2:4]) 4 | print(friends[2:]) 5 | print(friends[:4]) 6 | 7 | print(friends[:]) 8 | 9 | print(friends[-3:]) 10 | print(friends[:-2]) 11 | print(friends[-3:-1]) 12 | 13 | # You can slice with tuples and strings as well. 14 | # More advanced content in our slices blog posts! 15 | 16 | # https://blog.tecladocode.com/python-slices/ 17 | # https://blog.tecladocode.com/python-slices-part-2/ 18 | -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/notes/Comprehensions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/2_intro_to_python/notes/Comprehensions.pdf -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/notes/Conditional.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/2_intro_to_python/notes/Conditional.pdf -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/notes/Destructuring.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/2_intro_to_python/notes/Destructuring.pdf -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/notes/Functions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/2_intro_to_python/notes/Functions.pdf -------------------------------------------------------------------------------- /course_contents/2_intro_to_python/notes/Loops.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/2_intro_to_python/notes/Loops.pdf -------------------------------------------------------------------------------- /course_contents/3_first_milestone_project/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Introduction 3 | hidden: true 4 | --- 5 | # First Milestone Project 6 | 7 | In this section we build the first project of the course. Please see the [project brief](milestone_project_brief.pdf) for information on what the project should do. -------------------------------------------------------------------------------- /course_contents/3_first_milestone_project/milestone_1/incomplete_app.py: -------------------------------------------------------------------------------- 1 | # Incomplete app! 2 | 3 | MENU_PROMPT = "\nEnter 'a' to add a movie, 'l' to see your movies, 'f' to find a movie by title, or 'q' to quit: " 4 | movies = [] 5 | 6 | 7 | # You may want to create a function for this code 8 | title = input("Enter the movie title: ") 9 | director = input("Enter the movie director: ") 10 | year = input("Enter the movie release year: ") 11 | 12 | movies.append({ 13 | 'title': title, 14 | 'director': director, 15 | 'year': year 16 | }) 17 | 18 | 19 | # Create other functions for: 20 | # - listing movies 21 | # - finding movies 22 | 23 | 24 | # And another function here for the user menu 25 | selection = input(MENU_PROMPT) 26 | while selection != 'q': 27 | if selection == "a": 28 | pass 29 | elif selection == "l": 30 | pass 31 | elif selection == "f": 32 | pass 33 | else: 34 | print('Unknown command. Please try again.') 35 | 36 | selection = input(MENU_PROMPT) 37 | 38 | 39 | # Remember to run the user menu function at the end! -------------------------------------------------------------------------------- /course_contents/3_first_milestone_project/milestone_project_brief.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/3_first_milestone_project/milestone_project_brief.pdf -------------------------------------------------------------------------------- /course_contents/4_object_oriented_programming/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Introduction 3 | hidden: true 4 | --- 5 | # Object-Oriented Programming in Python 6 | 7 | This section talks about object-oriented programming in Python. We start off by looking at dictionaries, and how dictionaries are similar to objects--but limited in certain ways. 8 | 9 | You can see the complete section in the [video-course](https://go.tecla.do/cpc). Alternatively we also have a long article on [Object-Oriented Programming in Python for beginners](https://blog.teclado.com/introduction-to-object-oriented-programming-in-python/) that you can read. -------------------------------------------------------------------------------- /course_contents/5_errors/README.md: -------------------------------------------------------------------------------- 1 | # Errors in Python 2 | 3 | In this section we'll learn about errors and exceptions in Python. We can use them for flow control by following the "ask for forgiveness rather than permission" part of the Python ethos. 4 | 5 | Doing so means: try to do something, and if an error happens, handle it. 6 | 7 | The alternative would be to check if something can be done first, and then only do it if it can be. 8 | 9 | Asking for forgiveness (i.e. catching the errors) is usually simpler and results in simpler code. -------------------------------------------------------------------------------- /course_contents/5_errors/errors_project/app.py: -------------------------------------------------------------------------------- 1 | class Car: 2 | def __init__(self, make, model): 3 | self.make = make 4 | self.model = model 5 | 6 | def __repr__(self): 7 | return f'' 8 | 9 | 10 | class Garage: 11 | def __init__(self): 12 | self.cars = [] 13 | 14 | def __len__(self): 15 | return len(self.cars) 16 | 17 | def add_car(self, car): 18 | if not isinstance(car, Car): 19 | raise ValueError(f'Tried to add a `{car.__class__.__name__}` to the garage, but you can only add `Car` objects.') 20 | self.cars.append(car) 21 | 22 | 23 | ford = Garage() 24 | fiesta = Car('Ford', 'Fiesta') 25 | 26 | try: 27 | ford.add_car('Fiesta') 28 | except TypeError: 29 | print('Your car was not a Car!') 30 | except ValueError: 31 | print('Something weird happened...') 32 | finally: 33 | print(f'The garage now has {len(ford)} cars.') 34 | -------------------------------------------------------------------------------- /course_contents/5_errors/errors_project/errors.py: -------------------------------------------------------------------------------- 1 | class RuntimeErrorWithCode(Exception): 2 | def __init__(self, message, code): 3 | super().__init__(f'Error code {code}: {message}') 4 | self.code = code 5 | 6 | 7 | err = RuntimeErrorWithCode('An error happened.', 500) 8 | -------------------------------------------------------------------------------- /course_contents/5_errors/errors_project/user_score.py: -------------------------------------------------------------------------------- 1 | class User: 2 | def __init__(self, name, engagement): 3 | self.name = name 4 | self.engagement_metrics = engagement 5 | self.score = 0 6 | 7 | def __repr__(self): 8 | return f'' 9 | 10 | 11 | def email_engaged_user(user): 12 | try: 13 | user.score = perform_calculation(user.engagement_metrics) 14 | except KeyError: 15 | print('Incorrect values provided to our calculation function.') 16 | else: 17 | if user.score > 500: 18 | send_engagement_notification(user) 19 | 20 | 21 | def perform_calculation(metrics): 22 | return metrics['clicks'] * 5 + metrics['hits'] * 2 23 | 24 | 25 | def send_engagement_notification(user): 26 | print(f'Notification sent to {user}.') 27 | 28 | 29 | my_user = User('Rolf', {'clicks': 61, 'hits': 100}) 30 | email_engaged_user(my_user) 31 | -------------------------------------------------------------------------------- /course_contents/5_errors/lectures/02_built_in_errors/README.md: -------------------------------------------------------------------------------- 1 | # Built-in errors in Python 2 | 3 | This is a presentation lecture. You can find this in the video course on Udemy. -------------------------------------------------------------------------------- /course_contents/5_errors/side_projects/handling_errors_in_user_input/errors.py: -------------------------------------------------------------------------------- 1 | def power_of_two(): 2 | user_input = input('Please enter a number: ') 3 | try: 4 | n = float(user_input) 5 | n_square = n ** 2 6 | return n_square 7 | except ValueError: 8 | print('Your input was invalid. Using default value 0') 9 | return 0 10 | 11 | 12 | print(power_of_two()) 13 | print(power_of_two()) 14 | -------------------------------------------------------------------------------- /course_contents/6_files/files_project/app.py: -------------------------------------------------------------------------------- 1 | my_file = open('data.txt', 'r') 2 | file_content = my_file.read() 3 | 4 | my_file.close() 5 | 6 | print(file_content) 7 | 8 | user_name = input('Enter your name: ') 9 | 10 | my_file_writing = open('data.txt', 'w') 11 | my_file_writing.write(user_name) 12 | 13 | my_file_writing.close() 14 | -------------------------------------------------------------------------------- /course_contents/6_files/files_project/cars_json.txt: -------------------------------------------------------------------------------- 1 | [{"make": "Ford", "model": "Fiesta"}, {"make": "Ford", "model": "Focus"}] -------------------------------------------------------------------------------- /course_contents/6_files/files_project/csv_data.txt: -------------------------------------------------------------------------------- 1 | name,age,university,degree 2 | rolf,25,mit,computer science 3 | jose,90,oxford,computing 4 | anna,30,cambridge,physics -------------------------------------------------------------------------------- /course_contents/6_files/files_project/csv_read.py: -------------------------------------------------------------------------------- 1 | file = open('csv_data.txt', 'r') 2 | lines = file.readlines() 3 | file.close() 4 | 5 | lines = [line.strip() for line in lines[1:]] 6 | 7 | for line in lines: 8 | person_data = line.split(',') 9 | name = person_data[0].title() 10 | age = person_data[1] 11 | university = person_data[2].title() 12 | degree = person_data[3].capitalize() 13 | 14 | print(f'{name} is {age}, studying {degree} at {university}.') 15 | -------------------------------------------------------------------------------- /course_contents/6_files/files_project/data.txt: -------------------------------------------------------------------------------- 1 | Rolf -------------------------------------------------------------------------------- /course_contents/6_files/files_project/friends.py: -------------------------------------------------------------------------------- 1 | # Ask the user for a list of 3 friends 2 | # For each friend, we'll tell the user whether they are nearby 3 | # For each nearby friend, we'll save their name to `nearby_friends.txt` 4 | 5 | friends = input('Enter three friend names, separated by commas (no spaces, please): ').split(',') 6 | 7 | people = open('people.txt', 'r') 8 | people_nearby = [line.strip() for line in people.readlines()] 9 | 10 | people.close() 11 | 12 | friends_set = set(friends) 13 | people_nearby_set = set(people_nearby) 14 | 15 | friends_nearby_set = friends_set.intersection(people_nearby_set) 16 | 17 | nearby_friends_file = open('nearby_friends.txt', 'w') 18 | 19 | for friend in friends_nearby_set: 20 | print(f'{friend} is nearby! Meet up with them.') 21 | nearby_friends_file.write(f'{friend}\n') 22 | 23 | nearby_friends_file.close() 24 | -------------------------------------------------------------------------------- /course_contents/6_files/files_project/friends_json.txt: -------------------------------------------------------------------------------- 1 | { 2 | "friends": [ 3 | { 4 | "name": "Jose", 5 | "degree": "Applied Computing" 6 | }, 7 | { 8 | "name": "Rolf", 9 | "degree": "Computer Science" 10 | }, 11 | { 12 | "name": "Anna", 13 | "degree": "Physics" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /course_contents/6_files/files_project/json_context_managers.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open('friends_json.txt', 'r') as file: 4 | file_contents = json.load(file) # reads file and turns it to dictionary 5 | 6 | print(file_contents['friends'][0]) 7 | 8 | 9 | cars = [ 10 | {'make': 'Ford', 'model': 'Fiesta'}, 11 | {'make': 'Ford', 'model': 'Focus'} 12 | ] 13 | 14 | with open('cars_json.txt', 'w') as file: 15 | json.dump(cars, file) 16 | 17 | 18 | my_json_string = '[{"name": "Alfa Romeo", "released": 1950}]' 19 | 20 | incorrect_car = json.loads(my_json_string) 21 | print(incorrect_car[0]['name']) 22 | -------------------------------------------------------------------------------- /course_contents/6_files/files_project/json_imports.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | file = open('friends_json.txt', 'r') 4 | file_contents = json.load(file) # reads file and turns it to dictionary 5 | 6 | file.close() 7 | 8 | print(file_contents['friends'][0]) 9 | 10 | cars = [ 11 | {'make': 'Ford', 'model': 'Fiesta'}, 12 | {'make': 'Ford', 'model': 'Focus'} 13 | ] 14 | 15 | file = open('cars_json.txt', 'w') 16 | json.dump(cars, file) 17 | file.close() 18 | 19 | my_json_string = '[{"name": "Alfa Romeo", "released": 1950}]' 20 | 21 | incorrect_car = json.loads(my_json_string) 22 | print(incorrect_car[0]['name']) 23 | -------------------------------------------------------------------------------- /course_contents/6_files/files_project/nearby_friends.txt: -------------------------------------------------------------------------------- 1 | Rolf 2 | Jose 3 | -------------------------------------------------------------------------------- /course_contents/6_files/files_project/people.txt: -------------------------------------------------------------------------------- 1 | Rolf 2 | Jose 3 | Chris 4 | Anna 5 | Charlie 6 | Mary 7 | David 8 | Sophie 9 | Steve 10 | -------------------------------------------------------------------------------- /course_contents/6_files/imports_project/app.py: -------------------------------------------------------------------------------- 1 | from utils import utils 2 | from utils.common.file_operations import save_to_file, read_file 3 | 4 | save_to_file('Hello, world!', 'test.txt') 5 | print(read_file('test.txt')) 6 | 7 | print(f'app is {__name__}') 8 | -------------------------------------------------------------------------------- /course_contents/6_files/imports_project/test.txt: -------------------------------------------------------------------------------- 1 | Hello, world! -------------------------------------------------------------------------------- /course_contents/6_files/imports_project/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/6_files/imports_project/utils/__init__.py -------------------------------------------------------------------------------- /course_contents/6_files/imports_project/utils/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/6_files/imports_project/utils/common/__init__.py -------------------------------------------------------------------------------- /course_contents/6_files/imports_project/utils/common/file_operations.py: -------------------------------------------------------------------------------- 1 | from ..json_operations import dict_to_json 2 | 3 | 4 | def save_to_file(content, filename): 5 | with open(filename, 'w') as file: 6 | file.write(content) 7 | 8 | 9 | def read_file(filename): 10 | with open(filename, 'r') as file: 11 | return file.read() 12 | 13 | print(f'file_operations is {__name__}')# 14 | -------------------------------------------------------------------------------- /course_contents/6_files/imports_project/utils/json_operations.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def dict_to_json(d): 5 | return json.dumps(d) 6 | 7 | print(f'json_operations is {__name__}') -------------------------------------------------------------------------------- /course_contents/6_files/imports_project/utils/utils.py: -------------------------------------------------------------------------------- 1 | from .common.file_operations import save_to_file 2 | 3 | print(f'utils is {__name__}') -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/Milestone Project 2 Brief.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/7_second_milestone_project/Milestone Project 2 Brief.pdf -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Intermediate 3 | hidden: true 4 | --- 5 | # Milestone Project 2 6 | 7 | In this section we build the second project of the course. Please see the [project brief](Milestone%20Project%202%20Brief.pdf) for information on what the project should do. -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_files/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/7_second_milestone_project/milestone_2_files/utils/__init__.py -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_json/books.json: -------------------------------------------------------------------------------- 1 | [{"name": "Test", "author": "Paco", "read": "1"}] -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_json/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/7_second_milestone_project/milestone_2_json/utils/__init__.py -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_json/utils/database.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | books_file = 'books.json' 5 | 6 | 7 | def create_book_table(): 8 | with open(books_file, 'w') as file: 9 | json.dump([], file) # initialize file as empty list 10 | 11 | 12 | def get_all_books(): 13 | with open(books_file, 'r') as file: 14 | return json.load(file) 15 | 16 | 17 | def insert_book(name, author): 18 | books = get_all_books() 19 | books.append({'name': name, 'author': author, 'read': False}) 20 | _save_all_books(books) 21 | 22 | 23 | def _save_all_books(books): 24 | with open(books_file, 'w') as file: 25 | json.dump(books, file) 26 | 27 | 28 | def mark_book_as_read(name): 29 | books = get_all_books() 30 | for book in books: 31 | if book['name'] == name: 32 | book['read'] = '1' 33 | _save_all_books(books) 34 | 35 | 36 | def delete_book(name): 37 | books = get_all_books() 38 | books = [book for book in books if book['name'] != name] 39 | _save_all_books(books) 40 | -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_lists/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/7_second_milestone_project/milestone_2_lists/utils/__init__.py -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_lists/utils/database.py: -------------------------------------------------------------------------------- 1 | books = [] 2 | 3 | 4 | def create_book_table(): 5 | pass 6 | 7 | 8 | def get_all_books(): 9 | return books 10 | 11 | 12 | def insert_book(name, author): 13 | books.append({'name': name, 'author': author, 'read': False}) 14 | 15 | 16 | def mark_book_as_read(name): 17 | for book in books: 18 | if book['name'] == name: 19 | book['read'] = True 20 | 21 | 22 | def delete_book(name): 23 | global books 24 | books = [book for book in books if book['name'] != name] 25 | 26 | 27 | # def delete_book(name): 28 | # for book in books: 29 | # if book['name'] == name: 30 | # books.remove(book) 31 | -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_sql/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/7_second_milestone_project/milestone_2_sql/utils/__init__.py -------------------------------------------------------------------------------- /course_contents/7_second_milestone_project/milestone_2_sql/utils/database_connection.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | 4 | class DatabaseConnection: 5 | def __init__(self, host: str): 6 | self.connection = None 7 | self.host = host 8 | 9 | def __enter__(self) -> sqlite3.Connection: 10 | self.connection = sqlite3.connect(self.host) 11 | return self.connection 12 | 13 | def __exit__(self, exc_type, exc_val, exc_tb): 14 | self.connection.commit() 15 | self.connection.close() 16 | -------------------------------------------------------------------------------- /course_contents/8_type_hinting/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Intermediate 3 | hidden: true 4 | --- 5 | # Type hinting in Python 6 | 7 | This is a short section in the video course which covers an introduction to type hinting in Python by adding type hints to Milestone Project 2. 8 | 9 | If you want to read an abridged version, please see the [lecture](lectures/01_typing_in_python/README.md). -------------------------------------------------------------------------------- /course_contents/9_advanced_built_in_functions/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | group: Intermediate 3 | hidden: true 4 | --- 5 | # Advanced built-in functions in Python 6 | 7 | In this section we will learn about generators, iterators, and iterables. Then we'll look at some advanced built-in functions that can come in very handy: `filter`, `map`, `any`, `all`, and `enumerate`. -------------------------------------------------------------------------------- /course_contents/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/assets/images/favicon.png -------------------------------------------------------------------------------- /course_contents/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/course_contents/assets/logo.png -------------------------------------------------------------------------------- /course_contents/index-page-contents.md: -------------------------------------------------------------------------------- 1 | # Complete Python Course 2 | 3 | Welcome to the Complete Python Course! 4 | 5 | This could will give you a strong Python foundation, and show you most features of the language and what Python has to offer. 6 | 7 | It will take you from the very basics of coding, such as variables and expressions, all the way to advanced object-oriented programming, algorithms, and data structures. 8 | 9 | At the end of the course we'll also show you how to use popular third-party libraries to build projects, such as to: 10 | 11 | - Perform web scraping 12 | - Browser automation with Selenium 13 | - Create web services with `flask` 14 | - Interact with other APIs using `requests` 15 | - Build desktop apps with Tkinter 16 | - Add unit tests to your applications with `unittest` 17 | 18 | The e-book, this very thing you're reading right now, is a work in progress. Most of the course content is available exclusively on Udemy. This e-book is something we're working on to help our Udemy students learn. 19 | 20 | If you want to join the course, **[check it out on Udemy](https://go.tecla.do/complete-python-sale)**! -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^2.2.0", 18 | "@docusaurus/preset-classic": "^2.2.0", 19 | "@docusaurus/theme-mermaid": "^2.2.0", 20 | "@docusaurus/theme-search-algolia": "^2.2.0", 21 | "@mdx-js/react": "^1.6.22", 22 | "clsx": "^1.1.1", 23 | "prism-react-renderer": "^1.3.1", 24 | "react": "^17.0.2", 25 | "react-dom": "^17.0.2" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.5%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [ 18 | "intro/intro", 19 | { 20 | type: "category", 21 | label: "Python Fundamentals", 22 | items: [ 23 | "intro/lectures/and_or/README", 24 | // { 25 | // type: "autogenerated", 26 | // dirName: "1_intro/lectures", 27 | // }, 28 | ], 29 | }, 30 | { 31 | type: "category", 32 | label: "Errors in Python", 33 | items: ["errors/lectures/handling_user_errors_exercise/README"], 34 | }, 35 | ], 36 | }; 37 | 38 | module.exports = sidebars; 39 | -------------------------------------------------------------------------------- /src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /src/components/VideoEmbed/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function VideoEmbed({ url }) { 4 | return ( 5 |
6 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tecladocode/complete-python-course/6089fcad60465482188bd94e95bd0a0991d3845a/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/folder-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /static/img/folder-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | --------------------------------------------------------------------------------