├── .gitignore ├── README.md ├── final_project ├── Readme.md ├── config.py ├── debug_server.py ├── epub │ ├── __init__.py │ ├── location_sync.py │ ├── read_book.py │ ├── static │ │ ├── book.css │ │ └── test.epub │ └── templates │ │ └── read_book.html └── epub_metadata.py ├── lecture1 ├── Introduction to computers and programming.pdf ├── Readme.md ├── images │ ├── Triangle.png │ ├── arrow.png │ ├── blue_star.png │ ├── circle.png │ ├── deathly_hallows.png │ ├── hexagon.png │ ├── letter_T.png │ ├── olympic_rings.png │ └── square_circle.png └── logo_programming.md ├── lecture10 ├── Readme.md ├── assignment_dictionary.md ├── assignment_photo_filters.md ├── example_projects │ ├── dictionary_webapp │ │ ├── debug_server.py │ │ ├── dict_service │ │ │ ├── __init__.py │ │ │ └── dictionary.py │ │ └── dictionary_webapp │ │ │ ├── __init__.py │ │ │ ├── dictionary_home.py │ │ │ ├── display_word_defs.py │ │ │ ├── forms.py │ │ │ └── templates │ │ │ ├── definitions.html │ │ │ ├── definitions_error.html │ │ │ └── dictionary_home.html │ ├── fortune_teller_webapp │ │ ├── config.py │ │ ├── debug_server.py │ │ └── fortune_teller │ │ │ ├── __init__.py │ │ │ ├── forms.py │ │ │ ├── home.py │ │ │ ├── show_fortune.py │ │ │ ├── static │ │ │ ├── cookie_rich.jpg │ │ │ └── quote.css │ │ │ └── templates │ │ │ ├── fortune.html │ │ │ └── home.html │ ├── photo_filter │ │ ├── config.py │ │ ├── debug_server.py │ │ ├── instagram_filters │ │ │ ├── __init__.py │ │ │ └── image_filters.py │ │ └── vintage_photo │ │ │ ├── __init__.py │ │ │ ├── apply_filter.py │ │ │ ├── file_upload.py │ │ │ ├── forms.py │ │ │ ├── show_image.py │ │ │ ├── slideshow.py │ │ │ └── templates │ │ │ ├── file_upload.html │ │ │ ├── slideshow.html │ │ │ └── slideshow_not_found.html │ └── requirements.txt ├── fortune_css.png ├── fortunes.txt ├── quiz.md └── webapp.md ├── lecture11 ├── Readme.md ├── assignments.md ├── example_projects │ ├── quiz_webapp │ │ ├── config.py │ │ ├── debug_server.py │ │ └── quiz │ │ │ ├── __init__.py │ │ │ ├── add_question.py │ │ │ ├── db_tables.py │ │ │ ├── forms.py │ │ │ ├── quiz.py │ │ │ └── templates │ │ │ ├── add_question.html │ │ │ ├── add_question_success.html │ │ │ ├── macros.html │ │ │ ├── quiz.html │ │ │ └── quiz_answers.html │ └── requirements.txt ├── question_html.png ├── quiz_page_uml2.png ├── quiz_pages_uml1.png ├── quiz_user_flow1.png ├── quiz_user_flow2.png └── webapp_using_db.md ├── lecture12 ├── Public_key.svg ├── Readme.md ├── example_projects │ ├── requirements.txt │ └── writer_webapp │ │ ├── cert.pem │ │ ├── config.py │ │ ├── debug_server.py │ │ ├── key.pem │ │ └── writer │ │ ├── __init__.py │ │ ├── all_posts.py │ │ ├── create_post.py │ │ ├── db_tables.py │ │ ├── forms.py │ │ ├── login_register.py │ │ ├── profile.py │ │ ├── templates │ │ ├── create_post.html │ │ ├── login.html │ │ ├── macros.html │ │ ├── navigation.html │ │ ├── posts_list.html │ │ ├── profile.html │ │ ├── register.html │ │ ├── reset_email.html │ │ ├── reset_email_confirm.html │ │ ├── update_password.html │ │ └── view_post.html │ │ ├── update_password.py │ │ └── view_post.py ├── no_cookie.gif ├── no_cookie.psd ├── pages_uml1.png ├── password_reset_flow.png ├── user_accounts.md ├── user_flow_uml1.png ├── with_cookie.gif └── with_cookie.psd ├── lecture13 ├── Readme.md ├── important_code.py ├── test_important_code.py └── testing.md ├── lecture2 ├── Readme.md ├── assignments.md └── variables.md ├── lecture3 ├── Readme.md ├── assignments.md ├── conditional_and_loops.md ├── google-france.png ├── if-else_diagram.jpg ├── if_diagram.jpg ├── loop_diagram.jpg ├── phone_unlock.jpg └── tetris.jpg ├── lecture4 ├── Readme.md ├── assignments.md ├── city_streets.png ├── function.png ├── function.svg.png ├── functions.md ├── quiz.md ├── sphinx_doc.png └── stats.html ├── lecture5 ├── Readme.md ├── assignments.md ├── ca_staff_data_2016.csv ├── ca_student_data_2016.csv ├── collections_forloop.md └── data_analysis.md ├── lecture6 ├── Algorithms.pdf └── Readme.md ├── lecture7 ├── Readme.md ├── assignments.md ├── modules.md ├── pi.gif └── sides_of_right_triangle.gif ├── lecture8 ├── Readme.md ├── classes.md ├── rental_service.py ├── watch_face.jpg └── watch_movement.jpg ├── lecture9 ├── Readme.md └── internet_and_www.pdf └── next_steps └── next_steps.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/python,pycharm+iml 3 | 4 | ### PyCharm+iml ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff: 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/dictionaries 12 | 13 | # Sensitive or high-churn files: 14 | .idea/**/dataSources/ 15 | .idea/**/dataSources.ids 16 | .idea/**/dataSources.xml 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/vcs.xml 22 | 23 | # Gradle: 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # CMake 28 | cmake-build-debug/ 29 | 30 | # Mongo Explorer plugin: 31 | .idea/**/mongoSettings.xml 32 | 33 | ## File-based project format: 34 | *.iws 35 | 36 | ## Plugin-specific files: 37 | 38 | # IntelliJ 39 | /out/ 40 | 41 | # mpeltonen/sbt-idea plugin 42 | .idea_modules/ 43 | 44 | # JIRA plugin 45 | atlassian-ide-plugin.xml 46 | 47 | # Cursive Clojure plugin 48 | .idea/replstate.xml 49 | 50 | # Ruby plugin and RubyMine 51 | /.rakeTasks 52 | 53 | # Crashlytics plugin (for Android Studio and IntelliJ) 54 | com_crashlytics_export_strings.xml 55 | crashlytics.properties 56 | crashlytics-build.properties 57 | fabric.properties 58 | 59 | ### PyCharm+iml Patch ### 60 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 61 | 62 | *.iml 63 | modules.xml 64 | .idea/misc.xml 65 | *.ipr 66 | 67 | ### Python ### 68 | # Byte-compiled / optimized / DLL files 69 | __pycache__/ 70 | *.py[cod] 71 | *$py.class 72 | 73 | # C extensions 74 | *.so 75 | 76 | # Distribution / packaging 77 | .Python 78 | build/ 79 | develop-eggs/ 80 | dist/ 81 | downloads/ 82 | eggs/ 83 | .eggs/ 84 | lib/ 85 | lib64/ 86 | parts/ 87 | sdist/ 88 | var/ 89 | wheels/ 90 | *.egg-info/ 91 | .installed.cfg 92 | *.egg 93 | 94 | # PyInstaller 95 | # Usually these files are written by a python script from a template 96 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 97 | *.manifest 98 | *.spec 99 | 100 | # Installer logs 101 | pip-log.txt 102 | pip-delete-this-directory.txt 103 | 104 | # Unit test / coverage reports 105 | htmlcov/ 106 | .tox/ 107 | .coverage 108 | .coverage.* 109 | .cache 110 | .pytest_cache/ 111 | nosetests.xml 112 | coverage.xml 113 | *.cover 114 | .hypothesis/ 115 | 116 | # Translations 117 | *.mo 118 | *.pot 119 | 120 | # Flask stuff: 121 | instance/ 122 | .webassets-cache 123 | 124 | # Scrapy stuff: 125 | .scrapy 126 | 127 | # Sphinx documentation 128 | docs/_build/ 129 | 130 | # PyBuilder 131 | target/ 132 | 133 | # Jupyter Notebook 134 | .ipynb_checkpoints 135 | 136 | # pyenv 137 | .python-version 138 | 139 | # celery beat schedule file 140 | celerybeat-schedule.* 141 | 142 | # SageMath parsed files 143 | *.sage.py 144 | 145 | # Environments 146 | .env 147 | .venv 148 | env/ 149 | venv/ 150 | ENV/ 151 | env.bak/ 152 | venv.bak/ 153 | 154 | # Spyder project settings 155 | .spyderproject 156 | .spyproject 157 | 158 | # Rope project settings 159 | .ropeproject 160 | 161 | # mkdocs documentation 162 | /site 163 | 164 | # mypy 165 | .mypy_cache/ 166 | 167 | 168 | # End of https://www.gitignore.io/api/python,pycharm+iml 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coding bootcamp 2 | 3 | This is the material that is used for the [free coding bootcamp](https://www.meetup.com/SF-Free-Coding-Bootcamp/) meetup that I run in SF. The material contains a collection of lectures, assignments and occasional quizzes. 4 | 5 | This bootcamp **uses Python as the language of choice** to introduce programming. It's a very popular language and is being used more and more frequently for production systems - including webserver frameworks, data analysis and machine learning. 6 | 7 | The material is organized by lecture. The Readme inside every lecture directory contains the details about that lecture. 8 | 9 | # The Syllabus 10 | This the list of topics we discuss in the lecture series. 11 | - All lectures after the first one has a set of assignments. 12 | - Some lectures are long and cannot be covered in one bootcamp class. Thus, it takes more classes than the number of lectures to go through them. 13 | 14 | The lectures are designed to teach enough Python for one to be able to write meaningful real life applications themselves. The final project is to create a web app which is an online E-book reader. I will also discuss the ways you can use this learning to become a professional programmer in the last lecture. 15 | 16 | ### Lecture 1 - What is programming? 17 | The first lecture introduces what a computer is, and what programming is. In the class, we also write our first little program. 18 | 19 | ### Lecture 2 - Variables and Expressions 20 | In this lecture we start learning Python, by understanding what a variable is, and how we can use constants and variables in expressions. We learn about the basic data types built into Python. 21 | 22 | ### Lecture 3 - If statements and while loops 23 | We look at conditional logic in this lecture. Based on certain criteria, we can execute some code but not other (using `if` statements) or execute the same code numerous times (using `while` statements). 24 | 25 | ### Lecture 4 - Functions and recursion 26 | The concept of _functions_ is introduced, which is used to modularize the code. We also discuss the topic of recursion using functions, and learn how it can be used to solve some interesting problems. 27 | 28 | ### Lecture 5 - Collection data types and for loops 29 | We start looking at more advanced datatypes; specifically we learn about types which can store a collection of data. That allows me to also introduce the `for` loop statement, which makes writing loops easier. 30 | 31 | As an application of what've learnt till now, we do some data analysis on real life data. We use the data from California Public School system to gain insights about the system (like what is the average student:teacher ratio) 32 | 33 | ### Lecture 6 - Algorithms 34 | We discuss a computer science topic - Algorithms. We define what an Algorithm is, look at some fundamental algorithms in computer science, and understand what it means for an algorithm to be good by using the measure of Time Complexity. 35 | 36 | ### Lecture 7 - Python Modules 37 | This is a shorter lecture where we discuss Python _modules_. Modules are useful to package a bunch of code and share it with others. We will learn how to create our own modules and to use Python's built in modules and third party modules. 38 | 39 | Assignments to this lecture are some of my favorite. 40 | 41 | ### Lecture 8 - Object Oriented Programming 42 | We cover the topic of Object Oriented Programming (OOP), and learn how to do write that style of programs in Python. This is a very fundamental concept without which it is practically impossible to work on any real life application. 43 | 44 | ### Lecture 9 - Internet and the World Wide Web 45 | We take a break from programming and learn how the Internet and the World Wide Web works. It sets us up for the next lectures, which are all about creating web sites using Python. 46 | 47 | ### Lecture 10 - Web App development using Flask 48 | In this lecture we will learn how to create websites using a popular Python web development framework called Flask. The lecture is a tutorial in which we create a Fortune teller app that shows the user a random _fortune_. 49 | 50 | The assignments make you develop a few more interesting websites, and you'll also learn some very important concepts while doing them (like REST APIs, JSON, etc.) 51 | 52 | ### Lecture 11 - Using databases for Web Applications 53 | We get introduced to the concept of databases and learn how we can use them to make websites which has more features and a persistent state. We make a quiz app which allows users to take a quiz and see their performance on it. 54 | 55 | The assignments make you extend that quiz app further, and make it full featured enough to be an useful product for people to use. 56 | 57 | ### Lecture 12 - Web Apps with user accounts 58 | We learn to build apps that have user accounts. We understand how user sessions work behind the scenes, and learn to implement all flows for management of user details (profile update, password reset, etc.). 59 | 60 | We also understand the basics of security, and learn to implement some ways to make sure our data storage mechanisms and connections are secure. 61 | 62 | There are no separate assignments. The tutorial has exercises and that should give you some good experience. 63 | 64 | ### Lecture 13 - Testing 65 | Learn about testing your code - writing tests, writing testable code, etc. Testing is one of the most crucial aspect of programming. It's also one of those things that many programming classes omit, and that's a shame in my view - as in professional work it is critically important to test your code. 66 | 67 | 68 | ### Final Project 69 | 70 | Specifications for the web app which implements an online ebook reader. 71 | 72 | ### Next Steps 73 | 74 | In this lecture, I describe what a professional programmer does, what choices do you have for a programming job, and how you can switch to or improve your programming career even if you don't have a CS degree or professional programming experience. 75 | 76 | # Installing Python on your computer 77 | 78 | Follow the instructions below to install Python interpreter and PyCharm Integrated Development Environment on your computer. 79 | 80 | ## Windows 81 | ### Installing Python interpreter 82 | 83 | 1. Download the Python executable installer, which is labeled **Windows x86-64 executable installer** from here: https://www.python.org/downloads/release/python-365/ 84 | 85 | 2. Run the downloaded file (may need privileges). 86 | 87 | ### Installing PyCharm 88 | 89 | 1. Download the PyCharm installer from the link here: https://download.jetbrains.com/python/pycharm-community-2018.1.2.exe 90 | 91 | 2. Run the installer to install PyCharm. 92 | 93 | 3. After it is installed, you can run PyCharm. It's opening screen will help you create a new project and you can try to write some code and run it. You might be asked to configure the Python intepreter, and you can use the instructions here to do that: https://www.jetbrains.com/help/pycharm/configuring-python-interpreter.html 94 | 95 | ## Mac OS 96 | 97 | ### Installing Python interpreter 98 | 99 | 1. Download the Python executable installer, which is labeled **macOS 64-bit installer** from here: https://www.python.org/downloads/release/python-365/ 100 | 101 | 2. Run the downloaded file (may need privileges). 102 | 103 | ### Installing PyCharm 104 | 105 | 1. Download the PyCharm installer from the link here: https://download.jetbrains.com/python/pycharm-community-2018.1.2.dmg 106 | 107 | 2. Run the installer to install PyCharm. 108 | 109 | 3. After it is installed, you can run PyCharm. It's opening screen will help you create a new project and you can try to write some code and run it. You might be asked to configure the Python intepreter, and you can use the instructions here to do that: https://www.jetbrains.com/help/pycharm/configuring-python-interpreter.html 110 | 111 | ## Ubuntu 112 | 113 | ### Installing Python interpreter 114 | 115 | 1. In your terminal, run the following commands: 116 | ```bash 117 | sudo add-apt-repository ppa:jonathonf/python-3.6 118 | sudo apt-get update 119 | sudo apt-get install python3.6 120 | ``` 121 | Enter your password when asked. 122 | 123 | 2. Run `python3 -V` to verify python is working. 124 | 125 | ### Installing PyCharm 126 | 127 | 1. Download the PyCharm archive from the link here: https://download.jetbrains.com/python/pycharm-community-2018.1.2.tar.gz 128 | 129 | 2. Copy this file to the directory where you want to install PyCharm. Then run `tar xzf pycharm-community-2018.1.2.tar.gz`. 130 | 131 | 3. Go to the `/bin` and run `./pycharm.sh` to run PyCharm. 132 | 133 | 4. It's opening screen will help you create a new project and you can try to write some code and run it. You might be asked to configure the Python intepreter, and you can use the instructions here to do that: https://www.jetbrains.com/help/pycharm/configuring-python-interpreter.html 134 | -------------------------------------------------------------------------------- /final_project/Readme.md: -------------------------------------------------------------------------------- 1 | # Final Project 2 | 3 | In the final project, you will create an ebook reader web app which allows anyone to read books which we've made available (using books available in the free domain), and allow users to upload their own as well (as a stretch goal). 4 | 5 | ## Features 6 | 7 | The web app allows both anonymous use and users with accounts to read books. 8 | 9 | ### Anonymous users 10 | 11 | - They can see a global library of ebooks. 12 | - They can read any book in that library in an online UI. 13 | 14 | ### Users with accounts 15 | 16 | In addition to above features, creating an account leads to these additional benefits: 17 | 18 | - They can create a personal library which is a selection of books from the global library (it's like starring the books they want to read). 19 | - Whenever they read a book, the web app will synchronize their reading location. Whenever users come back to the web app later and read the same book, they will start reading from where they left off. 20 | - (Stretch goal) Users can upload their own epub files, and that book is only visible in their own library. 21 | 22 | ### Design elements 23 | 24 | The global libary of ebooks, or even personal libraries, can potentially include hundreds or even thousands of ebooks. Here are some suggestions which can make it easier to find what want to read: 25 | 26 | - Navigation which uses the letters of the alphabet to help choose the book - either by the title, or by the author name. 27 | - A search bar that finds books with the words entered in the title or in the author fields of the book. 28 | 29 | ## Implementation considerations 30 | 31 | ### E-book format 32 | 33 | The web app will support the [ePUB e-book format](https://en.wikipedia.org/wiki/EPUB). This is a widely used format to store and share ebooks, and most books available to download online are shared in this format. 34 | 35 | ePub format contains the _metadata_ of the book (title, author, etc.), and also includes the whole contents of the book formatted in a readable manner. We need to be able to extract that metadata, and also render the book in the browser. 36 | 37 | #### Reading ePUB metadata 38 | 39 | I have included a file called `epub_metadata.py` in this folder which has example code to extract the book details from the epub file. Note that it needs an external dependency `lxml` to be installed. 40 | 41 | For all the ebooks on the web app, you will need to extract the metadata from the book and store it in a DB table. 42 | 43 | #### Rendering ePUB books 44 | 45 | We will use the [Epub.js](https://github.com/futurepress/epub.js/) library to render ebooks. I have created an example web app which has the `read_book()` view. It uses the `read_book.html` template to render. The `render_template` call looks like this: 46 | 47 | ```python 48 | return render_template("read_book.html", cfi=cfi_location, title=title, author=author, 49 | book=url_for('static', filename='test.epub')) 50 | ``` 51 | 52 | - Everything should be obvious, except the parameter `cfi`, which we will discuss soon. 53 | - There is a CSS file called `book.css` and some javascript in the template which makes this experience work. 54 | - Navigation of the book can be done by clicking on the arrows on the left and right, or using left and right arrow keys on the keyboard. 55 | - I highly recommend that you run this web app `epub` and see how it works. 56 | 57 | ### Synchronizing read location 58 | 59 | To implement this feature, we need to use AJAX. Periodically, the javascript in the page will find the location at which the user is at, in the book, and send a POST HTTP request to our web app. The web app must build a view to receive that request, and then update the location for that book in the user records. 60 | 61 | The read location is encoded as an [ePUB Canonical Fragment Identifier](http://www.idpf.org/epub/linking/cfi/epub-cfi.html), or ePUB CFI. This is a string that identifies the exact location in one specific book (it will not work on another book). It can look like this: 62 | 63 | ``` 64 | epubcfi(/6/2[item58]!/4/66[THE_OUTDOOR_LABORATORY]/2/1:387) 65 | ``` 66 | 67 | In the javascript code in `read_book.html` template, I have already implemented the mechanism which sends an update to the view `location_sync()` every few seconds. The data sent (CFI string) is available using the expression `request.form["cfi"]` (ignore the fact that we are using `request.form` even though there is no form). 68 | 69 | When opening a book to read, you can provide this CFI string as the `cfi` parameter to the template. If the parameter is an empty string, then the book opens at the beginning. 70 | 71 | ### Uploading books 72 | 73 | Look at [this section](https://github.com/amangup/coding-bootcamp/blob/master/lecture10/assignment_photo_filters.md#uploading-files) in the Photo filters assignment to learn how you can upload books. 74 | 75 | ## Deploying the website 76 | 77 | When you are developing (and when its complete), keep deploying the website at PythonAnywhere.com to make sure everything works as intended. Make sure that use of HTTPS protocol is enforced. 78 | -------------------------------------------------------------------------------- /final_project/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | SECRET_KEY = 'really-hard-password' 3 | -------------------------------------------------------------------------------- /final_project/debug_server.py: -------------------------------------------------------------------------------- 1 | from epub import app 2 | 3 | 4 | def main(): 5 | app.run(host='127.0.0.1', port=8080, debug=True) 6 | 7 | 8 | if __name__ == '__main__': 9 | main() 10 | -------------------------------------------------------------------------------- /final_project/epub/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | from config import Config 4 | 5 | app = Flask(__name__) 6 | app.config.from_object(Config) 7 | 8 | from epub import read_book, location_sync -------------------------------------------------------------------------------- /final_project/epub/location_sync.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | 3 | from epub import app 4 | 5 | @app.route('/location_sync', methods=['POST']) 6 | def location_sync(): 7 | print(request.form['cfi']) 8 | return "Location received" -------------------------------------------------------------------------------- /final_project/epub/read_book.py: -------------------------------------------------------------------------------- 1 | from flask import render_template, url_for 2 | 3 | from epub import app 4 | 5 | @app.route('/') 6 | def read_book(): 7 | title = "A Catalogue of Play Equipment" 8 | author = "Jean Lee Hunt" 9 | cfi_location = "" 10 | return render_template("read_book.html", cfi=cfi_location, title=title, author=author, 11 | book=url_for('static', filename='test.epub')) 12 | -------------------------------------------------------------------------------- /final_project/epub/static/book.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 40px; 3 | } 4 | 5 | .left { 6 | grid-area: left; 7 | } 8 | 9 | .right { 10 | grid-area: right; 11 | } 12 | 13 | .content { 14 | grid-area: content; 15 | position: relative; 16 | } 17 | 18 | .header { 19 | grid-area: header; 20 | } 21 | 22 | .wrapper { 23 | display: grid; 24 | grid-gap: 10px; 25 | grid-template-columns: 100px 600px 100px; 26 | grid-template-areas: 27 | "... header ..." 28 | "left content right"; 29 | } 30 | 31 | .centerme { 32 | margin: 0 auto; 33 | display: table; 34 | position: relative; 35 | top: 50%; 36 | -webkit-transform: translateY(-50%); 37 | -moz-transform: translateY(-50%); 38 | -o-transform: translateY(-50%); 39 | -ms-transform: translateY(-50%); 40 | transform: translateY(-50%); 41 | } 42 | 43 | a { 44 | text-decoration: none; 45 | display: inline-block; 46 | font-size: 50px; 47 | padding: 8px 16px; 48 | } 49 | 50 | a:hover { 51 | background-color: #ddd; 52 | color: black; 53 | } 54 | 55 | .previous { 56 | background-color: #fff; 57 | color: black; 58 | } 59 | 60 | .next { 61 | background-color: #fff; 62 | color: black; 63 | } 64 | 65 | .round { 66 | border-radius: 50%; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /final_project/epub/static/test.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/final_project/epub/static/test.epub -------------------------------------------------------------------------------- /final_project/epub/templates/read_book.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Read Book 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

{{ title }}, by {{ author }}

