title
CS50 2021 in HDR - Lecture 9 - Flask

description
This is CS50, Harvard University's Introduction to the intellectual enterprises of computer science and the art of programming. Enroll for free at https://cs50.edx.org/. Slides, source code, and more at https://cs50.harvard.edu/x. Playlist at https://www.youtube.com/playlist?list=PLhQjrBD2T383f9scHRNYJkior2VvYjpSL. TABLE OF CONTENTS 00:00:00 - Introduction 00:01:17 - Web Programming 00:02:29 - http-server 00:03:36 - Paths and Routes 00:06:01 - Flask 00:08:47 - app.py 00:20:11 - Forms 00:32:58 - Templates 00:41:10 - GET vs. POST 00:46:50 - Model, View, Controller 00:52:37 - froshims 00:58:49 - Form Validation 01:08:05 - CSS and Flask 01:10:29 - Jinja Loops 01:16:08 - Model 01:22:41 - SQLite and FLask 01:34:13 - Email 01:40:46 - Sessions and Cookies 01:47:58 - Login 01:54:24 - Shopping Cart 02:02:50 - Search 02:10:09 - JSON 02:15:18 - This was CS50 *** This is CS50, Harvard University's introduction to the intellectual enterprises of computer science and the art of programming. *** HOW TO SUBSCRIBE http://www.youtube.com/subscription_center?add_user=cs50tv HOW TO TAKE CS50 edX: https://cs50.edx.org/ Harvard Extension School: https://cs50.harvard.edu/extension Harvard Summer School: https://cs50.harvard.edu/summer OpenCourseWare: https://cs50.harvard.edu/x HOW TO JOIN CS50 COMMUNITIES Discord: https://discord.gg/cs50 Ed: https://cs50.harvard.edu/x/ed Facebook Group: https://www.facebook.com/groups/cs50/ Faceboook Page: https://www.facebook.com/cs50/ GitHub: https://github.com/cs50 Gitter: https://gitter.im/cs50/x Instagram: https://instagram.com/cs50 LinkedIn Group: https://www.linkedin.com/groups/7437240/ LinkedIn Page: https://www.linkedin.com/school/cs50/ Medium: https://cs50.medium.com/ Quora: https://www.quora.com/topic/CS50 Reddit: https://www.reddit.com/r/cs50/ Slack: https://cs50.edx.org/slack Snapchat: https://www.snapchat.com/add/cs50 SoundCloud: https://soundcloud.com/cs50 Stack Exchange: https://cs50.stackexchange.com/ TikTok: https://www.tiktok.com/@cs50 Twitter: https://twitter.com/cs50 YouTube: http://www.youtube.com/cs50 HOW TO FOLLOW DAVID J. MALAN Facebook: https://www.facebook.com/dmalan GitHub: https://github.com/dmalan Instagram: https://www.instagram.com/davidjmalan/ LinkedIn: https://www.linkedin.com/in/malan/ Quora: https://www.quora.com/profile/David-J-Malan TikTok: https://www.tiktok.com/@davidjmalan Twitter: https://twitter.com/davidjmalan *** CS50 SHOP https://cs50.harvardshop.com/ *** LICENSE CC BY-NC-SA 4.0 Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License https://creativecommons.org/licenses/by-nc-sa/4.0/ David J. Malan https://cs.harvard.edu/malan malan@harvard.edu

