{"id":22420044,"url":"https://github.com/tempehs/flask_pwa_programming_for_the_web_task_source","last_synced_at":"2025-06-10T12:33:07.389Z","repository":{"id":258001991,"uuid":"864355009","full_name":"TempeHS/Flask_PWA_Programming_For_The_Web_Task_Source","owner":"TempeHS","description":"Completed source code for a guided development task for students to build their first PWA to the W3C standard using Python Flask and SQLite3 for the backend.","archived":false,"fork":false,"pushed_at":"2025-06-04T02:20:37.000Z","size":10611,"stargazers_count":0,"open_issues_count":0,"forks_count":17,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-04T09:16:18.472Z","etag":null,"topics":["bash-scripting","codespaces","css3","flask","html5","javascript","jinga2","js","learn-python","learning-by-doing","learning-exercise","progressive-web-app","pwa","python3","sqlite3","vscode"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TempeHS.png","metadata":{"files":{"readme":"docs/README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-28T02:35:09.000Z","updated_at":"2025-06-04T02:20:39.000Z","dependencies_parsed_at":"2024-10-27T10:47:16.415Z","dependency_job_id":"718cae91-f562-41eb-9164-d90994a86fc4","html_url":"https://github.com/TempeHS/Flask_PWA_Programming_For_The_Web_Task_Source","commit_stats":null,"previous_names":["tempehs/flask_pwa_programming_for_the_web_task_source"],"tags_count":0,"template":false,"template_full_name":"TempeHS/Secure_Flask_PWA_Template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TempeHS%2FFlask_PWA_Programming_For_The_Web_Task_Source","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TempeHS%2FFlask_PWA_Programming_For_The_Web_Task_Source/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TempeHS%2FFlask_PWA_Programming_For_The_Web_Task_Source/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TempeHS%2FFlask_PWA_Programming_For_The_Web_Task_Source/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TempeHS","download_url":"https://codeload.github.com/TempeHS/Flask_PWA_Programming_For_The_Web_Task_Source/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TempeHS%2FFlask_PWA_Programming_For_The_Web_Task_Source/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259075764,"owners_count":22801636,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash-scripting","codespaces","css3","flask","html5","javascript","jinga2","js","learn-python","learning-by-doing","learning-exercise","progressive-web-app","pwa","python3","sqlite3","vscode"],"created_at":"2024-12-05T16:17:29.776Z","updated_at":"2025-06-10T12:33:07.347Z","avatar_url":"https://github.com/TempeHS.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# INTRODUCTION TO FLASK AND PROGRESSIVE WEB APPS TUTORIAL\n\nThis guided tutorial will introduce HSC Software Engineering to the basics of developing websites with the [Python Flask framework](https://flask.palletsprojects.com/en/3.0.x/). The tutorial has been specifically designed for requirements in the [NESA Software Engineering Syllabus](https://curriculum.nsw.edu.au/learning-areas/tas/software-engineering-11-12-2022/content/n12/fa6aab137e) and for students in NSW Department of Education schools using eT4L computers.\n\nA [list of popular PWA's](https://business.adobe.com/blog/basics/progressive-web-app-examples) (including Ube, Spotify, Facebook and Google Maps)\n\n## Overview of Progressive Web Apps\n\nA [Progressive Web Apps (PWAs)](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps?ref=arctype.com) is an app that is built using web platform technologies, but that provides a user experience like that of a platform-specific app. Like a website, a PWA can run on multiple platforms and devices from a single codebase. Like a platform-specific app, it can be installed on the device, can operate while offline and in the background, and can integrate with the device and with other installed apps.\n\n### Technical features of PWAs\n\nBecause PWAs are websites, they have the same basic features as any other website: at least one HTML page, which loads CSS and JavaScript. Javascript is the language of the web and is exclusively used for the client-side front end; python, in the web context, can only be used in the back end. Like a normal website, the JavaScript loaded by the page has a global Window object and can access all the Web APIs that are available through that object. The PWA standard as defined by [W3C Standards](https://www.w3.org/standards/) has some specific features additional to a website:\n\n| Feature             | Purpose                                                                                                                                                                                                                                                                                                                                                       |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| manifest.json       | An app manifest file, which, at a minimum, provides information that the operating system needs to install the PWA, such as the app name, screen orientation and icon set for different-sized views.                                                                                                                                                          |\n| serviceworker.js    | A service worker, which, at a minimum, manages the caching that enables an online and offline experience whilst also interfacing with API's such as the [notification web API](https://developer.mozilla.org/en-US/docs/Web/API/Notification). It's important to understand that this JS file cannot control the DOM of the application for security reasons. |\n| Icons \u0026 screenshots | A set of icons and screenshots that are used when uploading to an app store and when installing it as a native application. It is these icons that will be used in the desktop or app launcher when installed.                                                                                                                                                |\n| Installable         | Because of the information contained in the manifest.json all PWA's can be installed like a native app. They can also be packaged and uploaded to the Google, Microsoft \u0026 Apple app stores.                                                                                                                                                                   |\n| Cached locally      | Because the service worker details all apps and pages to be cached (all pages must have a \\*.html name), the app and its resources can be cached locally for quick load times.                                                                                                                                                                                |\n\n_Note backend apps where the web server serves all pages from the DNS root do not meet the PWA specification._\n\nThe below image illustrates how the servicework manages online and offline behaviour.\n\n![A highlevel illustration of the service worker](/docs/README_resources/Progressive-Web-Apps-Architecture.png \"The service worker handles the initial requests and sets the behaviour depending on if the app is on or offline.\")\n\n## Your end product\n\nThis screen capture shows how the final PWA will be rendered to the user.\n\n![Screen capture of the finished PWA](/docs/README_resources/final_app.png \"This is what your application will look like\")\n\n## Requirements\n\n1. [VSCode](https://code.visualstudio.com/download)\n2. [Python 3.x](https://www.python.org/downloads/)\n3. [GIT 2.x.x +](https://git-scm.com/downloads)\n\n\u003e [!Important]\n\u003e MacOS and Linux users may have a `pip3` soft link instead of `pip`, run the below commands to see what path your system is configured with and use that command through the project. If neither command returns a version, then likely [Python 3.x](https://www.python.org/downloads/) needs to be installed.\n\u003e\n\u003e ```bash\n\u003e pip show pip\n\u003e pip3 show pip\n\u003e ```\n\n## Prior learning\n\n1. Bash basics\n2. SQL\n3. HTML Basics\n4. CSS Basics\n5. Python\n\n## STEPS TO BUILDING YOUR FIRST PWA\n\n### Setup your environment\n\n![Screen recording of setting up VSCode](/docs/README_resources/get_vscode_started.gif \"Follow these steps to setup VSCode\")\n\n\u003e [!Note]\n\u003e Helpful VSCode settings are configured in [.vscode/settings.json](/.vscode/settings.json) which will automatically apply if you are not using a custom profile. If you are using a custom profile, it is suggested you manually apply those settings to your profile, especially the \\*.md file association, so the README.md default opens in preview mode and setting _bash_ as your default terminal.\n\n1. Install the necessary extensions for this tutorial.\n\n| Required Extensions                                                                                              | Suggested Python Extensions                                                                                  |\n| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| [medo64.render-crlf](https://marketplace.visualstudio.com/items?itemName=medo64.render-crlf)                     | [ms-python.flake8](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8)                     |\n| [McCarter.start-git-bash](https://marketplace.visualstudio.com/items?itemName=McCarter.start-git-bash)           | [ms-python.black-formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter)\\* |\n| [yy0931.vscode-sqlite3-editor](https://marketplace.visualstudio.com/items?itemName=yy0931.vscode-sqlite3-editor) | [ms-python.python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)                     |\n|                                                                                                                  | [oderwat.indent-rainbow](https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow)         |\n|                                                                                                                  | [esbenp.prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)\\*       |\n\n_\\*You will need to configure your formatters, it is recommended esbenp.prettier-vscode is your default formatter and ms-python.black-formatter is the Python language formatter_\n\n\u003e [!Important]\n\u003e From now on, you should aim to run all commands from the CLI. You are discouraged from left/right clicking the GUI. You will find it feels slow at first, but through disciplined use, you will become much quicker and more accurate with CLI commands than GUI controls.\n\nMake sure you open a new terminal with the keys \u003ckbd\u003eCtrl\u003c/kbd\u003e + \u003ckbd\u003e`\u003c/kbd\u003e and choose Git Bash from the menu option in the top right of the terminal shell.\n\n![Screen capture of the menu options for terminals](/docs/README_resources/git_bash_shell.png \"Choose Git Bash from the list\")\n\n1. Get the working files, which include this README.md\n   - Open a new window in VSCode\n   - Choose your working directory\n\n```bash\ngit clone https://github.com/TempeHS/Flask_PWA_Programming_For_The_Web_Task_Template.git\ncd Flask_PWA_Programming_For_The_Web_Task_Template\n```\n\n\u003e [!TIP]\n\u003e Alternatively, you can fork the [template repository](https://github.com/TempeHS/Flask_PWA_Programming_For_The_Web_Task_Template) to your own GitHub account and open it in a Codespace in which all dependencies and extensions will be automatically installed.\n\n4. Install necessary dependencies.\n\n```bash\npip install flask\n```\n\n---\n\n### Create files and folders for your Flask Project\n\n1. Make a folder for all your working documents like photoshop \\*.psd files, developer documentation etc.\n\n```bash\nmkdir working_documents\n```\n\n2. Create a license file.\n\n```bash\ntouch LICENSE\ncode LICENSE\n```\n\nCopy the [GNU GPL license](https://www.gnu.org/licenses/gpl-3.0.txt) text into the file. GNU GPL is a free software license, or copyleft license, that guarantees end users the freedom to run, study, share, and modify the software. 3. Create your directory structure and some base files using BASH scripts reading text files.\n\n```text\n├── database\n├── working_documents\n├── static\n│   ├── css\n│   ├── icons\n│   ├── images\n│   ├── js\n├── templates\n├── LICENSE\n├── main.py\n└── database_manager.py\n```\n\n3. Populate a text file with a list of folders you need at the root of your project.\n\n```bash\ntouch folders.txt\ncode folder.txt\n```\n\n4. Run a BASH script to read the text file and create the folders listed in it.\n\n```bash\nwhile read -r line; do\necho $line\nmkdir -p $line\ndone \u003c folders.txt\n```\n\n5. Populate the file with a list of files you need at the root of your project.\n\n```bash\ntouch files.txt\ncode files.txt\n```\n\n6. Run a BASH script to read the text file and create the files listed in it.\n\n```bash\nwhile read -r line; do\necho $line\ntouch -p $line\ndone \u003c files.txt\\\n```\n\n\u003e [!Important]\n\u003e\n\u003e - The last list item needs a line ending, so make sure there is a blank last line in the file.\n\u003e - You will find that all file and folder names have an unwanted `space` character at the end. This is because you are using a BASH emulator on the Windows operating system. Bash is a Unix language that uses [LF Unicode character 000A while Windows uses CRLF Unicode characters 000D + 000A](https://learn.microsoft.com/en-us/visualstudio/ide/encodings-and-line-breaks?view=vs-2022). Because you have installed the [medo64.render-crlf](https://marketplace.visualstudio.com/items?itemName=medo64.render-crlf) extension, click on `CRLF` in the bottom bar of VSCode and choose `LF` to change the line ending before running your BASH script.\n\n---\n\n### Setup your SQLite3 Database\n\n```bash\ncd database\ntouch data_source.db\ntouch my_queries.sql\ncode my_queries.sql\n```\n\n\u003e [!Note]\n\u003e The following SQL queries are provided as an example only. Students are encouraged to select their content and design a database schema for it; ideas include:\n\u003e\n\u003e - Favourite bands\n\u003e - Favourite movies\n\u003e - Favourite games\n\u003e - Favourite books\n\u003e - etc\n\n1. To run SQLite3 SQL queries in VSCode\n   Open the DB file, then choose \"Query Editor\" from the top menu.\n\n```bash\ncode data_source.db\n```\n\n![Screen capture of query editor](/docs/README_resources/query_editor.png \"Choose Query Editor from the top menu\")\n\n```sql\nCREATE TABLE extension(extID INTEGER NOT NULL PRIMARY KEY,name TEXT NOT NULL, hyperlink TEXT NOT NULL,about TEXT NOT NULL,image TEXT NOT NULL,language TEXT NOT NULL);\n```\n\n2. After running each query put `--` infront of the query to turn it into a comment so it doesn't run again and error.\n3. Run SQL queries to populate your table.\n\n```sql\nINSERT INTO extension(extID,name,hyperlink,about,image,language) VALUES (1,\"Live Server\",\"https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer\",\"Launch a development local Server with live reload feature for static \u0026 dynamic pages\",\"https://ritwickdey.gallerycdn.vsassets.io/extensions/ritwickdey/liveserver/5.7.9/1661914858952/Microsoft.VisualStudio.Services.Icons.Default\",\"HTML CSS JS\");\n```\n\n```sql\nINSERT INTO extension(extID,name,hyperlink,about,image,language) VALUES (2,\"Render CR LF\",\"https://marketplace.visualstudio.com/items?itemName=medo64.render-crlf\",\"Displays the line ending symbol and optionally extra whitespace when 'Render whitespace' is turned on.\",\"https://medo64.gallerycdn.vsassets.io/extensions/medo64/render-crlf/1.7.1/1689315206970/Microsoft.VisualStudio.Services.Icons.Default\",\"#BASH\");\n```\n\n```sql\nINSERT INTO extension(extID,name,hyperlink,about,image,language) VALUES (3,\"Start GIT BASH\",\"https://marketplace.visualstudio.com/items?itemName=McCarter.start-git-bash\",\"Adds a bash command to VSCode that allows you to start git-bash in the current workspace's root folder.\",\"https://mccarter.gallerycdn.vsassets.io/extensions/mccarter/start-git-bash/1.2.1/1499505567572/Microsoft.VisualStudio.Services.Icons.Default\",\"#BASH\");\n```\n\n```sql\nINSERT INTO extension(extID,name,hyperlink,about,image,language) VALUES (4,\"SQLite3 Editor\",\"https://marketplace.visualstudio.com/items?itemName=yy0931.vscode-sqlite3-editor\",\"Edit SQLite3 files like you would in spreadsheet applications.\",\"https://yy0931.gallerycdn.vsassets.io/extensions/yy0931/vscode-sqlite3-editor/1.0.85/1690893830873/Microsoft.VisualStudio.Services.Icons.Default\",\"SQL\");\n```\n\n4. Run some SQL queries to test your database.\n\n```sql\nSELECT * FROM extension;\nSELECT * FROM extension WHERE language LIKE '#BASH';\n```\n\n---\n\n### Make your graphic assets\n\n\u003e [!Note]\n\u003e Graphic design is not the focus of this course. It is suggested that you do not spend excessive time designing logos and icons.\n\n1. Use Photoshop or [Canva](https://www.canva.com/en_au/signup/?signupRedirect=%2Fedu-signup\u0026loginRedirect=%2Fedu-signup\u0026brandingVariant=edu) to design a simple square logo 1080px X 1080px named logo.png. Save all working files (\\*.psd, pre-optimised originals, etc) into the working_documents directory.\n2. Design a simplified app icon 512px X 512px named favicon.png.\n3. Web optimise the images using [TinyPNG](https://tinypng.com/).\n4. Save the files into the static/images folder.\n5. Rename the 512x512 icon to icon-512x512.png, then resize and rename it as follows:\n   - icon-128x128.png\n   - icon-192x192.png\n   - icon-384x384.png\n   - icon-512x512.png\n6. Web optimise the images using [TinyPNG](https://tinypng.com/)\n7. Save the optimised icons to static/icons\n8. Save the optimised logo and favicon to static/images\n\n---\n\n### Setup your index.html using the Jinga2 template system\n\n\u003e [!Note]\n\u003e Adjust titles, headings and content to match your concept.\n\n```bash\ncd ../templates\ntouch layout.html\ncode layout.html\n```\n\n1. Insert the basic HTML structure in your templates/layout.html file.\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" /\u003e\n    \u003cmeta http-equiv=\"Content-Security-Policy\" content=\"script-src 'self';\" /\u003e\n    \u003clink rel=\"stylesheet\" href=\"static/css/style.css\" /\u003e\n    \u003ctitle\u003eVSCode Extension Catalogue\u003c/title\u003e\n    \u003clink rel=\"manifest\" href=\"static/manifest.json\" /\u003e\n    \u003clink rel=\"icon\" type=\"image/x-icon\" href=\"static/images/favicon.png\" /\u003e\n  \u003c/head\u003e\n\n  \u003cbody\u003e\n    \u003cmain\u003e\n      {% include \"partials/menu.html\" %} {% block content %}{% endblock %}\n    \u003c/main\u003e\n    \u003cscript src=\"static/js/app.js\"\u003e\u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n6. Insert the block content into index.html, you will add more later.\n\n```bash\ntouch index.html\ncode index.html\n```\n\n```html\n{% extends 'layout.html' %} {% block content %}\n\u003cdiv class=\"container\"\u003e\u003c/div\u003e\n{% endblock %}\n```\n\n---\n\n### Style the HTML core\n\n```bash\ncd ../static/css\ntouch style.css\ncode style.css\n```\n\n1. Insert the css code into static/css/style.css.\n\n```css\n@import url(\"https://fonts.googleapis.com/css?family=Nunito:400,700\u0026display=swap\");\n\n* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\nbody {\n  background: #fdfdfd;\n  font-family: \"Nunito\", sans-serif;\n  font-size: 1rem;\n}\n\nmain {\n  max-width: 900px;\n  margin: auto;\n  padding: 0.5rem;\n  text-align: center;\n}\n```\n\n---\n\n### Make and style the menu\n\n```bash\ncd ../..templates\nmkdir partials\ncd partials\ntouch menu.html\ncode menu.html\n```\n\n1. Insert the menu HTML into menu.html.\n\n```html\n\u003cnav\u003e\n  \u003cimg src=\"static\\images\\logo.png\" alt=\"VSCode Extensions site logo.\" /\u003e\n  \u003ch1\u003eVSCode Extensions\u003c/h1\u003e\n  \u003cul class=\"topnav\"\u003e\n    \u003cli\u003e\u003ca href=\"#\"\u003eHome\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"add.html\"\u003eAdd me\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"about.html\"\u003eAbout\u003c/a\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/nav\u003e\n```\n\n```bash\n`cd ../../static/css`\n`code style.css`\n```\n\n2. Style the menu by inserting this below your existing CSS in static/css/style.css.\n\n```css\nnav {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\nnav img {\n  height: 100px;\n}\n\nnav ul {\n  list-style: none;\n  display: flex;\n}\n\nnav li {\n  margin-right: 1rem;\n}\n\nnav ul li a {\n  text-decoration-line: none;\n  text-transform: uppercase;\n  color: #393b45;\n}\n\nnav ul li a:hover {\n  color: #14e6dd;\n}\n\nnav h1 {\n  color: #106d69;\n  margin-bottom: 0.5rem;\n}\n```\n\n\u003cHR\n\n### Render your website\n\n```bash\ncd ../..\ncode main.py\n```\n\n1. Insert the Flask python to the backend script.\n\n```python\nfrom flask import Flask\nfrom flask import render_template\nfrom flask import request\nimport database_manager as dbHandler\n\napp = Flask(__name__)\n\n@app.route('/index.html', methods=['GET'])\n@app.route('/', methods=['POST', 'GET'])\ndef index():\n  return render_template('/index.html')\n\nif __name__ == '__main__':\n  app.run(debug=True, host='0.0.0.0', port=5000)\n```\n\n2. Run the builtin webserver.\n\n```bash\npython main.py\n```\n\n3. Visit your website and look at the source in developer tools to see how the page has been rendered.\n\n\u003e [!Note]\n\u003e To explain how Jinga2 works in this example when index.html is called, the render will start with layout.html with the code from partials/menu.html inserted where `{% include \"partials/menu.html\" %}` is and the index.html content that is between the `{% block content %}` and `{% endblock %}` will be inserted in the same tags in the layout.html.\n\n---\n\n### Query your SQL database and migrate the content to the frontend as HTML\n\n```bash\ncode database_manager.py\n```\n\n1. Query the database and store the data in a variable.\n\n```python\nimport sqlite3 as sql\n\ndef listExtension():\n  con = sql.connect(\"database/data_source.db\")\n  cur = con.cursor()\n  data = cur.execute('SELECT * FROM extension').fetchall()\n  con.close()\n  return data\n```\n\n```bash\ncode main.py\n```\n\n2. Pass the data to the front end by modifying the existing `app.route`.\n\n```python\ndef index():\n   data = dbHandler.listExtension()\n   return render_template('/index.html', content=data)\n```\n\n```bash\ncd templates\ncode index.html\n```\n\n3. Use Janga2 to pass the data (which is a [tuple](https://www.w3schools.com/python/python_tuples.asp)) to front end content. Insert the HTML inside the `\u003cdiv class=\"container\"\u003e` of the index.html.\n\n```html\n{% for row in content %}\n\u003cdiv class=\"card\"\u003e\n  \u003cimg\n    class=\"card-image\"\n    src=\"{{ row[4] }}\"\n    alt=\"Product image for the {{ row[1] }} VSCode extension.\"\n  /\u003e\n  \u003ch1 class=\"card-name\"\u003e{{ row[1] }}\u003c/h1\u003e\n  \u003cp class=\"card-about\"\u003e{{ row[3] }}\u003c/p\u003e\n  \u003ca class=\"card-link\" href=\"{{ row[2] }}\"\n    \u003e\u003cbutton class=\"btn\"\u003eRead More\u003c/button\u003e\u003c/a\n  \u003e\n\u003c/div\u003e\n{% endfor %}\n```\n\n```bash\ncd ../static/css\ncode style.css\n```\n\n4. Style the cards by inserting this below your existing CSS in static/css/style.css.\n\n```css\n.container {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));\n  grid-gap: 1rem;\n  justify-content: center;\n  align-items: center;\n  margin: auto;\n  padding: 1rem 0;\n}\n\n.card {\n  display: flex;\n  align-items: center;\n  flex-direction: column;\n  width: 17rem;\n  background: #fff;\n  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);\n  border-radius: 10px;\n  margin: auto;\n  overflow: hidden;\n}\n\n.card-image {\n  width: 100%;\n  height: 15rem;\n  object-fit: cover;\n}\n\n.card-name {\n  color: #222;\n  font-weight: 700;\n  text-transform: capitalize;\n  font-size: 1.1rem;\n  margin-top: 0.5rem;\n}\n\n.card-about {\n  text-overflow: ellipsis;\n  width: 15rem;\n  white-space: nowrap;\n  overflow: hidden;\n  margin-bottom: 1rem;\n}\n\n.btn {\n  border: none;\n  background: none;\n  border-radius: 5px;\n  box-shadow: 1px 1px 2px rgba(21, 21, 21, 0.1);\n  cursor: pointer;\n  font-size: 1.25rem;\n  margin: 0 1rem;\n  padding: 0.25rem 2rem;\n  transition: all 0.25s ease-in-out;\n  background: hsl(110, 21%, 93%);\n  color: hsl(141, 100%, 22%);\n  margin-bottom: 1rem;\n}\n\n.btn:focus,\n.btn:hover {\n  box-shadow: 1px 1px 2px rgba(21, 21, 21, 0.2);\n  background: hsl(111, 21%, 86%);\n}\n\n.about-container {\n  font-size: 1.25rem;\n  margin-top: 2rem;\n  text-align: justify;\n  text-justify: inter-word;\n}\n```\n\n---\n\n### Finish the PWA code so it is compliant to W3 web standards\n\n1. Take a screen shot of the website. Then size the image to 1080px X 1920px, web optimise the images using [TinyPNG](https://tinypng.com/) and save it to static/icons.\n\n```bash\ncd ..\ncode manifest.json\n```\n\n2. Configure the manifest.json to the PWA standard by inserting the JSON below and validating the JSON with [jsonlint](https://jsonlint.com/). The manifest.json sets the configuration for the installation and caching of the PWA.\n\n```json\n{\n  \"name\": \"VSCode Extension Catalogue\",\n  \"short_name\": \"vscodeextcat\",\n  \"start_url\": \"/\",\n  \"display\": \"standalone\",\n  \"background_color\": \"#fdfdfd\",\n  \"theme_color\": \"#14E6DD\",\n  \"orientation\": \"landscape-primary\",\n  \"icons\": [\n    {\n      \"src\": \"icons/icon-128x128.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"128x128\",\n      \"purpose\": \"maskable\"\n    },\n    {\n      \"src\": \"icons/icon-128x128.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"128x128\",\n      \"purpose\": \"any\"\n    },\n    {\n      \"src\": \"icons/icon-192x192.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"192x192\",\n      \"purpose\": \"maskable\"\n    },\n    {\n      \"src\": \"icons/icon-192x192.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"192x192\",\n      \"purpose\": \"any\"\n    },\n    {\n      \"src\": \"icons/icon-384x384.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"384x384\",\n      \"purpose\": \"maskable\"\n    },\n    {\n      \"src\": \"icons/icon-384x384.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"384x384\",\n      \"purpose\": \"any\"\n    },\n    {\n      \"src\": \"icons/icon-512x512.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\",\n      \"purpose\": \"maskable\"\n    },\n    {\n      \"src\": \"icons/icon-512x512.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\",\n      \"purpose\": \"any\"\n    }\n  ],\n  \"screenshots\": [\n    {\n      \"src\": \"icons/desktop_screenshot.png\",\n      \"sizes\": \"1920x1080\",\n      \"type\": \"image/png\",\n      \"label\": \"\"\n    },\n    {\n      \"src\": \"icons/mobile_screenshot.png\",\n      \"sizes\": \"1080x1920\",\n      \"type\": \"image/png\",\n      \"form_factor\": \"wide\",\n      \"label\": \"\"\n    }\n  ]\n}\n```\n\n```bash\ncd js\ncode app.js\n```\n\n2. Configure the app.js to initiate the servicework.js by inserting the JS. This ensures that when the window (app) loads the serviceworker.js is called to memory.\n\n```js\nif (\"serviceworker\" in navigator) {\n  window.addEventListener(\"load\", function () {\n    navigator.serviceworker\n      .register(\"static/js/serviceworker.js\")\n      .then((res) =\u003e console.log(\"service worker registered\"))\n      .catch((err) =\u003e console.log(\"service worker not registered\", err));\n  });\n}\n```\n\n```bash\ncd js\ncode serviceworker.js\n```\n\n1. Configure the serviceworker.js by inserting the JS. The serviceworker.js, as the name suggests, is the file that does all the work in a PWA, including caching and API integration for the [WEB APIs](https://developer.mozilla.org/en-US/docs/Web/API).\n\n```js\nconst assets = [\n  \"/\",\n  \"static/css/style.css\",\n  \"static/js/app.js\",\n  \"static/images/logo.png\",\n  \"static/images/favicon.png\",\n  \"static/icons/icon-128x128.png\",\n  \"static/icons/icon-192x192.png\",\n  \"static/icons/icon-384x384.png\",\n  \"static/icons/icon-512x512.png\",\n  \"static/icons/desktop_screenshot.png\",\n  \"static/icons/mobile_screenshot.png\",\n];\n\nconst CATALOGUE_ASSETS = \"catalogue-assets\";\n\nself.addEventListener(\"install\", (installEvt) =\u003e {\n  installEvt.waitUntil(\n    caches\n      .open(CATALOGUE_ASSETS)\n      .then((cache) =\u003e {\n        console.log(cache);\n        cache.addAll(assets);\n      })\n      .then(self.skipWaiting())\n      .catch((e) =\u003e {\n        console.log(e);\n      })\n  );\n});\n\nself.addEventListener(\"activate\", function (evt) {\n  evt.waitUntil(\n    caches\n      .keys()\n      .then((keyList) =\u003e {\n        return Promise.all(\n          keyList.map((key) =\u003e {\n            if (key === CATALOGUE_ASSETS) {\n              console.log(\"Removed old cache from\", key);\n              return caches.delete(key);\n            }\n          })\n        );\n      })\n      .then(() =\u003e self.clients.claim())\n  );\n});\n\nself.addEventListener(\"fetch\", function (evt) {\n  evt.respondWith(\n    fetch(evt.request).catch(() =\u003e {\n      return caches.open(CATALOGUE_ASSETS).then((cache) =\u003e {\n        return cache.match(evt.request);\n      });\n    })\n  );\n});\n```\n\n---\n\n### Validate your PWA\n\nValidation is important to ensure the app is compliant with [W3 web standards](https://www.w3.org/standards/).\n\n1. Open your website in Chrome, open developer tools (F12), and run a Lighthouse report.\n\n![Screen cpature of Chrome Lighthouse report](/docs/README_resources/Chrome_Lighthouse_Report.png \"Click F12 and choose Lighthouse on the top menu of your developer tools\")\n\n. 2. Open your website in Edge, open developer tools (F12), and look at the application report.\n\n![Screen cpature of Chrome Lighthouse report](/docs/README_resources/Edge_Application_Report.png \"Click F12 and choose Lighthouse on the top menu of your developer tools\")\n\n.\n\n---\n\n### Take your app further\n\nThe following code snippets will help you create a simple form on the add.html page. This form allows people to add their details to an email database for updates on your catalogue. Less explicit instructions have been provided; students are encouraged to practice their BASH, SQL Flask, and HTML to bring it all together. The screenshot below shows what the page should look like, and when users submit, the database is updated.\n\n![Screen capture of the finished PWA](/docs/README_resources/form_example.png \"This is what your application will look like\")\n\n.\n\n1. Page specifications:\n   - Simple form where the user inserts their name and email address\n   - When they click submit, the database is updated\n   - The input form must be styled to be consistent with the rest of the website\n   - A message confirming submission is returned to the user\n2. SQL schema specifications:\n\n   - A new table called contact_list\n   - 3 columns\n\n     - id is the primary key and should increment automatically\n     - email must be unique\n     - name\n\n```python\ndef insertContact(email,name):\n  con = sql.connect(\"database/data_source.db\")\n  cur = con.cursor()\n  cur.execute(\"INSERT INTO contact_list (email,name) VALUES (?,?)\", (email,name))\n  con.commit()\n  con.close()\n```\n\n\u003e [!Note]\n\u003e You will need to catch the expectation of a duplicate email\n\n```python\n@app.route('/add.html', methods=['POST', 'GET'])\ndef add():\n   if request.method=='POST':\n      email = request.form['email']\n      name = request.form['name']\n      dbHandler.insertContact(email,name)\n      return render_template('/add.html', is_done=True\")\n   else:\n      return render_template('/add.html')\n```\n\n```html\n{% if is_done %} \u003c--DO THIS--\u003e {% else %} \u003c--DO THIS--\u003e {% endif %}\n```\n\n```html\n\u003cform action=\"/app.html\" method=\"POST\" class=\"box\"\u003e\n  \u003cdiv\u003e\n    \u003clabel class=\"form-label\"\u003eEmail address\u003c/label\u003e\n    \u003cinput\n      name=\"email\"\n      type=\"email\"\n      class=\"form-control\"\n      id=\"email\"\n      pattern=\"[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2, 4}$\"\n      placeholder=\"name@example.com\"\n    /\u003e\n  \u003c/div\u003e\n  \u003cdiv\u003e\n    \u003clabel class=\"form-label\"\u003eName\u003c/label\u003e\n    \u003ctextarea class=\"form-control\" name=\"text\" id=\"name\" rows=\"1\"\u003e\u003c/textarea\u003e\n  \u003c/div\u003e\n  \u003cbr /\u003e\n  \u003cdiv\u003e\n    \u003cbutton type=\"submit\" class=\"btn\"\u003eSubmit\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/form\u003e\n```\n\n```css\n.form-control {\n}\n```\n\n\u003cp xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dct=\"http://purl.org/dc/terms/\"\u003e\u003ca property=\"dct:title\" rel=\"cc:attributionURL\" href=\"https://github.com/TempeHS/Flask_PWA_Programming_For_The_Web_Task_Source\"\u003eFlask PWA Programming For The Web Task Source\u003c/a\u003e and \u003ca property=\"dct:title\" rel=\"cc:attributionURL\" href=\"https://github.com/TempeHS/Flask_PWA_Programming_For_The_Web_Task_Template\"\u003eFlask PWA Programming For The Web Task Template\u003c/a\u003e by \u003ca rel=\"cc:attributionURL dct:creator\" property=\"cc:attributionName\" href=\"https://github.com/benpaddlejones\"\u003eBen Jones\u003c/a\u003e is licensed under \u003ca href=\"https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1\" target=\"_blank\" rel=\"license noopener noreferrer\" style=\"display:inline-block; \"\u003eCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International\u003cimg style=\"height:22px!important; margin-left:3px; vertical-align:text-bottom; \" src=\"https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1\" alt=\"\"\u003e\u003cimg style=\"height:22px!important; margin-left:3px; vertical-align:text-bottom; \" src=\"https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1\" alt=\"\"\u003e\u003cimg style=\"height:22px!important; margin-left:3px; vertical-align:text-bottom; \" src=\"https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1\" alt=\"\"\u003e\u003cimg style=\"height:22px!important; margin-left:3px; vertical-align:text-bottom; \" src=\"https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1\" alt=\"\"\u003e\u003c/a\u003e\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftempehs%2Fflask_pwa_programming_for_the_web_task_source","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftempehs%2Fflask_pwa_programming_for_the_web_task_source","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftempehs%2Fflask_pwa_programming_for_the_web_task_source/lists"}