15 |
16 |
17 | 18 |
19 |
20 |
21 | 22 |
23 |
24 | 88 | 89 | -------------------------------------------------------------------------------- /final_project/epub_metadata.py: -------------------------------------------------------------------------------- 1 | import zipfile 2 | from lxml import etree 3 | 4 | def get_epub_info(fname): 5 | ns = { 6 | 'n':'urn:oasis:names:tc:opendocument:xmlns:container', 7 | 'pkg':'http://www.idpf.org/2007/opf', 8 | 'dc':'http://purl.org/dc/elements/1.1/' 9 | } 10 | 11 | # prepare to read from the .epub file 12 | zip = zipfile.ZipFile(fname) 13 | 14 | # find the contents metafile 15 | txt = zip.read('META-INF/container.xml') 16 | tree = etree.fromstring(txt) 17 | cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path',namespaces=ns)[0] 18 | 19 | # grab the metadata block from the contents metafile 20 | cf = zip.read(cfname) 21 | tree = etree.fromstring(cf) 22 | p = tree.xpath('/pkg:package/pkg:metadata',namespaces=ns)[0] 23 | 24 | # repackage the data 25 | res = {} 26 | for s in ['title','language','creator','date','identifier']: 27 | res[s] = p.xpath('dc:%s/text()'%(s),namespaces=ns)[0] 28 | 29 | return res 30 | 31 | print(get_epub_info("epub/static/test.epub")) -------------------------------------------------------------------------------- /lecture1/Introduction to computers and programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/Introduction to computers and programming.pdf -------------------------------------------------------------------------------- /lecture1/Readme.md: -------------------------------------------------------------------------------- 1 | ## What is a computer and what is programming? 2 | 3 | This lecture introduces the concept of a computer (from a programmer's perspective), and then also introduces the idea of _programming_. This is a set of slides, and pdf is uploaded here. The google slides link to the presentation is below: 4 | 5 | [Introduction to Computers and Programming](https://docs.google.com/presentation/d/1-0GJxFE_Pzj5J7WuSjxkx2VnnAdtP6UrJnnP44uDaws/edit?usp=sharing) 6 | 7 | To get introduced to programming in a fun way, I've also created [this document](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/logo_programming.md) which introduced the Logo programming language. 8 | -------------------------------------------------------------------------------- /lecture1/images/Triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/Triangle.png -------------------------------------------------------------------------------- /lecture1/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/arrow.png -------------------------------------------------------------------------------- /lecture1/images/blue_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/blue_star.png -------------------------------------------------------------------------------- /lecture1/images/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/circle.png -------------------------------------------------------------------------------- /lecture1/images/deathly_hallows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/deathly_hallows.png -------------------------------------------------------------------------------- /lecture1/images/hexagon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/hexagon.png -------------------------------------------------------------------------------- /lecture1/images/letter_T.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/letter_T.png -------------------------------------------------------------------------------- /lecture1/images/olympic_rings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/olympic_rings.png -------------------------------------------------------------------------------- /lecture1/images/square_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture1/images/square_circle.png -------------------------------------------------------------------------------- /lecture1/logo_programming.md: -------------------------------------------------------------------------------- 1 | # Logo Programming language 2 | 3 | [Logo](https://en.wikipedia.org/wiki/Logo_(programming_language)) is a programmming language that was designed for educational reasons. In my view, it's one of the cutest and fun ways to get introduced to programming. 4 | 5 | In the logo programming language, you instruct a _turtle_ to move around on the screen, while it draws a line underneath as it moves. This allows you to draw any shape and pattern that you like, using programming! It's not just a toy language - it has many features available in otherwise serious and popular programming languages. 6 | 7 | This page has some very simple programs to get you familiar with Logo. To learn more, I highly recommend [reading this tutorial](http://abz.inf.ethz.ch/wp-content/uploads/unterrichtsmaterialien/primarschulen/logo_heft_en.pdf). 8 | 9 | We also need an _interpreter_, where we can write our programs and see the output. The website [http://www.calormen.com/jslogo/](http://www.calormen.com/jslogo/) allows you to do that. 10 | 11 | ## Simple programs 12 | 13 | At any point, to clear the screen, you can use the following command: 14 | 15 | ``` 16 | clearscreen 17 | ``` 18 | 19 | You can also show or hide the turtle pointer using the following commands: 20 | 21 | ``` 22 | hideturtle 23 | showturtle 24 | ``` 25 | 26 | ### Letter T 27 | 28 | ![Letter T](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/letter_T.png) 29 | 30 | ``` 31 | forward 200 32 | right 90 33 | forward 100 34 | back 200 35 | ``` 36 | 37 | - `forward` is used to move forward, `back` is used to move backward, and `right` is used to turn the turtle clockwise. 38 | 39 | 40 | ### An arrow 41 | 42 | ![An arrow](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/arrow.png) 43 | 44 | 45 | ``` 46 | forward 200 47 | left 135 48 | forward 50 49 | back 50 50 | left 90 51 | forward 50 52 | ``` 53 | 54 | ### A Triangle 55 | 56 | ![Triangle](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/Triangle.png) 57 | 58 | 59 | ``` 60 | left 30 61 | forward 200 62 | left 120 63 | forward 200 64 | left 120 65 | forward 200 66 | ``` 67 | 68 | - We are repeating the same commands multiple times for the triangle. I wonder if we can do something about that and save some typing effort... 69 | 70 | ### A hexagon 71 | 72 | ![Hexagon](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/hexagon.png) 73 | 74 | ``` 75 | repeat 6 [forward 100 right 60] 76 | ``` 77 | 78 | - You can repeat the same command multiple times by using the instruction `repeat [command]` 79 | 80 | ### A Circle 81 | 82 | ![Circle](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/circle.png) 83 | 84 | ``` 85 | repeat 360 [forward 1 right 1] 86 | ``` 87 | 88 | - As you can see, the creation of circle is just an extension of the creation of a hexagon. 89 | 90 | - What is the radius of this circle? (remember `2 x pi x radius = circumference`) 91 | 92 | ### A circle inside a square 93 | 94 | ![Square and Circle](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/square_circle.png) 95 | 96 | ``` 97 | repeat 4 [forward 200 right 90] 98 | penup 99 | forward 150 100 | right 90 101 | forward 100 102 | pendown 103 | repeat 360 [forward 314/360 right 1] 104 | ``` 105 | 106 | - `penup` command stops the turtle from drawing anything when you just want to move the turtle. `pendown` brings the drawing back. 107 | - The command for drawing a circle was created to draw a circle of radius = 50. 108 | 109 | ### A Blue star 110 | 111 | ![A Blue Star](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/blue_star.png) 112 | 113 | ``` 114 | setpencolor 1 115 | repeat 5 [right 15 forward 100 right 150 forward 100 left 93] 116 | ``` 117 | 118 | - `setpencolor` sets the color of the line you are drawing. The commands accepts a color number - and I checked that 0 is for black, 1 for blue, 2 for green and 4 for red. Experiment yourselves with other colors! 119 | 120 | That's the end of samples. 121 | 122 | ## Exercises 123 | 124 | Here are a couple of exercises, which you can use to practice further 125 | 126 | ### Olympic rings 127 | 128 | ![Olympic Rings](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/olympic_rings.png) 129 | 130 | ### Deathly Hallows from Harry Potter 131 | 132 | ![Deathly Hallows](https://github.com/amangup/coding-bootcamp/blob/master/lecture1/images/deathly_hallows.png) 133 | -------------------------------------------------------------------------------- /lecture10/Readme.md: -------------------------------------------------------------------------------- 1 | ## Building web applications using Flask 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture10/webapp.md) is essentially a tutorial to help you learn how to build web applications and deploy them on the internet for everyone to use. We use a popular web developlment framework called **Flask** to build websites. 4 | 5 | There is a [quiz](https://github.com/amangup/coding-bootcamp/blob/master/lecture10/quiz.md) which is helpful in reinforcing concepts learned in this lecture and a couple of previous lectures. 6 | 7 | Finally, there are a couple of assignments: 8 | - [Dictionary](https://github.com/amangup/coding-bootcamp/blob/master/lecture10/assignment_dictionary.md) 9 | - [Photo filters](https://github.com/amangup/coding-bootcamp/blob/master/lecture10/assignment_photo_filters.md) 10 | 11 | These assignments are full websites, and requires to learn more concepts on top of what we covered in the tutorial. For new programmers looking to switch careers, these assignments are projects worthy to be included in your portfolio, especially if personalize them by adding new features or making them look professional. -------------------------------------------------------------------------------- /lecture10/assignment_dictionary.md: -------------------------------------------------------------------------------- 1 | ## Assignment: Dictionary website 2 | 3 | In this assignment, you are going to create a website which asks user for a word and shows all the definitions of that word. To get the definitions, we are going to use a service created by the Oxford Dictionaries team. 4 | 5 | On top of lecture 10, you will need to learn a few more things to be able to finish this assignment: 6 | - Using a Oxford Dictionaries RESTful API service 7 | - Understand what JSON it and parse it 8 | - Learn how to do URL redirection 9 | - `if` statements and `for` loops in HTML templates 10 | 11 | Let's get started. 12 | 13 | ### REST API 14 | 15 | When we learned about HTTP servers, we learned that an HTTP response body doesn't have to be HTML. HTTP servers can be used to implement any kind of service. There are many such services which provide information relevant to the query asked by the user of the service. These services are commonly called REST API services (the precise definition of REST API is somewhat more complicated). 16 | 17 | The Oxford dictionaries provides a REST API to provide dictionary definitions. The user can call the service with a specific URL (which includes the word, language, etc.), and the HTTP response is a JSON object containing a bunch of information including it's definitions, different parts of speech it can be used as, and more. 18 | 19 | The Oxford Dictionaries REST API details are here: [https://developer.oxforddictionaries.com/](https://developer.oxforddictionaries.com/). 20 | 21 | First, register on the website and get the API key. You will need that key to access their API. 22 | 23 | You can read the documentation on its documentation page. The relevant section is **Dictionary entries**. You can even try it right there - it shows you how to construct the relevant URL and how does the response looks like. It even has the following snippet to show you how to use it using Python: 24 | 25 | ```python 26 | import requests 27 | import json 28 | 29 | # TODO: replace with your own app_id and app_key 30 | # for more information on how to install requests 31 | # http://docs.python-requests.org/en/master/user/install/#install 32 | import requests 33 | import json 34 | 35 | # TODO: replace with your own app_id and app_key 36 | app_id = '' 37 | app_key = '' 38 | 39 | language = 'en' 40 | word_id = 'Ace' 41 | 42 | url = 'https://od-api.oxforddictionaries.com:443/api/v1/entries/' + language + '/' + word_id.lower() 43 | 44 | r = requests.get(url, headers = {'app_id': app_id, 'app_key': app_key}) 45 | 46 | print("code {}\n".format(r.status_code)) 47 | print("text \n" + r.text) 48 | print("json \n" + json.dumps(r.json())) 49 | ``` 50 | 51 | - This code uses the `requests` module to make an HTTP GET request to the Oxford API's URL and capture the response. In your assignment, you will do something very similar to get the word definitions. 52 | 53 | ### JSON 54 | 55 | JSON stands for **JavaScript Object Notation** and it's a string data format. It's commonly used to transfer _structured data_ (like lists and dictionaries in Python) over the internet. To transfer data over the internet, you have to be able to format the data as a string. Structured data, like a dictionary, has a very complicated representation in the memory (different elements of dictionary are at discontiguous memory locations) and that memory structure can't be transferred as it is. This is why we need formats like JSON. 56 | 57 | For this assignment, you will need to understand JSON well. 58 | 59 | - Here is an article which explains what JSON is: [Working with JSON](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON). You don't need to do the "Active Learning" section, as it uses Javascript. 60 | - A great reference: [https://www.json.org/](https://www.json.org/) 61 | 62 | In Python, the `json` module is used to parse a JSON string, and returns an object representing the JSON data (if the JSON string represents a list, you will get a list, if it's an key:value object, you will get a dictionary, etc). Note that for this exercise, you don't really have to parse JSON - the return value of `r.json()` (see snippet in the previous section) is a dictionary object which you can use straightaway (`r.json()['results']` gives you value for the key `results`, which is a list of dictionaries itself.) 63 | 64 | You will have to spend some time and effort to figure out how to extract what you need from the Oxford API's response. A good dictionary website should show the part of speech, the definition and example sentences (all of which are included in the response). 65 | 66 | ### URL redirection 67 | 68 | URL redirection means that when you go to an URL on your browser, that URL automatically redirects you to another one without your input. This can be useful to make your dictionary website better, but it's optional. 69 | 70 | In a dictionary website, it would be desirable to allow users to go to an URL like: `www.mydictionary.com/definitions/mushroom` and get the definition of the word mushroom. This allows users to bookmark pages, share URLs, and the output is the same for all users at any time. Note that our fortune teller website doesn't support that - in fact, if the user went to the `/fortune` path without using the form on the home page, it would give an error. 71 | 72 | To do that, we need to do two things: 73 | 74 | 1. Show the word definitions if someone navigates to `www.mydictionary.com/definitions/mushroom` in their browser. To do that, your code needs to be able to access the word `mushroom`, so that you can show it's definitions. 75 | 76 | The URL rules in Flask can have variable names. [This link](http://flask.pocoo.org/docs/1.0/quickstart/#variable-rules) shows how they work (you will do something similar to the username example shown at the link). 77 | 78 | 2. When the user types a word and submits the form (on your website's home page), you will have to read the word user entered, and then _redirect_ to the link as shown above. 79 | 80 | Let's say, you implemented `definitions` view function as follows: 81 | 82 | ```python 83 | @app.route('/definitions/') 84 | def show_definitions(word): 85 | # respond with an HTML which contains the word's definitions 86 | pass 87 | ``` 88 | 89 | Then you can write your home page like this: 90 | 91 | ```python 92 | from flask import redirect, render_template, url_for, request 93 | 94 | @app.route('/', methods=['GET', 'POST']) 95 | def home(): 96 | if request.method == 'POST': 97 | # get 'word' from the form 98 | return redirect(url_for('show_definitions', word=word)) 99 | else: 100 | # create a 'form' object 101 | return render_template('home.html', form=form) 102 | ``` 103 | 104 | You will also need to make sure that the form's submit url is the home page of your website: 105 | 106 | ```html 107 |
108 | ``` 109 | 110 | ### `if` and `for` statements in HTML templates 111 | 112 | The `jinja2` template system allows you to use to use `for` and `if` control statements inside HTML templates. This is useful for this assignment 113 | 114 | - a word can have many definitions, and you need for loops inside the template to create an HTML for all of those. 115 | - Some word definitions have example sentences, others not. You will need `if` statements do check if they are present, and show them if they are. 116 | 117 | They are implemented in a very convenient manner. For example, this is how `for` statement works, when you pass `alist` to the template: 118 | 119 | ```html 120 | {% for item in alist %} 121 | item 122 | <% endfor %} 123 | ``` 124 | 125 | - Note that the for condition is written exactly as you would in Python. The tag `` makes the text bold. 126 | 127 | Here is how the `if` statement works: 128 | 129 | ```html 130 | {% if definitions %} 131 | 132 | {% endif %} 133 | ``` 134 | 135 | The detailed documentation is [here](http://jinja.pocoo.org/docs/2.10/templates/#list-of-control-structures). -------------------------------------------------------------------------------- /lecture10/assignment_photo_filters.md: -------------------------------------------------------------------------------- 1 | ## Assignment : Photo filters 2 | 3 | If you like photos, then this will be a fun assignment for you. We will create a webapp where an user can upload any picture, and the app creates multiple versions of it by applying different image filters (like making it look like an aged photo). The app will display those filtered images in a slideshow to the user. 4 | 5 | To implement this, you will need to learn a few more things over what we learned in the lecture: 6 | - How to upload files 7 | - How to apply filters on images 8 | - How to create an image slideshow (includes learning to use `for` statements in HTML templates) 9 | - URL redirection 10 | 11 | For someone who is new to building webapps, this one till take a bit of effort and time, so do persevere. Whenever I get stuck, I find that it helps to come back after some time (especially if I've had a sleep in that time). You also have to search on the internet about concepts you don't already know, see examples of how others have done similar stuff and even search for errors you're getting to find explanations of why they're occurring. 12 | 13 | ### Uploading files 14 | 15 | To apply image filters, we will first upload the user's image file, and then apply filters to that image. Flask has a method to do that. 16 | 17 | First, we need the form to allow user to select an image from their device. `Flask-WTF` package has a form field type for that: 18 | 19 | ```python3 20 | from wtforms import FileField 21 | 22 | 23 | class FileUploadForm(FlaskForm): 24 | file_path = FileField('File Path') 25 | ``` 26 | 27 | When you render the `file_path` field in the form, it will show a "Browse..." button to the user. 28 | 29 | Once you submit the form, the view function receiving that HTTP POST request can access an object which helps you save that file, as shown below: 30 | 31 | ```python3 32 | if request.method == 'POST': 33 | # this is a file upload request 34 | 35 | # 'file' is an object of type FileStorage defined by Flask 36 | file = request.files['file_path'] 37 | 38 | # We are doing some basic checks on the filename before saving it. 39 | if file and '.' in file.filename: 40 | extension = get_extension(file.filename) 41 | 42 | # We only save files which have an extension referring to an image type 43 | if allowed_extension(extension): 44 | # create a variable `filepath` with the location where you're 45 | # going to save the file 46 | file.save(filepath) 47 | ``` 48 | 49 | Flask's own documentation for file uploads is here: [http://flask.pocoo.org/docs/1.0/patterns/fileuploads/](http://flask.pocoo.org/docs/1.0/patterns/fileuploads/). 50 | 51 | ### Applying image filters 52 | To implement image filters, we are going to use a third party library called `Wand`. `Wand` is a python library which is a _wrapper_ over the **ImageMagick** library, i.e., it creates Python classes and methods which in turn execute the commands in ImageMagick (as ImageMagick is not implemented in Python). ImageMagick is a well-known library for manipulating images and is available for all platforms (Windows, Mac, Linux). 53 | 54 | To install `Wand`, you can follow the same instructions as you did to install `Flask` or `Flask-WTF` (the latest version is 0.4.4). For `Wand` to function, you also need to install ImageMagick separately on your OS. You can follow the instructions here to do that: [http://docs.wand-py.org/en/0.4.4/guide/install.html](http://docs.wand-py.org/en/0.4.4/guide/install.html). 55 | 56 | Once installed, we will use the `wand` package's `Image` class to read, manipulate and write image files. You're not required to learn to do that, but if you are interested in creating your own filters, this can be a fun sub-exercise for you. I have created some simple filters implemented in the code listed below - you can study that code, and use the documentation on Wand's website to come up with more (e.g., try to create a filter which adds some text to the middle of an image). 57 | 58 | ```python3 59 | from wand.image import Image 60 | from wand.color import Color 61 | from wand.drawing import Drawing 62 | 63 | 64 | def cross_process(image_filename, output_path): 65 | with Image(filename=image_filename) as image: 66 | image.contrast_stretch(black_point=0.15, white_point=0.90, 67 | channel='red') 68 | image.contrast_stretch(black_point=0.10, white_point=0.95, 69 | channel='green') 70 | image.save(filename=output_path) 71 | 72 | 73 | def aged_photo(image_filename, output_path): 74 | with Image(filename=image_filename) as image: 75 | tone_image = Image(height=image.height, width=image.width, 76 | background=Color('#705a41')) 77 | image.modulate(brightness=100, saturation=20, hue=100) 78 | image.composite_channel(channel='all_channels', operator='overlay', 79 | image=tone_image, left=0, top=0) 80 | 81 | image.save(filename=output_path) 82 | 83 | 84 | def vivid(image_filename, output_path): 85 | with Image(filename=image_filename) as image: 86 | image.contrast_stretch(black_point=0.05, white_point=0.95, 87 | channel='all_channels') 88 | image.modulate(brightness=100, saturation=150, hue=100) 89 | image.unsharp_mask(radius=1.5, amount=200, threshold=0.2, sigma=0.5) 90 | image.save(filename=output_path) 91 | 92 | 93 | def bw_punch(image_filename, output_path): 94 | with Image(filename=image_filename) as image: 95 | image.transform_colorspace('gray') 96 | image.normalize() 97 | image.contrast_stretch(black_point=0.1, white_point=0.90) 98 | image.gamma(1.2) 99 | image.save(filename=output_path) 100 | 101 | 102 | def vignette(image_filename, output_path): 103 | with Image(filename=image_filename) as image: 104 | with Drawing() as draw: 105 | draw.stroke_color = Color('black') 106 | draw.stroke_width = 2 107 | draw.fill_color = Color('white') 108 | perimeter_point = (image.height + image.width) / 5000 109 | draw.circle((image.width // 200, image.height // 200), # Center point 110 | (perimeter_point, perimeter_point)) # Perimeter point 111 | with Image(width=(image.width // 100), height=(image.height // 100), 112 | background=Color('black')) as vignette_border: 113 | draw(vignette_border) 114 | vignette_border.resize(width=image.width, height=image.height, 115 | filter='hermite', blur=6.0) 116 | image.composite_channel(channel='all_channels', 117 | operator='multiply', 118 | image=vignette_border, 119 | left=0, top=0) 120 | image.save(filename=output_path) 121 | ``` 122 | 123 | ### Creating a slideshow 124 | 125 | For image slideshows, we need some JS and CSS code to make it run. Fortunately, there are numerous such libraries. I have chosen a library called [bxSlider](https://bxslider.com/), as it looked to be one of the simplest to use. It's also mobile friendly, so this app should run well on your phone's browser as well. 126 | 127 | It's a good idea to look at one of the examples here to understand how to use it: [https://bxslider.com/examples/image-slideshow-captions/](https://bxslider.com/examples/image-slideshow-captions/). In the example, there is some Javascript that is used to configure bxSlider. In the HTML section, it shows that to create a slideshow, you need an outer `div` tag with `class="bxslider"`, and then inside it you need a `div` tag for each image in the slideshow. Finally the `img` tag for your image should be present inside that `div`. 128 | 129 | To create a bunch of internal `div` and `img` tags, you need to use `for` statements functionality in the HTML template system. I have explained this in the [dictionary assignment document](https://github.com/amangup/coding-bootcamp/blob/master/lecture10/assignment_dictionary.md#if-and-for-statements-in-html-templates). 130 | 131 | You can configure bxSlider to your liking as it has many configuration parameters. Here is how the `head` section of my template looks like: 132 | 133 | ```html 134 | 135 | 136 | 137 | 138 | 152 | 153 | ``` 154 | 155 | - Note how all the JS and CSS files are just downloaded from the internet instead of being stored as part of our project. 156 | 157 | What is the image src that is used in the img tag? To create that, we will use another of Flask's utility functions called `send_from_directory`. Here is an example usage: 158 | 159 | ```python 160 | from flask import send_from_directory 161 | 162 | @app.route('/show_image//') 163 | def show_image(hash_id, filename): 164 | # create filepath variable which points to the image file you want to show 165 | return send_from_directory(filepath) 166 | ``` 167 | 168 | Then, the image URL can be created as follows: 169 | 170 | ```python3 171 | url_for('show_image', hash_id=hash_id, filename=image_filename) 172 | ``` 173 | 174 | ### URL redirection 175 | 176 | This topic has also been discussed in the [dictionary assignment document](https://github.com/amangup/coding-bootcamp/blob/master/lecture10/assignment_dictionary.md#url-redirection). 177 | 178 | The use case for this app is similar: When the user sees a slideshow, we want to create an URL which the user can then share. To achieve that, the view function which receives the content of the form will have to redirect to another page which applies image filters and returns the slideshow to the user. 179 | 180 | Here are some more tips on how to do this well for this assignment: 181 | 182 | - Save images on a new random folder every time the user uploads one (and the filtered output images). You can generate the folder name using the [python module `secrets`](https://docs.python.org/3/library/secrets.html) (use the `token_urlsafe()` function). This can be used to create a link like `http://myapp.com/slideshow/`. 183 | - The process to manipulate images is CPU heavy, and it is wasteful to do that every time the user opens the `slideshow` view. To solve this issue, you can use double redirection: The file upload view redirects to a temporary view `apply_filters` (that applies filters and stores the resulting images), which redirects to the `slideshow` view when it finishes applying filters. When an user returns to the `slideshow` link later on, the filtered images already exist. 184 | 185 | 186 | -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/debug_server.py: -------------------------------------------------------------------------------- 1 | from dictionary_webapp import app 2 | 3 | 4 | def main(): 5 | app.run(host='127.0.0.1', port=8080, debug=True) 6 | 7 | 8 | if __name__ == '__main__': 9 | main() 10 | -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dict_service/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture10/example_projects/dictionary_webapp/dict_service/__init__.py -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dict_service/dictionary.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | APP_ID = 'ce9bfac4' 4 | APP_KEY = '93524c8a5a1ddc2344b2ba36295f37bb' 5 | 6 | # e.g. - 7 | # https://od-api.oxforddictionaries.com:443/api/v1/entries/en/ace/regions=us 8 | # 9 | # Note that regions=us will make the query fail if language is not english. 10 | URL_FORMAT = ("https://od-api.oxforddictionaries.com:443/api/v1/entries/" 11 | "{0}/{1}{2}") 12 | 13 | class WordDefinition: 14 | def __init__(self, domains, definitions, examples, lexicalCategory): 15 | self.domains = domains 16 | self.definitions = definitions 17 | self.examples = examples 18 | self.lexicalCategory = lexicalCategory 19 | 20 | class WordDetails: 21 | def __init__(self, word, language, definitions): 22 | self.word = word 23 | self.language = language 24 | self.definitions = definitions 25 | 26 | def get_word_definitions(sense, lexicalCategory): 27 | domains = [] 28 | if 'domains' in sense: 29 | for domain in sense['domains']: 30 | domains.append(domain) 31 | 32 | definitions = [] 33 | if 'definitions' in sense: 34 | for definition in sense['definitions']: 35 | definitions.append(definition) 36 | 37 | examples = [] 38 | if 'examples' in sense: 39 | for example in sense['examples']: 40 | examples.append(example['text']) 41 | 42 | return WordDefinition(domains, definitions, examples, lexicalCategory) 43 | 44 | def get_word_details(word, language): 45 | """ 46 | Get the details of this word from the RESTful Dictionary service. 47 | :param word: The word for details are requested 48 | :param language: 'en' for english, 'es' for spanish 49 | :return: an object of WordDetails if we find the details, None otherwise 50 | """ 51 | 52 | region = '/regions=us' if language is 'en' else '' 53 | rest_url = URL_FORMAT.format(language, word.lower(), region) 54 | response = requests.get(rest_url, 55 | headers={'app_id': APP_ID, 'app_key': APP_KEY}) 56 | 57 | if response.status_code != 200: 58 | return None 59 | 60 | dict_json = response.json() 61 | 62 | definitions = [] 63 | 64 | for result in dict_json['results']: 65 | for lexicalEntry in result['lexicalEntries']: 66 | lexicalCategory = lexicalEntry['lexicalCategory'] 67 | for entry in lexicalEntry['entries']: 68 | for sense in entry['senses']: 69 | definitions.append( 70 | get_word_definitions(sense, lexicalCategory)) 71 | if 'subsenses' in sense: 72 | for subsense in sense['subsenses']: 73 | definitions.append( 74 | get_word_definitions(subsense, lexicalCategory)) 75 | 76 | return WordDetails(word, language, definitions) 77 | 78 | def main(): 79 | word_details = get_word_details("pedagogical", "en") 80 | 81 | for definition in word_details.definitions: 82 | print ("\n** Entry\n") 83 | print ("Type: {0},\n Domains: {1},\n Definitions: {2},\n Examples: {3}".format( 84 | definition.lexicalCategory, definition.domains, definition.definitions, 85 | definition.examples 86 | )) 87 | 88 | if __name__ == '__main__': 89 | main() -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dictionary_webapp/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | app.config['SECRET_KEY'] = 'very-hard-password' 5 | 6 | from dictionary_webapp import dictionary_home, display_word_defs 7 | -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dictionary_webapp/dictionary_home.py: -------------------------------------------------------------------------------- 1 | from flask import render_template, request, redirect, url_for 2 | from dictionary_webapp import app, forms 3 | 4 | @app.route('/', methods=['GET', 'POST']) 5 | def dictionary_home(): 6 | if request.method == 'POST': 7 | word = request.form['word'].lower() 8 | language = request.form['language'] 9 | return redirect(url_for('display_word_defs', word=word, lang=language)) 10 | else: 11 | form = forms.WordForm() 12 | return render_template('dictionary_home.html', form=form) -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dictionary_webapp/display_word_defs.py: -------------------------------------------------------------------------------- 1 | from flask import render_template 2 | 3 | from dict_service import dictionary 4 | from dictionary_webapp import app 5 | 6 | @app.route('/definitions//', methods=['GET']) 7 | def display_word_defs(word, lang): 8 | word_details = dictionary.get_word_details(word, lang) 9 | if word_details: 10 | return render_template('definitions.html', word_details=word_details) 11 | else: 12 | return (render_template('definitions_error.html', word=word, lang=lang), 13 | 404) 14 | 15 | -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dictionary_webapp/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField, SelectField, SubmitField 3 | 4 | 5 | class WordForm(FlaskForm): 6 | word = StringField('Word') 7 | language = SelectField(u'Language', choices=[('en', 'English (US)'), 8 | ('es', 'Spanish')]) 9 | submit = SubmitField('See Word Definitions') 10 | -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dictionary_webapp/templates/definitions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Definition for {{ word_details.word }} 5 | 6 | 7 | 8 |
9 |