detail
{'title': 'CS50 2021 in HDR - Lecture 9 - Flask', 'heatmap': [{'end': 489.563, 'start': 403.974, 'weight': 0.854}, {'end': 819.846, 'start': 569.009, 'weight': 0.947}, {'end': 980.856, 'start': 897.619, 'weight': 0.81}, {'end': 1387.991, 'start': 1305.302, 'weight': 0.778}, {'end': 2692.752, 'start': 2525.939, 'weight': 0.88}, {'end': 3021.015, 'start': 2849.567, 'weight': 0.801}, {'end': 4571.309, 'start': 4242.15, 'weight': 0.75}, {'end': 4821.369, 'start': 4649.889, 'weight': 0.709}, {'end': 5223.092, 'start': 4976.446, 'weight': 0.844}, {'end': 5718.945, 'start': 5622.403, 'weight': 0.71}, {'end': 5957.701, 'start': 5874.124, 'weight': 0.721}, {'end': 6531.066, 'start': 6445.543, 'weight': 0.702}, {'end': 6862.101, 'start': 6607.813, 'weight': 0.756}, {'end': 7020.268, 'start': 6930.211, 'weight': 0.746}], 'summary': 'Covers cs50 week 9, emphasizing programming fundamentals, web development, and tools like html, css, javascript, python, and sql, including creating custom routes, using flask framework, dynamic web page interaction, form handling, web application building, web development essentials, automating email generation, implementing sessions, using python, sqlite, flask, and javascript for dynamic web applications.', 'chapters': [{'end': 264.474, 'segs': [{'end': 96.946, 'src': 'embed', 'start': 10.623, 'weight': 0, 'content': [{'end': 12.132, 'text': 'Thank you.', 'start': 10.623, 'duration': 1.509}, {'end': 78.922, 'text': 'Amen DAVID J.', 'start': 65.525, 'duration': 13.397}, {'end': 82.343, 'text': 'All right, so this is CS50, and this is week 9.', 'start': 78.922, 'duration': 3.421}, {'end': 85.404, 'text': 'And this is kind of it in terms of programming fundamentals.', 'start': 82.343, 'duration': 3.061}, {'end': 90.665, 'text': "Today, we come rather full circle with so many of the languages that we've been looking at over the past several weeks.", 'start': 85.824, 'duration': 4.841}, {'end': 96.946, 'text': "And with HTML and CSS and JavaScript last week, we're going to add back into the mix Python and SQL.", 'start': 90.805, 'duration': 6.141}], 'summary': 'Cs50 week 9 focuses on programming fundamentals, covering html, css, javascript, python, and sql.', 'duration': 86.323, 'max_score': 10.623, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E10623.jpg'}, {'end': 159.945, 'src': 'embed', 'start': 131.613, 'weight': 1, 'content': [{'end': 137.656, 'text': 'on HTML, CSS and JavaScript, coupled with other languages like Python and SQL, on the so-called back end.', 'start': 131.613, 'duration': 6.043}, {'end': 143.979, 'text': "And so today we'll tie all of those together and give you the last of the tools in your toolkit with which to tackle final projects,", 'start': 137.696, 'duration': 6.283}, {'end': 147.941, 'text': 'to go off into the real world ultimately and somehow solve problems with programming.', 'start': 143.979, 'duration': 3.962}, {'end': 153.124, 'text': "But we need an additional tool today, and we've sort of outgrown HTTP Server.", 'start': 148.281, 'duration': 4.843}, {'end': 154.104, 'text': 'This is just an example.', 'start': 153.184, 'duration': 0.92}, {'end': 159.945, 'text': 'program that comes on certain computers that you can install for free happens to be written in a language called javascript.', 'start': 154.224, 'duration': 5.721}], 'summary': 'Training on html, css, javascript, python, sql, and a need for additional tool to handle final projects.', 'duration': 28.332, 'max_score': 131.613, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E131613.jpg'}, {'end': 231.582, 'src': 'embed', 'start': 205.546, 'weight': 2, 'content': [{'end': 209.929, 'text': 'that allows us to not only serve web pages but also process user input.', 'start': 205.546, 'duration': 4.383}, {'end': 216.053, 'text': 'And recall that all that input is going to come ultimately from the URL or more deeply inside of those virtual envelopes.', 'start': 209.969, 'duration': 6.084}, {'end': 221.956, 'text': "So here's the canonical URL we talked about last week for a random website like www.example.com.", 'start': 216.393, 'duration': 5.563}, {'end': 227.459, 'text': "And I've highlighted the slash just to connote the root of the web server, like the default folder,", 'start': 222.216, 'duration': 5.243}, {'end': 231.582, 'text': "where presumably there's a file called index.html or something else in there.", 'start': 227.459, 'duration': 4.123}], 'summary': 'Web server processes user input from canonical url like www.example.com.', 'duration': 26.036, 'max_score': 205.546, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E205546.jpg'}], 'start': 10.623, 'title': 'Web development and programming fundamentals', 'summary': 'Covers cs50 week 9, emphasizing the importance of programming fundamentals, web development, and tools like html, css, javascript, python, and sql. it discusses the limitations of http server and introduces a server in python for processing user input.', 'chapters': [{'end': 131.613, 'start': 10.623, 'title': 'Cs50 week 9: programming fundamentals and web development', 'summary': 'Focuses on the importance of programming fundamentals and web development, emphasizing the significance of languages like python and sql in creating web and mobile applications.', 'duration': 120.99, 'highlights': ['The chapter emphasizes the importance of programming fundamentals and web development, highlighting the significance of languages like Python and SQL in creating web and mobile applications.', 'The world is starting to standardize, at least for the next some number of years, with people increasingly using laptops, desktops, and browsers to access applications.']}, {'end': 188.474, 'start': 131.613, 'title': 'Web development tools and technologies', 'summary': 'Introduces web development tools like html, css, javascript, python, and sql, emphasizing the importance of a versatile toolkit for tackling final projects and real-world problem-solving. it also discusses the limitations of http server in serving static content and its inability to interact with users beyond simple clicks.', 'duration': 56.861, 'highlights': ['The chapter introduces web development tools like HTML, CSS, JavaScript, Python, and SQL, emphasizing the importance of a versatile toolkit for tackling final projects and real-world problem-solving.', 'It discusses the limitations of HTTP Server in serving static content and its inability to interact with users beyond simple clicks.']}, {'end': 264.474, 'start': 188.754, 'title': 'Introduction to web servers and user input processing', 'summary': 'Introduces a new type of server in python that not only serves web pages but also processes user input, highlighting the concept of urls, paths, and routes.', 'duration': 75.72, 'highlights': ['The server introduced in the chapter can process user input and serve web pages, expanding the capabilities of the system.', 'The concept of URLs, paths, and routes is explained, emphasizing the structure of website addresses and the organization of files and folders within the server.', "The term 'route' is presented as a synonym for 'path,' providing a better generic description of the structure of URLs."]}], 'duration': 253.851, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E10623.jpg', 'highlights': ['The chapter emphasizes the importance of programming fundamentals and web development, highlighting the significance of languages like Python and SQL in creating web and mobile applications.', 'The chapter introduces web development tools like HTML, CSS, JavaScript, Python, and SQL, emphasizing the importance of a versatile toolkit for tackling final projects and real-world problem-solving.', 'The server introduced in the chapter can process user input and serve web pages, expanding the capabilities of the system.', 'The concept of URLs, paths, and routes is explained, emphasizing the structure of website addresses and the organization of files and folders within the server.']}, {'end': 811.779, 'segs': [{'end': 290.526, 'src': 'embed', 'start': 264.534, 'weight': 0, 'content': [{'end': 270.356, 'text': "Because it turns out they don't have to map to, that is, refer to a specific folder or a specific file.", 'start': 264.534, 'duration': 5.822}, {'end': 276.819, 'text': 'You can come up with your own routes in a website and just make sure that when the user visits that, you give them a certain web page.', 'start': 270.657, 'duration': 6.162}, {'end': 278.9, 'text': 'If they visit something else, you give them a different web page.', 'start': 276.879, 'duration': 2.021}, {'end': 282.102, 'text': "It doesn't have to map to a very specific file, as we'll soon see.", 'start': 279.181, 'duration': 2.921}, {'end': 288.445, 'text': 'And if you want to get input from the user, just like Google does, like Q equals cats, you can add a question mark.', 'start': 282.642, 'duration': 5.803}, {'end': 290.526, 'text': 'at the end of this route,', 'start': 289.085, 'duration': 1.441}], 'summary': 'Websites can create custom routes and pages, without mapping to specific files or folders.', 'duration': 25.992, 'max_score': 264.534, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E264534.jpg'}, {'end': 489.563, 'src': 'heatmap', 'start': 403.974, 'weight': 0.854, 'content': [{'end': 408.638, 'text': "So, similarly, in the world of Python, you Is there another framework we're going to start using today?", 'start': 403.974, 'duration': 4.664}, {'end': 414.344, 'text': 'And whereas Bootstrap is used for CSS and JavaScript, Flask is going to be used for Python.', 'start': 409.199, 'duration': 5.145}, {'end': 417.087, 'text': 'And it just solves a lot of common problems for us.', 'start': 414.504, 'duration': 2.583}, {'end': 421.651, 'text': "It's going to make it easier for us to analyze the URLs and get at key value pairs.", 'start': 417.147, 'duration': 4.504}, {'end': 427.357, 'text': "It's going to make it easier for us to find files or images that the human wants to see when visiting our website.", 'start': 422.011, 'duration': 5.346}, {'end': 430.558, 'text': "It's even going to make it easier to send emails automatically.", 'start': 427.597, 'duration': 2.961}, {'end': 435.02, 'text': 'Like when someone fills out a form, you can dynamically, using code, send them an email as well.', 'start': 430.638, 'duration': 4.382}, {'end': 440.362, 'text': 'So Flask and with it some related libraries is just going to make stuff like that easier for us.', 'start': 435.1, 'duration': 5.262}, {'end': 447.045, 'text': 'And to do this, all we have to do is adhere to some pretty minimalist requirements of this framework.', 'start': 440.862, 'duration': 6.183}, {'end': 450.546, 'text': "We're going to have to create a file for ourselves called app.py.", 'start': 447.405, 'duration': 3.141}, {'end': 453.247, 'text': 'This is where our web app or application is going to live.', 'start': 450.666, 'duration': 2.581}, {'end': 461.856, 'text': 'If we have any libraries that we want to use, the convention in the Python world is to have a very simple text file called requirements.txt,', 'start': 453.887, 'duration': 7.969}, {'end': 466.501, 'text': 'where you list the names of those libraries top to bottom in that text file,', 'start': 461.856, 'duration': 4.645}, {'end': 471.907, 'text': 'similar in spirit to the include or the import statements that we saw in C and Python respectively.', 'start': 466.501, 'duration': 5.406}, {'end': 480.455, 'text': "We're going to have a static folder or static directory, which means any files you create that are not ever going to change, like images, CSS files,", 'start': 472.608, 'duration': 7.847}, {'end': 481.576, 'text': 'JavaScript files.', 'start': 480.455, 'duration': 1.121}, {'end': 482.577, 'text': "they're going to go in this folder.", 'start': 481.576, 'duration': 1.001}, {'end': 489.563, 'text': 'And then lastly, any HTML that you write, web pages you want the human to see, are going to go in a folder called templates.', 'start': 483.057, 'duration': 6.506}], 'summary': 'Flask simplifies web development by easing url analysis, file retrieval, and email automation, adhering to minimalist requirements.', 'duration': 85.589, 'max_score': 403.974, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E403974.jpg'}, {'end': 435.02, 'src': 'embed', 'start': 409.199, 'weight': 1, 'content': [{'end': 414.344, 'text': 'And whereas Bootstrap is used for CSS and JavaScript, Flask is going to be used for Python.', 'start': 409.199, 'duration': 5.145}, {'end': 417.087, 'text': 'And it just solves a lot of common problems for us.', 'start': 414.504, 'duration': 2.583}, {'end': 421.651, 'text': "It's going to make it easier for us to analyze the URLs and get at key value pairs.", 'start': 417.147, 'duration': 4.504}, {'end': 427.357, 'text': "It's going to make it easier for us to find files or images that the human wants to see when visiting our website.", 'start': 422.011, 'duration': 5.346}, {'end': 430.558, 'text': "It's even going to make it easier to send emails automatically.", 'start': 427.597, 'duration': 2.961}, {'end': 435.02, 'text': 'Like when someone fills out a form, you can dynamically, using code, send them an email as well.', 'start': 430.638, 'duration': 4.382}], 'summary': 'Flask simplifies url analysis, file retrieval, and email automation, aiding in web development.', 'duration': 25.821, 'max_score': 409.199, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E409199.jpg'}], 'start': 264.534, 'title': 'Creating custom routes and using frameworks in web development', 'summary': 'Explains how to create custom routes and parse parameters using python, as well as the concept of using flask framework for web app development. it simplifies tasks such as url analysis, file handling, and email sending, and demonstrates the process of creating a web app with the required file structure.', 'chapters': [{'end': 355.971, 'start': 264.534, 'title': 'Creating custom routes and parsing parameters in python', 'summary': 'Explains how to create custom routes in a website and parse parameters using python, allowing the server to automatically look for key-value pairs after the question mark and hand them to the user in the form of a python dictionary.', 'duration': 91.437, 'highlights': ["The chapter demonstrates the ability to create custom routes in a website, allowing the server to provide different web pages based on the user's visit, without the need to map to specific files.", 'The transcript also explains the process of parsing parameters in Python, enabling the server to automatically look for any key-value pairs after the question mark and provide them to the user in the form of a Python dictionary.', "The chapter highlights the use of Python's dict object to handle key-value pairs, serving as a perfect fit for parsing and handling parameters in a web server implemented in Python.", 'The chapter mentions the limitation of the tools discussed in the previous week in parsing and analyzing input from the user, emphasizing the need for additional techniques like using fancy JavaScript to achieve that functionality.']}, {'end': 811.779, 'start': 356.031, 'title': 'Using frameworks in web development with flask', 'summary': 'Explains the concept of frameworks in web development, focusing on the flask framework for python, which simplifies web app development by providing libraries and conventions, making tasks such as url analysis, file handling, and email sending easier. it also demonstrates the process of creating a simple web app using flask and the required file structure.', 'duration': 455.748, 'highlights': ['Flask simplifies web app development by providing libraries and conventions Flask solves common problems in web development, such as analyzing URLs, handling files, and sending emails, making these tasks easier for developers.', 'Creating a simple web app using Flask and defining the required file structure The process involves creating a file named app.py, a requirements.txt file for listing required libraries, a static directory for storing unchanging files like images and CSS, and a templates directory for storing HTML files. Adhering to these conventions is essential in Flask development.', 'Introduction to Flask and its role in web development Flask is introduced as a micro framework for Python, requiring adherence to minimalist requirements to get a web app up and running, making it a convenient choice for developers.']}], 'duration': 547.245, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E264534.jpg', 'highlights': ["The chapter demonstrates the ability to create custom routes in a website, allowing the server to provide different web pages based on the user's visit, without the need to map to specific files.", 'Flask simplifies web app development by providing libraries and conventions Flask solves common problems in web development, such as analyzing URLs, handling files, and sending emails, making these tasks easier for developers.']}, {'end': 1475.333, 'segs': [{'end': 897.079, 'src': 'embed', 'start': 871.031, 'weight': 0, 'content': [{'end': 876.995, 'text': 'But let me do something explicit, like my name equals quote unquote David.', 'start': 871.031, 'duration': 5.964}, {'end': 881.658, 'text': "So there's a key value pair that I've manually typed into my URL bar and hit Enter.", 'start': 877.155, 'duration': 4.503}, {'end': 884.309, 'text': 'OK, nothing happens, nothing changes.', 'start': 882.768, 'duration': 1.541}, {'end': 885.73, 'text': 'It still says hello world.', 'start': 884.389, 'duration': 1.341}, {'end': 892.996, 'text': 'But the opportunity today is to now dynamically get at the input from that URL and start displaying it to the user.', 'start': 885.97, 'duration': 7.026}, {'end': 897.079, 'text': 'So let me go back over here to my terminal window and code.', 'start': 893.516, 'duration': 3.563}], 'summary': 'Demonstrating manual entry of key-value pair into url bar and its impact on the display.', 'duration': 26.048, 'max_score': 871.031, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E871031.jpg'}, {'end': 980.856, 'src': 'heatmap', 'start': 897.619, 'weight': 0.81, 'content': [{'end': 899.76, 'text': 'Let me move that down to the bottom there.', 'start': 897.619, 'duration': 2.141}, {'end': 902.643, 'text': 'And what if I want to say huh, hello, name?', 'start': 900.221, 'duration': 2.422}, {'end': 908.127, 'text': "I ideally want to say something like I don't want to hard code, David, because then it's never going to say hello to anyone else.", 'start': 903.303, 'duration': 4.824}, {'end': 913.931, 'text': 'I kind of want to put a variable name here, like name should go here.', 'start': 908.667, 'duration': 5.264}, {'end': 916.973, 'text': "But it's not an HTML tag, so I need some kind of placeholder.", 'start': 913.991, 'duration': 2.982}, {'end': 919.455, 'text': "Well, here's what I can do.", 'start': 917.694, 'duration': 1.761}, {'end': 921.577, 'text': "If I go back to my Python code, I'm.", 'start': 919.695, 'duration': 1.882}, {'end': 932.847, 'text': "I can now define a variable called name and I can ask Flask to go into the current request, into its arguments, that is, in the URL as they're called,", 'start': 922.017, 'duration': 10.83}, {'end': 936.29, 'text': 'and get whatever the value of the parameter called name is.', 'start': 932.847, 'duration': 3.443}, {'end': 938.792, 'text': 'That puts that into a variable for me.', 'start': 937.051, 'duration': 1.741}, {'end': 943.613, 'text': 'And then in renderTemplate, this is one of those functions that can take more than one argument.', 'start': 939.112, 'duration': 4.501}, {'end': 947.574, 'text': 'If it takes another argument, you can pass in the name of any variable you want.', 'start': 943.893, 'duration': 3.681}, {'end': 951.576, 'text': 'So if I want to pass in my name, I can literally say name equals name.', 'start': 947.594, 'duration': 3.982}, {'end': 956.699, 'text': 'So this is the name of a variable I want to give to the template.', 'start': 952.256, 'duration': 4.443}, {'end': 961.382, 'text': 'This is the actual variable that I want to get the value from.', 'start': 957.179, 'duration': 4.203}, {'end': 965.204, 'text': 'And now, lastly, in my index.html,', 'start': 962.122, 'duration': 3.082}, {'end': 972.829, 'text': 'the syntax as of today in Flask is to do two curly braces and then put the name of the variable that you want to plug in.', 'start': 965.204, 'duration': 7.625}, {'end': 975.611, 'text': "So here's what we mean by a template.", 'start': 973.769, 'duration': 1.842}, {'end': 980.856, 'text': "A template is kind of like a blueprint in the real world, where it's like plans to make something.", 'start': 975.911, 'duration': 4.945}], 'summary': 'Using flask, a variable name is dynamically inserted into html templates from url parameters.', 'duration': 83.237, 'max_score': 897.619, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E897619.jpg'}, {'end': 1032.948, 'src': 'embed', 'start': 1003.309, 'weight': 1, 'content': [{'end': 1008.371, 'text': 'The convention in Flask in their templates is to use two curly braces here.', 'start': 1003.309, 'duration': 5.062}, {'end': 1014.332, 'text': 'The hope is that you, the programmer, will never want to display two curly braces in your actual web page.', 'start': 1008.751, 'duration': 5.581}, {'end': 1016.393, 'text': "But even if you do, there's a workaround.", 'start': 1014.352, 'duration': 2.041}, {'end': 1017.393, 'text': 'We can escape that.', 'start': 1016.613, 'duration': 0.78}, {'end': 1020.956, 'text': 'So now let me go ahead and go back to my browser tab here.', 'start': 1017.893, 'duration': 3.063}, {'end': 1027.122, 'text': 'Previously, even though I added name equals David to the end of the URL with a question mark, it still said hello world.', 'start': 1021.376, 'duration': 5.746}, {'end': 1032.948, 'text': 'But now, hopefully, if I made these changes, let me go ahead and open up my terminal window.', 'start': 1027.142, 'duration': 5.806}], 'summary': 'Flask templates use two curly braces for convention. escaping is possible. url parameter test conducted.', 'duration': 29.639, 'max_score': 1003.309, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E1003309.jpg'}, {'end': 1093.837, 'src': 'embed', 'start': 1063.028, 'weight': 2, 'content': [{'end': 1070.651, 'text': "And the only thing we have to do that is call this function request.args, You and I don't have to bother figuring out.", 'start': 1063.028, 'duration': 7.623}, {'end': 1072.671, 'text': 'where is the question mark, where is the equal sign?', 'start': 1070.651, 'duration': 2.02}, {'end': 1074.372, 'text': 'where are the ampersands potentially?', 'start': 1072.671, 'duration': 1.701}, {'end': 1076.972, 'text': 'The framework, Flask, does all of that for us.', 'start': 1074.772, 'duration': 2.2}, {'end': 1086.035, 'text': 'OK, any questions then on these principles thus far? Yeah, in back.', 'start': 1078.473, 'duration': 7.562}, {'end': 1093.837, 'text': 'Why do you need a question mark in the URL?', 'start': 1092.396, 'duration': 1.441}], 'summary': 'Flask framework handles url parsing and query string for us.', 'duration': 30.809, 'max_score': 1063.028, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E1063028.jpg'}, {'end': 1152.926, 'src': 'embed', 'start': 1125.237, 'weight': 4, 'content': [{'end': 1127.678, 'text': 'Sure, this is this annoying thing about Python.', 'start': 1125.237, 'duration': 2.441}, {'end': 1134.34, 'text': 'When you pass in parameters to functions that have names, you typically say something equals something else.', 'start': 1127.718, 'duration': 6.622}, {'end': 1136.341, 'text': 'So let me make a slight tweak here.', 'start': 1134.66, 'duration': 1.681}, {'end': 1148.004, 'text': 'How about I say name of person here? This allows me to invent my own variable for my template and assign it the value of name.', 'start': 1137.361, 'duration': 10.643}, {'end': 1152.926, 'text': 'I now, though, have to go into my index file and say name of person.', 'start': 1148.524, 'duration': 4.402}], 'summary': 'In python, parameters are passed to functions by assigning values, allowing for flexibility in variable naming and assignment.', 'duration': 27.689, 'max_score': 1125.237, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E1125237.jpg'}, {'end': 1387.991, 'src': 'heatmap', 'start': 1305.302, 'weight': 0.778, 'content': [{'end': 1307.663, 'text': "And I'm going to turn off autocomplete like we did last week.", 'start': 1305.302, 'duration': 2.361}, {'end': 1311.384, 'text': "I'm going to turn on autofocus so it puts the cursor in the text box for me.", 'start': 1307.683, 'duration': 3.701}, {'end': 1316.685, 'text': "I'm going to give the name of this input the name name, not to be too confusing.", 'start': 1311.404, 'duration': 5.281}, {'end': 1321.007, 'text': "But I'm asking the human for their name, so it makes sense that the name of the input should be quote unquote name.", 'start': 1316.725, 'duration': 4.282}, {'end': 1327.23, 'text': "The placeholder I want the human to see in light gray text will be name with a capital N, just so it's a little grammatical.", 'start': 1321.827, 'duration': 5.403}, {'end': 1331.313, 'text': 'And then type of this input is going to be text.', 'start': 1327.591, 'duration': 3.722}, {'end': 1334.435, 'text': "Then I'm just going to give myself, like last week, a submit button.", 'start': 1331.733, 'duration': 2.702}, {'end': 1335.616, 'text': "And I don't care what it says.", 'start': 1334.595, 'duration': 1.021}, {'end': 1338.217, 'text': "It's just going to say the default submit terminology.", 'start': 1335.676, 'duration': 2.541}, {'end': 1343.02, 'text': 'Let me go ahead now and open up my terminal window again.', 'start': 1338.817, 'duration': 4.203}, {'end': 1348.143, 'text': 'Let me go to that same URL so that I can see.', 'start': 1343.44, 'duration': 4.703}, {'end': 1353.648, 'text': 'Whoops There we go.', 'start': 1348.203, 'duration': 5.445}, {'end': 1355.449, 'text': 'So that was just cached from earlier.', 'start': 1353.988, 'duration': 1.461}, {'end': 1358.75, 'text': 'Let me go back to that same URL, my githubpreview.dev URL.', 'start': 1355.489, 'duration': 3.261}, {'end': 1359.931, 'text': 'And here I have the form.', 'start': 1358.81, 'duration': 1.121}, {'end': 1361.892, 'text': 'And now I can type in anything I want.', 'start': 1360.231, 'duration': 1.661}, {'end': 1366.834, 'text': "The catch, though, is when I click Submit, where is it going to go? Well, let's be explicit.", 'start': 1362.212, 'duration': 4.622}, {'end': 1368.015, 'text': 'It does have a default value.', 'start': 1366.874, 'duration': 1.141}, {'end': 1369.976, 'text': 'But let me go into my index.html.', 'start': 1368.055, 'duration': 1.921}, {'end': 1377.439, 'text': 'And let me add, just like we did last week for Google, whereas previously I said something like www.google.com slash search.', 'start': 1370.336, 'duration': 7.103}, {'end': 1380.062, 'text': "But today, we're not going to rely on some third party.", 'start': 1377.799, 'duration': 2.263}, {'end': 1382.444, 'text': "I'm going to implement the so-called back end.", 'start': 1380.402, 'duration': 2.042}, {'end': 1387.991, 'text': "And I'm going to have the user submit this form to a second route, not just slash.", 'start': 1382.885, 'duration': 5.106}], 'summary': 'Configuring form input fields and setting up a backend route for form submission.', 'duration': 82.689, 'max_score': 1305.302, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E1305302.jpg'}, {'end': 1369.976, 'src': 'embed', 'start': 1343.44, 'weight': 3, 'content': [{'end': 1348.143, 'text': 'Let me go to that same URL so that I can see.', 'start': 1343.44, 'duration': 4.703}, {'end': 1353.648, 'text': 'Whoops There we go.', 'start': 1348.203, 'duration': 5.445}, {'end': 1355.449, 'text': 'So that was just cached from earlier.', 'start': 1353.988, 'duration': 1.461}, {'end': 1358.75, 'text': 'Let me go back to that same URL, my githubpreview.dev URL.', 'start': 1355.489, 'duration': 3.261}, {'end': 1359.931, 'text': 'And here I have the form.', 'start': 1358.81, 'duration': 1.121}, {'end': 1361.892, 'text': 'And now I can type in anything I want.', 'start': 1360.231, 'duration': 1.661}, {'end': 1366.834, 'text': "The catch, though, is when I click Submit, where is it going to go? Well, let's be explicit.", 'start': 1362.212, 'duration': 4.622}, {'end': 1368.015, 'text': 'It does have a default value.', 'start': 1366.874, 'duration': 1.141}, {'end': 1369.976, 'text': 'But let me go into my index.html.', 'start': 1368.055, 'duration': 1.921}], 'summary': 'Demonstrating form submission with default value on githubpreview.dev url.', 'duration': 26.536, 'max_score': 1343.44, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E1343440.jpg'}], 'start': 812.059, 'title': 'Dynamic web page in flask and using forms in python', 'summary': 'Introduces dynamically retrieving and displaying input from the url in a flask web application using request.args function and template placeholders, and discusses implementing a form in an html file to obtain user input and modify the url.', 'chapters': [{'end': 1120.823, 'start': 812.059, 'title': 'Dynamic web page in flask', 'summary': 'Introduces how to dynamically retrieve and display input from the url in a flask web application, demonstrating the use of request.args function and template placeholders with two curly braces.', 'duration': 308.764, 'highlights': ['The chapter introduces how to dynamically retrieve and display input from the URL in a Flask web application The instructor demonstrates the process of accessing and displaying input from the URL dynamically in a Flask web application, providing practical insights into web development.', 'The use of request.args function and template placeholders with two curly braces The instructor explains the usage of request.args function in Flask to programmatically access URL parameters, and the usage of template placeholders with two curly braces to dynamically display input values in the web page.', 'Flask framework automatically handles parsing key-value pairs after the question mark in the URL The instructor mentions that Flask framework simplifies the process of parsing key-value pairs after the question mark in the URL, emphasizing the convenience and automation provided by the framework.']}, {'end': 1475.333, 'start': 1125.237, 'title': 'Using forms in python', 'summary': 'Discusses the verbose method of passing parameters to functions in python and demonstrates the implementation of a form in an html file to automatically obtain user input and modify the url.', 'duration': 350.096, 'highlights': ['The chapter discusses the verbose method of passing parameters to functions in Python. The speaker explains the verbosity of passing parameters to functions in Python, highlighting the typical requirement of using the same name as the variable itself, even if it appears kind of stupid.', "The chapter demonstrates the implementation of a form in an HTML file to automatically obtain user input and modify the URL. The speaker explains the process of creating a form in an HTML file to obtain user input, specifying the use of input tags and the method 'get' to modify the URL automatically upon submission."]}], 'duration': 663.274, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E812059.jpg', 'highlights': ['The chapter introduces how to dynamically retrieve and display input from the URL in a Flask web application.', 'The use of request.args function and template placeholders with two curly braces.', 'Flask framework automatically handles parsing key-value pairs after the question mark in the URL.', 'The chapter demonstrates the implementation of a form in an HTML file to automatically obtain user input and modify the URL.', 'The chapter discusses the verbose method of passing parameters to functions in Python.']}, {'end': 3147.387, 'segs': [{'end': 2219.529, 'src': 'embed', 'start': 2184.347, 'weight': 2, 'content': [{'end': 2185.568, 'text': "And let's consider what I've done.", 'start': 2184.347, 'duration': 1.221}, {'end': 2187.449, 'text': 'This is starting to look weird fast.', 'start': 2185.808, 'duration': 1.641}, {'end': 2191.19, 'text': 'And this is now a mix of HTML with templating code.', 'start': 2187.529, 'duration': 3.661}, {'end': 2198.881, 'text': 'index.html, first line now says, hey, Flask, this file extends layout.html, whatever that is.', 'start': 2192.219, 'duration': 6.662}, {'end': 2205.622, 'text': 'This next line, 3 through 10, says, hey, Flask, here is what I consider my body block to be.', 'start': 2199.461, 'duration': 6.161}, {'end': 2210.203, 'text': "Plug this into the layout's placeholder, therefore.", 'start': 2206.062, 'duration': 4.141}, {'end': 2219.529, 'text': "So if I now go back to layout.html, In layout.html, it's almost all HTML by contrast, but there is this placeholder.", 'start': 2210.883, 'duration': 8.646}], 'summary': 'Transcript discusses integrating html with flask templating code.', 'duration': 35.182, 'max_score': 2184.347, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E2184347.jpg'}, {'end': 2694.833, 'src': 'heatmap', 'start': 2520.617, 'weight': 1, 'content': [{'end': 2525.359, 'text': 'Let me go into my index.html where my form is.', 'start': 2520.617, 'duration': 4.742}, {'end': 2530.02, 'text': 'And in my form, I can just change the method from get to post.', 'start': 2525.939, 'duration': 4.081}, {'end': 2534.702, 'text': "It's still going to send key value pairs to the server, but it's not going to put them in the URL.", 'start': 2530.72, 'duration': 3.982}, {'end': 2540.566, 'text': "The upside of which is that we can assuage this privacy concern, but I'm going to have to make one other change too.", 'start': 2535.342, 'duration': 5.224}, {'end': 2549.572, 'text': 'Because now, if I go ahead and run Flask again after making that change, and I now reload the form to make sure I have the latest version,', 'start': 2540.626, 'duration': 8.946}, {'end': 2552.453, 'text': 'you should be in the habit of going to View Developer,', 'start': 2549.572, 'duration': 2.881}, {'end': 2557.377, 'text': "View Source or Developer Tools just to make sure that what you're seeing in your browser is what you intend.", 'start': 2552.453, 'duration': 4.924}, {'end': 2560.059, 'text': 'And yes, I do see what I wanted.', 'start': 2557.657, 'duration': 2.402}, {'end': 2561.499, 'text': 'Method equals post now.', 'start': 2560.379, 'duration': 1.12}, {'end': 2564.241, 'text': 'Let me go ahead and type in David and click Submit.', 'start': 2562.02, 'duration': 2.221}, {'end': 2566.623, 'text': 'I get a different error.', 'start': 2565.582, 'duration': 1.041}, {'end': 2570.626, 'text': 'This one is HTTP 405, method not allowed.', 'start': 2567.223, 'duration': 3.403}, {'end': 2577.952, 'text': "Well, why is that? Well, in my Flask application, I've only defined a couple of routes so far, one of which is for slash.", 'start': 2570.646, 'duration': 7.306}, {'end': 2579.493, 'text': 'Then that worked fine.', 'start': 2578.552, 'duration': 0.941}, {'end': 2582.615, 'text': 'One of which is for slash greet, and that used to work fine.', 'start': 2579.893, 'duration': 2.722}, {'end': 2588.22, 'text': 'But apparently, what Flask is doing is it only supports get by default.', 'start': 2583.056, 'duration': 5.164}, {'end': 2598.933, 'text': 'So if I want to change this route to support different methods, I can say quote unquote inside of this parameter here,', 'start': 2588.54, 'duration': 10.393}, {'end': 2603.436, 'text': 'so that now I can actually support post, not just get.', 'start': 2598.933, 'duration': 4.503}, {'end': 2608.439, 'text': 'And if I now restart Flask, so Flask run, Enter.', 'start': 2604.156, 'duration': 4.283}, {'end': 2610.761, 'text': 'And I go back to this URL.', 'start': 2609.3, 'duration': 1.461}, {'end': 2616.705, 'text': 'Let me go back one screen to the form, reload the page just to make sure I have the latest, even though nothing there has changed.', 'start': 2610.881, 'duration': 5.824}, {'end': 2618.226, 'text': 'Type David and click Submit Now.', 'start': 2616.805, 'duration': 1.421}, {'end': 2620.328, 'text': 'Now I should see Hello World.', 'start': 2618.827, 'duration': 1.501}, {'end': 2624.991, 'text': "Notice that I'm at the slash greet route, but there's no mention of David.", 'start': 2620.788, 'duration': 4.203}, {'end': 2629.348, 'text': 'name equals anything in the URL.', 'start': 2626.727, 'duration': 2.621}, {'end': 2633.55, 'text': "All right, so that's kind of an interesting takeaway, right? Like, it's a simple change.", 'start': 2630.268, 'duration': 3.282}, {'end': 2637.211, 'text': 'But whereas get puts things in the URL, post does not.', 'start': 2633.85, 'duration': 3.361}, {'end': 2644.153, 'text': 'But it still works so long as you tweak the back end to look at a post request, which kind of means look deeper in the envelope.', 'start': 2637.271, 'duration': 6.882}, {'end': 2646.354, 'text': "It's not going to be as simple as looking at the URL itself.", 'start': 2644.193, 'duration': 2.161}, {'end': 2653.697, 'text': "Why shouldn't we just always use post? Like, why not use post everywhere??", 'start': 2646.914, 'duration': 6.783}, {'end': 2656.576, 'text': 'Any thoughts??', 'start': 2655.996, 'duration': 0.58}, {'end': 2666.302, 'text': "Because it's kind of obnoxious to be putting any information in URLs if you're leaving these little breadcrumbs in your history and people can poke around and see what you've been doing.", 'start': 2657.557, 'duration': 8.745}, {'end': 2670.564, 'text': 'Yeah, what do you think??', 'start': 2669.944, 'duration': 0.62}, {'end': 2675.247, 'text': "Yeah, but also just to keep track of what you've been doing.", 'start': 2670.584, 'duration': 4.663}, {'end': 2684.927, 'text': 'Yeah, I mean, if you get rid of get requests and put nothing in the URL, your history, like your autocomplete, gets pretty less useful, right?', 'start': 2677.503, 'duration': 7.424}, {'end': 2686.928, 'text': 'Because none of the information is there for storage.', 'start': 2684.987, 'duration': 1.941}, {'end': 2688.429, 'text': "You can't just go through the menu and hit Enter.", 'start': 2686.968, 'duration': 1.461}, {'end': 2690.19, 'text': "You'd have to refill out the form.", 'start': 2688.729, 'duration': 1.461}, {'end': 2692.071, 'text': "And there's this other symptom that you can see here.", 'start': 2690.43, 'duration': 1.641}, {'end': 2692.752, 'text': 'Let me zoom out.', 'start': 2692.111, 'duration': 0.641}, {'end': 2694.833, 'text': 'And let me just reload this page.', 'start': 2693.092, 'duration': 1.741}], 'summary': 'The transcript discusses changing the form method from get to post, modifying flask routes to support post requests, and the implications of using post instead of get.', 'duration': 174.216, 'max_score': 2520.617, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E2520617.jpg'}, {'end': 3021.015, 'src': 'heatmap', 'start': 2849.567, 'weight': 0.801, 'content': [{'end': 2858.934, 'text': "It must be that it's not seeing a key called name in request.args which is this gives you access to everything after the URL.", 'start': 2849.567, 'duration': 9.367}, {'end': 2863.961, 'text': "That's because there's this other thing we should know about, which is not just request.args, but request.form.", 'start': 2859.274, 'duration': 4.687}, {'end': 2868.248, 'text': 'These are horribly named, but request.args is for get requests.', 'start': 2863.981, 'duration': 4.267}, {'end': 2871.112, 'text': 'Request.form is for post requests.', 'start': 2868.628, 'duration': 2.484}, {'end': 2873.213, 'text': "Otherwise, they're pretty much functionally the same.", 'start': 2871.272, 'duration': 1.941}, {'end': 2877.934, 'text': "But the onus is on you, the user, or the programmer, to make sure you're using the right one.", 'start': 2873.253, 'duration': 4.681}, {'end': 2883.396, 'text': 'So I think, if we want to get rid of the world and actually see what I the human typed in,', 'start': 2878.014, 'duration': 5.382}, {'end': 2888.218, 'text': 'I think I can just change request.args to request.form still.', 'start': 2883.396, 'duration': 4.822}, {'end': 2889.859, 'text': 'get, still quote unquote name.', 'start': 2888.218, 'duration': 1.641}, {'end': 2898.342, 'text': "And now if I go ahead and rerun Flask, in my terminal window, go back to my browser, go back to, and actually, I won't even go back to the form.", 'start': 2890.139, 'duration': 8.203}, {'end': 2901.425, 'text': 'I will literally just reload, Command-R or Control-R.', 'start': 2898.402, 'duration': 3.023}, {'end': 2908.271, 'text': "And what this warning is saying is it's going to submit the same information to the website when I click Continue.", 'start': 2901.745, 'duration': 6.526}, {'end': 2911.114, 'text': 'Now I should see hello comma David.', 'start': 2908.991, 'duration': 2.123}, {'end': 2914.999, 'text': 'So again, you too are going to encounter probably all these kinds of little subtleties.', 'start': 2911.174, 'duration': 3.825}, {'end': 2920.927, 'text': 'But if you focus on really the first principles of last week, like what is HTTP, how does a GET request work?', 'start': 2915.059, 'duration': 5.868}, {'end': 2922.249, 'text': 'how does a POST request work?', 'start': 2920.927, 'duration': 1.322}, {'end': 2925.973, 'text': 'now, you should have a lot of the mental building blocks with which to solve problems.', 'start': 2922.249, 'duration': 3.724}, {'end': 2927.074, 'text': 'like these.', 'start': 2926.494, 'duration': 0.58}, {'end': 2930.395, 'text': "And let me give you one other mental model now for what it is we're doing.", 'start': 2927.414, 'duration': 2.981}, {'end': 2937.338, 'text': 'This framework called Flask is just an example of many different frameworks that all implement the same paradigm,', 'start': 2930.595, 'duration': 6.743}, {'end': 2940.679, 'text': 'the same way of thinking and the same way of programming applications.', 'start': 2937.338, 'duration': 3.341}, {'end': 2944.16, 'text': "And that's known as MVC, Model View Controller.", 'start': 2940.719, 'duration': 3.441}, {'end': 2950.382, 'text': "And here's a very simple diagram that represents the process that you and I have been implementing thus far.", 'start': 2944.2, 'duration': 6.182}, {'end': 2953.203, 'text': "And actually, this is more than we've been implementing thus far.", 'start': 2950.442, 'duration': 2.761}, {'end': 2957.604, 'text': 'In app.py is what a programmer would typically call the controller.', 'start': 2953.763, 'duration': 3.841}, {'end': 2964.646, 'text': "That's the code you're writing, the so-called business logic that makes all of the decisions, decides what to render, what values to show,", 'start': 2957.724, 'duration': 6.922}, {'end': 2965.326, 'text': 'and so forth.', 'start': 2964.646, 'duration': 0.68}, {'end': 2970.908, 'text': 'In layout.html, index.html, greet.html is the so-called view.', 'start': 2966.046, 'duration': 4.862}, {'end': 2976.312, 'text': 'templates, that is the visualizations that the human actually sees, the user interface.', 'start': 2971.488, 'duration': 4.824}, {'end': 2977.753, 'text': 'Those things are kind of dumb.', 'start': 2976.652, 'duration': 1.101}, {'end': 2980.556, 'text': 'They pretty much just say, plop some values here.', 'start': 2977.893, 'duration': 2.663}, {'end': 2983.238, 'text': 'All of the hard work is done in app.py.', 'start': 2980.956, 'duration': 2.282}, {'end': 2987.902, 'text': 'So controller, aka app.py, is where your Python code generally is.', 'start': 2983.298, 'duration': 4.604}, {'end': 2997.365, 'text': 'And in your view is where your HTML and your Jinja code, your Jinja templating, the curly braces, the curly braces with percent signs usually is.', 'start': 2988.462, 'duration': 8.903}, {'end': 3001.206, 'text': "We haven't added an M to MVC yet model.", 'start': 2997.925, 'duration': 3.281}, {'end': 3008.328, 'text': "That's going to refer to things like CSV files or databases, the model where do you keep actual data, typically long term.", 'start': 3001.466, 'duration': 6.862}, {'end': 3009.248, 'text': "So we'll come back to that.", 'start': 3008.368, 'duration': 0.88}, {'end': 3015.191, 'text': 'But this picture, where you have one of these, Each of these components kind of intercommunicating with one another,', 'start': 3009.268, 'duration': 5.923}, {'end': 3017.433, 'text': 'is representative of how a lot of frameworks work.', 'start': 3015.191, 'duration': 2.242}, {'end': 3021.015, 'text': "What we're teaching today, this week, is not really specific to Python.", 'start': 3017.473, 'duration': 3.542}], 'summary': 'Understanding flask, request.args for get requests, request.form for post requests, and the mvc paradigm.', 'duration': 171.448, 'max_score': 2849.567, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E2849567.jpg'}, {'end': 2970.908, 'src': 'embed', 'start': 2940.719, 'weight': 0, 'content': [{'end': 2944.16, 'text': "And that's known as MVC, Model View Controller.", 'start': 2940.719, 'duration': 3.441}, {'end': 2950.382, 'text': "And here's a very simple diagram that represents the process that you and I have been implementing thus far.", 'start': 2944.2, 'duration': 6.182}, {'end': 2953.203, 'text': "And actually, this is more than we've been implementing thus far.", 'start': 2950.442, 'duration': 2.761}, {'end': 2957.604, 'text': 'In app.py is what a programmer would typically call the controller.', 'start': 2953.763, 'duration': 3.841}, {'end': 2964.646, 'text': "That's the code you're writing, the so-called business logic that makes all of the decisions, decides what to render, what values to show,", 'start': 2957.724, 'duration': 6.922}, {'end': 2965.326, 'text': 'and so forth.', 'start': 2964.646, 'duration': 0.68}, {'end': 2970.908, 'text': 'In layout.html, index.html, greet.html is the so-called view.', 'start': 2966.046, 'duration': 4.862}], 'summary': 'Mvc architecture involves model, view, and controller, with app.py serving as the controller, and layout.html, index.html, and greet.html as the views.', 'duration': 30.189, 'max_score': 2940.719, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E2940719.jpg'}], 'start': 1475.333, 'title': 'Web development with flask', 'summary': 'Covers handling form submission, url changes, and defining routes in app.py, encountering server errors and troubleshooting template creation, improving web template design with flask, and discussing web form privacy and design, with a focus on privacy implications of using get method, advantages of using post method, and introducing the model view controller (mvc) paradigm in web development.', 'chapters': [{'end': 1572.419, 'start': 1475.333, 'title': 'Handling form submission and url changes', 'summary': 'Discusses handling form submission, url changes, and defining routes in app.py, including retrieving user input and returning a new template, with a focus on url changes and 404 errors.', 'duration': 97.086, 'highlights': ['Handling form submission and URL changes The chapter focuses on handling form submission and URL changes, including the automatic addition of a question mark in the URL, and the importance of looking for the so-called route.', "Defining routes and functions in app.py The chapter discusses defining a new route for /greet and a function greet in app.py, which retrieves the user's name and returns a new template, greet.html.", 'Focus on URL changes and 404 error The chapter emphasizes the importance of URL changes and the occurrence of a 404 not found error, highlighting the significance of the URL change to slash greet question mark name equals David.']}, {'end': 1930.708, 'start': 1572.459, 'title': 'Server error and template creation', 'summary': 'Discusses encountering a 500 internal server error due to a missing template file, and the process of creating and troubleshooting the template file to fix the error, along with demonstrating error handling and default parameter usage in a web application.', 'duration': 358.249, 'highlights': ["Encountering a 500 internal server error due to a missing template file The speaker discusses encountering a 500 internal server error due to the absence of a 'greet.html' template file, leading to the error message 'Template not found.'", "Process of creating and troubleshooting the 'greet.html' template file The speaker demonstrates the process of creating the 'greet.html' template file, including its structure with HTML tags, and the use of the terminal to check the file's presence and content.", 'Demonstrating error handling and default parameter usage in a web application The speaker illustrates error handling in a web application by showing the use of a default parameter in the context of Python dictionaries, and discusses the importance of not relying solely on client-side safety checks for input validation.']}, {'end': 2476.51, 'start': 1931.673, 'title': 'Improving web template design with flask', 'summary': 'Discusses the drawbacks of repetitive web template design, introduces the concept of using layout templates with flask to address the issue, and highlights the benefits in terms of design and memory.', 'duration': 544.837, 'highlights': ['The drawbacks of repetitive web template design are discussed, emphasizing the issue of tedious copy-pasting and the need to change multiple files for a single update.', 'The concept of using layout templates with Flask to address the issue of repetitive web template design is introduced, highlighting the ability to factor out commonalities and the syntax of block body and end block.', 'The benefits of using layout templates with Flask are explained, indicating improvements in design for scalability and memory optimization by allowing the server to perform optimizations underneath the hood.']}, {'end': 3147.387, 'start': 2476.59, 'title': 'Web form privacy and design', 'summary': 'Discusses the privacy implications of using get method in web forms and the advantages of using post method to avoid sensitive information being exposed in the url. it also explains the significance of using request.args for get requests and request.form for post requests in flask applications, and introduces the model view controller (mvc) paradigm in web development.', 'duration': 670.797, 'highlights': ['Using post method in web forms can prevent sensitive information like passwords and credit card details from being exposed in the URL, addressing privacy concerns. By changing the method from get to post in the web form, sensitive information is not included in the URL, thus mitigating privacy risks.', 'Using request.args for get requests and request.form for post requests is crucial in Flask applications to access the parameters correctly. The distinction between request.args and request.form is important, as request.args is used for get requests, while request.form is used for post requests in Flask applications.', "Introduction of the Model View Controller (MVC) paradigm in web development, where the controller (app.py) handles the business logic and decisions, and the view (HTML and Jinja templates) represents the user interface. The MVC paradigm is introduced, highlighting the controller's role in making decisions and the view's role in representing the user interface in web development."]}], 'duration': 1672.054, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E1475333.jpg', 'highlights': ['Introduction of the Model View Controller (MVC) paradigm in web development, where the controller (app.py) handles the business logic and decisions, and the view (HTML and Jinja templates) represents the user interface.', 'Using post method in web forms can prevent sensitive information like passwords and credit card details from being exposed in the URL, addressing privacy concerns.', 'The benefits of using layout templates with Flask are explained, indicating improvements in design for scalability and memory optimization by allowing the server to perform optimizations underneath the hood.', 'The concept of using layout templates with Flask to address the issue of repetitive web template design is introduced, highlighting the ability to factor out commonalities and the syntax of block body and end block.']}, {'end': 4082.081, 'segs': [{'end': 3176.405, 'src': 'embed', 'start': 3147.487, 'weight': 0, 'content': [{'end': 3151.452, 'text': 'And it was really just controller code written not in Python but in Perl.', 'start': 3147.487, 'duration': 3.965}, {'end': 3157.115, 'text': 'And it was really just the same building blocks that we here already today now have.', 'start': 3151.952, 'duration': 5.163}, {'end': 3161.517, 'text': "So we'll get rid of all of the imagery and focus more on the functionality and the aesthetics.", 'start': 3157.315, 'duration': 4.202}, {'end': 3168.341, 'text': "But let's see if we can't whip up a web application via which someone could register for one such intramural sport.", 'start': 3161.797, 'duration': 6.544}, {'end': 3169.121, 'text': 'So in app.py.', 'start': 3168.681, 'duration': 0.44}, {'end': 3172.143, 'text': 'Let me go ahead and import some familiar things now.', 'start': 3170.242, 'duration': 1.901}, {'end': 3172.903, 'text': 'From Flask.', 'start': 3172.243, 'duration': 0.66}, {'end': 3176.405, 'text': "let's import capital Flask, which is that function.", 'start': 3172.903, 'duration': 3.502}], 'summary': 'Code written in perl, building blocks already available. developing a web app for intramural sports registration.', 'duration': 28.918, 'max_score': 3147.487, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E3147487.jpg'}, {'end': 3214.406, 'src': 'embed', 'start': 3187.111, 'weight': 1, 'content': [{'end': 3191.073, 'text': 'Let me go ahead and create the application itself using this magical incantation here.', 'start': 3187.111, 'duration': 3.962}, {'end': 3194.035, 'text': "And then let's go ahead and define a route.", 'start': 3191.713, 'duration': 2.322}, {'end': 3200.218, 'text': "For slash, for instance, first, I'm going to define a function called index.", 'start': 3195.155, 'duration': 5.063}, {'end': 3205.121, 'text': 'But just to be clear, this function could be anything, foo, bar, baz, anything else.', 'start': 3200.278, 'duration': 4.843}, {'end': 3209.243, 'text': "But I tend to name them in a manner that's consistent with what the route is called.", 'start': 3205.521, 'duration': 3.722}, {'end': 3210.824, 'text': 'But you could call it anything you want.', 'start': 3209.283, 'duration': 1.541}, {'end': 3214.406, 'text': "It's just the function that will get called for this particular route.", 'start': 3211.144, 'duration': 3.262}], 'summary': 'Creating an application and defining a route for a function called index.', 'duration': 27.295, 'max_score': 3187.111, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E3187111.jpg'}, {'end': 3257.187, 'src': 'embed', 'start': 3229.493, 'weight': 2, 'content': [{'end': 3234.836, 'text': 'OK, let me go ahead and in my templates directory code a file called index.html.', 'start': 3229.493, 'duration': 5.343}, {'end': 3244.18, 'text': "And let's just do extends layout.html at the top, just so that we get benefit from that template.", 'start': 3236.616, 'duration': 7.564}, {'end': 3250.263, 'text': "And down here, I'm just going to say to do, just so that I have something going on visually to make sure I've not screwed up yet.", 'start': 3244.54, 'duration': 5.723}, {'end': 3253.265, 'text': 'In my froshims directory, let me do flask run.', 'start': 3250.904, 'duration': 2.361}, {'end': 3257.187, 'text': 'Let me now go back to my previous URL, which used to be my hello example.', 'start': 3254.085, 'duration': 3.102}], 'summary': 'Creating index.html file, extending layout.html, and running flask', 'duration': 27.694, 'max_score': 3229.493, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E3229493.jpg'}, {'end': 3549.153, 'src': 'embed', 'start': 3512.98, 'weight': 3, 'content': [{'end': 3519.821, 'text': 'All right, so now we really have the beginnings of the user interface that I created some years ago to let people actually register for the sport.', 'start': 3512.98, 'duration': 6.841}, {'end': 3523.842, 'text': "So let's go now and create maybe the other route that we might need.", 'start': 3520.082, 'duration': 3.76}, {'end': 3525.262, 'text': 'Let me go into app.py.', 'start': 3523.922, 'duration': 1.34}, {'end': 3530.984, 'text': "And in here, if we want to allow the user to register, let's do a little bit of error checking, which I promised we'd come back to.", 'start': 3525.663, 'duration': 5.321}, {'end': 3534.644, 'text': 'Like, what could the user do wrong? Because assume that they will.', 'start': 3531.064, 'duration': 3.58}, {'end': 3536.645, 'text': 'One, they might not type their name.', 'start': 3535.364, 'duration': 1.281}, {'end': 3538.505, 'text': 'Two, they might not choose a sport.', 'start': 3537.045, 'duration': 1.46}, {'end': 3540.406, 'text': 'So they might just submit an empty form.', 'start': 3539.085, 'duration': 1.321}, {'end': 3545.73, 'text': "So that's two things we could check for just so that we're not storing bogus entries in our database ultimately.", 'start': 3540.426, 'duration': 5.304}, {'end': 3549.153, 'text': "So let's create another route called greet slash greet.", 'start': 3546.09, 'duration': 3.063}], 'summary': 'Creating error checking for user registration to prevent storing invalid entries.', 'duration': 36.173, 'max_score': 3512.98, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E3512980.jpg'}, {'end': 3993.269, 'src': 'embed', 'start': 3966.955, 'weight': 4, 'content': [{'end': 3973.076, 'text': 'But names, or more generally key value pairs, is how information is sent from a form to the server.', 'start': 3966.955, 'duration': 6.121}, {'end': 3978.619, 'text': "So if there's no name, there's no key to send, even if the human types a value.", 'start': 3973.576, 'duration': 5.043}, {'end': 3982.282, 'text': "It would be like nothing equals ultimate Frisbee, and that just doesn't work.", 'start': 3978.659, 'duration': 3.623}, {'end': 3983.883, 'text': "The browser's just not going to send it.", 'start': 3982.322, 'duration': 1.561}, {'end': 3993.269, 'text': "However, in app.py, I was naively assuming that in my request's form, there would be a name called quote unquote sport.", 'start': 3984.703, 'duration': 8.566}], 'summary': 'Key value pairs are crucial for sending form information to the server, without which the browser will not send the data.', 'duration': 26.314, 'max_score': 3966.955, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E3966955.jpg'}], 'start': 3147.487, 'title': 'Building web application in flask', 'summary': 'Delves into building a web application using flask, with a focus on functionality and aesthetics, and includes implementing a registration form for intramural sports. it also covers user registration for sports, including options for football, frisbee, and basketball, refining the user interface, adding error validation, and creating routes for success and failure. additionally, the chapter details the process of troubleshooting a form registration error, emphasizing the importance of key-value pairs in html form submission.', 'chapters': [{'end': 3388.938, 'start': 3147.487, 'title': 'Building web application in flask', 'summary': 'Discusses building a web application using flask, focusing on functionality and aesthetics, and implementing a registration form for intramural sports.', 'duration': 241.451, 'highlights': ['Creating a Flask web application for registering intramural sports The chapter discusses the process of creating a web application in Flask for registering intramural sports, emphasizing functionality and aesthetics.', 'Defining routes and functions in app.py The process involves defining routes and functions in app.py, such as defining a function called index and creating a route for slash.', 'Implementing templates and layout in Flask The chapter covers the implementation of templates and layout in Flask, including extending layout.html and using render template to print out templates.', 'Troubleshooting and fixing template issues The transcript details the troubleshooting process, including fixing issues in index.html by defining block body and resolving layout-related problems.', 'Enhancing index.html with a registration form The chapter discusses enhancing index.html by adding a registration form with an input box for name and a dropdown menu for sports selection.']}, {'end': 3776.78, 'start': 3389.038, 'title': 'Implementing user registration for sports', 'summary': 'Discusses the implementation of user registration for sports, including creating options for football, frisbee, and basketball, refining the user interface, adding error validation, and creating routes for registration success and failure.', 'duration': 387.742, 'highlights': ['The chapter discusses the implementation of user registration for sports, including creating options for football, Frisbee, and basketball, refining the user interface, adding error validation, and creating routes for registration success and failure.', 'The process of restarting the server can be automated, which is done for problem set 9 to avoid stopping Flask multiple times.', 'Error checking is implemented to ensure that users provide their name and choose a sport before submitting the form to avoid storing bogus entries in the database.', 'Different routes are created for registration success and failure, with error messages displayed in HTML files to inform the user about the status of their registration.']}, {'end': 4082.081, 'start': 3777.08, 'title': 'Troubleshooting form registration error', 'summary': 'Details the process of debugging a form registration error, starting with encountering the issue, identifying the root cause, and utilizing development tools to diagnose and resolve the problem, emphasizing the importance of key-value pairs in html form submission.', 'duration': 305.001, 'highlights': ['The importance of key-value pairs in HTML form submission is highlighted, explaining how the absence of a name attribute prevents the form data from being transmitted to the server, resulting in an error.', 'The process of utilizing developer tools to inspect the HTTP request and identify the absence of form data being submitted is detailed as a crucial step in diagnosing form registration errors.', 'The significance of logical reasoning and thorough code examination in identifying the root cause of the form registration error is emphasized, showcasing the problem-solving approach in debugging web development issues.']}], 'duration': 934.594, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E3147487.jpg', 'highlights': ['Creating a Flask web application for registering intramural sports emphasizing functionality and aesthetics.', 'Defining routes and functions in app.py, such as defining a function called index and creating a route for slash.', 'Implementing templates and layout in Flask, including extending layout.html and using render template to print out templates.', 'The chapter discusses the implementation of user registration for sports, including creating options for football, Frisbee, and basketball, refining the user interface, adding error validation, and creating routes for registration success and failure.', 'The importance of key-value pairs in HTML form submission is highlighted, explaining how the absence of a name attribute prevents the form data from being transmitted to the server, resulting in an error.', 'Error checking is implemented to ensure that users provide their name and choose a sport before submitting the form to avoid storing bogus entries in the database.']}, {'end': 5647.294, 'segs': [{'end': 4571.309, 'src': 'heatmap', 'start': 4242.15, 'weight': 0.75, 'content': [{'end': 4246.314, 'text': 'In my form here, the dropdown menu is perfectly fine, nothing wrong with it.', 'start': 4242.15, 'duration': 4.164}, {'end': 4249.596, 'text': 'But suppose that I wanted to change it to, like, checkboxes instead.', 'start': 4246.334, 'duration': 3.262}, {'end': 4253.398, 'text': 'Maybe I want students to be able to register for multiple sports instead.', 'start': 4249.676, 'duration': 3.722}, {'end': 4257.059, 'text': 'Well, it might make sense to clean this up in a couple of ways.', 'start': 4253.798, 'duration': 3.261}, {'end': 4257.799, 'text': "And let's do this.", 'start': 4257.139, 'duration': 0.66}, {'end': 4262.921, 'text': "Before we even get into the checkboxes, there's one subtle bad design here.", 'start': 4257.839, 'duration': 5.082}, {'end': 4266.803, 'text': "Notice that I've hard-coded basketball, soccer, and Ultimate Frisbee here.", 'start': 4263.061, 'duration': 3.742}, {'end': 4271.704, 'text': 'And if you recall, in app.py, I also enumerated all three of those here.', 'start': 4267.703, 'duration': 4.001}, {'end': 4276.426, 'text': 'And any time you see copy, paste, or the equivalent thereof, it feels like we could do better.', 'start': 4271.744, 'duration': 4.682}, {'end': 4278.367, 'text': 'So what if I instead do this?', 'start': 4276.906, 'duration': 1.461}, {'end': 4282.848, 'text': 'What if I instead give myself a global variable of sports??', 'start': 4278.607, 'duration': 4.241}, {'end': 4288.85, 'text': "I'll capitalize the word just to connote that it's meant to be constant, even though Python does not have constants per se.", 'start': 4282.988, 'duration': 5.862}, {'end': 4291.691, 'text': 'The first sport will be basketball.', 'start': 4289.49, 'duration': 2.201}, {'end': 4293.832, 'text': 'The second will be soccer.', 'start': 4292.191, 'duration': 1.641}, {'end': 4296.833, 'text': 'The third will be Ultimate Frisbee.', 'start': 4294.032, 'duration': 2.801}, {'end': 4304.493, 'text': 'Now I have one convenient place to store all of my sports if it changes next semester or next year or whatnot.', 'start': 4298.385, 'duration': 6.108}, {'end': 4306.456, 'text': 'But notice what I could do too.', 'start': 4304.914, 'duration': 1.542}, {'end': 4308.118, 'text': 'I could now do something like this.', 'start': 4306.876, 'duration': 1.242}, {'end': 4316.168, 'text': "Let me pass into my index template a variable called sports that's equal to that global variable sports.", 'start': 4308.318, 'duration': 7.85}, {'end': 4318.671, 'text': 'Let me go into my index now.', 'start': 4316.869, 'duration': 1.802}, {'end': 4323.738, 'text': 'And this is really now going to hint at the power of templating and Jinja in this case here.', 'start': 4318.812, 'duration': 4.926}, {'end': 4327.363, 'text': 'Let me go ahead and get rid of all three of these hard-coded options.', 'start': 4324.158, 'duration': 3.205}, {'end': 4331.848, 'text': 'And let me show you some slightly different syntax for sport in sports.', 'start': 4327.623, 'duration': 4.225}, {'end': 4334.283, 'text': 'Then end for.', 'start': 4332.762, 'duration': 1.521}, {'end': 4338.545, 'text': "We've not seen this end for syntax, just like end block syntax.", 'start': 4335.203, 'duration': 3.342}, {'end': 4339.806, 'text': "But it's as simple as that.", 'start': 4338.585, 'duration': 1.221}, {'end': 4342.928, 'text': 'So you have a start and an end to your block without indentation mattering.', 'start': 4339.826, 'duration': 3.102}, {'end': 4344.269, 'text': 'Watch what I can do here.', 'start': 4343.368, 'duration': 0.901}, {'end': 4350.412, 'text': 'Option, curly brace, sport, close curly brace.', 'start': 4344.889, 'duration': 5.523}, {'end': 4351.713, 'text': 'Let me save that.', 'start': 4350.852, 'duration': 0.861}, {'end': 4355.095, 'text': 'Let me go back into my terminal window, do flask run.', 'start': 4352.253, 'duration': 2.842}, {'end': 4358.376, 'text': "And if I didn't mess up here, let me go back to this.", 'start': 4355.315, 'duration': 3.061}, {'end': 4360.238, 'text': "The red's going to go away because I deleted my CSS.", 'start': 4358.416, 'duration': 1.822}, {'end': 4364.719, 'text': 'And now I still have a sport dropdown, and all of those sports are still there.', 'start': 4361.238, 'duration': 3.481}, {'end': 4366.38, 'text': 'I can make one more improvement now.', 'start': 4364.999, 'duration': 1.381}, {'end': 4369.921, 'text': "I don't need to mention these same sports manually in app.py.", 'start': 4366.64, 'duration': 3.281}, {'end': 4377.503, 'text': "I can now just say, if the user's inputted sport is not in my global variable sports, and ask the same question.", 'start': 4370.301, 'duration': 7.202}, {'end': 4383.425, 'text': "And this is really handy because if there's another sport, for instance, that gets added, like, say, football,", 'start': 4377.543, 'duration': 5.882}, {'end': 4385.786, 'text': 'all I have to do is change my global variable.', 'start': 4383.425, 'duration': 2.361}, {'end': 4391.929, 'text': 'And if I reload the form now and look in the dropdown, boom, now I have support for a fourth sport.', 'start': 4386.446, 'duration': 5.483}, {'end': 4393.509, 'text': 'And I can keep adding and adding there.', 'start': 4391.949, 'duration': 1.56}, {'end': 4402.554, 'text': "So here's where templating starts to get really powerful, in that now, in this template, I'm using Jinja's for loop syntax,", 'start': 4393.59, 'duration': 8.964}, {'end': 4409.057, 'text': 'which is almost identical to Python here, except you need the curly brace and the percent sign and you need the weird ending end for.', 'start': 4402.554, 'duration': 6.503}, {'end': 4410.998, 'text': "But it's the same idea as in Python.", 'start': 4409.557, 'duration': 1.441}, {'end': 4415.101, 'text': 'Iterating over something with a for loop lets you generate more and more HTML.', 'start': 4411.098, 'duration': 4.003}, {'end': 4417.223, 'text': 'And this is like every website out there.', 'start': 4415.401, 'duration': 1.822}, {'end': 4418.083, 'text': 'For instance, Gmail.', 'start': 4417.263, 'duration': 0.82}, {'end': 4424.708, 'text': 'When you visit your inbox and you see all of this big table of emails, Google has not hardcoded your emails manually.', 'start': 4418.123, 'duration': 6.585}, {'end': 4426.369, 'text': 'They have grabbed them from a database.', 'start': 4424.748, 'duration': 1.621}, {'end': 4433.595, 'text': 'They have some kind of for loop like this and are just outputting table row after table row or div after div dynamically.', 'start': 4426.65, 'duration': 6.945}, {'end': 4442.35, 'text': "All right, so now let's go ahead and change this maybe to, oh, how about little check boxes or radio buttons.", 'start': 4434.42, 'duration': 7.93}, {'end': 4443.512, 'text': 'So let me go ahead and do this.', 'start': 4442.411, 'duration': 1.101}, {'end': 4448.479, 'text': "Instead of a select menu, I'm going to go ahead and do something like this.", 'start': 4444.013, 'duration': 4.466}, {'end': 4451.202, 'text': 'For each of these sports.', 'start': 4448.799, 'duration': 2.403}, {'end': 4453.345, 'text': "I'm going, Let me go ahead and output.", 'start': 4451.202, 'duration': 2.143}, {'end': 4463.198, 'text': 'not an option, but let me go ahead and output an input tag, the name for which is quote unquote sport, the type of which is checkbox,', 'start': 4453.345, 'duration': 9.853}, {'end': 4466.722, 'text': 'the value of which is going to be the current sport.', 'start': 4463.198, 'duration': 3.524}, {'end': 4468.324, 'text': 'quote unquote.', 'start': 4467.423, 'duration': 0.901}, {'end': 4474.351, 'text': 'And then afterward, I need to redundantly seemingly output the sport so you see a word next to the checkbox.', 'start': 4468.825, 'duration': 5.526}, {'end': 4476.153, 'text': "And we'll look at the result of this in just a moment.", 'start': 4474.371, 'duration': 1.782}, {'end': 4482.681, 'text': "So it's actually a little simpler than a select menu, a dropdown menu, because now watch what happens if I reload my form.", 'start': 4476.414, 'duration': 6.267}, {'end': 4484.602, 'text': 'Different user interface.', 'start': 4483.362, 'duration': 1.24}, {'end': 4490.364, 'text': "And it's not as pretty, but it's going to allow users to sign up for multiple sports at once now, it would seem.", 'start': 4485.003, 'duration': 5.361}, {'end': 4496.046, 'text': 'Now I can click on basketball and football and soccer or some other combination thereof.', 'start': 4490.764, 'duration': 5.282}, {'end': 4499.847, 'text': "If I view the page's source, this is, again, the power of templating.", 'start': 4496.406, 'duration': 3.441}, {'end': 4502.008, 'text': "I didn't have to type out four inputs.", 'start': 4500.167, 'duration': 1.841}, {'end': 4504.048, 'text': 'I got them now automatically.', 'start': 4502.408, 'duration': 1.64}, {'end': 4507.249, 'text': "And these things all have the same name, but that's OK.", 'start': 4504.268, 'duration': 2.981}, {'end': 4513.071, 'text': "It turns out with Flask, if it sees multiple values for the same name, it's going to hand them back to you as a list.", 'start': 4507.729, 'duration': 5.342}, {'end': 4514.472, 'text': 'if you use the right function.', 'start': 4513.311, 'duration': 1.161}, {'end': 4518.377, 'text': "All right, but suppose we don't want users registering for multiple sports.", 'start': 4514.493, 'duration': 3.884}, {'end': 4519.619, 'text': 'Maybe capacity is an issue.', 'start': 4518.457, 'duration': 1.162}, {'end': 4525.306, 'text': 'Let me go ahead and change this checkbox to radio button, which a radio button is mutually exclusive.', 'start': 4519.939, 'duration': 5.367}, {'end': 4526.868, 'text': 'So you can only sign up for one.', 'start': 4525.326, 'duration': 1.542}, {'end': 4531.566, 'text': 'So now, once I reload the page, it now There we go.', 'start': 4527.348, 'duration': 4.218}, {'end': 4533.987, 'text': 'It now looks like this.', 'start': 4531.986, 'duration': 2.001}, {'end': 4542.271, 'text': "And because I've given each of these inputs the same name, quote unquote, sport, that's what makes them mutually exclusive.", 'start': 4534.387, 'duration': 7.884}, {'end': 4545.492, 'text': 'The browser knows all four of these things are types of sports.', 'start': 4542.331, 'duration': 3.161}, {'end': 4548.654, 'text': "Therefore, I'm only going to let you select one of these things.", 'start': 4545.872, 'duration': 2.782}, {'end': 4551.275, 'text': "And that's simply because they all have the same name.", 'start': 4549.154, 'duration': 2.121}, {'end': 4556.157, 'text': 'Again, if I view page source, notice all of them, name equals sport, name equals sport, name equals sport.', 'start': 4551.335, 'duration': 4.822}, {'end': 4560.639, 'text': 'But what differs is the value that each one is going to have.', 'start': 4556.217, 'duration': 4.422}, {'end': 4562.734, 'text': 'All right.', 'start': 4562.432, 'duration': 0.302}, {'end': 4565.273, 'text': 'any questions, then on this approach?', 'start': 4562.734, 'duration': 2.539}, {'end': 4571.309, 'text': 'All right, well, let me go ahead and open a version of this that I made in advance.', 'start': 4567.688, 'duration': 3.621}], 'summary': 'Using global variable for sports, jinja templating for dynamic form, switching from dropdown to checkboxes and radio buttons for multiple or single sport selection.', 'duration': 329.159, 'max_score': 4242.15, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E4242150.jpg'}, {'end': 4304.493, 'src': 'embed', 'start': 4278.607, 'weight': 3, 'content': [{'end': 4282.848, 'text': 'What if I instead give myself a global variable of sports??', 'start': 4278.607, 'duration': 4.241}, {'end': 4288.85, 'text': "I'll capitalize the word just to connote that it's meant to be constant, even though Python does not have constants per se.", 'start': 4282.988, 'duration': 5.862}, {'end': 4291.691, 'text': 'The first sport will be basketball.', 'start': 4289.49, 'duration': 2.201}, {'end': 4293.832, 'text': 'The second will be soccer.', 'start': 4292.191, 'duration': 1.641}, {'end': 4296.833, 'text': 'The third will be Ultimate Frisbee.', 'start': 4294.032, 'duration': 2.801}, {'end': 4304.493, 'text': 'Now I have one convenient place to store all of my sports if it changes next semester or next year or whatnot.', 'start': 4298.385, 'duration': 6.108}], 'summary': 'Using a global variable to store sports like basketball, soccer, and ultimate frisbee for future changes.', 'duration': 25.886, 'max_score': 4278.607, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E4278607.jpg'}, {'end': 4424.708, 'src': 'embed', 'start': 4391.949, 'weight': 0, 'content': [{'end': 4393.509, 'text': 'And I can keep adding and adding there.', 'start': 4391.949, 'duration': 1.56}, {'end': 4402.554, 'text': "So here's where templating starts to get really powerful, in that now, in this template, I'm using Jinja's for loop syntax,", 'start': 4393.59, 'duration': 8.964}, {'end': 4409.057, 'text': 'which is almost identical to Python here, except you need the curly brace and the percent sign and you need the weird ending end for.', 'start': 4402.554, 'duration': 6.503}, {'end': 4410.998, 'text': "But it's the same idea as in Python.", 'start': 4409.557, 'duration': 1.441}, {'end': 4415.101, 'text': 'Iterating over something with a for loop lets you generate more and more HTML.', 'start': 4411.098, 'duration': 4.003}, {'end': 4417.223, 'text': 'And this is like every website out there.', 'start': 4415.401, 'duration': 1.822}, {'end': 4418.083, 'text': 'For instance, Gmail.', 'start': 4417.263, 'duration': 0.82}, {'end': 4424.708, 'text': 'When you visit your inbox and you see all of this big table of emails, Google has not hardcoded your emails manually.', 'start': 4418.123, 'duration': 6.585}], 'summary': 'Templating with jinja allows for dynamic html generation, similar to python for loops, used in websites like gmail.', 'duration': 32.759, 'max_score': 4391.949, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E4391949.jpg'}, {'end': 4821.369, 'src': 'heatmap', 'start': 4649.889, 'weight': 0.709, 'content': [{'end': 4654.353, 'text': "And so let's see what the logic is that handles this.", 'start': 4649.889, 'duration': 4.464}, {'end': 4660.377, 'text': "Here, in my register route, in the code I've pre-made, notice that I'm validating the user's name.", 'start': 4655.213, 'duration': 5.164}, {'end': 4662.419, 'text': 'Slightly differently from before, but same idea.', 'start': 4660.477, 'duration': 1.942}, {'end': 4666.802, 'text': "I'm using request.form.get to get the human's name, if not name.", 'start': 4662.779, 'duration': 4.023}, {'end': 4671.446, 'text': "So if the human did not type a name, I'm going to output error.html.", 'start': 4666.942, 'duration': 4.504}, {'end': 4675.869, 'text': "But notice I've started to make the user interface more expressive.", 'start': 4671.506, 'duration': 4.363}, {'end': 4679.532, 'text': "I'm telling the user, apparently with a message, what they did wrong.", 'start': 4675.889, 'duration': 3.643}, {'end': 4686.636, 'text': "Well, how? I'm apparently passing to my error template, instead of just failure.html, a specific message.", 'start': 4679.992, 'duration': 6.644}, {'end': 4688.177, 'text': "So let's go down this rabbit hole.", 'start': 4686.936, 'duration': 1.241}, {'end': 4692.499, 'text': 'Let me actually go into template slash error.html.', 'start': 4688.197, 'duration': 4.302}, {'end': 4695.981, 'text': "And sure enough, here's a new file I created here.", 'start': 4693.139, 'duration': 2.842}, {'end': 4700.305, 'text': 'that adorably is apparently going to have a grumpy cat as part of the error message.', 'start': 4696.601, 'duration': 3.704}, {'end': 4701.326, 'text': "But notice what I've done.", 'start': 4700.365, 'duration': 0.961}, {'end': 4706.571, 'text': "In my block body, I've got an h1 tag that just says error, big and bold.", 'start': 4701.726, 'duration': 4.845}, {'end': 4712.978, 'text': 'I then have a paragraph tag that plugs in whatever the error message is that the controller app.py is passing in.', 'start': 4706.972, 'duration': 6.006}, {'end': 4717.622, 'text': 'And then just for fun, I have a picture of a grumpy cat connoting that there was, in fact, an error.', 'start': 4713.278, 'duration': 4.344}, {'end': 4718.684, 'text': "Let's keep looking.", 'start': 4718.023, 'duration': 0.661}, {'end': 4726.733, 'text': 'How do I validate sport? I do similarly request.form.get of sport, and I store it in a variable called sport.', 'start': 4719.144, 'duration': 7.589}, {'end': 4732.72, 'text': "If there's no such sport that is, the human did not check any of the boxes then I'm going to render error.html2,.", 'start': 4726.893, 'duration': 5.827}, {'end': 4734.982, 'text': "but I'm going to give a different message missing sport.", 'start': 4732.72, 'duration': 2.262}, {'end': 4743.724, 'text': "Else, if the sport they did type in is not in my sports global variable, I'm going to render error.html, but complain differently.", 'start': 4735.803, 'duration': 7.921}, {'end': 4746.165, 'text': 'You gave me an invalid sport somehow.', 'start': 4744.185, 'duration': 1.98}, {'end': 4751.846, 'text': 'They, like a hacker, went into the HTML of the page, changed it to add their own sport, like volleyball.', 'start': 4746.225, 'duration': 5.621}, {'end': 4753.726, 'text': "Even though it's not offered, they submitted volleyball.", 'start': 4751.926, 'duration': 1.8}, {'end': 4754.726, 'text': "But that's OK.", 'start': 4754.086, 'duration': 0.64}, {'end': 4760.668, 'text': "I'm rejecting it, even though they might have maliciously tried to send it to me by changing the DOM locally.", 'start': 4754.766, 'duration': 5.902}, {'end': 4762.508, 'text': 'And then, really, the magic is just this.', 'start': 4761.068, 'duration': 1.44}, {'end': 4770.912, 'text': 'I remember that this person is registered by indexing into the registrants dictionary, using the name, the human typed in,', 'start': 4763.268, 'duration': 7.644}, {'end': 4773.674, 'text': 'as the key and assigning it a value of sport.', 'start': 4770.912, 'duration': 2.762}, {'end': 4776.855, 'text': 'Why is this useful? Well, I added one final route here.', 'start': 4773.994, 'duration': 2.861}, {'end': 4783.879, 'text': 'I have a slash registrants route with a registrants function that renders a template called registrants.html,', 'start': 4777.616, 'duration': 6.263}, {'end': 4786.921, 'text': 'but it takes as input that global variable.', 'start': 4783.879, 'duration': 3.042}, {'end': 4788.685, 'text': 'Just like before.', 'start': 4787.864, 'duration': 0.821}, {'end': 4789.866, 'text': "So let's go down this rabbit hole.", 'start': 4788.745, 'duration': 1.121}, {'end': 4794.912, 'text': 'Let me go into templates, registrants.html.', 'start': 4789.886, 'duration': 5.026}, {'end': 4796.434, 'text': "Here's this template.", 'start': 4795.433, 'duration': 1.001}, {'end': 4800.018, 'text': 'It looks a little crazy big, but it extends the layout.', 'start': 4796.654, 'duration': 3.364}, {'end': 4801.44, 'text': 'Here comes the body.', 'start': 4800.539, 'duration': 0.901}, {'end': 4804.284, 'text': "I've got an h1 tag that says registrants, big and bold.", 'start': 4801.68, 'duration': 2.604}, {'end': 4806.665, 'text': "Then I've got a table that we saw last week.", 'start': 4804.724, 'duration': 1.941}, {'end': 4810.526, 'text': 'This has a table head that just says name sport for two columns.', 'start': 4807.045, 'duration': 3.481}, {'end': 4821.369, 'text': "Then it has a table body wherein, using this for loop in Jinja syntax, I'm saying for each name in the registrants variable output a table row,", 'start': 4810.906, 'duration': 10.463}], 'summary': 'Code implements user input validation, error handling, and user registration with specific error messages and templates.', 'duration': 171.48, 'max_score': 4649.889, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E4649889.jpg'}, {'end': 5223.092, 'src': 'heatmap', 'start': 4976.446, 'weight': 0.844, 'content': [{'end': 4979.248, 'text': "It's thrown away when you lose power or stop the program.", 'start': 4976.446, 'duration': 2.802}, {'end': 4981.309, 'text': "So maybe this isn't the best approach.", 'start': 4979.568, 'duration': 1.741}, {'end': 4983.33, 'text': 'Maybe it would be better to use a CSV file.', 'start': 4981.369, 'duration': 1.961}, {'end': 4986.211, 'text': "And in fact, some 20 years ago, that's literally what I did.", 'start': 4983.67, 'duration': 2.541}, {'end': 4987.852, 'text': 'I stored everything in a CSV file.', 'start': 4986.271, 'duration': 1.581}, {'end': 4993.555, 'text': "But let's skip that step, because we already saw last week, or a couple of weeks ago now, how we can use SQLite.", 'start': 4988.132, 'duration': 5.423}, {'end': 5000.079, 'text': "Let's see if we can't marry in some SQL here to store an actual database for the program.", 'start': 4993.916, 'duration': 6.163}, {'end': 5007.444, 'text': 'Let me go back here, and let me open up, say, version 4 of this, which is almost the same, but it adds a bit more functionality.', 'start': 5000.359, 'duration': 7.085}, {'end': 5012.967, 'text': 'Let me close these tabs, and let me open up app.py now in version 4.', 'start': 5007.564, 'duration': 5.403}, {'end': 5014.388, 'text': "So notice it's almost the same.", 'start': 5012.967, 'duration': 1.421}, {'end': 5020.292, 'text': "But at the top, I'm creating a database connection to a database called froshims.db.", 'start': 5014.848, 'duration': 5.444}, {'end': 5022.353, 'text': "So that's a database I created in advance.", 'start': 5020.592, 'duration': 1.761}, {'end': 5023.634, 'text': "So let's go down that rabbit hole.", 'start': 5022.393, 'duration': 1.241}, {'end': 5026.496, 'text': 'What does it look like? Let me make my terminal window bigger.', 'start': 5023.734, 'duration': 2.762}, {'end': 5030.138, 'text': 'Let me run SQLite3 of froshims.db.', 'start': 5026.876, 'duration': 3.262}, {'end': 5031.259, 'text': "OK, I'm in.", 'start': 5030.158, 'duration': 1.101}, {'end': 5032.34, 'text': "Let's do dot schema.", 'start': 5031.299, 'duration': 1.041}, {'end': 5035.242, 'text': "And let's just infer what I designed this to be.", 'start': 5032.76, 'duration': 2.482}, {'end': 5041.971, 'text': "I have a table called registrants, which has one to three columns an ID column that's an integer.", 'start': 5035.842, 'duration': 6.129}, {'end': 5046.937, 'text': "a name column that's text but cannot be null and a sport column that's also text, cannot be null.", 'start': 5041.971, 'duration': 4.966}, {'end': 5051.043, 'text': 'And the primary key is just ID, so that I have a unique ID for every registration.', 'start': 5046.957, 'duration': 4.086}, {'end': 5053.805, 'text': "Let's see if there's anyone in there yet.", 'start': 5051.764, 'duration': 2.041}, {'end': 5057.447, 'text': 'Select star from registrants.', 'start': 5054.445, 'duration': 3.002}, {'end': 5058.948, 'text': "OK, there's no one in there.", 'start': 5058.007, 'duration': 0.941}, {'end': 5060.529, 'text': 'No one is yet registered for sports.', 'start': 5059.068, 'duration': 1.461}, {'end': 5062.77, 'text': "So let's go back to the code and continue on.", 'start': 5060.609, 'duration': 2.161}, {'end': 5068.453, 'text': "In my code now, I've got the same global variable for validation and generation of my HTML.", 'start': 5063.43, 'duration': 5.023}, {'end': 5071.535, 'text': 'It looks like my index route is the same.', 'start': 5069.193, 'duration': 2.342}, {'end': 5075.079, 'text': "It's dynamically generating the menu of sports.", 'start': 5071.876, 'duration': 3.203}, {'end': 5077.221, 'text': "Interestingly, we'll come back to this.", 'start': 5075.759, 'duration': 1.462}, {'end': 5085.508, 'text': "There's a deregister route that's going to allow someone to deregister themselves if they want to exit the sport or undo their registration.", 'start': 5077.261, 'duration': 8.247}, {'end': 5086.589, 'text': 'But this is the juicy part.', 'start': 5085.548, 'duration': 1.041}, {'end': 5089.192, 'text': "Here's my new and improved register route.", 'start': 5087.15, 'duration': 2.042}, {'end': 5092.274, 'text': 'Still works on post, so some mild privacy there.', 'start': 5089.812, 'duration': 2.462}, {'end': 5095.536, 'text': "I'm validating the submission as follows.", 'start': 5093.115, 'duration': 2.421}, {'end': 5099.118, 'text': "I'm getting the user's inputted name, the user's inputted sport.", 'start': 5095.696, 'duration': 3.422}, {'end': 5105.201, 'text': "And if it is not a name or the sport is not in sports, I'm going to render failure.html.", 'start': 5099.238, 'duration': 5.963}, {'end': 5105.921, 'text': 'So I kept it simple.', 'start': 5105.221, 'duration': 0.7}, {'end': 5107.141, 'text': "There's no cat in this version.", 'start': 5105.941, 'duration': 1.2}, {'end': 5108.142, 'text': 'It just says failure.', 'start': 5107.342, 'duration': 0.8}, {'end': 5112.464, 'text': 'Otherwise, recall how we commingled SQL and Python before.', 'start': 5108.962, 'duration': 3.502}, {'end': 5117.806, 'text': "We're using CS50's SQL library, but that just makes it a little easier to execute SQL queries.", 'start': 5112.504, 'duration': 5.302}, {'end': 5118.787, 'text': "And we're executing this.", 'start': 5117.846, 'duration': 0.941}, {'end': 5120.628, 'text': 'Insert into registrants.', 'start': 5119.367, 'duration': 1.261}, {'end': 5122.73, 'text': 'Name comma sport.', 'start': 5121.468, 'duration': 1.262}, {'end': 5127.095, 'text': 'What two values? The name and the sport that came from that HTML form.', 'start': 5122.83, 'duration': 4.265}, {'end': 5135.545, 'text': "And then lastly, and this is a new function that we're calling out explicitly now Flask also gives you access to a redirect function, which is how,", 'start': 5127.415, 'duration': 8.13}, {'end': 5143.031, 'text': 'which is how safetyschool.org, harvardsucks.org and those other sites we played around with last week were all implemented,', 'start': 5136.886, 'duration': 6.145}, {'end': 5145.272, 'text': 'redirecting the user from one place to another.', 'start': 5143.031, 'duration': 2.241}, {'end': 5152.378, 'text': 'This flask function redirect comes from my just having imported it at the very top of this file.', 'start': 5145.633, 'duration': 6.745}, {'end': 5157.321, 'text': 'It handles the HTTP 301 or 302 or 307 code, whatever the appropriate one is.', 'start': 5152.558, 'duration': 4.763}, {'end': 5157.902, 'text': 'It does that.', 'start': 5157.441, 'duration': 0.461}, {'end': 5158.923, 'text': 'for me.', 'start': 5158.522, 'duration': 0.401}, {'end': 5164.308, 'text': "All right, so that's it for registering via this route.", 'start': 5159.563, 'duration': 4.745}, {'end': 5167.451, 'text': "Let's look at what the slash registrants route is.", 'start': 5164.688, 'duration': 2.763}, {'end': 5171.255, 'text': 'Here we have a new route for slash registrants.', 'start': 5168.392, 'duration': 2.863}, {'end': 5176.541, 'text': "And instead of just iterating over a dictionary like before, we're getting back, let's see, db.execute.", 'start': 5171.295, 'duration': 5.246}, {'end': 5179.524, 'text': 'of select star from registrants.', 'start': 5177.802, 'duration': 1.722}, {'end': 5182.606, 'text': "So that's literally the programmatic version of what I just did manually.", 'start': 5179.544, 'duration': 3.062}, {'end': 5187.51, 'text': 'That gives me back a list of dictionaries, each of which represents one row in the table.', 'start': 5182.626, 'duration': 4.884}, {'end': 5197.078, 'text': "Then I'm going to render registrants.html, passing in literally that list of dictionaries, just like using CS50's library in the past.", 'start': 5188.111, 'duration': 8.967}, {'end': 5200.701, 'text': "So let's go and look at that form.", 'start': 5197.939, 'duration': 2.762}, {'end': 5208.765, 'text': "If I go into templates and open up registrants.html, oh, OK, it's just a table like before.", 'start': 5200.801, 'duration': 7.964}, {'end': 5212.367, 'text': 'And actually, let me change this syntactically for consistency.', 'start': 5209.685, 'duration': 2.682}, {'end': 5218.35, 'text': 'We have a Jinja for loop that iterates over each registrant.', 'start': 5213.147, 'duration': 5.203}, {'end': 5221.231, 'text': 'And for each of them outputs a table row.', 'start': 5218.67, 'duration': 2.561}, {'end': 5223.092, 'text': 'But this is interesting.', 'start': 5222.112, 'duration': 0.98}], 'summary': 'The transcript discusses transitioning from csv to sqlite for storing a database, and demonstrates the use of sql queries and flask functions to register and display registrants.', 'duration': 246.646, 'max_score': 4976.446, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E4976446.jpg'}, {'end': 5143.031, 'src': 'embed', 'start': 5112.504, 'weight': 1, 'content': [{'end': 5117.806, 'text': "We're using CS50's SQL library, but that just makes it a little easier to execute SQL queries.", 'start': 5112.504, 'duration': 5.302}, {'end': 5118.787, 'text': "And we're executing this.", 'start': 5117.846, 'duration': 0.941}, {'end': 5120.628, 'text': 'Insert into registrants.', 'start': 5119.367, 'duration': 1.261}, {'end': 5122.73, 'text': 'Name comma sport.', 'start': 5121.468, 'duration': 1.262}, {'end': 5127.095, 'text': 'What two values? The name and the sport that came from that HTML form.', 'start': 5122.83, 'duration': 4.265}, {'end': 5135.545, 'text': "And then lastly, and this is a new function that we're calling out explicitly now Flask also gives you access to a redirect function, which is how,", 'start': 5127.415, 'duration': 8.13}, {'end': 5143.031, 'text': 'which is how safetyschool.org, harvardsucks.org and those other sites we played around with last week were all implemented,', 'start': 5136.886, 'duration': 6.145}], 'summary': "Using cs50's sql library to insert registrants' name and sport into a database and flask provides access to a redirect function.", 'duration': 30.527, 'max_score': 5112.504, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E5112504.jpg'}, {'end': 5187.51, 'src': 'embed', 'start': 5159.563, 'weight': 2, 'content': [{'end': 5164.308, 'text': "All right, so that's it for registering via this route.", 'start': 5159.563, 'duration': 4.745}, {'end': 5167.451, 'text': "Let's look at what the slash registrants route is.", 'start': 5164.688, 'duration': 2.763}, {'end': 5171.255, 'text': 'Here we have a new route for slash registrants.', 'start': 5168.392, 'duration': 2.863}, {'end': 5176.541, 'text': "And instead of just iterating over a dictionary like before, we're getting back, let's see, db.execute.", 'start': 5171.295, 'duration': 5.246}, {'end': 5179.524, 'text': 'of select star from registrants.', 'start': 5177.802, 'duration': 1.722}, {'end': 5182.606, 'text': "So that's literally the programmatic version of what I just did manually.", 'start': 5179.544, 'duration': 3.062}, {'end': 5187.51, 'text': 'That gives me back a list of dictionaries, each of which represents one row in the table.', 'start': 5182.626, 'duration': 4.884}], 'summary': 'New route for registrants retrieves list of dictionaries from database.', 'duration': 27.947, 'max_score': 5159.563, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E5159563.jpg'}], 'start': 4082.862, 'title': 'Web development essentials', 'summary': 'Discusses css editing, ui enhancement, user registration, and validation, along with the implementation of a slash registrants route, covering flask, jinja templating, sql integration, and database manipulation, providing practical demonstrations.', 'chapters': [{'end': 4610.509, 'start': 4082.862, 'title': 'Css editing and ui enhancement', 'summary': 'Discusses css editing and ui enhancement using flask, including a demonstration of using static files, the power of templating with jinja, and implementing different user interface mechanisms. it also explores the use of global variables for efficient storage and the implementation of radio buttons for mutually exclusive options.', 'duration': 527.647, 'highlights': ['The chapter discusses CSS editing and UI enhancement using Flask. The chapter delves into the process of editing CSS using Flask, including the convention of placing static files in the static folder and making changes to the layout to reference the static folder.', "Demonstration of using static files and the power of templating with Jinja. The demonstration showcases the process of creating a static folder, editing the CSS file, and utilizing Jinja's for loop syntax to dynamically generate HTML, thereby highlighting the power of templating and Jinja in web development.", 'Implementation of radio buttons for mutually exclusive options. The implementation of radio buttons is showcased as a user interface mechanism for allowing users to select mutually exclusive options, with the code dynamically generating the inputs and utilizing Flask to manage the selected options.', 'Utilization of global variables for efficient storage. The use of global variables is highlighted for efficient storage of sports options, allowing for easy modification and maintenance of the options in a central location, thus demonstrating a more scalable and maintainable approach.']}, {'end': 5158.923, 'start': 4611.37, 'title': 'Handling user registration and validation', 'summary': 'Covers the implementation of user registration and validation, including the use of dictionaries for storing registration information, error handling, and the introduction of sql for database storage, with a demonstration of how to integrate sql with python.', 'duration': 547.553, 'highlights': ['Use of dictionaries for storing registration information Dictionaries are utilized to associate names with sports, allowing for the storage of registration details with key value pairs.', 'Error handling and user interface improvement The implementation includes error handling to validate user input and provide expressive error messages, enhancing the user interface for a more user-friendly experience.', 'Integration of SQL for database storage The chapter introduces the use of SQL to store registration data in a database, leveraging SQL queries to insert user registration details into a table.']}, {'end': 5647.294, 'start': 5159.563, 'title': 'Working with slash registrants route', 'summary': "Explains the implementation of a slash registrants route to display registrants' details in a table, allowing registration and deregistration, using a database and forms in flask, resulting in a table with three columns and the usage of unique ids for deletion.", 'duration': 487.731, 'highlights': ["Creating a new route for slash registrants to display registrants' details in a table, iterating over a list of dictionaries obtained from db.execute, and rendering registrants.html with the list of dictionaries.", "Implementing a table with three columns (name, sport, and deregister button) in the registrants.html template for displaying registrants' details and allowing deregistration.", 'Successfully registering multiple users, resulting in the growth of the table in the database, and demonstrating the usage of unique IDs for deletion to avoid deleting multiple users with the same name.', 'Highlighting the importance of using POST instead of GET requests for deregistration to prevent vulnerabilities such as cross-site request forgery, emphasizing the risk of using GET requests that can be exploited via emails, Slack messages, or text messages.']}], 'duration': 1564.432, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E4082862.jpg', 'highlights': ["Demonstration of using static files and the power of templating with Jinja, showcasing the process of creating a static folder, editing the CSS file, and utilizing Jinja's for loop syntax to dynamically generate HTML.", 'Integration of SQL for database storage, introducing the use of SQL to store registration data in a database, leveraging SQL queries to insert user registration details into a table.', "Creating a new route for slash registrants to display registrants' details in a table, iterating over a list of dictionaries obtained from db.execute, and rendering registrants.html with the list of dictionaries.", 'Utilization of global variables for efficient storage, highlighted for efficient storage of sports options, allowing for easy modification and maintenance of the options in a central location.']}, {'end': 6946.998, 'segs': [{'end': 5683.137, 'src': 'embed', 'start': 5661.884, 'weight': 0, 'content': [{'end': 5671.55, 'text': 'I was also automatically generating an email to the proctor in charge of the intramural sports program so that they would have sort of a running history of people registering and they could easily reply to them as well.', 'start': 5661.884, 'duration': 9.666}, {'end': 5675.132, 'text': "Let me go into FroshAM's version 5, which I pre-created here.", 'start': 5671.93, 'duration': 3.202}, {'end': 5680.655, 'text': 'And let me go ahead and open up, say, app.py this time.', 'start': 5675.572, 'duration': 5.083}, {'end': 5683.137, 'text': 'And this is some code that I wrote in advance.', 'start': 5681.076, 'duration': 2.061}], 'summary': 'Automatically generating email to proctor for intramural sports program with running history of registrations.', 'duration': 21.253, 'max_score': 5661.884, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E5661884.jpg'}, {'end': 5751.444, 'src': 'embed', 'start': 5718.945, 'weight': 1, 'content': [{'end': 5727.829, 'text': 'And I just had to fill in a whole bunch of configuration values for the default sender address that I want to send email as the default password I want to use to send email,', 'start': 5718.945, 'duration': 8.884}, {'end': 5731.651, 'text': 'the port number, the TCP port that we talked about last week, the mail server.', 'start': 5727.829, 'duration': 3.822}, {'end': 5734.512, 'text': "I'm going to use Gmail's smtp.gmail.com server.", 'start': 5731.671, 'duration': 2.841}, {'end': 5735.593, 'text': 'Use TLS.', 'start': 5734.952, 'duration': 0.641}, {'end': 5736.773, 'text': 'This means use encryption.', 'start': 5735.633, 'duration': 1.14}, {'end': 5737.714, 'text': 'So I set that to true.', 'start': 5736.813, 'duration': 0.901}, {'end': 5738.834, 'text': 'Mail username.', 'start': 5738.074, 'duration': 0.76}, {'end': 5740.675, 'text': 'This is going to grab it from my environment.', 'start': 5738.854, 'duration': 1.821}, {'end': 5746.079, 'text': "For security purposes, I didn't want to hard code my own Gmail username and password into the code.", 'start': 5741.075, 'duration': 5.004}, {'end': 5749.182, 'text': "So I'm actually storing those in what are called environment variables.", 'start': 5746.48, 'duration': 2.702}, {'end': 5751.444, 'text': "You'll see more of these in problem set 9.", 'start': 5749.222, 'duration': 2.222}], 'summary': 'Configured email settings for gmail smtp server with tls encryption and environment variable for username.', 'duration': 32.499, 'max_score': 5718.945, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E5718945.jpg'}, {'end': 5957.701, 'src': 'heatmap', 'start': 5874.124, 'weight': 0.721, 'content': [{'end': 5875.745, 'text': "But I don't think this is broken.", 'start': 5874.124, 'duration': 1.621}, {'end': 5880.128, 'text': "It wouldn't have said success if it were.", 'start': 5875.765, 'duration': 4.363}, {'end': 5882.77, 'text': 'I just tried submitting again.', 'start': 5880.148, 'duration': 2.622}, {'end': 5884.652, 'text': 'So I just did another you are registered.', 'start': 5882.87, 'duration': 1.782}, {'end': 5889.355, 'text': "Oh, I'm really sad right now.", 'start': 5887.654, 'duration': 1.701}, {'end': 5903.143, 'text': "What's that? I could check spam, but then it's not sure we want to show spam here on the internet that every one of us gets.", 'start': 5893.598, 'duration': 9.545}, {'end': 5905.205, 'text': 'Oh, maybe.', 'start': 5904.444, 'duration': 0.761}, {'end': 5907.826, 'text': 'Oh Thank you.', 'start': 5905.225, 'duration': 2.601}, {'end': 5915.65, 'text': 'DAVID J.', 'start': 5915.29, 'duration': 0.36}, {'end': 5917.692, 'text': 'OK Wow, that was a risky click, I worried.', 'start': 5915.65, 'duration': 2.042}, {'end': 5921.494, 'text': 'All right, so you are registered is the email that I sent out.', 'start': 5917.712, 'duration': 3.782}, {'end': 5923.476, 'text': "And it doesn't have any actual information in it.", 'start': 5921.635, 'duration': 1.841}, {'end': 5929.36, 'text': "But back in the day, it would have, because I included the student's name and their dorm and all the other fields of information that we asked for.", 'start': 5923.536, 'duration': 5.824}, {'end': 5931.902, 'text': "So let's just take a quick look at how that code might work.", 'start': 5929.62, 'duration': 2.282}, {'end': 5940.589, 'text': 'I did have to configure Gmail in a certain way to allow what they call less secure apps using SMTP, which is the protocol used for outbound email.', 'start': 5932.702, 'duration': 7.887}, {'end': 5945.312, 'text': "But besides setting these things, let's look at the register route down here.", 'start': 5941.009, 'duration': 4.303}, {'end': 5946.734, 'text': "It's actually pretty straightforward.", 'start': 5945.533, 'duration': 1.201}, {'end': 5949.676, 'text': 'In my register route, I validated the submission just like before.', 'start': 5946.774, 'duration': 2.902}, {'end': 5950.617, 'text': 'Nothing new there.', 'start': 5949.876, 'duration': 0.741}, {'end': 5953.259, 'text': 'I then confirmed the registration down here.', 'start': 5950.637, 'duration': 2.622}, {'end': 5954.4, 'text': 'Nothing new there.', 'start': 5953.519, 'duration': 0.881}, {'end': 5957.701, 'text': 'All I did was use two new lines of code.', 'start': 5955.12, 'duration': 2.581}], 'summary': 'Email registration process updated with 2 new lines of code', 'duration': 83.577, 'max_score': 5874.124, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E5874124.jpg'}, {'end': 5949.676, 'src': 'embed', 'start': 5923.536, 'weight': 4, 'content': [{'end': 5929.36, 'text': "But back in the day, it would have, because I included the student's name and their dorm and all the other fields of information that we asked for.", 'start': 5923.536, 'duration': 5.824}, {'end': 5931.902, 'text': "So let's just take a quick look at how that code might work.", 'start': 5929.62, 'duration': 2.282}, {'end': 5940.589, 'text': 'I did have to configure Gmail in a certain way to allow what they call less secure apps using SMTP, which is the protocol used for outbound email.', 'start': 5932.702, 'duration': 7.887}, {'end': 5945.312, 'text': "But besides setting these things, let's look at the register route down here.", 'start': 5941.009, 'duration': 4.303}, {'end': 5946.734, 'text': "It's actually pretty straightforward.", 'start': 5945.533, 'duration': 1.201}, {'end': 5949.676, 'text': 'In my register route, I validated the submission just like before.', 'start': 5946.774, 'duration': 2.902}], 'summary': 'Configured gmail for outbound email, validated submission in register route.', 'duration': 26.14, 'max_score': 5923.536, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E5923536.jpg'}, {'end': 6035.374, 'src': 'embed', 'start': 6004.112, 'weight': 3, 'content': [{'end': 6006.774, 'text': "And if you look up here now on line 7, here's the new library.", 'start': 6004.112, 'duration': 2.662}, {'end': 6014.619, 'text': 'From flask mail, I imported capital mail, capital message, so that I had the ability to create a message and send a mail.', 'start': 6007.174, 'duration': 7.445}, {'end': 6020.263, 'text': 'So such a simple thing, whether you want to confirm things for users, you want to do password resets, it can be this easy.', 'start': 6014.719, 'duration': 5.544}, {'end': 6025.967, 'text': 'to actually generate emails, provided you have the requisite access and software installed.', 'start': 6021.183, 'duration': 4.784}, {'end': 6030.39, 'text': 'And just to make clear that I did add something here, let me open up my requirements.txt file.', 'start': 6026.027, 'duration': 4.363}, {'end': 6035.374, 'text': 'And indeed, I have both flask and flask-mail ready to go.', 'start': 6030.77, 'duration': 4.604}], 'summary': 'Demonstrating email setup using flask-mail for easy mail sending.', 'duration': 31.262, 'max_score': 6004.112, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6004112.jpg'}, {'end': 6149.472, 'src': 'embed', 'start': 6117.285, 'weight': 5, 'content': [{'end': 6120.428, 'text': "And when we come back, we'll look at cookies, sessions, and these final features.", 'start': 6117.285, 'duration': 3.143}, {'end': 6123.318, 'text': 'All right.', 'start': 6122.357, 'duration': 0.961}, {'end': 6130.401, 'text': "so the promise now is that we're going to implement this notion of a session, which is going to allow us to log users in and keep them logged in,", 'start': 6123.318, 'duration': 7.083}, {'end': 6132.282, 'text': 'and even implement things like a shopping cart.', 'start': 6130.401, 'duration': 1.881}, {'end': 6138.325, 'text': 'And the overarching goal here is to build an application that is quote unquote stateful.', 'start': 6132.803, 'duration': 5.522}, {'end': 6143.128, 'text': "Again, state refers to information and something that's stateful remembers information.", 'start': 6138.406, 'duration': 4.722}, {'end': 6149.472, 'text': 'And in this context, the curiosity is that HTTP is technically a stateless protocol.', 'start': 6143.448, 'duration': 6.024}], 'summary': 'Implementing sessions to keep users logged in and enable shopping cart, aiming for a stateful application.', 'duration': 32.187, 'max_score': 6117.285, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6117285.jpg'}, {'end': 6387.805, 'src': 'embed', 'start': 6359.813, 'weight': 6, 'content': [{'end': 6366.015, 'text': "That's essentially what a cookie is doing for you, whereby it's a way of reminding the website, we've already done this.", 'start': 6359.813, 'duration': 6.202}, {'end': 6367.975, 'text': 'You already asked me for my username and password.', 'start': 6366.055, 'duration': 1.92}, {'end': 6370.456, 'text': 'This is my pass to now come and go.', 'start': 6368.335, 'duration': 2.121}, {'end': 6377.5, 'text': 'Now, unlike this hand stamp, which can kind of be easily copied or transferred, or duplicated or kept on over multiple days,', 'start': 6370.956, 'duration': 6.544}, {'end': 6381.862, 'text': 'these cookies are really big, seemingly random values, letters and numbers.', 'start': 6377.5, 'duration': 4.362}, {'end': 6387.805, 'text': "So statistically, there's no way someone else is just going to guess your cookie value and pretend to be you,", 'start': 6382.222, 'duration': 5.583}], 'summary': 'Cookies act as unique passcodes, difficult to replicate, ensuring secure website access.', 'duration': 27.992, 'max_score': 6359.813, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6359813.jpg'}, {'end': 6435.896, 'src': 'embed', 'start': 6414.241, 'weight': 7, 'content': [{'end': 6423.647, 'text': "If I want to have the ability to stamp my user's hands virtually and implement sessions, I'm going to have to import from Flask support for sessions.", 'start': 6414.241, 'duration': 9.406}, {'end': 6428.591, 'text': 'So this is another feature you get for free by using a framework and not having to implement all this yourself.', 'start': 6423.728, 'duration': 4.863}, {'end': 6433.134, 'text': "And from the Flask session library, I'm going to import session capital S.", 'start': 6429.271, 'duration': 3.863}, {'end': 6435.896, 'text': "Why? I'm going to configure the session as follows.", 'start': 6433.134, 'duration': 2.762}], 'summary': 'Import flask support for sessions to enable virtual hand stamping and implement sessions, obtaining this feature for free by using a framework.', 'duration': 21.655, 'max_score': 6414.241, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6414241.jpg'}, {'end': 6531.066, 'src': 'heatmap', 'start': 6445.543, 'weight': 0.702, 'content': [{'end': 6447.405, 'text': 'We are telling it to store these cookies.', 'start': 6445.543, 'duration': 1.862}, {'end': 6450.086, 'text': "on the server's hard drive.", 'start': 6448.105, 'duration': 1.981}, {'end': 6457.607, 'text': "So, in fact, whenever you use sessions, as you will for Problem Set 9, you'll actually see a folder suddenly appear called flask underscore session,", 'start': 6450.126, 'duration': 7.481}, {'end': 6464.729, 'text': "inside of which are the cookies, essentially for any users or friends or yourself who've been visiting your particular application.", 'start': 6457.607, 'duration': 7.122}, {'end': 6466.769, 'text': "So I'm setting it to use the file system.", 'start': 6465.109, 'duration': 1.66}, {'end': 6471.49, 'text': "And I don't want them to be permanent, because I want, when you close your browser, the session to go away.", 'start': 6466.829, 'duration': 4.661}, {'end': 6473.931, 'text': 'They could be made to be permanent and last much longer.', 'start': 6471.77, 'duration': 2.161}, {'end': 6476.392, 'text': 'Then I tell my app to support sessions.', 'start': 6474.451, 'duration': 1.941}, {'end': 6477.653, 'text': "And that's it for now.", 'start': 6476.472, 'duration': 1.181}, {'end': 6481.615, 'text': "Let's see what this application actually does before we dissect the code.", 'start': 6478.333, 'duration': 3.282}, {'end': 6485.238, 'text': 'Let me go over to my terminal window, run flask run.', 'start': 6481.775, 'duration': 3.463}, {'end': 6490.04, 'text': 'And then let me go ahead and reload my preview URL.', 'start': 6486.078, 'duration': 3.962}, {'end': 6492.882, 'text': 'Give it a second to kick back in.', 'start': 6491.481, 'duration': 1.401}, {'end': 6495.604, 'text': 'Let me go ahead and open my URL.', 'start': 6493.843, 'duration': 1.761}, {'end': 6496.044, 'text': 'Come on.', 'start': 6495.644, 'duration': 0.4}, {'end': 6498.846, 'text': 'Whoops Let me go ahead.', 'start': 6497.565, 'duration': 1.281}, {'end': 6501.331, 'text': 'Too long of a break.', 'start': 6500.711, 'duration': 0.62}, {'end': 6501.732, 'text': 'There we go.', 'start': 6501.351, 'duration': 0.381}, {'end': 6504.853, 'text': 'So this website simply has a login form.', 'start': 6502.112, 'duration': 2.741}, {'end': 6507.915, 'text': "There's no password, though I could certainly add that and check for that, too.", 'start': 6504.873, 'duration': 3.042}, {'end': 6509.495, 'text': 'It just asks for your name.', 'start': 6508.255, 'duration': 1.24}, {'end': 6512.277, 'text': "So I'm going to log in as myself, David, and click Login.", 'start': 6509.555, 'duration': 2.722}, {'end': 6514.818, 'text': "And now notice, I'm currently at the slash login route.", 'start': 6512.357, 'duration': 2.461}, {'end': 6515.919, 'text': 'But notice this.', 'start': 6515.338, 'duration': 0.581}, {'end': 6523.963, 'text': 'If I try to go to the default route, just slash, which is where most websites live by default, notice that I magically get redirected to login.', 'start': 6516.279, 'duration': 7.684}, {'end': 6528.565, 'text': "So somehow my code knows, hey, if you're not logged in, you're going to slash login instead.", 'start': 6524.063, 'duration': 4.502}, {'end': 6531.066, 'text': 'Let me type in my name, David, and click Log In.', 'start': 6529.065, 'duration': 2.001}], 'summary': "Using sessions to store cookies on the server's hard drive, supporting temporary sessions for website login.", 'duration': 85.523, 'max_score': 6445.543, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6445543.jpg'}, {'end': 6862.101, 'src': 'heatmap', 'start': 6607.813, 'weight': 0.756, 'content': [{'end': 6610.174, 'text': "And then I've got some other syntax.", 'start': 6607.813, 'duration': 2.361}, {'end': 6614.036, 'text': "So we haven't seen this yet, but it's more Jinja stuff, which, again, is almost identical to Python.", 'start': 6610.194, 'duration': 3.842}, {'end': 6622.058, 'text': "If there's a name in the session variable, then literally say, you are logged in as, curly braces, session bracket name.", 'start': 6614.776, 'duration': 7.282}, {'end': 6623.318, 'text': 'And then notice this.', 'start': 6622.538, 'duration': 0.78}, {'end': 6626.599, 'text': "I've got a simple HTML link to log out via slash log out.", 'start': 6623.478, 'duration': 3.121}, {'end': 6631.62, 'text': 'Else, if there is no name in the session, then it apparently says, you are not logged in.', 'start': 6627.179, 'duration': 4.441}, {'end': 6634.501, 'text': 'And it leads me to an HTML link to slash log in.', 'start': 6632.04, 'duration': 2.461}, {'end': 6635.561, 'text': 'And then end if.', 'start': 6634.801, 'duration': 0.76}, {'end': 6638.444, 'text': 'So again, Jinja does not rely on indentation.', 'start': 6636.061, 'duration': 2.383}, {'end': 6641.447, 'text': "Recall that HTML and CSS don't really care about indentation.", 'start': 6638.484, 'duration': 2.963}, {'end': 6642.368, 'text': 'Only the human does.', 'start': 6641.527, 'duration': 0.841}, {'end': 6650.676, 'text': "But in code with Jinja, you need these end tags, end block, end for, end if, to make super obvious that you're done with that thought.", 'start': 6642.908, 'duration': 7.768}, {'end': 6655.758, 'text': 'So session is just this magic variable that we now have access to,', 'start': 6651.557, 'duration': 4.201}, {'end': 6666.06, 'text': "because we've included these two lines of code and these that handle that whole process of stamping every user's hand with a different unique identifier.", 'start': 6655.758, 'duration': 10.302}, {'end': 6672.241, 'text': 'If I made my code space public and I let all of you visit the exact same URL, all of you would be logged out by default.', 'start': 6666.12, 'duration': 6.121}, {'end': 6678.062, 'text': 'You could all type your own names individually, all log in at the same URL using different sessions.', 'start': 6672.281, 'duration': 5.781}, {'end': 6686.109, 'text': 'And in fact, I would then see, if I go into my terminal window here in my login directory, notice the flask session directory I mentioned.', 'start': 6678.422, 'duration': 7.687}, {'end': 6692.636, 'text': 'And if I cd into that and type ls, notice that I had two tabs open, or actually, I think I started the server twice.', 'start': 6686.129, 'duration': 6.507}, {'end': 6694.037, 'text': 'I have two files in there.', 'start': 6692.916, 'duration': 1.121}, {'end': 6696.759, 'text': 'I would ultimately have one file for every one of you.', 'start': 6694.137, 'duration': 2.622}, {'end': 6703.005, 'text': "And that's what's beautiful about sessions is it creates the illusion of per user storage.", 'start': 6697.02, 'duration': 5.985}, {'end': 6705.727, 'text': 'Inside of my session is my name.', 'start': 6703.766, 'duration': 1.961}, {'end': 6708.568, 'text': 'Inside of your session, so to speak, is your name.', 'start': 6705.867, 'duration': 2.701}, {'end': 6712.47, 'text': 'And the same is going to apply to shopping carts ultimately as well.', 'start': 6708.788, 'duration': 3.682}, {'end': 6714.191, 'text': "Let's see how login works here.", 'start': 6712.75, 'duration': 1.441}, {'end': 6718.653, 'text': 'My login route supports both get and post, so I could play around if I want.', 'start': 6714.591, 'duration': 4.062}, {'end': 6719.733, 'text': 'And notice this.', 'start': 6719.033, 'duration': 0.7}, {'end': 6723.034, 'text': 'This login route is kind of interesting as follows.', 'start': 6720.413, 'duration': 2.621}, {'end': 6729.257, 'text': 'If the user got to this route via post, my inference is that they must have submitted a form.', 'start': 6723.595, 'duration': 5.662}, {'end': 6732.699, 'text': "Why? Because that's how I'm going to design the HTML form in a second.", 'start': 6729.337, 'duration': 3.362}, {'end': 6741.181, 'text': "And if they did submit the form via post, I'm going to store in the session at the name key whatever the human's name is.", 'start': 6733.439, 'duration': 7.742}, {'end': 6743.502, 'text': "And then I'm going to redirect them back to slash.", 'start': 6741.381, 'duration': 2.121}, {'end': 6746.463, 'text': "Otherwise, I'm going to show them the login form.", 'start': 6744.342, 'duration': 2.121}, {'end': 6747.643, 'text': "So this is what's kind of cool.", 'start': 6746.523, 'duration': 1.12}, {'end': 6756.965, 'text': "If I go to this login form, which lives at literally slash login, by default, when you visit a URL like that, you're visiting via get.", 'start': 6748.023, 'duration': 8.942}, {'end': 6758.566, 'text': "And so that's why I see the form.", 'start': 6757.005, 'duration': 1.561}, {'end': 6760.327, 'text': 'However, notice this.', 'start': 6759.286, 'duration': 1.041}, {'end': 6764.389, 'text': 'The form very cleverly submits to itself.', 'start': 6760.787, 'duration': 3.602}, {'end': 6771.452, 'text': 'Like the one route slash login submits to its same self slash login, but it uses post when you submit the form.', 'start': 6764.569, 'duration': 6.883}, {'end': 6778.096, 'text': 'And this is a nice way of having one route, but for two different types of operations or views.', 'start': 6771.953, 'duration': 6.143}, {'end': 6782.618, 'text': "When I'm just there visiting slash login via a URL, it shows me the form.", 'start': 6778.516, 'duration': 4.102}, {'end': 6787.621, 'text': 'But if I submit the form, then this logic, these three lines kick in.', 'start': 6782.918, 'duration': 4.703}, {'end': 6792.483, 'text': 'And this just avoids my having to have both an index route and a greet route, for instance.', 'start': 6788.161, 'duration': 4.322}, {'end': 6796.904, 'text': 'I can just have one route that handles both get and post.', 'start': 6792.563, 'duration': 4.341}, {'end': 6800.705, 'text': "How about logout? What does this do? Well, it's as simple as this.", 'start': 6797.664, 'duration': 3.041}, {'end': 6808.368, 'text': "Change whatever name is in the session to be none, which is Python's version of null, essentially, and then redirect the user back to slash.", 'start': 6801.165, 'duration': 7.203}, {'end': 6814.055, 'text': 'Because now, in index.html, I will not notice a name there anymore.', 'start': 6808.428, 'duration': 5.627}, {'end': 6815.359, 'text': 'This will be false.', 'start': 6814.476, 'duration': 0.883}, {'end': 6818.106, 'text': "And so I'll tell the user instead, you are not logged in.", 'start': 6815.72, 'duration': 2.386}, {'end': 6824.814, 'text': 'So I want to say as simple as this is, though I realize this is a bunch of steps involved.', 'start': 6819.212, 'duration': 5.602}, {'end': 6829.276, 'text': 'this is the essence of every website on the internet that has usernames and passwords.', 'start': 6824.814, 'duration': 4.462}, {'end': 6830.937, 'text': 'And we skipped the password name step for that.', 'start': 6829.296, 'duration': 1.641}, {'end': 6832.618, 'text': 'More on that in problem set 9.', 'start': 6830.957, 'duration': 1.661}, {'end': 6836.659, 'text': "But this is how every website out there remembers that you're logged in.", 'start': 6832.618, 'duration': 4.041}, {'end': 6842.922, 'text': 'And how this works ultimately is that as soon as you use in Python lines like this and lines like this,', 'start': 6836.959, 'duration': 5.963}, {'end': 6847.166, 'text': 'Flask takes care of stamping the virtual hand of all of your users.', 'start': 6843.462, 'duration': 3.704}, {'end': 6856.075, 'text': 'And whenever Flask sees the same cookie coming back from a user, it grabs the appropriate file from that folder,', 'start': 6847.506, 'duration': 8.569}, {'end': 6862.101, 'text': 'loads it into the session global variable so that your code is now unique to that user and their name.', 'start': 6856.075, 'duration': 6.026}], 'summary': 'Jinja syntax for user login, logout, and session management in flask, creating unique session files for each user.', 'duration': 254.288, 'max_score': 6607.813, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6607813.jpg'}], 'start': 5647.514, 'title': 'Automating email generation and implementing sessions in flask', 'summary': 'Covers automating email generation for intramural sports, using flask-mail for email confirmation, encountering spam issues, and implementing sessions and cookies for stateful web applications.', 'chapters': [{'end': 5793.933, 'start': 5647.514, 'title': 'Automated email generation for frosh ims', 'summary': 'Covers the process of automatically generating emails to the proctor in charge of the intramural sports program, including the configuration of flask application with various settings and the use of environment variables for security purposes.', 'duration': 146.419, 'highlights': ['The process of automatically generating emails to the proctor in charge of the intramural sports program is described, including the use of CSV files and automatically generated emails for registering individuals. The speaker mentions the process of automatically generating emails to the proctor in charge of the intramural sports program and the use of CSV files for storing data, as well as the generation of a running history of people registering.', 'The configuration of Flask application with various settings such as default sender address, password, port number, mail server, and TLS is explained. The speaker explains the configuration of Flask application with various settings including default sender address, password, port number, mail server, and the use of TLS for encryption.', 'The use of environment variables for storing sensitive information like username and password is emphasized. The speaker emphasizes the use of environment variables for storing sensitive information like username and password for security purposes.']}, {'end': 6038.496, 'start': 5794.494, 'title': 'Automating email confirmation in flask', 'summary': 'Demonstrates automating the sending of emails in flask, using flask-mail to register a user for a sport event, and encountering an issue with emails ending up in spam, showcasing the simplicity of automating emails in flask by validating and confirming registrations with just a few lines of code.', 'duration': 244.002, 'highlights': ['The chapter demonstrates automating the sending of emails in Flask, showcasing the simplicity of automating emails in Flask by validating and confirming registrations with just a few lines of code, using Flask-Mail to register a user for a sport event.', "The speaker registers for a sport event using Flask Run on version 5, and when successful, an email is sent to the registered user's email address.", "The speaker encounters issues with the email ending up in spam due to 'less secure apps using SMTP', highlighting the necessity to configure Gmail in a certain way to allow outbound email.", 'The speaker explains the process of automating email confirmation in Flask, demonstrating simplicity by using only a few lines of code to validate and confirm registrations.', 'The speaker configures the current app with mail support and imports the necessary libraries to create and send emails, highlighting the ease of generating emails in Flask with the requisite access and software installed.']}, {'end': 6398.311, 'start': 6039.136, 'title': 'Implementing sessions and cookies', 'summary': 'Discusses the implementation of sessions and cookies, which are key components of web applications, allowing users to stay logged in, and it aims to build an application that is stateful using the building blocks explained so far.', 'duration': 359.175, 'highlights': ['Sessions and cookies allow users to stay logged in and implement features like a shopping cart, making HTTP stateful. Sessions and cookies enable users to stay logged in and implement features such as a shopping cart, addressing the challenge of making HTTP stateful.', 'Cookies are a way of reminding the website that the user has already provided their credentials, acting as a pass to come and go efficiently. Cookies act as a means of efficiently reminding the website that the user has already provided their credentials, allowing them to come and go efficiently.', 'Cookies are implemented as HTTP headers and consist of a big random value, ensuring low probability of someone guessing the value and pretending to be the user. Cookies are implemented as HTTP headers with a large, seemingly random value, minimizing the statistical probability of someone guessing the value and impersonating the user.']}, {'end': 6946.998, 'start': 6399.051, 'title': 'Flask login app and session implementation', 'summary': 'Discusses the implementation of sessions in a flask login app, covering the configuration of session storage, automatic redirection based on user login status, and the use of sessions for maintaining user state and shopping carts.', 'duration': 547.947, 'highlights': ['The chapter covers the implementation of sessions in a Flask login app, including the configuration of session storage and the use of Flask support for sessions. ', 'It explains the automatic redirection based on user login status, demonstrating how the code redirects users to the login page if they are not logged in. ', "The usage of sessions for maintaining user state and the illusion of per user storage is elaborated, highlighting how each user's session is unique and separate, allowing for individual user data storage. ", 'The demonstration of utilizing sessions for shopping carts is discussed, showcasing the potential use of sessions for e-commerce applications. ']}], 'duration': 1299.484, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E5647514.jpg', 'highlights': ['Automatically generate emails for intramural sports program using CSV files and Flask-Mail.', 'Configure Flask application with various settings including default sender address, password, and TLS for encryption.', 'Emphasize the use of environment variables for storing sensitive information like username and password for security purposes.', 'Demonstrate simplicity of automating email confirmation in Flask with just a few lines of code.', "Encounter and resolve issues with emails ending up in spam due to 'less secure apps using SMTP' by configuring Gmail.", 'Explain the implementation of sessions and cookies to enable users to stay logged in and implement features like a shopping cart.', 'Highlight the use of cookies as a means of efficiently reminding the website that the user has already provided their credentials.', 'Cover the implementation of sessions in a Flask login app, including configuration of session storage and automatic redirection based on user login status.']}, {'end': 8122.23, 'segs': [{'end': 6971.652, 'src': 'embed', 'start': 6947.498, 'weight': 5, 'content': [{'end': 6955.641, 'text': "In my slash route, I'm selecting star from books, which is going to give me a list of dictionaries, each of which represents a row of books.", 'start': 6947.498, 'duration': 8.143}, {'end': 6961.843, 'text': "And I'm going to pass that list of books into my books.html template, which is why this for loop.", 'start': 6956.001, 'duration': 5.842}, {'end': 6963.624, 'text': 'works the way it does.', 'start': 6962.503, 'duration': 1.121}, {'end': 6965.486, 'text': "Let's look at this actual database.", 'start': 6963.965, 'duration': 1.521}, {'end': 6971.652, 'text': 'Let me increase my terminal window and do SQLite of store.db.schema will show me everything.', 'start': 6965.506, 'duration': 6.146}], 'summary': 'Selecting all books from database and passing to template for display.', 'duration': 24.154, 'max_score': 6947.498, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6947498.jpg'}, {'end': 7024.851, 'src': 'embed', 'start': 6995.772, 'weight': 6, 'content': [{'end': 6999.434, 'text': "And what's magical here, just like deregister, even though I didn't highlight it at the time,", 'start': 6995.772, 'duration': 3.662}, {'end': 7005.558, 'text': "there's another type of input that allows you to specify a value without the human being able easily to change it.", 'start': 6999.434, 'duration': 6.124}, {'end': 7009.24, 'text': 'instead of type equals text or type equals submit, type equals hidden.', 'start': 7005.558, 'duration': 3.682}, {'end': 7012.922, 'text': 'will put the value in the form but not reveal it to the user.', 'start': 7009.66, 'duration': 3.262}, {'end': 7020.268, 'text': "So that's how I'm saying that the ID of this book is 1, the ID of this book is 2, the ID of this book is 3, and so forth.", 'start': 7013.283, 'duration': 6.985}, {'end': 7024.851, 'text': 'And each of these forms then will submit apparently to slash cart using post.', 'start': 7020.608, 'duration': 4.243}], 'summary': 'Using type=hidden input to specify values without user visibility, e.g., book ids, in forms submitted to slash cart using post.', 'duration': 29.079, 'max_score': 6995.772, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6995772.jpg'}, {'end': 7102.826, 'src': 'embed', 'start': 7058.569, 'weight': 4, 'content': [{'end': 7059.41, 'text': "But let's see.", 'start': 7058.569, 'duration': 0.841}, {'end': 7066.152, 'text': 'So the slash cart route supports both get or post, which is a nice way to consolidate things into one URL.', 'start': 7060.15, 'duration': 6.002}, {'end': 7068.253, 'text': 'All right, this is interesting.', 'start': 7066.252, 'duration': 2.001}, {'end': 7073.315, 'text': 'If there is not a quote, unquote cart key in session.', 'start': 7069.133, 'duration': 4.182}, {'end': 7078.937, 'text': "we haven't technically seen this syntax but, long story short, these lines here do ensure that the cart exists.", 'start': 7073.315, 'duration': 5.622}, {'end': 7087.92, 'text': "What do I mean by that? It makes sure that there's a cart key in the session global variable, and it's by default going to be an empty list.", 'start': 7079.017, 'duration': 8.903}, {'end': 7090.181, 'text': 'Why? That just means you have an empty shopping cart.', 'start': 7088.02, 'duration': 2.161}, {'end': 7097.824, 'text': 'But if the user visits this route via post, And the user did provide an ID.', 'start': 7090.801, 'duration': 7.023}, {'end': 7101.326, 'text': "They didn't muck with the form in any way and try to hack into the website.", 'start': 7097.864, 'duration': 3.462}, {'end': 7102.826, 'text': 'They gave me a valid ID.', 'start': 7101.546, 'duration': 1.28}], 'summary': 'The slash cart route handles get or post requests, ensures the cart exists, and populates it with a valid id.', 'duration': 44.257, 'max_score': 7058.569, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E7058569.jpg'}, {'end': 7174.209, 'src': 'embed', 'start': 7147.92, 'weight': 3, 'content': [{'end': 7152.783, 'text': "which is how this website knows that it's me adding these books to my cart and not you or not Carter or not Emma.", 'start': 7147.92, 'duration': 4.863}, {'end': 7160.426, 'text': 'Indeed, if all of us visited the same long URL and I made it public and allowed that then we would all have our own illusions of our own separate carts.', 'start': 7153.063, 'duration': 7.363}, {'end': 7167.027, 'text': 'And each of those carts, in practice, would just be stored in this flask session directory on the server,', 'start': 7160.766, 'duration': 6.261}, {'end': 7174.209, 'text': 'so that the server can keep track of each of us using again these cookie values that are being sent back and forth via these headers.', 'start': 7167.027, 'duration': 7.182}], 'summary': 'Website uses cookies to track individual carts, stored in flask session directory.', 'duration': 26.289, 'max_score': 7147.92, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E7147920.jpg'}, {'end': 7382.311, 'src': 'embed', 'start': 7353.212, 'weight': 0, 'content': [{'end': 7359.295, 'text': 'so that for final projects especially if you do want to add JavaScript functionality much more interactive user interface,', 'start': 7353.212, 'duration': 6.083}, {'end': 7364.758, 'text': 'you at least have the bare bones of a mental model for how you can tie these languages together,', 'start': 7359.295, 'duration': 5.463}, {'end': 7370.301, 'text': 'even though our focus generally has been more on Python and SQL than on JavaScript from last week.', 'start': 7364.758, 'duration': 5.543}, {'end': 7372.082, 'text': 'Let me go ahead and open up.', 'start': 7370.741, 'duration': 1.341}, {'end': 7375.605, 'text': 'an example called shows, version 0 of this.', 'start': 7373.042, 'duration': 2.563}, {'end': 7376.886, 'text': 'And let me do flask run.', 'start': 7375.685, 'duration': 1.201}, {'end': 7382.311, 'text': 'And let me go into my URL here and see what this application looks like by default.', 'start': 7377.366, 'duration': 4.945}], 'summary': 'Learn to integrate javascript for a more interactive user interface in final projects.', 'duration': 29.099, 'max_score': 7353.212, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E7353212.jpg'}, {'end': 7596.744, 'src': 'embed', 'start': 7568.968, 'weight': 2, 'content': [{'end': 7572.991, 'text': "All right, so these days, though, we're in the habit of seeing autocomplete.", 'start': 7568.968, 'duration': 4.023}, {'end': 7575.693, 'text': "And you start typing something, and you don't have to hit Submit.", 'start': 7573.031, 'duration': 2.662}, {'end': 7576.574, 'text': "You don't have to click a button.", 'start': 7575.713, 'duration': 0.861}, {'end': 7577.615, 'text': "You don't have to go to a new page.", 'start': 7576.594, 'duration': 1.021}, {'end': 7580.557, 'text': 'Web applications nowadays are much more dynamic.', 'start': 7578.015, 'duration': 2.542}, {'end': 7583.779, 'text': "So let's take a look at this version 1 of this thing.", 'start': 7581.057, 'duration': 2.722}, {'end': 7588.32, 'text': 'Let me go into shows 1.', 'start': 7584.199, 'duration': 4.121}, {'end': 7591.721, 'text': 'and close my previous tabs and run flask run in here.', 'start': 7588.32, 'duration': 3.401}, {'end': 7596.744, 'text': "And it's almost the same thing, but watch the behavior change a little bit.", 'start': 7592.182, 'duration': 4.562}], 'summary': 'Web applications have become more dynamic with autocomplete feature, eliminating the need for hitting submit or clicking a button.', 'duration': 27.776, 'max_score': 7568.968, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E7568968.jpg'}, {'end': 7867.826, 'src': 'embed', 'start': 7845.396, 'weight': 1, 'content': [{'end': 7853.441, 'text': 'like literally every cool application nowadays you load the page once and then it keeps on interacting with you without you reloading or having to change the URL.', 'start': 7845.396, 'duration': 8.045}, {'end': 7861.544, 'text': "let's actually use a format called JSON, JavaScript Object Notation, which is to say there's just a better, more efficient,", 'start': 7854.361, 'duration': 7.183}, {'end': 7863.924, 'text': 'better designed way to send that same data.', 'start': 7861.544, 'duration': 2.38}, {'end': 7867.826, 'text': "I'm going to go into shows 2 now and do flask run.", 'start': 7864.425, 'duration': 3.401}], 'summary': 'Modern applications use json for efficient data transfer and dynamic interaction.', 'duration': 22.43, 'max_score': 7845.396, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E7845396.jpg'}], 'start': 6947.498, 'title': 'Web development with python, sqlite, flask, and javascript', 'summary': 'Covers using python and sqlite for web development, implementing flask cart functionality for managing user shopping carts, and creating dynamic web applications with javascript for an interactive user experience. it also includes a live example and its application in problem set 9.', 'chapters': [{'end': 7012.922, 'start': 6947.498, 'title': 'Using python and sqlite for web development', 'summary': 'Demonstrates how to use python to interact with an sqlite database, displaying a list of books and adding a hidden value to a form for web applications.', 'duration': 65.424, 'highlights': ['Interacting with a SQLite database in Python to display a list of books in a web application. SQLite database, list of books', 'Using a for loop to pass a list of books into a template for display in the UI. For loop, template, UI', "Demonstrating the structure of the database with a table called 'books' and two columns, 'ID' and 'title'. Database structure, table and columns", 'Displaying seven unique books from the database. Seven unique books', 'Implementing a form with hidden input type to add a value without revealing it to the user. Hidden input type']}, {'end': 7547.815, 'start': 7013.283, 'title': 'Flask cart functionality', 'summary': 'Discusses the functionality of the slash cart route, which supports both get and post requests, ensures the existence of a cart key in the session variable, and allows users to add books to their shopping cart, with each user having their own separate cart stored in the flask session directory.', 'duration': 534.532, 'highlights': ['The slash cart route supports both get or post, consolidating functionality into one URL. The slash cart route supports both get or post, which consolidates functionality into one URL, providing a clean and succinct URL for shopping cart functionality.', 'Ensuring the existence of a cart key in the session global variable, by default being an empty list, to store the books added to the cart. The code ensures the existence of a cart key in the session global variable, by default being an empty list, to store the books added to the cart, allowing users to have their own separate carts.', "Allowing users to add books to their cart and storing each user's cart in the flask session directory. Users can add books to their cart, and each user has their own separate cart stored in the flask session directory, ensuring individual shopping carts for different users."]}, {'end': 8122.23, 'start': 7548.475, 'title': 'Creating dynamic web applications', 'summary': 'Explains the evolution from a basic web application to a more dynamic version using autocomplete, by implementing javascript to fetch and parse json data for a more efficient and interactive user experience, demonstrated through a live example and its application in problem set 9.', 'duration': 573.755, 'highlights': ['The evolution from a basic web application to a more dynamic version using autocomplete is demonstrated through a live example. The chapter demonstrates the transition from a traditional web application to a more dynamic version with autocomplete functionality, allowing users to see instant results without the need for reloading or changing the URL.', 'Implementing JavaScript to fetch and parse JSON data is highlighted as a more efficient and interactive approach. The use of JavaScript to fetch and parse JSON data is emphasized as a more efficient and interactive approach, enabling the application to receive compact and machine-friendly data, ultimately leading to an improved user experience.', 'The application of the concepts in problem set 9 is mentioned, focusing on real-world API usage and data conversion from JSON to HTML. The chapter mentions the practical application of the concepts in problem set 9, where students will utilize a real-world API to receive data in JSON format and convert it to HTML, demonstrating the real-world applicability of the discussed concepts.']}], 'duration': 1174.732, 'thumbnail': 'https://coursnap.oss-ap-southeast-1.aliyuncs.com/video-capture/CUIK3tKNH5E/pics/CUIK3tKNH5E6947498.jpg', 'highlights': ['Covers using python and sqlite for web development, implementing flask cart functionality for managing user shopping carts, and creating dynamic web applications with javascript for an interactive user experience.', 'The use of JavaScript to fetch and parse JSON data is emphasized as a more efficient and interactive approach, enabling the application to receive compact and machine-friendly data, ultimately leading to an improved user experience.', 'The chapter demonstrates the transition from a traditional web application to a more dynamic version with autocomplete functionality, allowing users to see instant results without the need for reloading or changing the URL.', 'Users can add books to their cart, and each user has their own separate cart stored in the flask session directory, ensuring individual shopping carts for different users.', 'The slash cart route supports both get or post, consolidating functionality into one URL, providing a clean and succinct URL for shopping cart functionality.', 'Interacting with a SQLite database in Python to display a list of books in a web application.', 'Implementing a form with hidden input type to add a value without revealing it to the user.', 'The evolution from a basic web application to a more dynamic version using autocomplete is demonstrated through a live example.', 'Ensuring the existence of a cart key in the session global variable, by default being an empty list, to store the books added to the cart.']}], 'highlights': ['The chapter emphasizes the importance of programming fundamentals and web development, highlighting the significance of languages like Python and SQL in creating web and mobile applications.', 'Flask simplifies web app development by providing libraries and conventions Flask solves common problems in web development, such as analyzing URLs, handling files, and sending emails, making these tasks easier for developers.', "Demonstration of using static files and the power of templating with Jinja, showcasing the process of creating a static folder, editing the CSS file, and utilizing Jinja's for loop syntax to dynamically generate HTML.", 'Automatically generate emails for intramural sports program using CSV files and Flask-Mail.', 'Covers using python and sqlite for web development, implementing flask cart functionality for managing user shopping carts, and creating dynamic web applications with javascript for an interactive user experience.']}