{{ word_details.word }}

10 |

{{ word_details.language }}

11 |
12 | {% for definition in word_details.definitions %} 13 |
14 |
15 | ({{ definition.lexicalCategory }}) 16 |
17 | {% for domain in definition.domains %} 18 | {{ domain }} 19 | {% endfor %} 20 | 21 |
22 | {% if definition.definitions %} 23 | {% for definition in definition.definitions %} 24 | {{ definition }}
25 | {% endfor %} 26 | {% endif %} 27 |
28 | {% if definition.examples %} 29 | Examples: 30 |
    31 | {% for example in definition.examples %} 32 |
  1. {{ example }}
  2. 33 | {% endfor %} 34 |
35 | {% endif %} 36 |
37 | {% endfor %} 38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dictionary_webapp/templates/definitions_error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Definition not found: {{ word }} in language: {{ lang }} 4 | 5 | 6 |

Definition not found

7 |

Word: {{ word }}

8 |

Language: {{ lang }}

9 | 10 | -------------------------------------------------------------------------------- /lecture10/example_projects/dictionary_webapp/dictionary_webapp/templates/dictionary_home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dictionary Online 5 | 6 | 7 |

Please enter the word

8 | 9 | {{ form.hidden_tag() }} 10 |

11 | {{ form.word.label }}
12 | {{ form.word(size=32) }} 13 |

14 |

15 | {{ form.language.label }}
16 | {{ form.language() }} 17 |

18 |

{{ form.submit() }}

19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | SECRET_KEY = 'very-hard-password' 3 | FORTUNES_FILEPATH = '/home/aman/fortunes.txt' -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/debug_server.py: -------------------------------------------------------------------------------- 1 | from fortune_teller import app, initialize 2 | 3 | 4 | def main(): 5 | initialize() 6 | app.run(host='127.0.0.1', port=8080, debug=True) 7 | 8 | 9 | if __name__ == '__main__': 10 | main() 11 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from config import Config 3 | 4 | app = Flask(__name__) 5 | app.config.from_object(Config) 6 | 7 | fortune_list = [] 8 | 9 | def initialize(): 10 | with open(app.config['FORTUNES_FILEPATH'], encoding='utf-8') as f: 11 | for fortune in f: 12 | fortune_list.append(fortune) 13 | 14 | from fortune_teller import home, show_fortune 15 | 16 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField, SubmitField 3 | 4 | 5 | class NameForm(FlaskForm): 6 | username = StringField('Name') 7 | submit = SubmitField('See your fortune!') 8 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/home.py: -------------------------------------------------------------------------------- 1 | from flask import render_template 2 | from fortune_teller import app, forms 3 | 4 | @app.route('/') 5 | def hello(): 6 | form = forms.NameForm() 7 | return render_template('home.html', form=form) 8 | 9 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/show_fortune.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from flask import render_template, request 4 | from fortune_teller import app, fortune_list, forms 5 | 6 | @app.route('/fortune', methods=['POST']) 7 | def show_fortune(): 8 | name = request.form['username'] 9 | fortune = fortune_list[random.randint(0, len(fortune_list) - 1)] 10 | return render_template('fortune.html', username=name, fortune_text=fortune) 11 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/static/cookie_rich.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture10/example_projects/fortune_teller_webapp/fortune_teller/static/cookie_rich.jpg -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/static/quote.css: -------------------------------------------------------------------------------- 1 | /* From https://www.kirstencassidy.com/css-fancy-quotes/ */ 2 | 3 | .quotation{ 4 | font-size: 30px; 5 | //margin: 0 auto; 6 | quotes: "\201C""\201D""\2018""\2019"; 7 | padding: 10px 20px; 8 | line-height: 1.4; 9 | } 10 | 11 | .quotation:before { 12 | content: open-quote; 13 | display: inline; 14 | height: 0; 15 | line-height: 0; 16 | left: -10px; 17 | position: relative; 18 | top: 30px; 19 | color: #ccc; 20 | font-size: 3em; 21 | } 22 | .quotation::after { 23 | content: close-quote; 24 | display: inline; 25 | height: 0; 26 | line-height: 0; 27 | left: 10px; 28 | position: relative; 29 | top: 35px; 30 | color: #ccc; 31 | font-size: 3em; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/templates/fortune.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fortune Teller 5 | 6 | 7 | 8 |

Hello, {{ username }}!

9 |
10 |
11 | {{ fortune_text }} 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /lecture10/example_projects/fortune_teller_webapp/fortune_teller/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fortune Teller 5 | 6 | 7 | 8 |

Please enter your name

9 |
10 | {{ form.hidden_tag() }} 11 |

12 | {{ form.username.label }}
13 | {{ form.username(size=32) }} 14 |

15 |

{{ form.submit() }}

16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | SECRET_KEY = 'very-hard-password' 3 | UPLOAD_FOLDER = '/home/aman/uploads' 4 | ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif']) 5 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/debug_server.py: -------------------------------------------------------------------------------- 1 | from vintage_photo import app, initialize 2 | 3 | def main(): 4 | app.run(host='127.0.0.1', port=8080, debug=True) 5 | 6 | 7 | if __name__ == '__main__': 8 | main() 9 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/instagram_filters/__init__.py: -------------------------------------------------------------------------------- 1 | from instagram_filters import image_filters -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/instagram_filters/image_filters.py: -------------------------------------------------------------------------------- 1 | from wand.image import Image 2 | from wand.color import Color 3 | from wand.drawing import Drawing 4 | 5 | 6 | def cross_process(image_filename, output_path): 7 | with Image(filename=image_filename) as image: 8 | image.contrast_stretch(black_point=0.15, white_point=0.90, 9 | channel='red') 10 | image.contrast_stretch(black_point=0.10, white_point=0.95, 11 | channel='green') 12 | image.save(filename=output_path) 13 | 14 | 15 | def aged_photo(image_filename, output_path): 16 | with Image(filename=image_filename) as image: 17 | tone_image = Image(height=image.height, width=image.width, 18 | background=Color('#705a41')) 19 | image.modulate(brightness=100, saturation=20, hue=100) 20 | image.composite_channel(channel='all_channels', operator='overlay', 21 | image=tone_image, left=0, top=0) 22 | 23 | image.save(filename=output_path) 24 | 25 | 26 | def vivid(image_filename, output_path): 27 | with Image(filename=image_filename) as image: 28 | image.contrast_stretch(black_point=0.05, white_point=0.95, 29 | channel='all_channels') 30 | image.modulate(brightness=100, saturation=150, hue=100) 31 | image.unsharp_mask(radius=1.5, amount=200, threshold=0.2, sigma=0.5) 32 | image.save(filename=output_path) 33 | 34 | 35 | def bw_punch(image_filename, output_path): 36 | with Image(filename=image_filename) as image: 37 | image.transform_colorspace('gray') 38 | image.normalize() 39 | image.contrast_stretch(black_point=0.1, white_point=0.90) 40 | image.gamma(1.2) 41 | image.save(filename=output_path) 42 | 43 | 44 | def vignette(image_filename, output_path): 45 | with Image(filename=image_filename) as image: 46 | with Drawing() as draw: 47 | draw.stroke_color = Color('black') 48 | draw.stroke_width = 2 49 | draw.fill_color = Color('white') 50 | perimeter_point = (image.height + image.width) / 5000 51 | draw.circle((image.width // 200, image.height // 200), # Center point 52 | (perimeter_point, perimeter_point)) # Perimeter point 53 | with Image(width=(image.width // 100), height=(image.height // 100), 54 | background=Color('black')) as vignette_border: 55 | draw(vignette_border) 56 | vignette_border.resize(width=image.width, height=image.height, 57 | filter='hermite', blur=6.0) 58 | image.composite_channel(channel='all_channels', 59 | operator='multiply', 60 | image=vignette_border, 61 | left=0, top=0) 62 | image.save(filename=output_path) 63 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask import Flask 4 | from config import Config 5 | from instagram_filters import image_filters 6 | 7 | app = Flask(__name__) 8 | app.config.from_object(Config) 9 | 10 | filters = [(image_filters.cross_process, "cross_process", "Cross Processed"), 11 | (image_filters.aged_photo, "aged_photo", "Aged Photo"), 12 | (image_filters.vivid, "vivid", "Vivid"), 13 | (image_filters.bw_punch, "bw_punch", "Black & White Punch"), 14 | (image_filters.vignette, "vignette", "Vignette")] 15 | 16 | 17 | def get_save_folder(hash_id): 18 | return os.path.join(app.config['UPLOAD_FOLDER'], hash_id) 19 | 20 | 21 | def get_original_image_path(hash_id, image_type): 22 | return os.path.join(get_save_folder(hash_id), "original." + image_type) 23 | 24 | from vintage_photo import file_upload, apply_filter, show_image, slideshow -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/apply_filter.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask import url_for, redirect 4 | 5 | from vintage_photo import app, get_save_folder, get_original_image_path, filters 6 | from instagram_filters.image_filters import * 7 | 8 | 9 | @app.route('/filter//') 10 | def image_filter(image_type, hash_id): 11 | file_folder = get_save_folder(hash_id) 12 | original_filepath = get_original_image_path(hash_id, image_type) 13 | 14 | for filter_func, filename, _ in filters: 15 | filtered_image_path = os.path.join(file_folder, filename + "." + 16 | image_type) 17 | filter_func(original_filepath, filtered_image_path) 18 | 19 | return redirect(url_for('slideshow', image_type=image_type, 20 | hash_id=hash_id)) 21 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/file_upload.py: -------------------------------------------------------------------------------- 1 | import os 2 | import secrets 3 | from flask import render_template, request, redirect, url_for 4 | 5 | from vintage_photo import app, forms, get_save_folder, get_original_image_path 6 | 7 | 8 | def get_extension(filename): 9 | return filename.rsplit('.', 1)[1].lower() 10 | 11 | 12 | def allowed_extension(extension): 13 | return extension in app.config['ALLOWED_EXTENSIONS'] 14 | 15 | 16 | @app.route('/', methods=['GET', 'POST']) 17 | def file_upload(): 18 | if request.method == 'POST': 19 | # this is a file upload request 20 | file = request.files['file_path'] 21 | if file and '.' in file.filename: 22 | extension = get_extension(file.filename) 23 | if allowed_extension(extension): 24 | hash_id = secrets.token_urlsafe(8) 25 | 26 | save_folder = get_save_folder(hash_id) 27 | os.mkdir(save_folder) 28 | 29 | filepath = get_original_image_path(hash_id, extension) 30 | file.save(filepath) 31 | 32 | return redirect(url_for('image_filter', image_type=extension, 33 | hash_id=hash_id)) 34 | else: 35 | form = forms.FileUploadForm() 36 | return render_template('file_upload.html', form=form) 37 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import FileField, SubmitField 3 | 4 | 5 | class FileUploadForm(FlaskForm): 6 | file_path = FileField('File Path') 7 | upload = SubmitField('Upload file') 8 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/show_image.py: -------------------------------------------------------------------------------- 1 | from flask import send_from_directory 2 | 3 | from vintage_photo import app, get_save_folder 4 | 5 | @app.route('/show_image//') 6 | def show_image(hash_id, filename): 7 | return send_from_directory(get_save_folder(hash_id), filename) 8 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/slideshow.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import render_template, url_for 3 | 4 | from vintage_photo import app, get_original_image_path, filters 5 | 6 | class ImageDetails: 7 | def __init__(self, url, caption): 8 | self.url = url 9 | self.caption = caption 10 | 11 | 12 | @app.route('/slideshow//') 13 | def slideshow(image_type, hash_id): 14 | # first check if this slideshow exists: 15 | original_image_path = get_original_image_path(hash_id, image_type) 16 | if not os.path.isfile(original_image_path): 17 | return render_template('slideshow_not_found.html'), 404 18 | 19 | slideshow_images = [ImageDetails( 20 | url_for('show_image', hash_id=hash_id, 21 | filename="original." + image_type), "Original picture")] 22 | for _, filename, caption in filters: 23 | slideshow_images.append(ImageDetails( 24 | url_for('show_image', hash_id=hash_id, 25 | filename=filename + "." + image_type), caption 26 | )) 27 | 28 | return render_template('slideshow.html', slideshow=slideshow_images) 29 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/templates/file_upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Please select the file to upload:

5 |
6 | {{ form.hidden_tag() }} 7 |

8 | {{ form.file_path.label }}
9 | {{ form.file_path() }} 10 |

11 |

{{ form.upload() }}

12 |
13 | 14 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/templates/slideshow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 21 | 22 | 23 |
24 | {% for image in slideshow %} 25 |
26 | {% endfor %} 27 |
28 | 29 | -------------------------------------------------------------------------------- /lecture10/example_projects/photo_filter/vintage_photo/templates/slideshow_not_found.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 - Slideshow not found 6 | 7 | 8 |

The slideshow you are looking for cannot be found

9 | 10 | -------------------------------------------------------------------------------- /lecture10/example_projects/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.2 2 | Flask-WTF==0.14.2 3 | Wand==0.4.4 4 | requests==2.20.0 5 | -------------------------------------------------------------------------------- /lecture10/fortune_css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture10/fortune_css.png -------------------------------------------------------------------------------- /lecture10/quiz.md: -------------------------------------------------------------------------------- 1 | # Quiz 2 | 3 | **Question 1** 4 | What is the output of the following program? 5 | 6 | ```python 7 | class A: 8 | value = 1 9 | 10 | class B: 11 | value = 2 12 | def __init__(self): 13 | self.value = 3 14 | 15 | a_instance = A() 16 | b_instance = B() 17 | print(A.value, a_instance.value, B.value, b_instance.value) 18 | ``` 19 | 20 | Options: 21 | a: 1, 0, 2, 3 22 | b: 1, 1, 2, 2 23 | c: 1, 1, 2, 3 24 | d: raises an error 25 | 26 | **Question 2** 27 | What is the output of the following program? 28 | 29 | ```python 30 | class Sum: 31 | def __init__(self, a, b): 32 | self.a = a 33 | self.b = b 34 | 35 | def sum(self): 36 | return self.a + self.b 37 | 38 | class Multiply(Sum): 39 | def __init__(self, a, b): 40 | self.c = a 41 | self.d = b 42 | 43 | def multiply(self): 44 | return self.c * self.d 45 | 46 | m = Multiply(2, 3) 47 | print(m.multiply()) 48 | print(m.sum()) 49 | ``` 50 | 51 | Options: 52 | a: 6, then raises an error 53 | b: 6, 0 54 | c: raises an error 55 | d: 6, 5 56 | 57 | 58 | **Question 3** 59 | Let's we have built a functional flask app called `my_webapp`, and we add the following view function to it. We then run the website at `www.mywebsite.com`. 60 | 61 | ```python 62 | from flask import request 63 | 64 | from my_webapp import app 65 | 66 | @app.route("/tester") 67 | def tester(): 68 | if request.method == 'POST': 69 | return "Got a post request!" 70 | else: 71 | return "Not a post request" 72 | ``` 73 | 74 | What will happen when send the following HTTP request to it? 75 | 76 | ``` 77 | POST /tester HTTP/1.1 78 | Host: www.mywebsite.com 79 | ``` 80 | 81 | Options: 82 | a: We'll see "Got a post request!" in the response 83 | b: We'll see "Not a post request" in the response 84 | c: We'll see an HTTP error saying "Page not Found" (404) 85 | d: We'll see an HTTP error saying "Method not allowed" (405) 86 | 87 | **Question 4** 88 | Launch your browser, go to the address URL, type "en.wikipedia.org" and press enter. Now launch your browser's network inspector (Ctrl/Cmd + Shift + C, then network tab) and do that again. 89 | 90 | Among all the `GET` requests, find the one with Request URL = `https://en.wikipedia.org/wiki/Main_Page`. 91 | 92 | What is the HTTP response status code for that request? 93 | 94 | Options: 95 | a: 30x 96 | b: 20x 97 | c: 40x 98 | d: 50x 99 | 100 | **Question 5** 101 | We send an HTTP GET request to an HTTP server. Which of the HTTP response content types listed below are valid (i.e., what kind of data can a HTTP server respond with)? 102 | 103 | Options: 104 | a: text/html 105 | b: image/jpg 106 | c: application/json 107 | d: All of the above 108 | 109 | **Question 6** 110 | In a functional flask app, let's say we have a page with the following template called `message.html`: 111 | 112 | ```html 113 | 114 | 115 | 116 | A message 117 | 118 | 119 |

{{ data.message() }}

120 | 121 | 122 | ``` 123 | 124 | And we implement the following view function in our app: 125 | 126 | ```python 127 | from flask import render_template 128 | 129 | from my_webapp import app 130 | 131 | class Data: 132 | def message(self): 133 | return ("What we think, we become. -- Buddha") 134 | 135 | @app.route("/message") 136 | def test(): 137 | return render_template("message.html", data=Data()) 138 | ``` 139 | 140 | What will happen when go to the path `/message` on your webserver? 141 | 142 | Options: 143 | a: There will be an error saying that you can't call methods in a template 144 | b: There will be an error saying that object `data` has no attribute `message` 145 | c: We will see a web page with the Buddha's quote. 146 | d: None of the above 147 | 148 | **Question 7** 149 | In a functional flask app, let's say we have a page with the following template called `home.html`: 150 | 151 | ```html 152 | 153 | 154 | 155 | Classifieds Home 156 | 157 | 158 | 160 | See the listings 161 | 162 | 163 | ``` 164 | 165 | And we implement the following two view functions in our app: 166 | 167 | ```python 168 | from flask import render_template 169 | 170 | from my_webapp import app 171 | 172 | @app.route("/") 173 | def home(): 174 | return render_template("home.html") 175 | ``` 176 | 177 | ```python 178 | from my_webapp import app 179 | 180 | @app.route("/listing") 181 | def show_classifieds(): 182 | # returns the page with all the listings 183 | ``` 184 | 185 | What will happen when go to the path `/` on your webserver? 186 | 187 | Options: 188 | a: We will see the home page with the link to the 'listing' page. 189 | b: There will be a page not found error (404). 190 | c: There will be an error saying that it can't find the endpoint 'listing' 191 | d: None of the above 192 | 193 | **Question 8** 194 | Let's we create a form class as follows, and put it in `forms.py`: 195 | ```python 196 | from flask_wtf import FlaskForm 197 | from wtforms import StringField, SubmitField 198 | 199 | class NameForm(FlaskForm): 200 | username = StringField('Name') 201 | submit = SubmitField('Submit') 202 | ``` 203 | 204 | And we implement the following view function: 205 | 206 | ```python 207 | from my_webapp import app, forms 208 | 209 | @app.route("/") 210 | def home(): 211 | form = forms.NameForm() 212 | print(form.submit()) 213 | # returns the rendered html page 214 | ``` 215 | 216 | What does the print statement in above code output when we go to the path `/`? 217 | 218 | Options: 219 | a: `
` 220 | b: `` 221 | c: `Submit` 222 | d: None of the above 223 | -------------------------------------------------------------------------------- /lecture11/Readme.md: -------------------------------------------------------------------------------- 1 | ## Using Databases for Web Applications 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture11/webapp_using_db.md) builds on what we learnt in the last lecture about web development using Flask. We learn about databases, and how to develop websites which use a DB as a backend for data store. In the latter half of the lecture, we also look into form validation and sanitizing form input for security. 4 | 5 | By following this tutorial, you will create a *Quiz webapp* that allows one to add questions to a data store, and then users can take a quiz which includes all those questions, and get to know how well they did. 6 | 7 | The [assignment](https://github.com/amangup/coding-bootcamp/blob/master/lecture11/assignments.md) asks you to extend that app further by adding features like questions on multiple subjects and having a leaderboard. 8 | -------------------------------------------------------------------------------- /lecture11/assignments.md: -------------------------------------------------------------------------------- 1 | # Assignments 2 | 3 | There is one assignment, in which we are going to modify the quiz app and make it more fully featured. Add the following features to the webapp. 4 | 5 | - Make available quizzes on multiple subjects. 6 | - Whenever someone adds a question, they can also type in a **subject** the question is about. 7 | - When an user comes to the home page, they should first be asked the subject they want to take the quiz on (from the list of subjects we have questions on - this list will be dynamically generated), and then they should be asked the questions belonging only to that subject. 8 | - Create a leaderboard. 9 | - Whenever someone submits a quiz, store the score in the DB. 10 | - Using this, create a page which shows the top 5 scorers in each subject (the **leaderboard**). 11 | - After any new user submits the quiz answers, along with the score and right/wrong answers, show them _their position_ in the leaderboard for that subject. 12 | - Allow quiz creators to update or delete questions. 13 | - Add a question update page which lists all the questions (maybe only the question text only along with the subject), with update or delete buttons alongside each question. 14 | - When someone clicks on delete, delete that question and show a confirmation to the user. 15 | - When someone clicks on update, show a form like the add question page (with the entries already filled with the question details), and allow the user to update it by submitting that form. 16 | 17 | Note that I've not added any details on anything new that you might need to learn something to accomplish these changes (for example, querying all questions with a specific subject, or changing an existing row in the DB). You need to figure out the unknowns yourself and use the web to get answers. 18 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | SECRET_KEY = 'really-hard-password' 3 | SQLALCHEMY_DATABASE_URI = 'sqlite:////home/aman/quiz.db' 4 | SERVER_NAME = 'localhost:8080' -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/debug_server.py: -------------------------------------------------------------------------------- 1 | from quiz import app 2 | 3 | 4 | def main(): 5 | app.run(host='127.0.0.1', port=8080, debug=True) 6 | 7 | 8 | if __name__ == '__main__': 9 | main() 10 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | 4 | from config import Config 5 | 6 | app = Flask(__name__) 7 | app.config.from_object(Config) 8 | db = SQLAlchemy(app) 9 | 10 | from quiz import db_tables, add_question, quiz 11 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/add_question.py: -------------------------------------------------------------------------------- 1 | import bleach 2 | 3 | from flask import request, render_template, redirect, url_for, flash 4 | from sqlalchemy.exc import SQLAlchemyError 5 | 6 | from quiz import app, db, forms 7 | from quiz.db_tables import Question 8 | from quiz.forms import QuestionForm 9 | 10 | ALLOWED_TAGS = ['p', 'br', 'i', 'b', 'sub', 'sup', 'code', 'samp', 'var'] 11 | 12 | 13 | @app.route('/add_question', methods=['GET', 'POST']) 14 | def add_question(): 15 | form = QuestionForm(request.form) 16 | if request.method == 'POST': 17 | if form.validate(): 18 | try: 19 | return _add_question_to_db_(request.form) 20 | except SQLAlchemyError: 21 | flash("We couldn't add your question due to a technical issue" 22 | " on our side. Please try again later.") 23 | else: 24 | flash("Not all required fields were filled.") 25 | 26 | return _render_form_(form) 27 | 28 | 29 | def _add_question_to_db_(formdata): 30 | question = Question(question_text=_clean_html_(formdata['question']), 31 | option_a=_clean_html_(formdata['option_a']), 32 | option_b=_clean_html_(formdata['option_b']), 33 | option_c=_clean_html_(formdata['option_c']), 34 | option_d=_clean_html_(formdata['option_d']), 35 | answer=int(formdata['answer'])) 36 | 37 | db.session.add(question) 38 | db.session.commit() 39 | return redirect(url_for('add_question_success')) 40 | 41 | 42 | def _clean_html_(text): 43 | return bleach.clean(text, tags=ALLOWED_TAGS, attributes=['style'], 44 | styles=['color']) 45 | 46 | 47 | def _render_form_(form): 48 | return render_template('add_question.html', form=form, 49 | allowed_tags=ALLOWED_TAGS) 50 | 51 | 52 | @app.route('/add_question_success') 53 | def add_question_success(): 54 | return render_template('add_question_success.html') 55 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/db_tables.py: -------------------------------------------------------------------------------- 1 | from quiz import db 2 | 3 | 4 | class Question(db.Model): 5 | id = db.Column(db.Integer, primary_key=True) 6 | question_text = db.Column(db.Text) 7 | option_a = db.Column(db.Text) 8 | option_b = db.Column(db.Text) 9 | option_c = db.Column(db.Text) 10 | option_d = db.Column(db.Text) 11 | answer = db.Column(db.Integer) 12 | 13 | def __repr__(self): 14 | return "Q{0}: {1}".format(self.id, self.question_text) 15 | 16 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField, SubmitField, TextAreaField, RadioField, \ 3 | FieldList 4 | from wtforms.validators import DataRequired 5 | 6 | 7 | CHOICES = [('1', 'Option A'), 8 | ('2', 'Option B'), 9 | ('3', 'Option C'), 10 | ('4', 'Option D')] 11 | 12 | 13 | class QuestionForm(FlaskForm): 14 | question = TextAreaField('Question', validators=[DataRequired()]) 15 | option_a = StringField('Option A', validators=[DataRequired()]) 16 | option_b = StringField('Option B', validators=[DataRequired()]) 17 | option_c = StringField('Option C', validators=[DataRequired()]) 18 | option_d = StringField('Option D', validators=[DataRequired()]) 19 | answer = RadioField('Correct Answer', 20 | choices=CHOICES, validators=[DataRequired()]) 21 | submit = SubmitField('Add the Question') 22 | 23 | 24 | class QuizForm(FlaskForm): 25 | answers = FieldList(RadioField('Correct Answer', choices=CHOICES), 26 | min_entries=0) 27 | submit = SubmitField('Submit your answers') 28 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/quiz.py: -------------------------------------------------------------------------------- 1 | from flask import render_template, request, flash 2 | 3 | from quiz import app, db 4 | from quiz.db_tables import Question 5 | from quiz.forms import QuizForm 6 | 7 | 8 | @app.route('/', methods=['GET', 'POST']) 9 | def quiz(): 10 | questions = Question.query.all() 11 | form = QuizForm(request.form) 12 | if request.method == 'POST': 13 | # we are checking if the number of answers in the form data is the same 14 | # as the number of questions in the quiz. 15 | if len(form.answers.entries) == len(questions): 16 | return _render_score_page_(questions) 17 | else: 18 | flash("You must answer all the questions.") 19 | 20 | return _render_quiz_page_(form, questions) 21 | 22 | 23 | def _render_quiz_page_(form, questions): 24 | for i, question in enumerate(questions): 25 | form.answers.append_entry() 26 | choices = [('1', question.option_a), 27 | ('2', question.option_b), 28 | ('3', question.option_c), 29 | ('4', question.option_d)] 30 | form.answers.entries[i].choices = choices 31 | 32 | return render_template("quiz.html", questions=questions, form=form) 33 | 34 | 35 | def _render_score_page_(questions): 36 | answers = [] 37 | user_score = 0 38 | for i, question in enumerate(questions): 39 | user_answer = int(request.form['answers-' + str(i)]) 40 | user_answer_letter = _get_answer_letter_(user_answer) 41 | if user_answer == question.answer: 42 | user_score += 1 43 | answers.append('{0} is correct'.format(user_answer_letter)) 44 | else: 45 | correct_answer_letter = _get_answer_letter_(question.answer) 46 | answers.append('{0} is incorrect. Correct answer is {1}' 47 | .format(user_answer_letter, correct_answer_letter)) 48 | 49 | return render_template('quiz_answers.html', questions=questions, 50 | answers=answers, score=user_score) 51 | 52 | 53 | def _get_answer_letter_(user_answer): 54 | return chr(ord('A') + user_answer - 1) 55 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/templates/add_question.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Add a question 4 | 5 | 6 | {% from "macros.html" import show_flashed_messages %} 7 |

Enter the question details:

8 | {{ show_flashed_messages() }} 9 | 10 |
11 | {{ form.hidden_tag() }} 12 | All fields in the form are required.
13 | You are allowed to use the following tags to format your question or answers: 14 |
    15 | {% for tag in allowed_tags %} 16 |
  • <{{ tag }}>
  • 17 | {% endfor %} 18 |
19 | You can also use the style attribute in a tag to set color. 20 |

21 | {{ form.question.label }}:
22 | {{ form.question(rows=10, cols=80) }} 23 |

24 | {{ form.option_a.label }}: {{ form.option_a(size=80) }} 25 | 26 |

27 | {{ form.option_b.label }}: {{ form.option_b(size=80) }} 28 | 29 |

30 | {{ form.option_c.label }}: {{ form.option_c(size=80) }} 31 | 32 |

33 | {{ form.option_d.label }}: {{ form.option_d(size=80) }} 34 | 35 |

36 | {{ form.answer.label }}:
37 |

38 | {% for subfield in form.answer %} 39 | {{ subfield(required=True) }} 40 | {{ subfield.label }}
41 | {% endfor %} 42 |
43 | 44 |

{{ form.submit() }} 45 |

46 | 47 | 48 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/templates/add_question_success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The question was successfully added 5 | 6 | 7 |

Your question was entered into the DB

8 | Add one more? 9 | 10 | 11 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/templates/macros.html: -------------------------------------------------------------------------------- 1 | {% macro show_flashed_messages() %} 2 | {% with messages = get_flashed_messages() %} 3 | {% if messages %} 4 | 5 | {% for message in messages %} 6 | {{ message }} 7 | {% endfor %} 8 | 9 | {% endif %} 10 | {% endwith %} 11 | {% endmacro %} 12 | 13 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/templates/quiz.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quiz 5 | 6 | 7 |

Quiz:

8 | {% from "macros.html" import show_flashed_messages %} 9 | {{ show_flashed_messages() }} 10 |

11 |

12 | {{ form.hidden_tag() }} 13 | {% for question in questions %} 14 | Question {{ loop.index }}: {{ question.question_text|safe }} 15 |

16 |

17 | {% for subfield in form.answers.entries[loop.index0] %} 18 | {{ subfield(required=True) }} 19 | {{ subfield.label|safe }}
20 | {% endfor %} 21 |
22 | 23 | {% endfor %} 24 | 25 | {{ form.submit() }} 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /lecture11/example_projects/quiz_webapp/quiz/templates/quiz_answers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quiz Answers 5 | 6 | 7 |

Your score is {{ score }}/{{ questions|length }}

8 |

9 | {% for question in questions %} 10 |

11 | Question {{ loop.index }}: {{ question.question_text|safe }} 12 |

13 | Options:
14 |

    15 |
  1. {{ question.option_a|safe }}
  2. 16 |
  3. {{ question.option_b|safe }}
  4. 17 |
  5. {{ question.option_c|safe }}
  6. 18 |
  7. {{ question.option_d|safe }}
  8. 19 |
20 |

21 | Your answer: {{ answers[loop.index0] }} 22 | {% endfor %} 23 | 24 | 25 | -------------------------------------------------------------------------------- /lecture11/example_projects/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.2 2 | Flask-WTF==0.14.2 3 | Flask-SQLAlchemy==2.3.2 4 | bleach==2.1.3 5 | -------------------------------------------------------------------------------- /lecture11/question_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture11/question_html.png -------------------------------------------------------------------------------- /lecture11/quiz_page_uml2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture11/quiz_page_uml2.png -------------------------------------------------------------------------------- /lecture11/quiz_pages_uml1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture11/quiz_pages_uml1.png -------------------------------------------------------------------------------- /lecture11/quiz_user_flow1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture11/quiz_user_flow1.png -------------------------------------------------------------------------------- /lecture11/quiz_user_flow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture11/quiz_user_flow2.png -------------------------------------------------------------------------------- /lecture12/Public_key.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/Public_key.svg -------------------------------------------------------------------------------- /lecture12/Readme.md: -------------------------------------------------------------------------------- 1 | ## Web applications with user accounts 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture12/user_accounts.md) first gives some grounding on what is needed to build apps that have user accounts. It also discusses fundamental aspects of security. It then uses the `Flask-login` module to build a writer app which allows users to read and rite articles on the website and follow their favorite authors. 4 | 5 | There are no separate assignments for this lecture. There are exercises which are part of the lecture which are required if you want to build a functioning app. 6 | -------------------------------------------------------------------------------- /lecture12/example_projects/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.2 2 | Flask-WTF==0.14.2 3 | Flask-SQLAlchemy==2.3.2 4 | Flask-Bcrypt=0.7.1 5 | Flask-Login=0.4.1 6 | bleach==3.1.4 7 | pyopenssl==18.0.0 8 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF3zCCA8egAwIBAgIJAOXWpjN2K6oBMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYD 3 | VQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZ 4 | BgNVBAoMElNGIENvZGluZyBCb290Y2FtcDESMBAGA1UEAwwJbG9jYWxob3N0MSAw 5 | HgYJKoZIhvcNAQkBFhFhbWFuQG15ZG9tYWluLmNvbTAeFw0xODA4MjkyMzIzNDFa 6 | Fw0xOTA4MjkyMzIzNDFaMIGFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAU 7 | BgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMElNGIENvZGluZyBCb290Y2Ft 8 | cDESMBAGA1UEAwwJbG9jYWxob3N0MSAwHgYJKoZIhvcNAQkBFhFhbWFuQG15ZG9t 9 | YWluLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALUvssektH30 10 | Ddmp3Qq3/ZBSIETxTrxWOQsMHlcrITOPsX+kW8MPFC1ZiyD7jYW0k5+Itwg5VMwg 11 | M2qNqvj9uooxyy0NDqiRD3aA8MSHC3dhw0osvj515FpEVoscUc2w10y4f1UBPNsx 12 | f8SieR5Q/CCHFJDur0GTK7tlRrtXdl6OpqXsyvcMYghc/p0KB6QBxWRNwgR83PWS 13 | a6n20JnXDZascvon91pnesxhG6g4A8K1+uDfNOSWSwU7u6Ui/G6FPyXVTJmm3Ne+ 14 | DxAyWTEffg8Wieu7cpyxTes+SDF0zYXryC1Q1/+P6s9HzH5/kCMv8LQSEpStiQGU 15 | DtNaF4xeVQlQK25Yy2SAWE9ig/FlGuLzwJD5TSC+mvARmtZO/ACOzCORHLy7MOTY 16 | 9FhCZzQNq1jRuJRaFTUqON/x030jGBuXgrJrTWwgGAynYliDtR4b1PYpwnaZOA10 17 | RydqJCP0QQ2vNbpq5tn+gj0uwQFDONfVgeJd4hQT+4mpC2JZg1dStLoMUygmu81f 18 | Ofrx5AZ802iXrKPRkz9+bhpAI1d1KGnw1PWHJuzoiseH6sN/063iKDz9GfskcD5H 19 | BuvGMB/OEr+aDOdC0FsQx3v+bXzB6tvLnT3nEeSjFXT7CFERUFDg9SYV8quGWBrS 20 | ElyRqYBCj8VpXH47SVuVWH0hHONnIAVLAgMBAAGjUDBOMB0GA1UdDgQWBBSmxND/ 21 | v9HmwFLLBG1usv3qxFQIuzAfBgNVHSMEGDAWgBSmxND/v9HmwFLLBG1usv3qxFQI 22 | uzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBqv+I+i6zXvk8RShHM 23 | nvs+SLoOsirnxUzlIu173e+UkCSCiFxzT8dQwRs4dS+asQr0v5IPmrDtkcMhEF0g 24 | yo4V1Y4zkSlF4MHa1Jf0fP/kTP1AwIDNEm6TO7KsbsyEx/oN1z4pMgMX3+e1UClj 25 | q0qkBFffbVYxS9+u6ARqolXqH6to54YqFr+T2qOB4m9Bz7hoEStFh3D+d3NVFb52 26 | Jy8tZFFW5mNUwPwNdaWde7MhCX9GnJBUfPmLR6Rk8hoq32xm5ozuVfFmlyNzVYs7 27 | z/1CATZQKXblFrWw8PA92OFbP67VpF+A1tcljbZpoUgmkhgGLY90jzOU5NwcP/oL 28 | 2O4nEPGLtIugEn3LW7SKCPW9ClgM5gtUgk6G/B4GywocqFV2DyM+KK1MStIj8wDk 29 | a6SgEVl1AM8wLb1IoNjecVl8ZU010/6GMk3LmN/OwWe5cW+6HXNLESryJkjCMtRj 30 | 1FEWWL2POvyqWf2AXWinnsoPCLcBkr6s2y57/F/m1qMmAiMSdQVp7U/lxpj/4Rvw 31 | rShJ0QONK2x+iA88Jl2r+lcYHGI0Y31vCvaxoy/0DBL2Dhfw9hW0i5HuIIkYQWX9 32 | Zdffh3Phayb5h9StFgGpcGXXXI+hWM3qDvmEaDpbxGYe8rLxBd6WYO/uig78daLL 33 | ieqj3QigLzXVIug6ha7xkpVLXw== 34 | -----END CERTIFICATE----- 35 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | SECRET_KEY = 'really-hard-password' 3 | SQLALCHEMY_DATABASE_URI = 'sqlite:////home/aman/writer2.db' 4 | SERVER_NAME = '127.0.0.1:8080' 5 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/debug_server.py: -------------------------------------------------------------------------------- 1 | from writer import app 2 | 3 | 4 | def main(): 5 | app.run(host='127.0.0.1', port=8080, ssl_context=('cert.pem', 'key.pem'), debug=True) 6 | 7 | 8 | if __name__ == '__main__': 9 | main() 10 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC1L7LHpLR99A3Z 3 | qd0Kt/2QUiBE8U68VjkLDB5XKyEzj7F/pFvDDxQtWYsg+42FtJOfiLcIOVTMIDNq 4 | jar4/bqKMcstDQ6okQ92gPDEhwt3YcNKLL4+deRaRFaLHFHNsNdMuH9VATzbMX/E 5 | onkeUPwghxSQ7q9Bkyu7ZUa7V3Zejqal7Mr3DGIIXP6dCgekAcVkTcIEfNz1kmup 6 | 9tCZ1w2WrHL6J/daZ3rMYRuoOAPCtfrg3zTklksFO7ulIvxuhT8l1UyZptzXvg8Q 7 | MlkxH34PFonru3KcsU3rPkgxdM2F68gtUNf/j+rPR8x+f5AjL/C0EhKUrYkBlA7T 8 | WheMXlUJUCtuWMtkgFhPYoPxZRri88CQ+U0gvprwEZrWTvwAjswjkRy8uzDk2PRY 9 | Qmc0DatY0biUWhU1Kjjf8dN9Ixgbl4Kya01sIBgMp2JYg7UeG9T2KcJ2mTgNdEcn 10 | aiQj9EENrzW6aubZ/oI9LsEBQzjX1YHiXeIUE/uJqQtiWYNXUrS6DFMoJrvNXzn6 11 | 8eQGfNNol6yj0ZM/fm4aQCNXdShp8NT1hybs6IrHh+rDf9Ot4ig8/Rn7JHA+Rwbr 12 | xjAfzhK/mgznQtBbEMd7/m18werby5095xHkoxV0+whREVBQ4PUmFfKrhlga0hJc 13 | kamAQo/FaVx+O0lblVh9IRzjZyAFSwIDAQABAoICAEw1FMAuAZCcjxrG/GbevnLu 14 | aA43ZMOIjJOd9ED7rgpO3Pxv9/ySIg11BTTJOVxMeG74hOubmZQF0zp0/b2ektA/ 15 | Y1K+lDTIVrWkRCCVIHiFESvop9gDpBlndvMp5SpbwCLP0xbA34TeS/w7zi+3JmTU 16 | DA1tpx8wtIssbNStU+cvnt5ZO4EvnygfXSRWLKKJ8ClhH0Ld9d4ASPKDEngyYM/I 17 | FtAq8ToAHkFKcuSNMt+5xRzCJTAkGsfHuQQbNAuUVdwm3AIgkfDYSuOoGaW0tkPI 18 | WhhhjjJjzP8uAhVzKGK0qkyNHVyrfrrSWyu1ob17E4XZuCD64I5eZOH2mcL7hk4g 19 | BI2p3vbHWPnw/lQTvRYUpt6DizmSj44O+xufmHHk8koXZJPeoBOWQjGhKNpAj9rr 20 | X4la1ncNz6mBj8p3GtUd6r+9Q/5n2ZGYeDRZmQZ+NDOvL5fuGhe99J+2zfpbqeyT 21 | z+xO3N6AVi8fIboonl65klJRqu1d4/THEkavRdEqJA3UNMMsawQ0Vx6jLtJ7XT0s 22 | 3XiMudvjvcZHpe0Pizr6w0hz1z/wI+Ob/gZTUMWi5tDlGYWAOFuyt5Fwjo6xvOFC 23 | ot3deoGul11CDicisFGG0hmH9Z09rC6NC4U274M5ZL9KdXjZcW16FQ/LasFaFzYF 24 | cYMcHWlVZhtUDNaH1HnRAoIBAQDkSDip157PAqbk3OyRORWyvnRBCw+J2fOFX7CD 25 | aOLGMGbb8B80CSiOP4UNyh2b7/yAg65scUWz+97ZsrOZWgtMD1fSMdGc6r9l3lyP 26 | tRISEiPJJhHYiKts0RgOhvCes2GCFpT6KojDwcgKRiICHBAFj0ATGcSRaI84s+Wi 27 | 8rxzfJcjn25Iy1nBpgJyErXJ5svzIpRG1ze0EBXpBpe4FnJBix/uMhg07xKCyI7z 28 | 69mNCOuhRoEeiu0djiB6gM21zbusOIERxr5mDdVGH77aLXVOkffgdtfQv/otDcUI 29 | 3bj54HpAz0fvD7Xl/3oWWGRF8CLaIGdP+trhE3jmhNL3EcBzAoIBAQDLL5Sv2OYE 30 | sr/w7mFt2h0bcTZzWZTHakkKfPgiEw+K9Wd0aKneYWp80mCzexMIM3w8KGGxy7Q1 31 | FRXP0M5grARFLHpGwH/xf8bXwWbXNOF2uHe41g5sGP0KU9cHAtZkTtyErRlcG3ON 32 | h+6KgogB+5qJvJCzzvW5Q1p6FV1wMQlHIYzh0vbmfa9F1uJ+yFGw39iqH8i8JccB 33 | 0un6vZFarfVv8puIe9cOkW226VPvEqAApG6pEDffhcpP4Yh0dvbH6XQVw2FnzOQj 34 | yrtQnkTSK7+F4SvybGFee3GGTyaC06k7u7jQZ2x6wmjPCYPegfY6CyHx87pw+Lta 35 | roe+G44wTKnJAoIBAQCpypHiuc6T+Ev8A9os9cGOzhuX2Us4ZBS9yft28kdh09Rd 36 | mA8NXBZ/Hv2u0PsO5gPv7oGIhQOsKBWL5F+zGwc2Dl9qpsVaqjur4CUGek4zhKx7 37 | S0aQwK6IZEk5XOKRl6hZ4sPyAjrxJyvLgHiBCzBbgj3NNyg+Yv/L0/pkJZlHaI7d 38 | 3xJfMGjVIM3G3OW7g1+Eut6AhkkXM7OoO9ATCxb2ay1XtIjOrlcdUWEKSgzo2o6v 39 | yMn21qwghHrhwvW+WIRTRz+w4hrxuy47VxdUHeWXh/hPSvWgfXntksVPn+KacH1d 40 | wQ6m4A+SP2AmSJ5kaoTxqpJNKa8tI5eUFYz4WYrDAoIBADflTgUWKSkPt4fjPEBJ 41 | KYq8GVugQDKfxZoyCNzu227B1gULKff4WqAeC+Y0LGcsKrgt77pGrLuRol96NNwZ 42 | Vk9lTfoayNe57Ay8srrvXDsZwi3CDVOliC8II3ZVTH9pxnZlXD2fe6lqxK+r+xh4 43 | i+nqSvB8NdMSeVhuejb79m5wB160d6Gu38HZvJNeLkL3QZaDiehgiB+zUxTcg5kr 44 | lAKI/kqMrvvd/A2+oI17VTL4CU1PHd/K4X4aMDzB4V/blEjMaAZzMiu6gM9Lyuj6 45 | igSdjkZxlFO2p+Nn8NM6wSqimNQUL56Rzt1t464ZmJAdMehqwVaExp3Kt6MP14zn 46 | +ikCggEBAK9UD3dsWAdMvcz5vGytvA7mMeLAIpheusH4LzZD3sGmqeuxY5/dW0B4 47 | 2VyFb6kJq3c3N7tVBzO9zghQexctMH9Q2S5Jvi4vzCN5r95RRZ43vuw+HaUqWbTe 48 | ljn/tGD8Z5oJ7vTpJxfrZpnmZID4wVjzqGcDyi36bChixVODroGSqpt4t/lRHY+d 49 | n4ZN4bASWRdUdGIfxEBdgCWSJdfJkTHLGc+4Dkbo8PFeDmAwv6duHKxPNJywDYUX 50 | ZCGDsJbadzMB4Kce9uPoKohVz2hYdMjhqFlCgT97xNM9DAoqCYt3vqqmf6IZb7Um 51 | pQxCdHla7ggJt4pREjMkNhR7HXxk5ao= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | from flask_login import LoginManager 4 | from flask_bcrypt import Bcrypt 5 | 6 | from config import Config 7 | 8 | app = Flask(__name__) 9 | app.config.from_object(Config) 10 | db = SQLAlchemy(app) 11 | 12 | login_manager = LoginManager() 13 | login_manager.init_app(app) 14 | login_manager.login_view = "login" 15 | 16 | bcrypt = Bcrypt(app) 17 | 18 | from writer import db_tables, login_register, create_post, view_post, all_posts, profile, update_password 19 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/all_posts.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from flask import render_template 4 | from flask_login import current_user, login_required 5 | from sqlalchemy import desc 6 | 7 | from writer import app 8 | from writer.db_tables import Article 9 | 10 | 11 | @app.route('/') 12 | def all_posts(): 13 | articles = Article.query.filter(Article.publish_date.isnot(None))\ 14 | .order_by(desc(Article.publish_date))\ 15 | .all() 16 | return render_template("posts_list.html", articles=articles) 17 | 18 | 19 | @app.route('/following') 20 | @login_required 21 | def following(): 22 | authors_following = [] 23 | if current_user.following is not None: 24 | authors_following = json.loads(current_user.following) 25 | 26 | articles = Article.query.filter(Article.publish_date.isnot(None)) \ 27 | .filter(Article.author_id.in_(authors_following)) \ 28 | .order_by(desc(Article.publish_date)) \ 29 | .all() 30 | return render_template("posts_list.html", articles=articles) 31 | 32 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/create_post.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timezone 2 | from secrets import token_urlsafe 3 | 4 | from flask import request, render_template, redirect, url_for, flash 5 | from flask_login import current_user, login_required 6 | from sqlalchemy.exc import SQLAlchemyError 7 | 8 | from writer import app, db 9 | from writer.db_tables import Article 10 | from writer.forms import CreatePostForm 11 | 12 | 13 | @app.route('/create', methods=['GET', 'POST']) 14 | @login_required 15 | def create_post(): 16 | article_id = request.args.get('article_id') 17 | article = None 18 | if article_id: 19 | article = Article.query.get(article_id) 20 | if not article: 21 | return _article_not_found() 22 | if article.author_id != current_user.id: 23 | # In case someone is trying to edit another author's article, 24 | # just redirect to home page without any notice. 25 | return redirect(url_for('all_posts')) 26 | 27 | form = CreatePostForm(request.form) 28 | if request.method == 'POST': 29 | try: 30 | return _add_post_to_db(form, article) 31 | except SQLAlchemyError: 32 | flash("We couldn't add your article due to a technical issue" 33 | " on our side. Please try again later.") 34 | elif article: 35 | form.title.data = article.article_title 36 | form.text.data = article.article_text 37 | 38 | return _render_form(form) 39 | 40 | 41 | def _add_post_to_db(form, article): 42 | is_draft = True if form.save.data else False 43 | publish_date = None if is_draft else datetime.now(timezone.utc) 44 | 45 | if not is_draft and not form.validate(): 46 | flash("Not all required fields were filled.") 47 | return _render_form(form) 48 | 49 | if article is None: 50 | article_id = token_urlsafe(16) 51 | article = Article(id=article_id, 52 | article_title=form.title.data, 53 | article_text=form.text.data, 54 | author_id=current_user.id, 55 | publish_date=publish_date) 56 | db.session.add(article) 57 | else: 58 | article_id = article.id 59 | article.article_title = form.title.data 60 | article.article_text = form.text.data 61 | article.publish_date = publish_date 62 | 63 | db.session.commit() 64 | 65 | if is_draft: 66 | flash("The article was saved as a draft.") 67 | return _render_form(form) 68 | else: 69 | return redirect(url_for('view_post', id=article_id)) 70 | 71 | 72 | def _render_form(form): 73 | return render_template('create_post.html', form=form) 74 | 75 | 76 | def _article_not_found(): 77 | return "Article Not Found", 404 -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/db_tables.py: -------------------------------------------------------------------------------- 1 | from flask_login import UserMixin 2 | from writer import db, bcrypt 3 | 4 | 5 | class User(UserMixin, db.Model): 6 | id = db.Column(db.String(32), primary_key=True) 7 | email = db.Column(db.String(320), unique=True, nullable=False, index=True) 8 | password_hash = db.Column(db.String(56), nullable=False) 9 | name = db.Column(db.String(100), nullable=False) 10 | 11 | # We store the list of authors being followed as a JSON list of user ids 12 | following = db.Column(db.Text) 13 | 14 | articles_written = db.relationship("Article", lazy=True, backref="author") 15 | 16 | def check_password(self, password): 17 | return bcrypt.check_password_hash(self.password_hash, password) 18 | 19 | @classmethod 20 | def hash_password(cls, password): 21 | return bcrypt.generate_password_hash(password).decode('utf-8') 22 | 23 | def __repr__(self): 24 | return"{0}: {1}".format(self.email, self.name) 25 | 26 | 27 | class Article(db.Model): 28 | id = db.Column(db.String(16), primary_key=True) 29 | article_title = db.Column(db.Text) 30 | article_text = db.Column(db.Text) 31 | author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False, 32 | index=True) 33 | publish_date = db.Column(db.TIMESTAMP(timezone=True)) 34 | 35 | def __repr__(self): 36 | return "{0}: {1}".format(self.id, self.article_title) 37 | 38 | 39 | class PasswordResetToken(db.Model): 40 | token_hash = db.Column(db.String(128), primary_key=True) 41 | user_id = db.Column(db.String(32), db.ForeignKey('user.id'), nullable=False) 42 | token_expiration = db.Column(db.TIMESTAMP(timezone=True), nullable=False) 43 | token_used = db.Column(db.Boolean, nullable=False) 44 | 45 | user = db.relationship("User", lazy=False) 46 | 47 | def __repr__(self): 48 | return "{0}: {1}".format(self.token_hash, self.user_id) 49 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/forms.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField, PasswordField, SubmitField, TextAreaField, HiddenField 3 | from wtforms.validators import DataRequired, Email, Length, EqualTo 4 | 5 | SMALL_PASSWORD_MESSAGE = "A password must have at least 8 characters" 6 | 7 | 8 | class LoginForm(FlaskForm): 9 | email = StringField("E-mail", validators=[DataRequired(), Email()]) 10 | password = PasswordField("Password", validators=[DataRequired()]) 11 | login = SubmitField("Login") 12 | 13 | 14 | class RegisterForm(FlaskForm): 15 | name = StringField("Name", validators=[DataRequired()]) 16 | email = StringField("E-mail", validators=[DataRequired(), Email()]) 17 | password = PasswordField("Password", validators=[DataRequired(), 18 | Length(min=8, 19 | message=SMALL_PASSWORD_MESSAGE)]) 20 | repeat_password = PasswordField('Repeat Password', 21 | validators=[DataRequired(), EqualTo('password')]) 22 | 23 | register = SubmitField("Register") 24 | 25 | 26 | class CreatePostForm(FlaskForm): 27 | title = StringField("Article Title", validators=[DataRequired()]) 28 | text = TextAreaField("Article Text", validators=[DataRequired()]) 29 | publish = SubmitField('Publish the article') 30 | save = SubmitField('Save as draft') 31 | 32 | 33 | class FollowAuthorForm(FlaskForm): 34 | follow = SubmitField("Follow author") 35 | unfollow = SubmitField("Unfollow author") 36 | 37 | 38 | class ProfileUpdateForm(FlaskForm): 39 | name = StringField("Name", validators=[DataRequired()]) 40 | email = StringField("E-mail", validators=[DataRequired(), Email()]) 41 | update = SubmitField("Update personal information") 42 | 43 | 44 | class ArticleUpdateForm(FlaskForm): 45 | edit = SubmitField("Edit Article") 46 | delete = SubmitField("Delete Article") 47 | article_id = HiddenField("article_id") 48 | 49 | 50 | class ResetPasswordEmailForm(FlaskForm): 51 | email = StringField("E-mail", validators=[DataRequired(), Email()]) 52 | send_reset_email = SubmitField("Send E-mail with Password Reset Link") 53 | 54 | 55 | class PasswordUpdateForm(FlaskForm): 56 | password = PasswordField("Password", validators=[DataRequired(), 57 | Length(min=8, 58 | message=SMALL_PASSWORD_MESSAGE)]) 59 | repeat_password = PasswordField('Repeat Password', 60 | validators=[DataRequired(), EqualTo('password')]) 61 | reset = SubmitField("Reset the password") 62 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/login_register.py: -------------------------------------------------------------------------------- 1 | from secrets import token_hex 2 | 3 | from flask import request, render_template, redirect, url_for, flash 4 | from flask_login import login_user, current_user, logout_user, login_required 5 | from sqlalchemy.exc import SQLAlchemyError 6 | from werkzeug.urls import url_parse 7 | 8 | from writer import app, db, login_manager 9 | from writer.forms import LoginForm, RegisterForm 10 | from writer.db_tables import User 11 | 12 | 13 | @login_manager.user_loader 14 | def load_user(user_id): 15 | return User.query.get(user_id) 16 | 17 | 18 | @app.route('/login', methods=['GET', 'POST']) 19 | def login(): 20 | next_page = request.args.get('next') 21 | 22 | if current_user.is_authenticated: 23 | return _continue_browsing(next_page) 24 | 25 | form = LoginForm(request.form) 26 | 27 | if request.method == 'POST': 28 | if form.validate(): 29 | user = User.query.filter_by(email=form.email.data).first() 30 | if user is not None and user.check_password(form.password.data): 31 | login_user(user) 32 | return _continue_browsing(next_page) 33 | else: 34 | flash("No user with email-id found, or the password is " 35 | "incorrect.") 36 | else: 37 | print("Invalid or incomplete input.") 38 | 39 | return render_template("login.html", form=form) 40 | 41 | 42 | @app.route('/logout') 43 | @login_required 44 | def logout(): 45 | logout_user() 46 | next_page = request.args.get('next') 47 | return _continue_browsing(next_page) 48 | 49 | 50 | @app.route('/register', methods=['GET', 'POST']) 51 | def register(): 52 | if current_user.is_authenticated: 53 | return _continue_browsing() 54 | 55 | form = RegisterForm(request.form) 56 | 57 | if request.method == 'POST': 58 | if form.validate(): 59 | try: 60 | return _add_user_to_db(form) 61 | except SQLAlchemyError: 62 | flash("We couldn't register you due to a technical issue" 63 | " on our side. Please try again later.") 64 | else: 65 | flash("The form was not properly completed.") 66 | 67 | return render_template("register.html", form=form) 68 | 69 | 70 | def _add_user_to_db(form): 71 | user = User(id=token_hex(32), 72 | email=form.email.data, 73 | password_hash=User.hash_password(form.password.data), 74 | name=form.name.data) 75 | db.session.add(user) 76 | db.session.commit() 77 | login_user(user) 78 | return _continue_browsing() 79 | 80 | 81 | def _continue_browsing(next_page=None): 82 | safe_next_page = _get_safe_url(next_page) 83 | if safe_next_page: 84 | return redirect(next_page) 85 | else: 86 | return redirect(url_for('all_posts')) 87 | 88 | 89 | def _get_safe_url(next_page): 90 | # URL form: scheme://netloc/path;parameters?query#fragment 91 | # If netloc is empty, it means the URL is relative, which we want to ensure 92 | # so that we don't send our user to other domains. 93 | if next_page and not url_parse(next_page).netloc: 94 | return next_page -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/profile.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from flask import request, render_template, flash, redirect, url_for 4 | from flask_login import current_user, login_required 5 | from sqlalchemy.exc import SQLAlchemyError 6 | 7 | from writer import app, db 8 | from writer.db_tables import Article 9 | from writer.forms import ProfileUpdateForm, ArticleUpdateForm 10 | 11 | 12 | @app.route('/profile', methods=['GET', 'POST']) 13 | @login_required 14 | def profile(): 15 | profile_form = ProfileUpdateForm(request.form) 16 | article_update_form = ArticleUpdateForm(request.form) 17 | 18 | if request.method == 'POST': 19 | try: 20 | if profile_form.update.data: 21 | _update_profile_data(profile_form) 22 | elif article_update_form.edit.data: 23 | return _edit_article(article_update_form.article_id.data) 24 | elif article_update_form.delete.data: 25 | _delete_article(article_update_form.article_id.data) 26 | except SQLAlchemyError: 27 | flash("Unable to update due to a technical issue. " 28 | "Please try again later.") 29 | 30 | return _render_profile_page() 31 | 32 | 33 | def _render_profile_page(): 34 | profile_form = ProfileUpdateForm() 35 | profile_form.name.data = current_user.name 36 | profile_form.email.data = current_user.email 37 | 38 | articles = current_user.articles_written.copy() 39 | articles.sort(reverse=True, 40 | key=lambda a: a.publish_date.timestamp() if a.publish_date else sys.maxsize) 41 | 42 | article_update_forms = [] 43 | for article in articles: 44 | article_update_form = ArticleUpdateForm() 45 | article_update_forms.append(article_update_form) 46 | article_update_form.article_id.data = article.id 47 | 48 | return render_template("profile.html", profile_form=profile_form, 49 | article_update_forms=article_update_forms, 50 | articles=articles) 51 | 52 | 53 | def _update_profile_data(profile_form): 54 | if profile_form.name.data: 55 | current_user.name = profile_form.name.data 56 | 57 | if profile_form.email.data: 58 | current_user.email = profile_form.email.data 59 | 60 | db.session.commit() 61 | 62 | 63 | def _delete_article(article_id): 64 | Article.query.filter(Article.id == article_id).delete() 65 | db.session.commit() 66 | 67 | 68 | def _edit_article(article_id): 69 | return redirect(url_for('create_post', article_id=article_id)) 70 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/create_post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Create Post 5 | 6 | 7 | {% include 'navigation.html' %} 8 | 9 |

10 | 11 | {% from "macros.html" import show_flashed_messages %} 12 | {{ show_flashed_messages() }} 13 |
14 | {{ form.hidden_tag() }} 15 |

16 | {{ form.title.label }}:
17 | {{ form.title(size=100, required=False) }} 18 |

19 | {{ form.text.label }}:
20 | {{ form.text(rows=10, cols=80, required=False, id="markdown-edit")}} 21 | 22 |

{{ form.publish() }}
23 | {{ form.save() }} 24 |

25 |
26 | 27 | 28 | 29 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Login | Writer 5 | 6 | 7 | {% include 'navigation.html' %} 8 | 9 |
10 | 11 | {% from "macros.html" import show_flashed_messages, show_field_errors %} 12 | {{ show_flashed_messages() }} 13 | 14 |
15 | {{ form.hidden_tag() }} 16 |

17 | {{ form.email.label }}:
18 | {{ form.email(size=75) }}
19 | {{ show_field_errors(form.email) }} 20 |

21 | {{ form.password.label }}:
22 | {{ form.password(size=75) }}
23 | {{ show_field_errors(form.password) }} 24 | 25 |

{{ form.login() }} 26 |

27 | 28 | Not registered?
29 | Forgot password? 30 |
31 | 32 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/macros.html: -------------------------------------------------------------------------------- 1 | {% macro show_flashed_messages() %} 2 | {% with messages = get_flashed_messages() %} 3 | {% if messages %} 4 | 5 | {% for message in messages %} 6 | {{ message }} 7 | {% endfor %} 8 | 9 | {% endif %} 10 | {% endwith %} 11 | {% endmacro %} 12 | 13 | {% macro show_field_errors(field) %} 14 | 15 | {% for error in field.errors %} 16 | {{ error }} 17 | {% endfor %} 18 | 19 | {% endmacro %} 20 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/navigation.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | Home 4 | Authors following 5 | Write 6 | {% if current_user.is_anonymous %} 7 | Login 8 | {% else %} 9 | Profile 10 | Logout 11 | {% endif %} 12 |
-------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/posts_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | All Articles | Writer 6 | 7 | 8 | {% include 'navigation.html' %} 9 | 10 |
11 | {% for article in articles %} 12 |

13 | 14 | 15 | {{ article.article_title }} 16 |
17 | Written By: {{ article.author.name }} 18 |

19 | 20 | {% endfor %} 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Profile | Writer 5 | 6 | 7 | {% include 'navigation.html' %} 8 | 9 |
10 | 11 | {% from "macros.html" import show_flashed_messages, show_field_errors %} 12 | {{ show_flashed_messages() }} 13 | 14 |
15 | {{ profile_form.hidden_tag() }} 16 |

17 | {{ profile_form.name.label }}:
18 | {{ profile_form.name(size=75) }}
19 | {{ show_field_errors(profile_form.name) }} 20 |

21 | {{ profile_form.email.label }}:
22 | {{ profile_form.email(size=75) }}
23 | {{ show_field_errors(profile_form.email) }} 24 |

{{ profile_form.update }} 25 |

26 | 27 |
28 | Change your password 29 | 30 |
31 |

Articles authored

32 | {% for article in articles %} 33 |

34 | 35 | 36 | {{ article.article_title }} 37 |
38 | 39 | {% if article.publish_date is none %} 40 | Draft 41 | {% else %} 42 | Published on: {{ article.publish_date.strftime("%d-%b-%Y") }} 43 | {% endif %} 44 | 45 |

46 | {{ article_update_forms[loop.index0].hidden_tag() }} 47 | {{ article_update_forms[loop.index0].edit() }} 48 | {{ article_update_forms[loop.index0].delete() }} 49 |
50 | 51 | {% endfor %} 52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Register | Writer 5 | 6 | 7 | 8 | {% include 'navigation.html' %} 9 | 10 |
11 | {% from "macros.html" import show_flashed_messages, show_field_errors %} 12 | {{ show_flashed_messages() }} 13 |
14 | {{ form.hidden_tag() }} 15 |

16 | {{ form.name.label }}:
17 | {{ form.name(size=75) }}
18 | {{ show_field_errors(form.name) }} 19 |

20 | {{ form.email.label }}:
21 | {{ form.email(size=75) }}
22 | {{ show_field_errors(form.email) }} 23 |

24 | {{ form.password.label }}:
25 | {{ form.password(size=75) }}
26 | {{ show_field_errors(form.password) }} 27 |

28 | {{ form.repeat_password.label }}:
29 | {{ form.repeat_password(size=75) }}
30 | {{ show_field_errors(form.repeat_password) }} 31 | 32 |

{{ form.register() }} 33 |

34 | 35 | Already have an account? 36 |
37 | 38 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/reset_email.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reset Password | Writer 5 | 6 | 7 | {% include 'navigation.html' %} 8 |
9 | {% from "macros.html" import show_flashed_messages, show_field_errors %} 10 | {{ show_flashed_messages() }} 11 |
12 | {{ form.hidden_tag() }} 13 |

14 | {{ form.email.label }}:
15 | {{ form.email(size=75) }}
16 | {{ show_field_errors(form.email) }} 17 | 18 |

{{ form.send_reset_email() }} 19 |

20 |
21 | 22 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/reset_email_confirm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reset Password | Writer 5 | 6 | 7 | {% include 'navigation.html' %} 8 |
9 | 10 | An email with a link to reset the password was successfully sent to {{ email }}. 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/update_password.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Update Password | Writer 6 | 7 | 8 | {% include 'navigation.html' %} 9 |
10 | 11 | {% from "macros.html" import show_flashed_messages, show_field_errors %} 12 | {{ show_flashed_messages() }} 13 |
14 | {{ form.hidden_tag() }} 15 |

16 | {{ form.password.label }}:
17 | {{ form.password(size=75) }}
18 | {{ show_field_errors(form.password) }} 19 |

20 | {{ form.repeat_password.label }}:
21 | {{ form.repeat_password(size=75) }}
22 | {{ show_field_errors(form.repeat_password) }} 23 | 24 |

{{ form.reset() }} 25 |

26 |
27 | 28 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/templates/view_post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | View Article | Writer 5 | 37 | 38 | 39 | {% include 'navigation.html' %} 40 | 41 | {% from "macros.html" import show_flashed_messages, show_field_errors %} 42 | {{ show_flashed_messages() }} 43 | 44 |
45 |

{{ article.article_title }}

46 | 47 | {% if current_user.is_authenticated %} 48 | {% if following == True %} 49 |
50 | {{ form.hidden_tag() }} 51 | {{ form.unfollow(class="button") }} 52 |
53 | {% else %} 54 |
55 | {{ form.hidden_tag() }} 56 | {{ form.follow(class="button") }} 57 |
58 | {% endif %}
59 | {% endif %} 60 | 61 |

62 |

{{ article_text|safe }}
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/update_password.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | 3 | from datetime import datetime, timezone, timedelta 4 | from email.mime.text import MIMEText 5 | from flask import request, flash, redirect, render_template, url_for 6 | from flask_login import current_user, login_user, logout_user 7 | from hashlib import blake2b 8 | from secrets import token_urlsafe 9 | from sqlalchemy.exc import SQLAlchemyError 10 | 11 | from writer import app, db 12 | from writer.db_tables import User, PasswordResetToken 13 | from writer.forms import ResetPasswordEmailForm, PasswordUpdateForm 14 | 15 | RESET_TOKEN_LENGTH = 32 16 | EMAIL_SUBJECT = "Password Reset link" 17 | EMAIL_TEMPLATE = "Please use the link below to reset your password.\n{0}" 18 | EMAIL_SENDER_ADDRESS = 'noreply@mywriter.web' 19 | 20 | 21 | @app.route('/password_reset_email', methods=['GET', 'POST']) 22 | def password_reset_email(): 23 | if request.method == 'POST': 24 | form = ResetPasswordEmailForm(request.form) 25 | if form.validate(): 26 | return _generate_token_and_send_email(form) 27 | else: 28 | flash("Provided email is not valid.") 29 | 30 | email = None 31 | if current_user.is_authenticated: 32 | email = current_user.email 33 | 34 | return _confirm_reset_link(email) 35 | 36 | 37 | @app.route('/update_password', methods=['GET', 'POST']) 38 | def update_password(): 39 | token_id = request.args.get('token') 40 | 41 | if not token_id: 42 | return redirect(url_for('all_posts')) 43 | 44 | if request.method == 'POST': 45 | form = PasswordUpdateForm(request.form) 46 | if form.validate(): 47 | try: 48 | _reset_password(token_id, form) 49 | return redirect(url_for('all_posts')) 50 | except SQLAlchemyError: 51 | flash("Couldn't reset the password due to a technical issue." 52 | "Please try again later.") 53 | else: 54 | flash("Correct the password field errors.") 55 | 56 | return _show_reset_password_form(token_id) 57 | 58 | 59 | def _generate_token_and_send_email(form): 60 | email = form.email.data 61 | user = _get_user(email) 62 | if user: 63 | try: 64 | reset_token_id = _add_reset_token_to_db(user) 65 | _send_email(email, reset_token_id) 66 | return _show_reset_email_confirmation(email) 67 | except (SQLAlchemyError, smtplib.SMTPConnectError): 68 | flash("Couldn't send the reset email due to a technical issue." 69 | "Please try again later.") 70 | else: 71 | flash("No user found with given email address.") 72 | 73 | return _confirm_reset_link() 74 | 75 | 76 | def _get_user(email): 77 | user = None 78 | if email: 79 | user = User.query.filter(User.email == email).first() 80 | 81 | return user 82 | 83 | 84 | def _add_reset_token_to_db(user): 85 | reset_token_id = token_urlsafe(RESET_TOKEN_LENGTH) 86 | token_hash = blake2b(reset_token_id.encode()).hexdigest() 87 | token_expiration = datetime.now(timezone.utc) + timedelta(hours=1) 88 | 89 | token = PasswordResetToken(user_id=user.id, 90 | token_hash=token_hash, 91 | token_expiration=token_expiration, 92 | token_used=False) 93 | db.session.add(token) 94 | db.session.commit() 95 | 96 | return reset_token_id 97 | 98 | 99 | def _send_email(email, reset_token_id): 100 | password_reset_url = url_for('update_password', _external=True, 101 | token=reset_token_id) 102 | email_text = EMAIL_TEMPLATE.format(password_reset_url) 103 | 104 | print (email_text) 105 | 106 | # Normally we would use the code below to send email 107 | # But, to send emails, we need to setup an SMTP Email server 108 | # which can take some effort. 109 | # 110 | # email_message = MIMEText(email_text) 111 | # email_message['Subject'] = EMAIL_SUBJECT 112 | # email_message['From'] = EMAIL_SENDER_ADDRESS 113 | # email_message['To'] = email 114 | # 115 | # with smtplib.SMTP('smtp.server.com') as smtp: 116 | # smtp.send_message(email_message) 117 | 118 | 119 | def _show_reset_email_confirmation(email): 120 | return render_template("reset_email_confirm.html", email=email) 121 | 122 | 123 | def _confirm_reset_link(email=None): 124 | form = ResetPasswordEmailForm() 125 | form.email.data = email 126 | return render_template("reset_email.html", form=form) 127 | 128 | 129 | def _reset_password(token_id, form): 130 | token, message = _get_token(token_id) 131 | if not token: 132 | return message 133 | 134 | logout_user() 135 | 136 | user = token.user 137 | user.password_hash = User.hash_password(form.password.data) 138 | token.token_used = True 139 | 140 | db.session.commit() 141 | 142 | login_user(user) 143 | 144 | 145 | def _show_reset_password_form(token_id): 146 | token, message = _get_token(token_id) 147 | if not token: 148 | return message 149 | 150 | logout_user() 151 | 152 | current_time = datetime.utcnow() 153 | if current_time > token.token_expiration: 154 | return "The password reset token has expired." 155 | 156 | form = PasswordUpdateForm() 157 | return render_template("update_password.html", form=form) 158 | 159 | 160 | def _get_token(token_id): 161 | token_hash = blake2b(token_id.encode()).hexdigest() 162 | token = PasswordResetToken.query.get(token_hash) 163 | if not token: 164 | return None, "The password reset token is not valid." 165 | 166 | if token.token_used: 167 | return None, "The password reset token has been used." 168 | 169 | return token, None 170 | -------------------------------------------------------------------------------- /lecture12/example_projects/writer_webapp/writer/view_post.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from flask import request, render_template, flash 4 | from flask_login import current_user 5 | from markdown2 import markdown 6 | from sqlalchemy.exc import SQLAlchemyError 7 | 8 | from writer import app, db 9 | from writer.db_tables import Article 10 | from writer.forms import FollowAuthorForm 11 | 12 | 13 | @app.route('/post', methods=['GET', 'POST']) 14 | def view_post(): 15 | article_id = request.args.get('id') 16 | if article_id is None: 17 | return _article_not_found() 18 | 19 | article = Article.query.get(article_id) 20 | if article is None or article.publish_date is None: 21 | return _article_not_found() 22 | 23 | author_followed = False 24 | form = FollowAuthorForm(request.form) 25 | if current_user.is_authenticated: 26 | following = [] 27 | if current_user.following is not None: 28 | following = json.loads(current_user.following) 29 | 30 | if request.method == 'POST': 31 | try: 32 | if form.follow.data: 33 | _follow_author(article.author_id, following) 34 | elif form.unfollow.data: 35 | _unfollow_author(article.author_id, following) 36 | except SQLAlchemyError: 37 | flash("Unable to execute follow/unfollow author action due to a" 38 | "technical issue. Please try again later.") 39 | 40 | if article.author_id in following: 41 | author_followed = True 42 | 43 | article_text_markdown = markdown(article.article_text) 44 | 45 | return render_template("view_post.html", article=article, article_text=article_text_markdown, 46 | form=form, following=author_followed) 47 | 48 | 49 | def _follow_author(author_id, following): 50 | if author_id not in following: 51 | following.append(author_id) 52 | _update_user_table(json.dumps(following)) 53 | 54 | 55 | def _unfollow_author(author_id, following): 56 | if author_id in following: 57 | following.remove(author_id) 58 | _update_user_table(json.dumps(following)) 59 | 60 | 61 | def _update_user_table(following): 62 | current_user.following = following 63 | db.session.commit() 64 | 65 | 66 | def _article_not_found(): 67 | return "Article Not Found", 404 68 | -------------------------------------------------------------------------------- /lecture12/no_cookie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/no_cookie.gif -------------------------------------------------------------------------------- /lecture12/no_cookie.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/no_cookie.psd -------------------------------------------------------------------------------- /lecture12/pages_uml1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/pages_uml1.png -------------------------------------------------------------------------------- /lecture12/password_reset_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/password_reset_flow.png -------------------------------------------------------------------------------- /lecture12/user_flow_uml1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/user_flow_uml1.png -------------------------------------------------------------------------------- /lecture12/with_cookie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/with_cookie.gif -------------------------------------------------------------------------------- /lecture12/with_cookie.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture12/with_cookie.psd -------------------------------------------------------------------------------- /lecture13/Readme.md: -------------------------------------------------------------------------------- 1 | ## Testing 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture13/testing.md) teaches you how to write tests for your code. Testing is a fundamental programming skill and typically a programmer spends a large portion of their time writing tests. 4 | 5 | There are no assignments, but I highly encourage you to try the examples showed in the lecture yourself. 6 | -------------------------------------------------------------------------------- /lecture13/important_code.py: -------------------------------------------------------------------------------- 1 | import math 2 | from flask_wtf import FlaskForm 3 | from wtforms import StringField, SelectField, SubmitField 4 | 5 | 6 | 7 | def abs_diff(a, b): 8 | diff = a - b 9 | if diff < 0: 10 | diff = -1 * diff 11 | 12 | return diff 13 | 14 | args = {'a': 3, 'b': 1} 15 | print(abs_diff(**args)) 16 | 17 | 18 | class Series: 19 | def transform(self, k): 20 | return k 21 | 22 | def series_sum(self, n): 23 | total = 0 24 | for i in range(n): 25 | total += self.transform(i + 1) 26 | 27 | return total 28 | 29 | 30 | class PiSeries(Series): 31 | def transform(self, k): 32 | return math.pow(-1, k - 1) * (1 / (2 * k - 1)) 33 | 34 | 35 | class ViewFunctionResponse: 36 | def __init__(self): 37 | self.render_response = False 38 | self.redirect_response = False 39 | 40 | def create_render_response(self, template_name, kwargs): 41 | self.render_response = True 42 | self.redirect_response = False 43 | self.render_template = template_name 44 | self.kwargs = kwargs 45 | 46 | def create_redirect_response(self, view_function, kwargs): 47 | self.redirect_response = True 48 | self.render_response = False 49 | self.redirect_view = view_function 50 | self.kwargs = kwargs 51 | 52 | 53 | class WordForm(FlaskForm): 54 | word = StringField('Word') 55 | language = SelectField(u'Language', choices=[('en', 'English (US)'), 56 | ('es', 'Spanish')]) 57 | submit = SubmitField('See Word Definitions') 58 | 59 | 60 | def dictionary_home_impl(request): 61 | view_response = ViewFunctionResponse() 62 | if request.method == 'POST': 63 | word = request.form['word'].lower() 64 | language = request.form['language'] 65 | kwargs = {'word': word, 'lang': language} 66 | view_response.create_redirect_response('display_word_defs', kwargs) 67 | else: 68 | #form = WordForm() 69 | kwargs = {'form': None} 70 | view_response.create_render_response('dictionary_home.html', kwargs) 71 | 72 | return view_response -------------------------------------------------------------------------------- /lecture13/test_important_code.py: -------------------------------------------------------------------------------- 1 | import math 2 | from important_code import abs_diff, Series, PiSeries, ViewFunctionResponse, dictionary_home_impl 3 | 4 | 5 | EPSILON = 1E-4 6 | 7 | 8 | class TestAbsDiff: 9 | def test_positive(self): 10 | assert 1 == abs_diff(2, 1) 11 | 12 | def test_negative(self): 13 | assert 1 == abs_diff(1, 2) 14 | 15 | 16 | class TestSeries: 17 | def test_100_terms(self): 18 | series = Series() 19 | assert 5050 == series.series_sum(100) 20 | 21 | 22 | class TestPiSeries: 23 | def test_20000_terms(self): 24 | series = PiSeries() 25 | assert math.isclose(math.pi / 4, series.series_sum(200000), abs_tol=EPSILON) 26 | 27 | 28 | class MockRequest: 29 | def __init__(self, method, form_dict): 30 | self.method = method 31 | self.form = form_dict 32 | 33 | 34 | class TestDictionaryHome: 35 | def test_get_request(self): 36 | request = MockRequest('GET', {}) 37 | view_response = dictionary_home_impl(request) 38 | assert True == view_response.render_response 39 | assert 'dictionary_home.html' == view_response.render_template 40 | # assert isinstance(view_response.kwargs['form'], WordForm) 41 | 42 | def test_post_request(self): 43 | request = MockRequest('POST', {'word': 'ace', 'language': 'en'}) 44 | view_response = dictionary_home_impl(request) 45 | assert True == view_response.redirect_response 46 | assert 'display_word_defs' == view_response.redirect_view 47 | assert 'ace' == view_response.kwargs['word'] 48 | assert 'en' == view_response.kwargs['lang'] -------------------------------------------------------------------------------- /lecture2/Readme.md: -------------------------------------------------------------------------------- 1 | # Variables and Expressions 2 | 3 | This is the [first lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture2/variables.md) in the series which discusses programming in Python. 4 | 5 | The assignments for this lecture are available at [this link](https://repl.it/classroom/invite/XYCIQ0n). You will need to sign up for [repl.it](https://repl.it) (don't check "I'm a teacher" when you sign up) before you click on the link above to join the "classroom" where you can attempt assignments. 6 | 7 | The assignments, along with solutions, are [also listed here](https://github.com/amangup/coding-bootcamp/blob/master/lecture2/assignments.md). If you're looking at the solution, make sure either you've already solved the problem, or tried hard. 8 | -------------------------------------------------------------------------------- /lecture2/assignments.md: -------------------------------------------------------------------------------- 1 | # Problem Definitions 2 | 3 | ## 1: GDP growth 4 | 5 | The US GDP in 2017 was $19.739 Trillions. Input a target year, and output the expected GDP for US in that year. Assume that GDP grows by 2% every year. 6 | 7 | Output: Simply print the expected GDP for the target year 8 | 9 | e.g. 10 | ``` 11 | What is the target year for which you want to know US GDP? 2020 12 | 20.947184712000002 13 | ``` 14 | 15 | ## 2: Fahrenheit to Celsius 16 | 17 | Input temperature in Fahrenheit, and print the same temperature in Celsius. 18 | 19 | e.g. 20 | ``` 21 | What is the temperature in Fahrenheit? 32 22 | 0.0 23 | ``` 24 | 25 | ## 3: Hodor Hodor Hodor 26 | 27 | Take a string as input, and print it 5 times with a space in between. 28 | 29 | e.g. 30 | ``` 31 | Enter a string: Hodor 32 | Hodor Hodor Hodor Hodor Hodor 33 | ``` 34 | 35 | ## 4: Has a or b 36 | 37 | Input a string and check if it contains the lowercase letters 'a' or 'b'. 38 | 39 | e.g. 40 | ``` 41 | Enter a string: The War on Drugs 42 | True 43 | ``` 44 | 45 | ``` 46 | Enter a string: Beirut 47 | False 48 | ``` 49 | 50 | ## 5: Find last digit 51 | 52 | Input an integer and print it's last digit. 53 | 54 | e.g. 55 | ``` 56 | Enter an integer: 42 57 | 2 58 | ``` 59 | 60 | ## 6: Calculate federal taxes 61 | 62 | Calculate federal taxes for someone with annual income in the range of $100,000 to $190,000 using the single person tax brackets and standard deduction as follows. 63 | 64 | - $0 to $9,325 : 10% 65 | - $9,325 to $37,950: 15% 66 | - $37,950 to $91,900: 25% 67 | - $91,900 to $191,650: 28% 68 | 69 | Standard deduction: $6,350 70 | 71 | e.g. 72 | ``` 73 | What is your income ($100,000 - $190,000)? 100000 74 | 19203.75 75 | ``` 76 | 77 | ## 7: Hourly pay 78 | 79 | Input someone's hourly pay and check if they earn more than or equal to $1,500 on a fortnightly basis. Assume they work 40 hours a week. Print True if yes, False otherwise. 80 | 81 | e.g. 82 | 83 | ``` 84 | What is your hourly_pay? 25 85 | True 86 | ``` 87 | 88 | ## 8: Movie runtime 89 | 90 | For a movie, input its runtime length in seconds and print it out in Hours:Minutes:Seconds. 91 | 92 | e.g. 93 | ``` 94 | What is the length of the movie in seconds? 10000 95 | 2:46:40 96 | ``` 97 | 98 | ## 9: Modern Olympics 99 | 100 | Input a year (1896 or later) and print True if the Summer Olympics were organized in that year (or will be), False otherwise. 101 | 102 | e.g. 103 | ``` 104 | Enter a year? 1896 105 | True 106 | ``` 107 | 108 | ## 10: Suitcases in a car trunk 109 | 110 | Input the volume of car's trunk in gallons, and the volume of one suitcase, and print the number of suitcases you can fit in that trunk. 111 | 112 | e.g. 113 | ``` 114 | What is the volume of the trunk in US gallons? 110 115 | What is the volume of the suitcase in US gallons? 12 116 | 9 117 | ``` 118 | 119 | ## 11: Sum of digits 120 | 121 | Input a 3 digit integer, and print the sum of its digits. 122 | 123 | e.g. 124 | ``` 125 | Enter a number (100 - 999)? 360 126 | 9 127 | ``` 128 | 129 | ## 12: Integer memory size 130 | 131 | This problem requires you to use the internet to find answers and learn more about python and programming. 132 | 133 | Input an integer, and print the number of bytes of memory space that the variable containing the integer takes up. 134 | 135 | e.g. 136 | ``` 137 | Enter an integer? 2 138 | 28 139 | ``` 140 | 141 | ## 13: ASCII values 142 | 143 | This problem requires you to use the internet to find answers and learn more about python and programming. 144 | 145 | Input a string, and print the ASCII value of its first and last characters. Output both the values in the same line separated by a space. 146 | 147 | e.g. 148 | ``` 149 | Enter a string: What's an ASCII character? 150 | 87 63 151 | ``` 152 | 153 | ## 14: Print formatting 154 | 155 | This problem requires you to use the internet to find answers and learn more about python and programming. 156 | 157 | Enter a real number and print it out rounded to only two decimal places. 158 | 159 | e.g. 160 | ``` 161 | Enter a real number: 19.3 162 | 19.30 163 | ``` 164 | 165 | ``` 166 | Enter a real number: 2.348 167 | 2.35 168 | ``` 169 | 170 | ## 15: Bill for items 171 | 172 | This problem requires you to use the internet to find answers and learn more about python and programming. 173 | 174 | For a person buying a large number of same items, ask the price of the item and number of items purchased, and output the total bill. 175 | 176 | e.g. 177 | ``` 178 | Enter price of the item: 2.1 179 | Enter number of items: 3 180 | 6.3 181 | ``` 182 | 183 | # Solutions 184 | 185 | ## 1: GDP growth 186 | 187 |
188 | Show code: 189 | 190 | ```python 191 | us_gdp_2017_trillions = 19.739 192 | target_year = int(input("What year do you want to know US GDP for?")) 193 | 194 | years_diff = target_year - 2017 195 | gdp_growth_rate = 0.02 196 | 197 | us_gdp_target_year = us_gdp_2017_trillions * ((1 + gdp_growth_rate) ** years_diff) 198 | print(us_gdp_target_year) 199 | ``` 200 | 201 |
202 | 203 | ## 2: Fahrenheit to Celsius 204 | 205 |
206 | Show code: 207 | 208 | ```python 209 | fahrenheit = float(input("What is the temperature in Fahrenheit?")) 210 | celsius = (fahrenheit - 32) * 5 / 9 211 | 212 | print(celsius) 213 | ``` 214 |
215 | 216 | ## 3: Hodor Hodor Hodor 217 | 218 |
219 | Show code: 220 | 221 | ```python 222 | input_str = input("Enter a string:") 223 | output_str = (input_str + ' ') * 4 224 | output_str = output_str + input_str 225 | print(output_str) 226 | ``` 227 | 228 |
229 | 230 | ## 4: Has a or b 231 | 232 |
233 | Show code: 234 | 235 | ```python 236 | input_str = input("Enter a string:") 237 | has_a_b = 'a' in input_str or 'b' in input_str 238 | print(has_a_b) 239 | ``` 240 | 241 |
242 | 243 | ## 5: Find last digit 244 | 245 |
246 | Show code: 247 | 248 | ```python 249 | input_int = int(input("Enter an integer:")) 250 | last_digit = input_int % 10 251 | print(last_digit) 252 | ``` 253 |
254 | 255 | ## 6: Calculate federal taxes 256 | 257 |
258 | Show code: 259 | 260 | ```python 261 | annual_income = float(input("What is your income ($95,000 - $190,000)?")) 262 | standard_deduction = 6350 263 | tax_slab1 = 9325 * 0.1 264 | tax_slab2 = (37950 - 9325) * 0.15 265 | tax_slab3 = (91900 - 37950) * 0.25 266 | tax_slab4 = (annual_income - standard_deduction - 91900) * 0.28 267 | 268 | total_tax = tax_slab1 + tax_slab2 + tax_slab3 + tax_slab4 269 | print(total_tax) 270 | ``` 271 | 272 |
273 | 274 | ## 7: Hourly pay 275 | 276 |
277 | Show code: 278 | 279 | ```python 280 | hourly_pay = float(input("What is your hourly_pay?")) 281 | hours_per_week = 40 282 | fornightly_pay = hourly_pay * hours_per_week * 2 283 | 284 | print(fornightly_pay >= 1500) 285 | ``` 286 |
287 | 288 | ## 8: Movie runtime 289 | 290 |
291 | Show code: 292 | 293 | ```python 294 | movie_runtime_secs = int(input("What is the length of the movie in seconds?")) 295 | seconds = movie_runtime_secs % 60 296 | minutes = int(movie_runtime_secs / 60) % 60 297 | hours = int(movie_runtime_secs / 3600) 298 | print("%s:%s:%s" % (hours, minutes, seconds)) 299 | ``` 300 | 301 |
302 | 303 | ## 9: Modern Olympics 304 | 305 |
306 | Show code: 307 | 308 | ```python 309 | year = int(input("Enter a year?")) 310 | is_olympic_year = (year % 4 == 0) 311 | print(is_olympic_year) 312 | ``` 313 | 314 |
315 | 316 | ## 10: Suitcases in a car trunk 317 | 318 |
319 | Show code: 320 | 321 | ```python 322 | volume_trunk = float(input("What is the volume of the trunk in US gallons?")) 323 | volume_suitcase = float(input("What is the volume of the suitcase in US gallons?")) 324 | num_suitcases_in_trunk = int(volume_trunk / volume_suitcase) 325 | 326 | print (num_suitcases_in_trunk) 327 | ``` 328 | 329 |
330 | 331 | ## 11: Sum of digits 332 | 333 |
334 | Show code: 335 | 336 | ```python 337 | input_int = int(input("Enter a number (100 - 999)?")) 338 | ones_digit = input_int % 10 339 | tens_digit = int(input_int / 10) % 10 340 | hundreds_digit = int(input_int / 100) % 10 341 | sum_of_digits = ones_digit + tens_digit + hundreds_digit 342 | 343 | print(sum_of_digits) 344 | ``` 345 | 346 |
347 | 348 | ## 12: Integer memory size 349 | 350 |
351 | Show code: 352 | 353 | ```python 354 | input_int = int(input("Enter an integer?")) 355 | from sys import getsizeof 356 | 357 | print(getsizeof(input_int)) 358 | ``` 359 | 360 |
361 | 362 | ## 13: ASCII values 363 | 364 |
365 | Show code: 366 | 367 | ```python 368 | input_str = input("Enter a string:") 369 | ascii_first = ord(input_str[0]) 370 | ascii_second = ord(input_str[-1]) 371 | 372 | print("%s %s" % (ascii_first, ascii_second)) 373 | ``` 374 | 375 |
376 | 377 | ## 14: Print formatting 378 | 379 |
380 | Show code: 381 | 382 | ```python 383 | real_num = float(input("Enter a real number:")) 384 | print ("%.2f" % real_num) 385 | ``` 386 | 387 |
388 | 389 | ## 15: Bill for items 390 | 391 |
392 | Show code: 393 | 394 | ```python 395 | price_of_item = float(input("Enter price of the item:")) 396 | num_items = int(input("Enter number of items:")) 397 | price_cents = price_of_item * 100 398 | total_bill_cents = num_items * price_cents 399 | 400 | print(total_bill_cents / 100) 401 | ``` 402 | 403 |
-------------------------------------------------------------------------------- /lecture3/Readme.md: -------------------------------------------------------------------------------- 1 | # Conditionals and Loops 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture3/conditional_and_loops.md) introduces the `if` conditional statement and `while` looping statement. 4 | 5 | The assignments for this lecture are available at [this link](https://repl.it/classroom/invite/PwPUU2u). You will need to sign up for [repl.it](https://repl.it) (don't check "I'm a teacher" when you sign up) before you click on the link above to join the "classroom" where you can attempt assignments. 6 | 7 | The assignments, along with solutions, are [also listed here](https://github.com/amangup/coding-bootcamp/blob/master/lecture3/assignments.md). If you're looking at the solution, make sure either you've already solved the problem, or tried hard. 8 | -------------------------------------------------------------------------------- /lecture3/assignments.md: -------------------------------------------------------------------------------- 1 | # Problem definitions 2 | 3 | ## 1: Punctuation spaces 4 | 5 | In correctly formed paragraphs, one space must be present after the following punctuation characters - period (.), comma(,), exclamation mark (!) and question mark (?). 6 | 7 | Input a paragraph from the user and check if the writer has correctly added these spaces after the punctuation. If there are no errors, output **Correct**. If there is an error, output **Incorrect** and the location of the error as follow: `[word1] + punctuation mark + [first letter of next word]` (see examples). 8 | 9 | Assume that the input doesn't contain any other punctuation marks, like colon(:), semi-colon(;), etc. And the sentence will always be non-empty and start with a word. 10 | 11 | e.g. 12 | ``` 13 | Input: 'Thank you, madam.' 14 | Output: 'Correct' 15 | ``` 16 | 17 | ``` 18 | Input: 'By the way,it is raining outside.' 19 | Output: 'Incorrect: way,i' 20 | ``` 21 | 22 | ``` 23 | Input: `Hi!You look beautiful.` 24 | Output: `Incorrect: Hi!Y` 25 | ``` 26 | 27 | 28 | ## 2: Taxes for any income 29 | 30 | Calculate federal taxes for someone with any annual income. Single person tax brackets and standard deduction as follows. 31 | 32 | - $0 to $9,325 : 10% 33 | - $9,325 to $37,950: 15% 34 | - $37,950 to $91,900: 25% 35 | - $91,900 to $191,650: 28% 36 | - $191,650 to $416,700: 33% 37 | - $416,700 to $418,400: 35% 38 | - $418,400+: 39.6% 39 | 40 | Standard deduction: $6,350 41 | 42 | e.g. 43 | 44 | ``` 45 | Input: '500000' 46 | Output: '151304.25' 47 | ``` 48 | 49 | ## 3: Is Number a prime? 50 | 51 | Input a number and check if it is prime. Prime number is a number `p` such that it has no divisors other than 1 and `p`. First few primes are 2, 3, 5, 7, 11, 13, 17, 19, 23. 52 | 53 | e.g. 54 | ``` 55 | Input: '12' 56 | Output: 'No' 57 | ``` 58 | 59 | ``` 60 | Input: '13' 61 | Output: 'Yes' 62 | ``` 63 | 64 | ## 4: Sum of digits (again) 65 | 66 | Input a number and print the sum of its digits. 67 | 68 | e.g. 69 | ``` 70 | Input: '125' 71 | Output: '8' 72 | ``` 73 | 74 | ## 5: Find largest 75 | 76 | Keep asking the user to input a number, until they enter 0 or have entered 10 numbers. Print the largest number as output. Don't consider 0 as part of the input when you compute the maximum. 77 | 78 | e.g. 79 | ``` 80 | Inputs: '2,5,0' 81 | Output: '5' 82 | ``` 83 | 84 | ``` 85 | Inputs: '4,10,33,9,10,5,45,-1,23,45,99' 86 | Output: '45' 87 | ``` 88 | 89 | ## 6: Parenthesis 90 | 91 | Input an expression with parenthesis, and check if the parenthesis are properly matching. 92 | 93 | e.g. 94 | 95 | ``` 96 | Input: '((x+1)*2)' 97 | Output: 'Yes' 98 | ``` 99 | 100 | ``` 101 | Input: '(x-1)/5)' 102 | Output: 'No' 103 | ``` 104 | 105 | ## 7: Reversal 106 | 107 | Input a word and print it's reverse. 108 | 109 | e.g. 110 | 111 | ``` 112 | Input: 'Park' 113 | Output: 'kraP' 114 | ``` 115 | 116 | ## 8: Star triangle 117 | 118 | Input a number N, and write a program to print a right angle with N rows like below using asterisks. 119 | 120 | ``` 121 | * 122 | * * 123 | * * * 124 | ``` 125 | 126 | e.g. 127 | ``` 128 | Input: '5' 129 | Output: 130 | * 131 | * * 132 | * * * 133 | * * * * 134 | * * * * * 135 | ``` 136 | 137 | ## 9: Count vowels 138 | 139 | Input a sentence and count the number of vowel characters in that sentence. Both uppercase and lowercase character characters should be counted. 140 | 141 | e.g. 142 | ``` 143 | Input: 'Life is strange' 144 | Output: '5' 145 | ``` 146 | # Solutions 147 | 148 | ## 1: Punctuation space 149 | 150 |
151 | Show code: 152 | 153 | ```python 154 | para = input() 155 | length = len(para) 156 | 157 | last_word = para[0] 158 | punctuation = '.,!?' 159 | i = 1 160 | while i < length: 161 | last_word += para[i] 162 | 163 | if para[i - 1] in punctuation and para[i] != ' ': 164 | print("Incorrect: %s" % last_word) 165 | break 166 | 167 | if para[i] == ' ': 168 | last_word = '' 169 | 170 | i += 1 171 | else: 172 | print("Correct") 173 | ``` 174 | 175 |
176 | 177 | ## 2: Taxes for any income 178 | 179 |
180 | Show code: 181 | 182 | ```python 183 | annual_income = float(input()) 184 | 185 | STD_DEDUCTION = 6350 186 | 187 | taxable_income = annual_income - STD_DEDUCTION 188 | 189 | total_tax = 0.0 190 | if taxable_income > 0: 191 | total_tax += min(taxable_income, 9325) * 0.1 192 | if taxable_income > 9325: 193 | total_tax += min(taxable_income - 9325, 37950 - 9325) * 0.15 194 | if taxable_income > 37950: 195 | total_tax += min(taxable_income - 37950, 91900 - 37950) * 0.25 196 | if taxable_income > 91900: 197 | total_tax += min(taxable_income - 91900, 191650 - 91900) * 0.28 198 | if taxable_income > 191650: 199 | total_tax += min(taxable_income - 191650, 416700 - 191650) * 0.33 200 | if taxable_income > 416700: 201 | total_tax += min(taxable_income - 416700, 418400 - 416700) * 0.35 202 | if taxable_income > 418400: 203 | total_tax += (taxable_income - 418400) * 0.396 204 | 205 | print(total_tax) 206 | ``` 207 | 208 |
209 | 210 | ## 3: Is Number a prime? 211 | 212 |
213 | Show code: 214 | 215 | ```python 216 | number = int(input()) 217 | divisor = 2 218 | while divisor * divisor <= number: 219 | if not number % divisor: 220 | print("No") 221 | break 222 | divisor += 1 223 | else: 224 | print("Yes") 225 | ``` 226 | 227 |
228 | 229 | ## 4: Sum of digits (again) 230 | 231 |
232 | Show code: 233 | 234 | ```python 235 | number = input() 236 | length = len(number) 237 | sum_digits = 0 238 | i = 0 239 | while i < length: 240 | sum += int(number[i]) 241 | i += 1 242 | 243 | print(sum_digits) 244 | ``` 245 | 246 |
247 | 248 | ## 5: Find largest 249 | 250 |
251 | Show code: 252 | 253 | ```python 254 | i = 0 255 | max_num = -9999999999999 256 | while i < 10: 257 | number = int(input()) 258 | if number == 0: 259 | break 260 | 261 | if number > max_num: 262 | max_num = number 263 | 264 | i += 1 265 | 266 | print(max_num) 267 | ``` 268 | 269 |
270 | 271 | ## 6: Parenthesis 272 | 273 |
274 | Show code: 275 | 276 | ```python 277 | expr = input() 278 | length = len(expr) 279 | i = 0 280 | paren_count = 0 281 | while i < length: 282 | if expr[i] == '(': 283 | paren_count += 1 284 | elif expr[i] == ')': 285 | paren_count -= 1 286 | 287 | if paren_count < 0: 288 | print("No") 289 | break 290 | 291 | i += 1 292 | 293 | if paren_count == 0: 294 | print("Yes") 295 | elif paren_count > 0: 296 | print("No") 297 | ``` 298 | 299 |
300 | 301 | ## 7: Reversal 302 | 303 |
304 | Show code: 305 | 306 | ```python 307 | word = input() 308 | length = len(word) 309 | reverse_word = "" 310 | i = 1 311 | while i <= length: 312 | reverse_word += word[-i] 313 | i += 1 314 | 315 | print(reverse_word) 316 | ``` 317 | 318 |
319 | 320 | ## 8: Star triangle 321 | 322 |
323 | Show code: 324 | 325 | ```python 326 | num = int(input()) 327 | i = 0 328 | while i < num: 329 | line = "* " * i + "*" 330 | print(line) 331 | i += 1 332 | ``` 333 | 334 |
335 | 336 | 337 | ## 9: Count vowels 338 | 339 |
340 | Show code: 341 | 342 | ```python 343 | sentence = input() 344 | sentence_length = len(sentence) 345 | i = 0 346 | count = 0 347 | while i < sentence_length: 348 | if sentence[i] in 'aAeEiIoOuU': 349 | count += 1 350 | i += 1 351 | 352 | print(count) 353 | ``` 354 | 355 |
356 | -------------------------------------------------------------------------------- /lecture3/conditional_and_loops.md: -------------------------------------------------------------------------------- 1 | # Conditionals 2 | 3 | - The main feature of conditional code is to run special code when a _condition_ is met. 4 | - It is also called branching. 5 | - It is one of the most common mechanisms in programming and practically all programming languages support this mechanism (with their own syntax) 6 | 7 | Let's look at some examples how conditional code could be useful in real programs. 8 | 9 | ![Lock screen](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture3/phone_unlock.jpg) 10 | 11 | A phone gets unlocked only if the code is correct, not otherwise. 12 | 13 | ![Google France](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture3/google-france.png) 14 | 15 | If you type google.com in your browser while you are in france, Google will show you the Google homepage in french. 16 | 17 | ![Tetris](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture3/tetris.jpg) 18 | 19 | In a video game, your score depends on your actions. 20 | 21 | ## Writing conditional code 22 | - The _condition_ in the conditional code can be any boolean variable, constant or an expression that evaluates to a boolean value. 23 | - This is the reason why comparison operators are so frequently used in coding. 24 | 25 | Here is an example of the `if` condition. 26 | 27 | ```python 28 | unlock_code = 2348 29 | code_entered = 1234 30 | 31 | if code_entered == unlock_code: 32 | # If block starts 33 | 34 | print ("Welcome!") 35 | print ("If you are a thief and just guessed the right code, log out!") 36 | 37 | # if block ends 38 | 39 | print ("This gets printed irrespective.") 40 | ``` 41 | 42 | Some elements to consider: 43 | - The indentation. 44 | - Python uses that to know if you're inside an if block. 45 | - Indentation is used many, _many_ times in Python. 46 | - You can use spaces, or use the tab character. It is recommended that you use spaces. Most editors can be changed to add 4 spaces when you press the tab key. The good ones have this setting as default. 47 | - I can't overestimate the importance of getting indentation right in Python. 48 | 49 | - The colon. 50 | - Tells python to expect an indented block after that line. 51 | - Without the colon, python will raise an error. 52 | - If you start an if statement (with the colon), and don't put an indented block after it, then the python will raise an error too. 53 | 54 | 55 | There is a way to visualize conditional code with a **flow diagram** as shown below: 56 | 57 | ![If flow diagram](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture3/if_diagram.jpg) 58 | 59 | 60 | ### Else clause 61 | - If you want to run one segment of code only if the condition is true, and another segment of code _only if the condition is false_, then we use the `else` condition. 62 | 63 | Let's update the example we used above with an `else` clause. 64 | 65 | ```python 66 | unlock_code = 2348 67 | code_entered = 1234 68 | 69 | if code_entered == unlock_code: 70 | # If block starts 71 | print ("Welcome!") 72 | print ("If you are a thief and just guessed the right code, log out!") 73 | 74 | # if block ends 75 | else: 76 | # else block starts 77 | print ("Try again") 78 | 79 | # else block ends 80 | 81 | print ("This still gets printed irrespective.") 82 | ``` 83 | 84 | - Note that the `else` keyword is not indented. It is on the same _level_ as the `if` keyword. This is important to be aware of. If `if` and `else` do not have the same level of indentation, python will not be able to match them up. 85 | 86 | The flow diagram can also be updated as shown below: 87 | 88 | ![If-else flow diagram](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture3/if-else_diagram.jpg) 89 | 90 | ### Elif keyword 91 | - `elif` is a short form for `else if`. 92 | - It allows you to check multiple conditions and execute the right code if only one condition can be true. 93 | - It is there for convenience, and helps keep the code looking organized. 94 | 95 | Let's take an example where we input a number and print it out as a word. First, let's try to implement it without `elif`. 96 | ```python 97 | input_num = 3 98 | 99 | if input_num == 1: 100 | print ("One") 101 | else: 102 | if input_num == 2: 103 | print ("Two") 104 | else: 105 | if input_num == 3: 106 | print ("Three") 107 | else: 108 | if input_num == 4: 109 | print ("Four") 110 | ``` 111 | 112 | And we could carry on like this. 113 | 114 | Let's see how the same code looks like if we use `elif`. 115 | 116 | ```python 117 | input_num = 3 118 | 119 | if input_num == 1: 120 | print ("One") 121 | elif input_num == 2: 122 | print ("Two") 123 | elif input_num == 3: 124 | print ("Three") 125 | elif input_num == 4: 126 | print ("Four") 127 | ``` 128 | 129 | It's obvious that the second listing of code is shorter and easier to read. 130 | 131 | ## Practice exercises 132 | - Input two integers, `a` and `b`, and check if `a` is divisible by `b` 133 | - Given the 3 letter name of the month, print the number of days in that month. (Assume it's not a leap year). 134 | 135 | # Looping 136 | - Looping mechanism allows us to run the same segment of code again and again. 137 | - Every loop must have an _exit condition_. Otherwise, the code inside the loop will keep getting executed forever. 138 | 139 | The movie Groundhog day is a perfect example: 140 | 141 | **Spoilers start** 142 | 143 | In the movie, Phil is a weatherman, and also a mean guy who only thinks about himself. He goes out of town to report weather, and wakes up every day to the _same_ day, and the same events repeat in the same order (like traveling backward in time). Only he remembers every _run_ of that same day. This is equivalent to running a code in loop - where the same code is running again and again, but something changes in every loop (otherwise what's the point?) 144 | 145 | Over the course of the movie, he becomes a better, more helpful, person and in the end gets out of this loop. Him becoming a nicer person is the exit condition in this example. 146 | 147 | **Spoilers end** 148 | 149 | ## Writing looping code 150 | For writing loops in Python, we use the `while` keyword. 151 | 152 | Here is an example, which prints the first 10 numbers. 153 | 154 | ```python 155 | i = 1 156 | while i <= 10: 157 | # Body of loop starts 158 | print (i) 159 | i += 1 160 | 161 | # Body of loop ends 162 | ``` 163 | 164 | - Note that the basic syntax is _identical_ to an `if` condition code, with the keyword `if` replaced by the keyword `while`. 165 | - The different keyword changes the meaning of the code. 166 | 167 | Here is the flow diagram looks like for a while loop. 168 | 169 | ![loop diagram](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture3/loop_diagram.jpg) 170 | 171 | ### Infinite Loops 172 | 173 | - Normally, every loop must have an exit condition. But there are some scenarios where _infinite_ loops are useful. Here is an example: 174 | 175 | ```python 176 | while True: 177 | year = int(input("Enter an year to check if it's a leap year:")) 178 | if (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0): 179 | print ("It's a leap year") 180 | else: 181 | print ("It's not a leap year") 182 | ``` 183 | 184 | - Most websites are in effect an infinite loop. Whenever user types the address of the website, there is some code waiting to show you the content of the website. It keeps on doing the same thing forever, for every user who requests a web page. 185 | 186 | - Sometimes our code becomes an infinite loop, even if that is not our intention. Here is a funny example of that: [Google Home and Amazon Echo stuck in an infinite loop](https://en.wikipedia.org/wiki/File:Google_Home_vs._Amazon_Echo.webm) 187 | 188 | ### Break keyword 189 | 190 | - `break` keyword allows you to get out of the loop even if the while loop condition is not met. 191 | 192 | An example of an infinite loop, which exits once you enter the right answer: 193 | 194 | ```python 195 | while True: 196 | answer = input("What is the answer to life, the universe and everything?") 197 | if answer == "42": 198 | print ("You got it!") 199 | break 200 | 201 | print ("Medidate, and try again") 202 | 203 | print ("Congratulations! You have achieved nirvana!") 204 | ``` 205 | 206 | ### continue keyword 207 | - `continue` keyword allows you to go back to the starting of the loop body without executing the rest of it. 208 | 209 | Here is an example: 210 | 211 | ```python 212 | while True: 213 | number = int(input("Enter a number:")) 214 | if number % 2 == 1: 215 | print ("You are odd! We don't serve your type here.") 216 | continue 217 | 218 | print ("Your half is %s", int(number / 2)) 219 | ``` 220 | 221 | ### while-else clause 222 | - Python supports the use of `else` clause with while statement, similar to how it's used with `if` statement. 223 | - The code inside the `else` block is executed once, whenever the condition in the `while` statement becomes `False`. If the condition is False to begin with, only the code in `else` block will be executed. 224 | - If you break out of a loop, then the `else` code block is _not_ executed. 225 | - Using `continue` doesn't effect the behaviour of while-else. 226 | 227 | Here are some examples to show how this works: 228 | ```python 229 | i = 0 230 | while i < 2: 231 | print (i) 232 | i += 1 233 | else: 234 | print ("We are done looping.") 235 | 236 | Output: 237 | 0 238 | 1 239 | We are done looping. 240 | ``` 241 | 242 | ```python 243 | i = 1 244 | while i < 1: 245 | print (i) 246 | i += 1 247 | else: 248 | print ("We never started looping!") 249 | 250 | Output: 251 | We never started looping! 252 | ``` 253 | 254 | ```python 255 | i = 0 256 | while i < 5: 257 | if i % 2 == 1: 258 | break 259 | print (i) 260 | i += 1 261 | else: 262 | print ("break stole away the attention") 263 | 264 | Output: 265 | 0 266 | ``` 267 | 268 | # Empty values in boolean expressions 269 | - In Python3, certain values of variables (or expressions or constants) can represent `True` or `False`, even if the variables are not of type `bool`. 270 | - This feature is there to make code writing a bit easier. 271 | - In the following cases, the value is evaluated to `False`: 272 | - an `int` value is 0 273 | - a `float` value is 0.0 274 | - a `string` value is "" (empty string) 275 | - There are more cases as well (for types we will learn about later)- essentially empty value for a built-in type will always evaluate to `False`. 276 | - If the value doesn't evaluate to `False`, it evaluates to `True`. 277 | 278 | ```python 279 | money = 0 280 | if money: 281 | print ("One Modelo for me") 282 | else: 283 | print ("No money for beer!") 284 | 285 | Output: 286 | No money for beer! 287 | ``` 288 | ```python 289 | numerator = 5.0 290 | denominator = 2.0 291 | 292 | if denominator: 293 | print (numerator / denominator) 294 | else: 295 | print ("Can't divide by 0") 296 | 297 | Output: 298 | 2.5 299 | ``` 300 | 301 | ## Practice exercises 302 | - Update the lock screen example, where you give the user 3 tries, and write "Success!" if the user succeeds within 3 tries, or write "Locked for one hour" if the user fails all 3 tries. 303 | - Calculate this expression upto 10 terms: 1 + 1/2 + 1/4 + 1/8 + 1/16 + 1/32 + ... 304 | -------------------------------------------------------------------------------- /lecture3/google-france.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture3/google-france.png -------------------------------------------------------------------------------- /lecture3/if-else_diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture3/if-else_diagram.jpg -------------------------------------------------------------------------------- /lecture3/if_diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture3/if_diagram.jpg -------------------------------------------------------------------------------- /lecture3/loop_diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture3/loop_diagram.jpg -------------------------------------------------------------------------------- /lecture3/phone_unlock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture3/phone_unlock.jpg -------------------------------------------------------------------------------- /lecture3/tetris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture3/tetris.jpg -------------------------------------------------------------------------------- /lecture4/Readme.md: -------------------------------------------------------------------------------- 1 | # Functions and recursion 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture4/functions.md) introduces the concept of functions in Python. Other than Python specific details on functions, this lecture also focuses on *Recursion*, which is a very powerful programming technique. Half the assignments in the set below are designed with recursion based solutions in mind. 4 | 5 | There is also a [quiz](https://github.com/amangup/coding-bootcamp/blob/master/lecture4/quiz.md) which you can attempt. This quiz is designed to test your understanding of concepts learned in lectures 2-4. 6 | 7 | The assignments for this lecture are available at [this link](https://repl.it/classroom/invite/QU4L6OS). You will need to sign up for [repl.it](https://repl.it) (don't check "I'm a teacher" when you sign up) before you click on the link above to join the "classroom" where you can attempt assignments. 8 | 9 | The assignments, along with solutions, are [also listed here](https://github.com/amangup/coding-bootcamp/blob/master/lecture4/assignments.md). If you're looking at the solution, make sure either you've already solved the problem, or tried hard. 10 | -------------------------------------------------------------------------------- /lecture4/city_streets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture4/city_streets.png -------------------------------------------------------------------------------- /lecture4/function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture4/function.png -------------------------------------------------------------------------------- /lecture4/function.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture4/function.svg.png -------------------------------------------------------------------------------- /lecture4/quiz.md: -------------------------------------------------------------------------------- 1 | # Quiz 2 | A multiple choice quiz with 8 questions where each question has a code listing and you have to choose an answer which corresponds to the output of that code listing. 3 | 4 | **Question 1** 5 | 6 | ```python 7 | x = 10 8 | print (next_number(x)) 9 | 10 | def next_number(i): 11 | return i + 1 12 | ``` 13 | 14 | Options: 15 | a: 11 16 | b: 10 17 | c. Python raises an error 18 | d. None of the above 19 | 20 | **Question 2** 21 | ```python 22 | def find_sum(n): 23 | i = 1 24 | while i <= n: 25 | summation += i 26 | i += 1 27 | 28 | summation = 0 29 | find_sum(5) 30 | print (summation) 31 | ``` 32 | 33 | Options: 34 | a: 10 35 | b: 15 36 | c: 0 37 | d: Python raises an error 38 | 39 | **Question 3** 40 | ```python 41 | def power(): 42 | return n ** n 43 | 44 | n = 1 45 | power_sum = 0 46 | while n <= 3: 47 | power_sum += power() 48 | n += 1 49 | 50 | print (power_sum) 51 | ``` 52 | 53 | Options: 54 | a: 5 55 | b: 32 56 | c: 0 57 | d: Python raises an error 58 | 59 | **Question 4** 60 | ```python 61 | def replace_char(string, to_find, replacement): 62 | i = 0 63 | while i < len(string): 64 | if string[i] == to_find: 65 | string[i] = replacement 66 | 67 | i += 1 68 | 69 | word = "zoom" 70 | replace_char(word, 'z', 'b') 71 | print(word) 72 | ``` 73 | 74 | Options: 75 | a: zoom 76 | b: boom 77 | c: None 78 | d: Python raises an error 79 | 80 | **Question 5** 81 | ```python 82 | def get_sum(n): 83 | result = 0 84 | while n > 0: 85 | result += n 86 | n -= 1 87 | return result 88 | 89 | n = 5 90 | print (get_sum(n), get_sum(n + 1)) 91 | ``` 92 | 93 | Options: 94 | a: 15 1 95 | b: 10 1 96 | c: 0 0 97 | d: 15 21 98 | 99 | **Question 6** 100 | ```python 101 | def get_sum_step(max, step): 102 | result = 0 103 | while max != 0: 104 | result += max 105 | max -= step 106 | 107 | return result 108 | 109 | print (get_sum_step(10, 2)) 110 | print (get_sum_step(10, 3)) 111 | ``` 112 | 113 | Options: 114 | a: 115 | 30 116 | 22 117 | 118 | b: 119 | 20 120 | 18 121 | 122 | c: 123 | 0 124 | 0 125 | 126 | d: None of the above 127 | 128 | **Question 7** 129 | ```python 130 | def get_num_words(string): 131 | last_word = '' 132 | num_words = 0 133 | i = 0 134 | while i < len(string): 135 | if string[i] == ' ': 136 | if last_word: 137 | num_words += 1 138 | last_word = '' 139 | continue 140 | 141 | last_word += string[i] 142 | i += 1 143 | 144 | return num_words 145 | 146 | print (get_num_words("a set of words that is complete in itself")) 147 | ``` 148 | 149 | Options: 150 | a: 8 151 | b: 9 152 | c: 0 153 | d: None of the above 154 | 155 | **Question 8** 156 | ```python 157 | # No leap year, m in [1, 12] 158 | def days_in_month(m): 159 | if (m < 8 and m % 2 == 1) or (m > 8 and m % 2 == 0): 160 | return 31 161 | elif (m < 8 and m % 2 == 0) or (m > 8 and m % 2 == 1): 162 | if m == 2: 163 | return 28 164 | else: 165 | return 30 166 | 167 | i = 1 168 | while i <= 12: 169 | print (i, days_in_month(i)) 170 | 171 | i += 1 172 | ``` 173 | 174 | Which month's output is incorrect? Options: 175 | a: Every month's output is correct 176 | b: m = 2 177 | c: m = 7 178 | d: m = 8 179 | 180 | **Question 9** 181 | ```python 182 | def sum(n, total=100): 183 | if n == 0 or n == 1: 184 | return total + n 185 | else: 186 | return sum(n - 1, total + n) 187 | 188 | print(sum(5)) 189 | ``` 190 | 191 | Options: 192 | a: 15 193 | b: 14 194 | c: 115 195 | d: 114 196 | 197 | -------------------------------------------------------------------------------- /lecture4/sphinx_doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture4/sphinx_doc.png -------------------------------------------------------------------------------- /lecture4/stats.html: -------------------------------------------------------------------------------- 1 | 2 | Python: module stats 3 | 4 | 5 | 6 | 7 | 8 |
 
9 |  
stats
index
/home/aman/aman_code/coding-bootcamp/lecture4/stats.py
12 |

This module contains functions to calculate statistics on data

13 |

14 | 15 | 16 | 18 | 19 | 20 |
 
17 | Functions
       
find_stat(x, y, stat_type)
Finds a statistic value for the two values given as input.
21 |  
22 | Args:
23 |     x: first value
24 |     y: second value
25 |     stat_type: One of "average", "maximum", or "minimum"
26 |  
27 | Returns:
28 |       the outcome of running the chosen statistic on x and y
29 |
30 | -------------------------------------------------------------------------------- /lecture5/Readme.md: -------------------------------------------------------------------------------- 1 | # Collection Types and For Loop 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture5/collections_forloop.md) introduces the list, tuple and dictionary types in Python which can store collections of data. It also introduces the `for` statement, which is a much more convenient way to create loops when compared to the `while` statement. 4 | 5 | The assignments for this lecture are available at [this link](https://repl.it/classroom/invite/US0qbyU). You will need to sign up for [repl.it](https://repl.it) (don't check "I'm a teacher" when you sign up) before you click on the link above to join the "classroom" where you can attempt assignments. 6 | 7 | As an application of the concepts learnt in this lecture, there is an additional tutorial which takes some real life data (about the California public school system) and tries to answer some questions about the schools in CA. That tutorial can be [seen here](https://github.com/amangup/coding-bootcamp/blob/master/lecture5/data_analysis.md). It also have a bunch of exercises. 8 | -------------------------------------------------------------------------------- /lecture6/Algorithms.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture6/Algorithms.pdf -------------------------------------------------------------------------------- /lecture6/Readme.md: -------------------------------------------------------------------------------- 1 | ## Algorithms 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture6/Algorithms.md) introduces the concept of an algorithm. Through the example of a sorting algorithm, it explains how to measure how good an algorithm is (based on running time). It also discusses the binary search algorithm. 4 | 5 | This is a set of slides, and pdf is uploaded here. The google slides link to the presentation is below: 6 | 7 | [Algorithms](https://docs.google.com/presentation/d/1-JKiV7gDISxXXJly5ihYFlAwp3RW312fhYpLg1CIRSs/edit?usp=sharing) 8 | 9 | The assignments for this lecture are available at [this link](https://repl.it/classroom/invite/VIphgYU). You will need to sign up for [repl.it](https://repl.it) (don't check "I'm a teacher" when you sign up) before you click on the link above to join the "classroom" where you can attempt assignments. 10 | 11 | -------------------------------------------------------------------------------- /lecture7/Readme.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture7/modules.md) introduces the concept of modules and shows how to use some modules from the Python Standard Library and third party modules. 4 | 5 | The assignments are [listed here](https://github.com/amangup/coding-bootcamp/blob/master/lecture7/assignments.md). This set of assignments is quite advanced, so take your time on each one. -------------------------------------------------------------------------------- /lecture7/assignments.md: -------------------------------------------------------------------------------- 1 | # Assignments 2 | 3 | This is the set of assignments for the lecture on modules. You can implement the solutions on your local machine in an IDE (like PyCharm). For some cases, there are no exact test cases to check correctness; in those cases some strategy to check correctness will be described. 4 | 5 | ## Problem 1 6 | Monte Carlo algorithms are a set of algorithms that use some form of randomization to calculate the answer to the question. These algorithms don't always find the best possible solution, but with large sample set, it's result can be quite close to the real answer. 7 | 8 | In this problem, we are going to use a monte carlo method to estimate the value of mathematical constant `pi`. Remember that the area of a circle with radius `r` is `pi * r * r`. 9 | 10 | ![Diagram](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture7/pi.gif) 11 | 12 | The above diagram shows a circle drawn such that it's radius is 1.0 units. There is also a square in that diagram, whose side is 2.0 units. For the circle in the diagram, the area is `pi * 1.0 * 1.0 = pi`. For the square in the diagram, the area is `2.0 * 2.0 = 4.0`. 13 | 14 | Let's say you generate one random point whose x coordinate lies in `(-1.0, 1.0)`, and the y coordinate also lies in `(-1.0, 1.0)`. This random point will always be inside the square. Some of those points will also be inside the circle. How do we figure out if the point is inside the circle? We can use the fact that the distance of the point `(x, y)` from the center `(0, 0)` is `x^2 + y^2`. Considering the radius of our circle is `1.0`, the point is inside the circle if `x^2 + y^2 < 1.0`. 15 | 16 | Write a function `monte_carlo_pi(n)` which generates n random points as described above, where `n` is a reasonably large number. All points are inside the square, so the number of those points can be assumed to be the area of the square (`= 4.0`). Many of the points will be inside the circle, the number of such points can be assumed to be the area of the circle (`= pi`). Thus, the ratio of points inside the circle to the total number of points should equal `pi / 4.0`. Use this algorithm to make the function return the estimated value of `pi`. 17 | 18 | e.g. 19 | 20 | ```python 21 | print(monte_carlo_pi(1000000)) 22 | 3.143512 23 | ``` 24 | 25 | You should try different values of the argument `n` in this function, and notice how accuracy of the value of `pi` increases as `n` increases. 26 | 27 | ## Problem 2 28 | When we discussed sorting algorithms, we learnt that insertion sort algorithm has `O(n^2)` time complexity whereas the best algorithms (as Python's internal sorting algorithm) has the time complexity of `O(n * log n)`. In this exercise, we are going to test if that makes a difference in actual running time. 29 | 30 | We are going to compare the running time of `insertion_sort()` as written in lecture 6, and Python's internal sorting algorithm. 31 | 32 | - To calculate the running time of an algorithm, use the function `process_time()` in the module `time`. 33 | - To be able to note the difference between running times, we need a large list to sort. Use random number generation (and generate numbers between 0 and 100) to create a list which is arbitrarily long. 34 | 35 | Write a function `compare_sort_times(n)`, where n is the size of the list which you generate, and print the time it takes to sort using the insertion sort algorithm and the time it takes using Python's internal sorting algorithm. 36 | 37 | e.g. 38 | 39 | ```python 40 | compare_sort_times(50000) 41 | ``` 42 | Output is: 43 | 44 | ``` 45 | Python's sort time in milliseconds: 15.625 46 | Insertion sort time in milliseconds: 33750.0 47 | ``` 48 | 49 | ## Problem 3 50 | A watch dial maker uses an automatic laser engraving machine to mark the hour markers on the dial. The machine assumes that the dial is present on the Cartesian plane, with its center at coordinates (0, 0). It needs to know the exact coordinates of the twelve hour marking points to do the engraving. 51 | 52 | The dial maker makes different dials with different radii. Write a function `hour_marker_coords(distance)`, where `distance` is the distance between the center and the hour marker, that returns the list of coordinates (as tuples) for the tweleve hour markers, starting from the 12 o clock marker and working clockwise from there. 53 | 54 | ![Diagram](https://raw.githubusercontent.com/amangup/coding-bootcamp/master/lecture7/sides_of_right_triangle.gif) 55 | 56 | The diagram above shows how you can calculate the coordinates given the radius and angle. 57 | 58 | You will need to use the `math` module to solve this problem. 59 | 60 | e.g. 61 | 62 | ```python 63 | print (hour_marker_coords(2.0)) 64 | [(0.0, 2.0), (0.9999999999999999, 1.7320508075688774), (1.7320508075688772, 1.0000000000000002), (2.0, 1.2246467991473532e-16), (1.7320508075688774, -0.9999999999999996), (0.9999999999999999, -1.7320508075688774), (2.4492935982947064e-16, -2.0), (-0.9999999999999994, -1.7320508075688776), (-1.732050807568877, -1.0000000000000009), (-2.0, -3.6739403974420594e-16), (-1.7320508075688772, 1.0000000000000002), (-1.0000000000000009, 1.7320508075688767)] 65 | ``` 66 | 67 | Note that due to floating point representation's inaccuracy, numbers like `1.0` can sometimes be represented as `0.9999999999999999`. 68 | 69 | ## Problem 4 70 | You are writing a scheduling assistant which automatically schedules a meeting at the next available slot. You are given the length of meeting to schedule, and a list of already reserved time slots, and you are to return the time at which the assistant schedules the meeting. 71 | 72 | The assistant should select the first available time slot, such that: 73 | - earliest scheduled time is _tomorrow_, in the morning at 9 am, 74 | - the time slot doesn't overlap with any already reserved time slot 75 | 76 | Write a function `schedule_meeting(length_in_mins, reserved_slots)`, where `reserved_slots` is a list of tuples containing the start time of that slot in the format like `2018-05-30 13:30`, and its length in minutes. 77 | 78 | It's obvious that you will need to use the `datetime` module for this problem. Take your time on this one as this will probably take more effort than you might think. 79 | 80 | Note that the following examples were run on the date `2018-06-01`. 81 | 82 | e.g. 83 | 84 | ```python 85 | print(schedule_meeting(120, [('2018-06-02 08:00', 120), 86 | ('2018-06-02 11:00', 60)])) 87 | ``` 88 | 89 | Output is: 90 | ``` 91 | 2018-06-02 12:00 92 | ``` 93 | 94 | e.g. 2 95 | 96 | ```python 97 | print(schedule_meeting(60, [('2018-06-02 09:00', 30), 98 | ('2018-06-02 11:00', 60)])) 99 | ``` 100 | 101 | Output is: 102 | ``` 103 | 2018-06-02 09:30 104 | ``` 105 | 106 | 107 | ## Problem 5 108 | You have to implement a file garbage collection mechanism. There are some directories which are used as temporary directories inside which the operating system stores files which are not important for future. This mechanism keeps a check on the number of files in such a directory. If the number of files increase beyond a threshold, it deletes the oldest files so that the number of files remains in control. 109 | 110 | Write a function `garbage_collection(directory, k)` that takes as input the path of a directory, and the number `k` which is the maximum number of files to keep. You have to make sure that you only keep the latest `k` files _undeleted_. Use an infinite `while` loop to make sure program never exits, and after every hour, check for the current status of files in a directory, and keep only the latest `k` files. 111 | 112 | - You need to use the `glob` module to get the list of files, and `os` module to get the last modified timestamp (using `os.path.getmtime()` function) and `os.remove()` function to delete any file. 113 | - Use the `sleep()` function in the `time` module to wait for one hour between runs. 114 | 115 | **To Test:** 116 | Change the interval to one minute, and create files manually using a text editor. As you create more files, the older files should be deleted by your program. 117 | 118 | -------------------------------------------------------------------------------- /lecture7/modules.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | - Modules are a way to split large amounts of code into multiple files. 4 | - It is a good practice to break large amounts of code into _logical modules_. Python language helps you in doing that by defining syntax to use code written in other files. 5 | 6 | ## Importing and using modules 7 | 8 | Let's say there was some code which was reused multiple times in different parts of your project. As an example, we write some utility function for working with dictionaries. We call this file `dict_utils.py` 9 | 10 | ```python 11 | sample_dict = {1:'a', 2:'b', 3:'c'} 12 | 13 | def get_keys_sorted_by_value(adict): 14 | return sorted(adict, key=adict.get) 15 | 16 | def get_sorted_values(adict): 17 | return sorted(adict.values()) 18 | 19 | def get_inverse_dict(adict): 20 | inverse = {} 21 | for key, value in adict.items(): 22 | inverse[value] = key 23 | 24 | return inverse 25 | 26 | def create_dict(keys, values): 27 | adict = {} 28 | for key, value in zip(keys, values): 29 | adict[key] = value 30 | return adict 31 | ``` 32 | 33 | Let's write another file (`dict_utils_test.py`) which contains this code: 34 | 35 | ```python 36 | import dict_utils 37 | 38 | print(dict_utils.sample_dict) 39 | x = dict_utils.sample_dict 40 | print(dict_utils.get_inverse_dict(x)) 41 | ``` 42 | 43 | This outputs the following: 44 | 45 | ``` 46 | {1: 'a', 2: 'b', 3: 'c'} 47 | {'a': 1, 'b': 2, 'c': 3} 48 | ``` 49 | 50 | There are two new syntax elements being used here: 51 | 1. We use the `import` keyword to refer to another module. This lets python _import_ the variables and functions defined inside that module 52 | 2. You use the `.` notation to access any variable, function (or class) in that module. 53 | 54 | ### Module level code 55 | 56 | Sometimes, there is code at the _root_ level of a module, that is not inside functions. For example, let's add the following line at the end of dict_utils.py 57 | 58 | ```python 59 | print ("This is the dict_utils module. Use it for all your dictionary needs.") 60 | ``` 61 | 62 | Let's say we run the `dict_utils_test.py` file. This is what we would see as the output: 63 | 64 | ``` 65 | This is the dict_utils module. Use it for all your dictionary needs. 66 | {1: 'a', 2: 'b', 3: 'c'} 67 | {'a': 1, 'b': 2, 'c': 3} 68 | ``` 69 | 70 | - As you can see, the print statement gets executed. 71 | - This happens because when python executes the `import dict_utils` statement, it runs all the code inside the module. 72 | - All the variables and functions are just definitions, and cause no impact unless you use them (e.g., we defined the `get_sorted_values` function but since we didn't use it, it sits there silently). 73 | - Any code which is defined at the root level will get executed. 74 | 75 | ### Module name 76 | - Python sets a variable called `__name__` which programmers can use to identify which module they are in. 77 | 78 | For example, let's modify the print statement in dict_utils.py to this: 79 | 80 | ```python 81 | print ("This is the module with name %s. Use it for all your dictionary needs" % __name__) 82 | ``` 83 | 84 | We get the folliwing output. 85 | 86 | ``` 87 | This is the module with name dict_utils. Use it for all your dictionary needs 88 | ``` 89 | 90 | - Thus, when we import the module, the variable `__name__` inside _that_ module is set to be equal to the name of that module. This can be useful (to name loggers, to identify the main module, etc.) 91 | 92 | ### The main module 93 | - Whenever you run a python program, you are always running one specific python file. If you are using the command line, you would be using a command like `python3 my_program.py` to run your program. 94 | - The `__name__` variable is set to `__main__` in that case (irrespective of the name of your file). 95 | - This is useful if your program has code which other files can use, but you want it to do something on it's own as well. 96 | 97 | As an example, let's modify the print statement in `dict_utils.py` one more time. 98 | 99 | ```python 100 | if __name__ == '__main__': 101 | print ("Variable __name__ is %s" % __name__) 102 | print ("This is the dict_utils module. Use it for all your dictionary needs") 103 | ``` 104 | 105 | If we run our `dict_utils_test.py` file again, we get the following output: 106 | 107 | ``` 108 | {1: 'a', 2: 'b', 3: 'c'} 109 | {'a': 1, 'b': 2, 'c': 3} 110 | ``` 111 | 112 | - Note that we don't see the output the print statements. As we saw in the previous section, 113 | when we import a module, it's `__name__` is set to the module name. Thus, the if statement evaluates to false and the print statements don't run. 114 | 115 | What if ran the `dict_utils.py` itself? We get the following output: 116 | 117 | ``` 118 | Variable __name__ is __main__ 119 | This is the dict_utils module. Use it for all your dictionary needs 120 | ``` 121 | 122 | - We see that the variable `__name__` is set to `__main__`. 123 | - Because of this, the if statement becomes true, and the print statements get executed. 124 | 125 | ### Standard practice for main 126 | - Whenever you write any program, it is possible that someone might want to use that program as a module in the future. 127 | - Thus, it is common practice to write your code so that _root level_ code is inside the if check which ascertains if the variable `__name__` is `__main__`. 128 | - If you see any python program from an established programmer, this is how they write their _main_ file: 129 | 130 | 131 | ```python 132 | def main(): 133 | # Do your work here 134 | pass 135 | 136 | 137 | if __name__ == '__main__': 138 | main() 139 | ``` 140 | 141 | ## Modules in Python standard library 142 | - Python has a very large number of modules built into its **standard library**. 143 | - An even larger number of modules are available which written by other programmers and shared for you to use for free. 144 | 145 | In the following sections, I am going to highlight a few popular built-in modules and show how to use _third party_ modules. 146 | 147 | ### datetime module 148 | 149 | - The module datetime defines many types inside it, like `date`, `time`, `datetime`. 150 | 151 | Here is a tiny sample of its capabilities. 152 | 153 | ```python 154 | > import datetime 155 | 156 | # In computer science, timestamp is defined as the number of seconds elapsed since 157 | # the beginning of the day of January 1st, 1970. This is the standard value to use 158 | # as time in programming. 159 | > print(datetime.datetime.now().timestamp()) 160 | 1527668953.615421 161 | 162 | # Print current date. Python has a standard notation for representing time and date 163 | > print(datetime.datetime.now().strftime('%Y-%m-%d')) 164 | 2018-05-30 165 | 166 | # Print current date and time in New York City 167 | > import pytz 168 | > timezone = pytz.timezone('America/New_York') 169 | > print(datetime.datetime.now(timezone).strftime('%Y-%m-%d, %H:%M:%S')) 170 | 2018-05-30, 04:38:43 171 | 172 | # Convert current time to timestamp 173 | > parsed_time = datetime.datetime.strptime("2018-05-30-4", "%Y-%m-%d-%H") 174 | > print(parsed_time.timestamp()) 175 | 1527652800.0 176 | ``` 177 | 178 | - The full documentation is [available here](https://docs.python.org/3/library/datetime.html). 179 | 180 | ### math module 181 | - This is another super useful module, defining a number of mathematical functions. 182 | 183 | ```python 184 | # Get the gcd of two integers 185 | > import math 186 | > print(math.gcd(32, 48)) 187 | 16 188 | 189 | > print(math.floor(-3.45)) 190 | -4 191 | 192 | > print(math.log2(65536)) 193 | 16.0 194 | 195 | > print(math.pi) 196 | 3.141592653589793 197 | ``` 198 | 199 | - The full documentation for the math module is [available here](https://docs.python.org/3/library/math.html). 200 | 201 | ### random module 202 | - `random` module is useful in certain cases. For example, 203 | - If we want to test a new UI, we may want to show the new UI only on a random selection of 1% users. 204 | - There are many algorithms (called **Randomized Algorithms**) which sometimes perform better than any non-randomized equivalent (Quick sort is a popular example). 205 | 206 | Here is some sample usage: 207 | 208 | ```python 209 | > import random 210 | # For getting a random integer 211 | > print(random.randint(5,10)) 212 | 7 213 | > print(random.randint(5,10)) 214 | 9 215 | 216 | # For getting a random float in the range [0.0, 1.0) 217 | > print(random.random()) 218 | 0.304776972595302 219 | 220 | # Print True with 25% probability, False otherwise 221 | # If you run this many times, you will see number of Trues and False 222 | # according to the probability (more or less) 223 | > print(True if random.random() <= 0.25 else False) 224 | False 225 | 226 | # It even supports some distributions other than uniform 227 | > print (random.gauss(mu=0.5, sigma=0.25)) 228 | 0.6755629832260551 229 | ``` 230 | 231 | ### os module 232 | - `os` module is used to execute code and perform actions that you would normally do in the command line, for example getting a list of files, getting the creation timestamp of a file, etc. 233 | - Most real projects use this module for some use case. 234 | 235 | Here is an example of using the `os` module's path submodule, and its function 236 | `os.path.getctime()` to get the creation time of files in a directory. 237 | 238 | ```python 239 | # Here is some code to list all files in descending order of their creation 240 | # timestamp 241 | import glob # This is a module frequently used if you are handling files. 242 | import os 243 | dir_location = "/home/aman/aman_code/coding-bootcamp/*" 244 | list_of_files = glob.glob(dir_location) 245 | print(sorted(list_of_files, key=os.path.getctime, reverse=True)) 246 | ``` 247 | 248 | Output is: 249 | ``` 250 | ['/home/aman/aman_code/coding-bootcamp/lecture7', '/home/aman/aman_code/coding-bootcamp/lecture6', '/home/aman/aman_code/coding-bootcamp/lecture5', '/home/aman/aman_code/coding-bootcamp/README.md', '/home/aman/aman_code/coding-bootcamp/file~', '/home/aman/aman_code/coding-bootcamp/lecture2', '/home/aman/aman_code/coding-bootcamp/lecture4', '/home/aman/aman_code/coding-bootcamp/lecture3', '/home/aman/aman_code/coding-bootcamp/lecture1', '/home/aman/aman_code/coding-bootcamp/Syllabus for Python Coding Bootcamp.pdf'] 251 | ``` 252 | 253 | ### Using third party modules 254 | - Third party modules are libraries written by community members, who (usually) share their module for free. 255 | - They are not already part of your installation, but you can install them using the pip3 utility. 256 | 257 | PyCharm is useful help you install modules. We have to structure our project in a certain way, and then PyCharm asks you by itself to install modules which are not already installed on your computer. 258 | 259 | For a small project, here is a very simple project structure to have: 260 | ``` 261 | Readme.rst 262 | requirements.txt 263 | my_project/my_main.py 264 | my_project/my_utils.py 265 | ... 266 | ``` 267 | 268 | Note the file `requirements.txt`. It's sole purpose is to list out all third party modules which are required for this project to run. Let's create a requirements.txt with only one third party module: 269 | 270 | ``` 271 | tinydb==3.9.0 272 | ``` 273 | 274 | This module uses a file to create a _tiny_ database of data. Let's create a project folder called `test_tinydb` and create a file called `tinydb_sample.py`, which contains: 275 | 276 | ```python 277 | import os 278 | import tinydb 279 | 280 | db_filename = 'test_db.json' 281 | 282 | # First let's delete the file we will use for db 283 | if (os.path.exists(db_filename)): 284 | os.remove(db_filename) 285 | 286 | db = tinydb.TinyDB(db_filename) 287 | db.insert({'name': 'Luke', 'age': 19}) 288 | db.insert({'name': 'Anakin', 'age': 42}) 289 | 290 | Jedi = tinydb.Query() 291 | print(db.search(Jedi.name == 'Luke')) 292 | ``` 293 | 294 | PyCharm should highlight that this module is not installed, asking you to install it. You can also install this from the command line using the following command: 295 | 296 | ``` 297 | pip3 install -r requirements.txt 298 | ``` 299 | 300 | Once the package is installed, the above program should work and print the record associated with Luke. 301 | -------------------------------------------------------------------------------- /lecture7/pi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture7/pi.gif -------------------------------------------------------------------------------- /lecture7/sides_of_right_triangle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture7/sides_of_right_triangle.gif -------------------------------------------------------------------------------- /lecture8/Readme.md: -------------------------------------------------------------------------------- 1 | # Classes 2 | 3 | [This lecture](https://github.com/amangup/coding-bootcamp/blob/master/lecture8/classes.md) introduces the concept of Object Oriented Programming and shows how to write object oriented code using something called a **Class** in Python. 4 | 5 | The assignments for this lecture are available at [this link](https://repl.it/classroom/invite/V2apwEt). You will need to sign up for [repl.it](https://repl.it) (don't check "I'm a teacher" when you sign up) before you click on the link above to join the "classroom" where you can attempt assignments. 6 | -------------------------------------------------------------------------------- /lecture8/rental_service.py: -------------------------------------------------------------------------------- 1 | class Car: 2 | def __init__(self, make, model, model_year, size, license_plate): 3 | self.make = make 4 | self.model = model 5 | self.model_year = model_year 6 | self.size = size 7 | self.license_plate = license_plate 8 | self.mileage = 0 9 | 10 | def add_mileage(self, miles): 11 | self.mileage += miles 12 | 13 | def __repr__(self): 14 | return "%s %s %s" % (self.model_year, self.make, self.model) 15 | 16 | class RentalService: 17 | def __init__(self, max_mileage): 18 | self.fleet = {} 19 | self.available = {} 20 | self.max_mileage = max_mileage 21 | 22 | def add_car_to_fleet(self, car): 23 | self.fleet[car.license_plate] = car 24 | self.available[car.license_plate] = True 25 | 26 | def get_available_cars(self, size): 27 | available_cars = [] 28 | for license_plate, car in self.fleet.items(): 29 | if (car.size == size and car.mileage <= self.max_mileage and 30 | self.available[license_plate]): 31 | available_cars.append(car) 32 | 33 | return available_cars 34 | 35 | def rent_car(self, license_plate): 36 | self.available[license_plate] = False 37 | 38 | def return_car(self, license_plate, miles_driven): 39 | self.available[license_plate] = True 40 | self.fleet[license_plate].mileage += miles_driven 41 | 42 | def get_cars_for_sale(self): 43 | cars_for_sale = [] 44 | for _, car in self.fleet.items(): 45 | if car.mileage > self.max_mileage: 46 | cars_for_sale.append(car) 47 | 48 | return cars_for_sale 49 | 50 | hurtz = RentalService(250) 51 | hurtz.add_car_to_fleet(Car("Ford", "Focus", 2017, "Compact", "YAB123")) 52 | hurtz.add_car_to_fleet(Car("Hyundai", "Sonata", 2018, "Mid-size", "ABC123")) 53 | hurtz.add_car_to_fleet(Car("Mazda", "6", 2018, "Mid-size", "Z0MZ0M")) 54 | 55 | print(hurtz.get_available_cars("Mid-size")) 56 | 57 | hurtz.rent_car("Z0MZ0M") 58 | 59 | print(hurtz.get_available_cars("Mid-size")) 60 | 61 | hurtz.return_car("Z0MZ0M", 300) 62 | 63 | print(hurtz.get_available_cars("Mid-size")) 64 | print(hurtz.get_cars_for_sale()) 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /lecture8/watch_face.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture8/watch_face.jpg -------------------------------------------------------------------------------- /lecture8/watch_movement.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture8/watch_movement.jpg -------------------------------------------------------------------------------- /lecture9/Readme.md: -------------------------------------------------------------------------------- 1 | ## Internet and the World Wide Web 2 | 3 | This lecture explains how the internet works. It sets you up for starting to write a code to build a custom web application. 4 | 5 | This is a set of slides, and pdf is uploaded here. The google slides link to the presentation is below: 6 | 7 | [Internet and WWW](https://docs.google.com/presentation/d/1nxNT0GajEEalEbrUC5vs84Jh9YUwtatormojFPGIbv8/edit?usp=sharing) 8 | 9 | There are no assignments for this lecture. -------------------------------------------------------------------------------- /lecture9/internet_and_www.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amangup/coding-bootcamp/a68b4f15dd19b5328ab88fc4ba50df66195b5f5e/lecture9/internet_and_www.pdf --------------------------------------------------------------------------------