├── README.md ├── hw1 ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── babydata │ ├── 2001.html │ ├── 2002.html │ ├── 2003.html │ ├── 2004.html │ ├── 2005.html │ ├── 2006.html │ ├── 2007.html │ ├── 2008.html │ ├── 2009.html │ ├── 2010.html │ ├── 2011.html │ ├── 2012.html │ ├── 2013.html │ ├── 2014.html │ ├── 2015.html │ ├── 2016.html │ ├── 2017.html │ ├── 2018.html │ └── babyname.report.csv ├── babyname_parser.py ├── fetch.py └── run.py ├── hw2 ├── README.md ├── app.js ├── base.css ├── base.js ├── chart │ └── chart.min.js ├── data │ └── babyname.report.csv ├── images │ ├── after_chart.png │ ├── after_list.png │ ├── before_chart.png │ ├── before_preview.png │ ├── disable_live_server.png │ ├── enable_live_server.png │ ├── invalid_example.png │ ├── live_server_extension.png │ └── valid_example.png ├── index.html └── materialize │ ├── LICENSE │ ├── README.md │ ├── css │ └── materialize.min.css │ └── js │ └── materialize.min.js ├── hw3 ├── .gitignore ├── README.md ├── api │ ├── db.json │ └── routes.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── serviceWorker.js ├── hw4 ├── LICENSE ├── README.md ├── blog │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py └── myblog │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── project ├── Add Badge in README.pdf ├── README.md ├── design-and-planning-examples └── Design_and_Planning_example.pdf ├── design-and-planning.md ├── final-report-examples ├── team11_final_report.pdf └── team2_final_report.pdf ├── poster-examples ├── Team21_poster.pdf ├── Team3_poster.pdf ├── Team6_poster.pdf └── Team7_poster.pdf ├── postersession.md ├── project-requirements-and-specification.md ├── projectproposal.md ├── proposal-examples ├── Team1 - BusyWrite.pdf └── Team8 - You&Meet - X.pdf └── sprint-instructions.md /README.md: -------------------------------------------------------------------------------- 1 | # M1522.002400 Principles and Practices of Software Development 2 | 3 | ## Announcements 4 | - [x] HW1 out: 9/2 (Wed); due: 9/11 (Fri) 6pm 5 | - [x] Team formation due 9/13 (Sun) 6pm 6 | - [x] HW2 out: 9/9 (Wed); due: 9/18 (Fri) 6pm 7 | - [x] Project proposal due 9/22 (Tue) 6pm 8 | - [x] HW3 out: 9/21 (Mon); due 10/7 (Wed) 6pm (feature), 10/15 (Thu) 6pm (unittest) 9 | - [x] HW4 out: 10/14 (Wed); due 10/29 (Thu) 6pm 10 | 11 | ## Assignments 12 | 13 | - Sep. 02 : [homework 1](hw1) is out 14 | - **Due : 9/11 (Fri) 18:00 (hard deadline)** 15 | - This is an individual assignment 16 | 17 | - Sep. 09 : [homework 2](hw2) is out 18 | - **Due : 9/18 (Fri) 18:00 (hard deadline)** 19 | - This is an individual assignment 20 | 21 | - Sep. 21: [homework 3](hw3) is out 22 | - **Due : 10/7 (Wed) 18:00 (feature), 10/15 (Thu) 18:00 (unittest) (hard deadline)** 23 | - This is an individual assignment 24 | 25 | - Oct. 14: [homework 4](hw4) is out 26 | - **Due : 10/29 (Thu) 18:00 (hard deadline)** 27 | - This is an individual assignment 28 | 29 | - ~~Oct. 28~~ (TBD): homework 5 is out 30 | - **Due : TBD** 31 | - This is an individual assignment 32 | 33 | ## [Project Guideline](project) 34 | 35 | Please follow the guideline in this [link](project). 36 | 37 | ## Schedule (TBD) 38 | 39 | There may be some changes to the schedule. 40 | 41 | | Week | Lecture | Practice Session | Homework / Project / Exam | 42 | |-------|---------|------------------|---------------------------| 43 | |9/2 | Course overview | Environment Setup + Python + HTML/Javascript (DOM) | HW1 out 9/2 | 44 | |9/7,9| Challenges to make software; Version control | Git | HW2 out 9/9,
HW1 due 9/11 6pm,
Team formation due 9/13 (Sun) 6pm | 45 | |9/14,16 | SaaS architecture; Building software; ORM | React | HW2 due 9/18 6pm | 46 | |9/21,23 | Project sprints; Requirements and specification; | Redux | HW3 out 9/21,
Project proposal due 9/22 6pm | 47 | |9/28 | Testing | Choosuk (No practice session) | | 48 | |10/5,7 | Software development process; Design patterns | Frontend Testing (Jest + Enzyme) | Project sprint 1 begin (bi-weekly meetings with TAs at the end of sprint) | 49 | |10/12,14 | Taming complexity; defensive programming; Design patterns | Django | HW4 out 10/14,
HW3 due 10/15 6pm | 50 | |10/19,21 | Design patterns | Django + Python Testing | Project sprint 2 begin | 51 | |10/26,28 | Design patterns; code refactoring | Integration + CI + SonarCloud | HW4 due 10/28 6pm | 52 | |11/2,4 | Operation | Deployment + Design Pattern | Project sprint 3 begin | 53 | |11/9,11 | Operation, Mid-presentation | Project Mid Presentation | The mid presentation session will be on 11/11 and 12. | 54 | |11/16,18 | ML pipeline | AWS SageMaker + Optimization | Project sprint 4 begin | 55 | |11/23,25 | ML pipeline | Final Exam 11/26 | Coding exams (In-class exam ~ 3 hours) | 56 | |11/30,12/2 | Operation | Code Refactoring | Project sprint 5 begin | 57 | |12/7,9 | TBD | Testing Session | | 58 | |12/17 | Project poster session | | Project final report (due 12/18 6pm) | 59 | 60 | ## Office hours 61 | **Professor** : 62 | - Office: Bldg. 302, Rm. 322 63 | - Mon,Wed 13:00-14:00, by appointment 64 | 65 | **TAs** : 66 | 67 | Haeyoon Cho 68 | - Email: chohy0555@gmail.com 69 | - Office: Bldg. 302, Rm. 420 70 | - Office hour: Monday 16:00~17:00 71 | 72 | Yunmo Koo 73 | - Email: mpbb03@gmail.com 74 | - Office: Bldg. 302, Rm. 420 75 | - Office hour: Monday 17:00~18:00 76 | 77 | Alchan Kim 78 | - Email: a9413miky@gmail.com 79 | - Office: Bldg. 302, Rm. 420 80 | - Office hour: Wednesday 16:00~17:00 81 | 82 | Donghyun Kim 83 | - Email: donghyunkim816@gmail.com 84 | - Office: Bldg. 302, Rm. 420 85 | - Office hour: N/A 86 | 87 | (Please send TAs an email ahead of time, we can set up a specific time and place) 88 | 89 | ## Project Teams 90 | - Team 1 : [Foodify](https://github.com/swsnu/swpp2020-team1) 91 | - Team 2 : [Almanac](https://github.com/swsnu/swpp2020-team2) 92 | - Team 3 : [Recipick](https://github.com/swsnu/swpp2020-team3) 93 | - Team 4 : [QuantCash](https://github.com/swsnu/swpp2020-team4) 94 | - Team 5 : [AllTastesMatter](https://github.com/swsnu/swpp2020-team5) 95 | - Team 6 : [Rotus](https://github.com/swsnu/swpp2020-team6) 96 | - Team 7 : [Naeng Pa](https://github.com/swsnu/swpp2020-team7) 97 | - Team 8 : [adoor](https://github.com/swsnu/swpp2020-team8) 98 | - Team 9 : [PLAND](https://github.com/swsnu/swpp2020-team9) 99 | - Team 10 : [F.R.I.D.G.E](https://github.com/swsnu/swpp2020-team10) 100 | - Team 11 : [ASAP GO](https://github.com/swsnu/swpp2020-team11) 101 | - Team 12 : [Caffeine Camera](https://github.com/swsnu/swpp2020-team12) 102 | - Team 13 : [Goaling Ball](https://github.com/swsnu/swpp2020-team13) 103 | - Team 14 : [Fontopia!](https://github.com/swsnu/swpp2020-team14) 104 | - Team 15 : [stockin'](https://github.com/swsnu/swpp2020-team15) 105 | - Team 16 : [Coding MBTI](https://github.com/swsnu/swpp2020-team16) 106 | - Team 17 : [Shall We Game?](https://github.com/swsnu/swpp2020-team17) 107 | - Team 18 : [Term'inator](https://github.com/swsnu/swpp2020-team18) 108 | -------------------------------------------------------------------------------- /hw1/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /hw1/NOTICE.txt: -------------------------------------------------------------------------------- 1 | Code for Google's Python Class 2 | Copyright 2010 Google Inc. 3 | 4 | This code developed by Nick Parlante 5 | at Google Inc, and modified by Alchan Kim 6 | at SNU Software Platform Lab 7 | for SWPP fall 2020 lecture. 8 | -------------------------------------------------------------------------------- /hw1/README.md: -------------------------------------------------------------------------------- 1 | # Homework 1 - Python Basics 2 | 3 | **Due: 9/11 (Fri) 18:00 (This is a hard deadline)** 4 | 5 | This assignment is for you to become familiar with Python, which will be used to build a backend system in hw4. 6 | This is an **individual** assignment. 7 | 8 | From the beginning to the end of the development, you must use **GitHub** for version control. 9 | This will be used for your submission as well, so please keep it in mind. 10 | 11 | ## Fix History 12 | Click the links to see the commits that made changes. 13 | 14 | - 9/6 15 | - [**Correction**: fix the example for `BabynameParser` in README.md](https://github.com/swsnu/swppfall2020/commit/8cf6a26a601eab25a89b033be2e3695873ce9039#diff-d4ebad57de104fd377fa5c0bd6f6dc92) 16 | 17 | - 9/5 18 | - [**Comment fix for clarification**: Clarified comments for BabynameParser.](https://github.com/swsnu/swppfall2020/commit/9db5bab5158d99f71b2350707c60147f9d841f05) 19 | - [**Comment fix for clarification**: Clarified comments for run.py.](https://github.com/swsnu/swppfall2020/commit/e431fd115dc8eb1fd1408d3747c275be36ada7ee) 20 | - [**Comment fix for clarification**: Clarified comments for check_filename_existence](https://github.com/swsnu/swppfall2020/commit/969175222c1d9c99eb904e94782d8289f6e43d47) 21 | 22 | - 9/4 23 | - [**Correction**: The output csv file name should be `babyname.report.csv`, not `output.csv`.](https://github.com/swsnu/swppfall2020/commit/0a01ada276c2052066777b0323c60db49b286adc) 24 | - [**Comment fix for clarification**: In `babyname_parser.py`, `parse` method returns a list of lambda function's output.](https://github.com/swsnu/swppfall2020/commit/559d1c68899789cb916cc8c0e5900152de16c9e3) 25 | - [**Correction**: `BabyFetchException` is raised when fetch is failed.](https://github.com/swsnu/swppfall2020/commit/f680a4ebbc9107971febbe5ec4fef1c4e8735d47) 26 | 27 | ## Python 28 | 29 | ### Objective 30 | 31 | In this assignment, you will practice fetching HTML files from the Internet website, parsing the HTML files, and creating a csv file for the parsed data. 32 | Based on the provided skeleton code, you will implement the functions needed to do the tasks above. 33 | 34 | You don't have to understand the implemented methods provided in the skeleton code, but studying them can be useful for your future term project. 35 | This project is a modification of google's [Baby Names Python Exercise](https://developers.google.com/edu/python/exercises/baby-names). 36 | 37 | The goal of this assignment is to practice using various functions of the Python language. 38 | In this assignment, you will get familiar with: 39 | 40 | - Built-in functions 41 | - Python provides many useful built-in functions. You can write code more efficiently and effectively if you are fluent with such functions. 42 | - Class 43 | - You will need to modularize and encapsulate your code to neatly manage your project. Class is a core concept to achieve such objectives. It is an important component in design patterns that you will learn in future classes. 44 | Since you will define your own models with classes in homework 3, you should be familiar with it. 45 | - Decorator 46 | - [Decorator](https://www.python.org/dev/peps/pep-0318/) is a special syntax in python for transforming functions using closure. It is also an important feature for applying design patterns in python, such as defining static methods or class methods. 47 | Many functions from Django are provided as decorators as well, especially with user authentication, which you will be using in homework 3. 48 | - Lambda function 49 | - Lambda function helps you write your python code in a functional programming style. In certain situations, functional style makes your code more concise and easy to modularize. 50 | For more information, read [Functional Programming HowTo](https://docs.python.org/3/howto/functional.html). 51 | 52 | ### 1. Implement script to fetch the html files of popular babynames 53 | You need to implement the `fetch.py` file that sends a request to fetch the HTML files from the [Social Security Administration website](https://www.ssa.gov/cgi-bin/popularnames.cgi), which provides neat data by year of what names are the most popular for babies born that year in the USA. We will extract HTML files ranging from 2001 to 2018. After fetching the files, you have to save them under `babydata` directory. The expected HTML files are given for reference under `babydata` directory. They should be matched with the output files that your script generates. To complete the fetching script, you should implement the following functions: 54 | 55 | **1) `fetch_top_1000`** 56 | This method receives two arguments: the year of interest and the target url. It returns the contents of the html file fetched from the given webpage. Use `urllib` to fetch the html file. Refer to [python documentation](https://docs.python.org/3/library/urllib.request.html#urllib.request.urlopen) for more information on `urllib`. 57 | 58 | **2) `safe_internet_fetch`** 59 | You need to implement a decorator that checks whether the request was successful. This decorator will decorate the `fetch_top_1000` function to handle the situations where fetching fails due to an invalid url or internet connection error by throwing `BabyFetchException`. 60 | You can catch the above error using [urllib.error.URLError] (https://docs.python.org/3/library/urllib.error.html) 61 | This method should be implemented as a decorator in order to be easily applied to other methods. Please check out [this document](https://www.programiz.com/python-programming/decorator) to understand how to implement python decorator. 62 | 63 | For debuggability, defining your own exception and intentionally raising the exception is useful because you can identify the root cause of the exception and distinguish the exception from others. We provide `BabyFetchException` for this purpose. If the decorated function fails to fetch the data, this decorator has to raise the custom `BabyFetchException` with custom error message: `Request Failed`. We will not grade your decorator with it's stack trace but just test the class of the raised exception and it's message. 64 | 65 | ### 2. Implement `BabynameParser` 66 | 67 | You need to implement the babyname parser class that parses the popular names and their ranks from the html files you saved. 68 | In the skeleton code, `BabyParser` class is defined in `babyname_parser.py`. To complete the parser, you should implement the following functions: 69 | 70 | **1) `__init__`** 71 | 72 | This method is the constructor of the parser. 73 | In this method, you need to extract a list of the tuples of (rank, male-name, female-name) from the given html file. 74 | 75 | **2) `check_filename_existence`** 76 | 77 | You need to implement a decorator that checks whether the file exists or not. 78 | This decorator will decorate the `__init__` of the parser to prevent constructing a parser with non-existing file name. 79 | 80 | Just like the `safe_internet_fetch` above, we provide `BabynameFileNotFoundException` for this purpose. 81 | If there is no such file with the generated filename following the convention of the function to decorate, this decorator has to raise the custom `BabynameFileNotFoundException` with custom error message: `No such file: {filename}`. 82 | You can assume that `filename` is the name generated using the first and second argument (i.e. directory path and year) for the function to decorate. 83 | 84 | **3) `parse`** 85 | 86 | This method applies a given lambda function to all of the extracted tuples and returns the results. 87 | 88 | **Usage** 89 | 90 | When you are done, you can parse any information about the popluar name ranks in a year by using the implemented parser. 91 | In the python console, you can test your code with the provided `2001.html` file like below. 92 | 93 | ``` 94 | >>> from babyname_parser import BabynameParser 95 | >>> parser = BabynameParser("/{YOUR_HW_PATH}/babydata", 2001) 96 | >>> parser.year 97 | '2001' 98 | >>> parser.rank_to_names_tuples 99 | [('1', 'Jacob', 'Emily'), ('2', 'Michael', 'Madison'), ('3', 'Matthew', 'Hannah'), ...] 100 | >>> parser.parse(lambda rank_to_names_tuple: rank_to_names_tuple[0]) 101 | ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', ...] 102 | >>> BabynameParser("./babydata", 1900) 103 | Traceback (most recent call last): 104 | File "", line 1, in 105 | File "babyname_parser.py", line 61, in decorated 106 | raise BabynameFileNotFoundException("No such file: {}".format(filename)) 107 | babyname_parser.BabynameFileNotFoundException: No such file: ./babydata/1900.html 108 | ``` 109 | 110 | You should **NOT** edit parts other than those marked as `TODO`s in your skeleton code when you submit your assignment, because we will grade your `BabynameParser` implementation by not only running `run.py` file but also importing the class and test the methods like above (with different files and lambdas, of course). 111 | Also, the error message that comes out when the entered file does not exist must be 112 | `No such file: {filename}` and the result of `parse` method must be a list of all processed tuples. 113 | Same applies to the `fetch.py`. 114 | 115 | ### 3. Fill the run script 116 | 117 | In `run.py` file, a skeleton code using the parser is provided. 118 | Your task is to complete the code to parse all the html files that contain the popular baby names `BabynameParser`, and save all the data in a single csv file. 119 | 120 | When you run `python run.py 2001 2018`,the script must generate a csv file named `babyname.report.csv`. 121 | The content of the csv file should be as follows: 122 | 123 | ``` 124 | $ tail -10 /{YOUR_HW_PATH}/babydata/babyname.report.csv 125 | 2018,991,Emmarie,F, 126 | 2018,992,Esperanza,F,30 127 | 2018,993,Kailyn,F,90 128 | 2018,994,Aiyana,F,147 129 | 2018,995,Keilani,F, 130 | 2018,996,Austyn,F, 131 | 2018,997,Whitley,F, 132 | 2018,998,Elina,F, 133 | 2018,999,Kimora,F,34 134 | 2018,1000,Maliah,F,72 135 | 136 | $ head -10 /{YOUR_HW_PATH}/babydata/babyname.report.csv 137 | year,rank,name,gender,rank_change 138 | 2001,1,Jacob,M, 139 | 2001,2,Michael,M, 140 | 2001,3,Matthew,M, 141 | 2001,4,Joshua,M, 142 | 2001,5,Christopher,M, 143 | 2001,6,Nicholas,M, 144 | 2001,7,Andrew,M, 145 | 2001,8,Joseph,M, 146 | 2001,9,Daniel,M, 147 | ``` 148 | 149 | ## Grading 150 | 151 | ### Python (15 points) 152 | 153 | We will test your code under Python 3.7. 154 | 155 | - `fetch.py` (total 3 points) 156 | - `safe_internet_fetch` : 1 point 157 | - `fetch_top_1000` : 2 points 158 | - `babyname_parser.py` (total 6 points) 159 | - `check_filename_existence` : 1 point 160 | - `BabynameParser > parse` : 5 points (includes the `__init__` constructor) 161 | - `run.py` (total 6 points) 162 | - `BabyRecord > to_csv_record` : 1 point 163 | - `main` : 5 points 164 | - Partial points for minor errors 165 | 166 | 167 | ## Submission 168 | 169 | Due: 9/11 (Fri) 18:00 (This is a hard deadline) 170 | 171 | You must create your own *private* repository under your account. 172 | You don't need to submit your homework by email. 173 | **Make sure to name your repository properly and to add TA as your collaborator in the repository settings!** (Detailed instructions below). 174 | We will check the snapshot of the *master* branch of your Github repository at the deadline and grade it. 175 | Since there are many students in this class, we need to automatize the grading process, so your homework may not be graded properly if your directory hierarchy doesn't look like this: 176 | ``` 177 | repository_root 178 | ├── LICENSE.txt 179 | ├── NOTICE.txt 180 | ├── babydata 181 | │ ├── 2001.html-2018.html 182 | │ └── babyname.report.csv 183 | ├── babyname_parser.py 184 | ├── fetch.py 185 | └── run.py 186 | ``` 187 | Also, make sure to push your work on Github on time. 188 | 189 | ### Instructions on creating a private repository 190 | 191 | #### I. Get GitHub Student Developer Pack 192 | 193 | 1. Go to `https://education.github.com`. 194 | 2. Follow instructions to `Request a discount`. 195 | 196 | #### II. Make your private repository 197 | 198 | 1. Go to your github profile: `https://github.com/YOUR_USERNAME`. 199 | 2. Under `Repositories`, click on `New`. 200 | 3. Fill in `swpp-hw1-USERNAME` as your repository name, and mark is as `Private`. (e.g., `swpp-hw1-kdh0102`) 201 | 4. Hit `Create repository`. 202 | 5. In terminal, go to the directory that you will be working in (e.g., `~/workspace/swpp-hw1-USERNAME` or `~/swpp-hw1-USERNAME`) 203 | 6. Type in and run the following commands, which is also shown on the page that you will be looking at after step 4: 204 | 205 | ``` 206 | echo "# asdf" >> README.md 207 | git init 208 | git add README.md 209 | git commit -m "first commit" 210 | git remote add origin https://github.com/USERNAME/swpp-hw1-USERNAME 211 | git push -u origin master 212 | ``` 213 | 214 | Alternatively you can start by copying skeleton codes :) 215 | ``` 216 | cp -r ${swppfall2020-home}/hw1 ~/workspace/swpp-hw1-USERNAME 217 | git init 218 | git add . 219 | git commit -m "first commit" 220 | git remote add origin https://github.com/USERNAME/swpp-hw1-USERNAME 221 | git push -u origin master 222 | ``` 223 | 224 | 7. Under `Settings` then `Collaborators` tab, Add Homework TA as your collaborator: **`kdh0102`**. 225 | 8. You're all set! After finishing your homework, push your contents to your repository on time! 226 | -------------------------------------------------------------------------------- /hw1/babyname_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright 2010 Google Inc. 3 | # Licensed under the Apache License, Version 2.0 4 | # http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | # Google's Python Class 7 | # http://code.google.com/edu/languages/google-python-class/ 8 | 9 | # Modified by Alchan Kim at SNU Software Platform Lab for 10 | # SWPP fall 2020 lecture. 11 | 12 | import sys 13 | import re 14 | import os 15 | 16 | from functools import wraps 17 | 18 | """Baby Names exercise 19 | 20 | Implement the babyname parser class that parses the popular names and their ranks from a html file. 21 | 22 | 1) At first, you need to implement a decorator that checks whether the html file exists or not. 23 | 2) Also, the parser should extract tuples of (rank, male-name, female-name) from the file by using regex. 24 | For writing regex, it's nice to include a copy of the target text for inspiration. 25 | 3) Finally, you need to implement `parse` method in `BabynameParser` class that parses the extracted tuples 26 | with the given lambda and return a list of processed results. 27 | """ 28 | 29 | 30 | class BabynameFileNotFoundException(Exception): 31 | """ 32 | A custom exception for the cases that the babyname file does not exist. 33 | """ 34 | pass 35 | 36 | 37 | 38 | def check_filename_existence(func): 39 | """ 40 | (1 point) 41 | A decorator that catches the non-exiting filename argument and raises a custom `BabynameFileNotFoundException`. 42 | 43 | Args: 44 | func: The function to decorate. 45 | Raises: 46 | BabynameFileNotFoundException: if there is no such file while func tries to open a file. 47 | We assume func receives directory path and year to generate a filename to open. 48 | """ 49 | # TODO: Implement this decorator 50 | 51 | 52 | class BabynameParser: 53 | 54 | @check_filename_existence 55 | def __init__(self, dirname, year): 56 | """ 57 | (3 points) 58 | Given directory path and year, extracts the name of a file to open the corresponding file 59 | and a list of the (rank, male-name, female-name) tuples from the file read by using regex. 60 | [('1', 'Michael', 'Jessica'), ('2', 'Christopher', 'Ashley'), ....] 61 | 62 | Args: 63 | dirname: The name of the directory where baby name html files are stored 64 | year: The year number. int. 65 | """ 66 | # TODO: Open and read html file of the corresponding year, and assign the content to `text`. 67 | # Also, make the BabynameParser to have the year attribute. 68 | text = "TODO" 69 | 70 | # TODO: Implement the tuple extracting code. 71 | # `self.rank_to_names_tuples` should be a list of tuples of ("rank", "male name", "female name"). 72 | # You can process the file line-by-line, but regex on the whole text at once is even easier. 73 | # (If you resolve the previous TODO, the html content is saved in `text`.) 74 | # You may find the following method useful: `re.findall`. 75 | # See https://docs.python.org/3/library/re.html#re.findall. 76 | self.rank_to_names_tuples = [("1", "TODO_male", "TODO_female"), ("2", "TODO_male", "TODO_female")] 77 | 78 | def parse(self, parsing_lambda): 79 | """ 80 | (2 points) 81 | Collects a list of babynames parsed from the (rank, male-name, female-name) tuples. 82 | The list must contains all results processed with the given lambda. 83 | 84 | Args: 85 | parsing_lambda: The parsing lambda. 86 | It must process an single (string, string, string) tuple and return something. 87 | Returns: 88 | A list of lambda function's output 89 | """ 90 | # TODO: Implement this method 91 | 92 | -------------------------------------------------------------------------------- /hw1/fetch.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import urllib.request 3 | import urllib.error 4 | import os 5 | 6 | from functools import wraps 7 | 8 | class BabyFetchException(Exception): 9 | """ 10 | A custom exception for the cases where it fails to fetch the html file from the internet. 11 | """ 12 | pass 13 | 14 | 15 | def safe_internet_fetch(func): 16 | """ 17 | (1 point) 18 | A decorator that catches fetching error (i.e. urllib.error.URLERROR) and raises a custom `BabyFetchException`. 19 | 20 | Args: 21 | func: The function to decorate 22 | Raises: 23 | BabyFetchException: if it fails to fetch the html codes from the url. 24 | """ 25 | # TODO: Implement this decorator 26 | 27 | 28 | @safe_internet_fetch 29 | def fetch_top_1000(url, year): 30 | """ 31 | (2 points) 32 | Given a year of interest, fetches the html file from the top1000 popular names 33 | of that year and return the html codes in text 34 | 35 | Args: 36 | url: the target url to send request 37 | year: The year of interest in integer. 38 | Return: 39 | text: a string. HTML content of the fetch webpage. 40 | """ 41 | 42 | # TODO: Implement this function. 43 | # Send POST request to the given url (https://www.ssa.gov/cgi-bin/popularnames.cgi) with the data by inspecting the web page. 44 | # Hint: You can concatenate multiple data using '&' (e.g. data = "month=December&day=25") 45 | # You must use standard library `urllib` to send request. (i.e. 3rd party libraries are not allowed.) 46 | # urllib reference link: https://docs.python.org/3/library/urllib.request.html#urllib.request.urlopen 47 | 48 | def main(): 49 | # NOTE: DO NOT change this function. 50 | # This function fetches and saves the html file of popular names in 2001-2018. 51 | # The example output html files are provided in `babydata/` directory. 52 | # The output html files you generate should be same with the provided example html files. 53 | # You can check the difference with `diff` command. 54 | for year in range(2001, 2019): 55 | pathname = os.path.join("babydata", "{}.html".format(year)) 56 | with open(pathname, "w") as f: 57 | url = "https://www.ssa.gov/cgi-bin/popularnames.cgi" # target url to fetch baby data 58 | f.write(fetch_top_1000(url ,year)) 59 | 60 | 61 | if __name__ == '__main__': 62 | main() 63 | -------------------------------------------------------------------------------- /hw1/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright 2010 Google Inc. 3 | # Licensed under the Apache License, Version 2.0 4 | # http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | # Google's Python Class 7 | # http://code.google.com/edu/languages/google-python-class/ 8 | 9 | # Modified by Alchan Kim at SNU Software Platform Lab for 10 | # SWPP fall 2020 lecture 11 | 12 | import sys 13 | import os 14 | 15 | from babyname_parser import BabynameParser 16 | 17 | """ 18 | Parse html files where the popular baby names are listed for each year, 19 | collect records and save them into "babydata/" as a csv format. 20 | The name of the output CSV file is "babyname.report.csv". 21 | """ 22 | 23 | class BabyRecord: 24 | def __init__(self, year, rank, name, gender, rank_change=None): 25 | """ 26 | Args: 27 | year: The corresponding year of the data in integer. 28 | rank: The rank of the data in integer. 29 | name: The name of the data in string. 30 | gender: The gender of the data, either 'M' or 'F'. 31 | rank_change: The change of the rank compared to the previous year. `None` if no comparison possible. 32 | """ 33 | self.year = year 34 | self.rank = rank 35 | self.name = name 36 | self.gender = gender 37 | self.rank_change = rank_change 38 | 39 | def to_csv_record(self): 40 | """ 41 | (1 point) 42 | Convert the record into a comma-seperated string line, as format of "year,rank,name,gender(M/F),rank_change". 43 | The return value is a line of body of CSV file output. 44 | If rank_change is None, save it as blank (""). 45 | 46 | e.g. 2018,1,Joan,F,-2 47 | """ 48 | # TODO: Implment this function 49 | 50 | 51 | def __repr__(self): 52 | """ 53 | NOTE: This method is provided for debugging. You don't need to modify this. 54 | """ 55 | return "".format( 56 | self.year, 57 | self.rank, 58 | self.name, 59 | self.gender, 60 | self.rank_change, 61 | ) 62 | 63 | def save(filename, records): 64 | """ 65 | NOTE: DO NOT change this function. 66 | This function saves the parsed records in csv format. 67 | 68 | Args: 69 | filename: The name of the output file. 70 | records: The list of records. 71 | """ 72 | with open(filename, "w") as f: 73 | f.write("year,rank,name,gender,rank_change\n") 74 | for record in records: 75 | f.write(record.to_csv_record()) 76 | f.write("\n") 77 | 78 | 79 | def main(): 80 | """ 81 | (5 points) 82 | """ 83 | args = sys.argv[1:] 84 | 85 | if len(args) < 2: 86 | print('usage: python run.py `starting year` `ending year`') 87 | sys.exit(1) 88 | 89 | year1, year2 = int(args[0]), int(args[1]) 90 | 91 | records = [] # list of BabyRecord objects 92 | prev_male_ranking = {} # use this to calculate the rank if you need 93 | prev_female_ranking = {} 94 | 95 | for year in range(year1, year2 + 1): 96 | parser = BabynameParser("babydata", year) 97 | 98 | # TODO: In the following two lines, change `None` to your lambda function to parse baby name records. 99 | # By using the lambda function, `parse` method should return a list of `BabyRecord` objects 100 | # that contain year, rank, name, and gender data. 101 | male_records = parser.parse(None) # Parse the male ranks and store them as a list of `BabyRecord` objects. 102 | female_records = parser.parse(None) # Parse the female ranks and store it as a list of `BabyRecord` objects. 103 | 104 | # TODO: Calculate the rank change for each of `male_records` and `female_records`. 105 | # For example, if the rank of the previous year is 8 and the rank of the current year is 5, 106 | # -3 is the rank change. (Beware the sign of the value. Rank-up is respresented with a negative value!) 107 | # If the rank of previous year is not available, set `rank_change` to `None`. 108 | 109 | # TODO: Save the result as a csv file named `babyname.report.csv` under `babydata/` directory. 110 | # To verify correctness, try running this function using the html files we provided in the swppfall2020 repository 111 | # and compare the content of the babyname.report.csv. 112 | # Due to the inconsistency of the ranking information provided by ssa.gov, your output and the provided example output may differ 113 | # See issue #12 for more info 114 | 115 | save(os.path.join("babydata", "babyname.report.csv"), records) 116 | 117 | if __name__ == '__main__': 118 | main() 119 | -------------------------------------------------------------------------------- /hw2/README.md: -------------------------------------------------------------------------------- 1 | # Homework 2 - JavaScript Basics 2 | 3 | **Due: 9/18 (Fri) 18:00 (This is a hard deadline)** 4 | 5 | This is an **individual** assignment. 6 | This assignment is for you to become familiar with JavaScript. JavaScript will be used to build a frontend system in hw3, using the React framework. By implementing features in this assignment, you will learn the followings: 7 | 8 | 1. JavaScript built-in methods (e.g. `map`, `filter`, etc) 9 | 2. JavaScript regular expression `RegExp` 10 | 3. Handling HTML form element 11 | 12 | ### Popular Baby Name Website 13 | 14 | Using the information parsed in hw1, you will implement a website that shows the statistics (e.g. annual ranking,year-on-year change of name popularity) of popular baby names in the USA. To complete this assignment, you have to implement 4 functions (`parseAndSave`, `provideYearData`, `provideChartData`, and `handleSignUpFormSubmit`) in `app.js`. The skeleton of each function is provided. 15 | 16 | ### File Description 17 | 18 | ```bash 19 | [hw2 root] 20 | ├── app.js <= you need to modify this file. 21 | ├── base.css 22 | ├── base.js 23 | ├── chart 24 | │ └── chart.min.js 25 | ├── data 26 | │ └── babyname.report.csv 27 | ├── index.html 28 | └── materialize 29 | ├── LICENSE 30 | ├── README.md 31 | ├── css 32 | │ └── materialize.min.css 33 | └── js 34 | └── materialize.min.js 35 | ``` 36 | 37 | Each of the file or directory in this repository serves the following role: 38 | 39 | **NOTE: Students are only required to modify `app.js` file in this section.** 40 | 41 | - `app.js` : includes data parsing/processing and form checking functions. 42 | - `base.js` : includes event handling and rendering codes. Functions in `app.js` are called in `base.js`. 43 | - `index.html` : defines the layout of the webpage. 44 | - `data/babyname.report.csv` : includes the top 1000 baby names for each year (it is the output of hw1). 45 | - `base.css` : style formats utilized by `index.html`. 46 | - `chart/chart.min.js` : includes a function which visualizes a baby name's rank trend. 47 | - `materialize/` : `Materialize` CSS framework code. 48 | 49 | The following 4 functions in `app.js` are to be filled in by students: 50 | 51 | - `parseAndSave(text)`: parses `text` (the content of CSV file) and saves the parsed data into the global variable, `records`. 52 | - `provideYearData(year)`: filters baby names of the `year` from `records` and joins the male and female records with the same rank. The output is an array of joined data that are organized in ascending order based on the rank. 53 | - `provideChartData(name, gender)`: returns the ranks from 2001 to 2018 of a baby name to show the chart of name popularity by year. 54 | - `handleSignUpFormSubmit(form)`: checks if the sign up form at the bottom of the web page satisfies the requirements. 55 | 56 | You can refer to `base.js` to figure out the usages of each function and how the elements of the html changes using DOM APIs. 57 | (This is optional; It's totally okay to skip it over.) 58 | 59 | ### How To Start 60 | 61 | 62 | To open the website, you should run an HTTP server. There are two options to do this: 63 | 64 | **[Option 1] Open with Python `http.server`.** 65 | 66 | ```bash 67 | $ cd swpp-hw2-USERNAME # You should run the server in hw2 root directory. 68 | $ python -m http.server 69 | Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... 70 | 71 | # You will a message like above. Copy and paste the address to your web browser. 72 | ``` 73 | 74 | When you open the url (`http://0.0.0.0:8000/` in the example above), you can see the initial skeleton website. 75 | 76 | 77 | **[Option 2] Open with VSCode extension "Live Server".** 78 | 79 |

live server extension

80 | 81 | - Open VSCode extension tab (Code > Preferences > Extensions) and search **"Live Server"**. 82 | - When you click **"Install"**, then you can find a **"Go Live"** button on the right-bottom side of the status bar. 83 | 84 |

start live server

85 | 86 | - Open your hw2 root folder (e.g. swpp-hw2-USERNAME) on VSCode. (File > Open) 87 | - Click **"Go Live"** button, then your default web browser will be automatically opened, and you will see the initial website like below. 88 | 89 |

site overview

90 | 91 | - When you click a name, an empty chart will be dropped down. 92 | 93 |

chart overview

94 | 95 | - When you want to close the live server, click the button highlighted with the red box. 96 | 97 |

disable live server

98 | 99 | ### Expected Output 100 | 101 | Correctly implemented functions in `app.js` will show the results as follows: 102 | 103 | **1) `parseAndSave` and `provideYearData`** 104 | 105 | By implementing both `parseAndSave` and `provideYearData`, name and rank data of the year will show up in the table. 106 | 107 |

correct list

108 | 109 | **2) `provideChartData`** 110 | 111 | A baby name's rank chart will be drawn with the data provided by `provideChartData`, when the user clicks a name in the table. 112 | 113 |

correct chart

114 | 115 | **3) `handleSignUpFormSubmit`** 116 | 117 | By clicking the sign-up button, `handleSignUpFormSubmit` will conduct form checking and an alert message will show up. There are four input fields to fill in and each field should meet the following requirements to be validated. 118 | 119 | - Email: Start with characters other than @ or whitespace, followed by an @ sign, followed by more characters (except `@`, `.`, or whitespace), then a `.`, and finally followed by 2 to 3 alphabets from a to z. 120 | - `characters(except for whitespace and '@')` **@** `characters(except for whitespace, '@' and '.')` **.** `2-3 alphabets` 121 | - *characters* mean one or more characters including alphabets, numbers or special characters. 122 | - *alphabets* include both lowercase and uppercase. 123 | - e.g.) valid@javascript.com (O), invalid@snu.ac.kr (X) 124 | - First name (English) : Start with a capital letter, followed by one or more lowercase letters. Should only contain alphabets (A-Z, a-z) 125 | - Last name (English) : Start with a capital letter, followed by one or more lowercase letters. Should only contain alphabets (A-Z, a-z) 126 | - Date of birth: The format should be YYYY-MM-DD. 127 | - YYYY (Year) : Must be a number between 1900 and 2020 (inclusive). 128 | - MM (Month) : Must be a number with two digits between 01 and 12. 129 | - DD (Date) : Must be a number with two digits between 01 and 31. 130 | 131 | If users submit invalid data, the alert message should pop up and validation error messages show up right below the corresponding input fields. The validation error messages of each field should be: 132 | 133 | - First Name : "Invalid first name" 134 | - Last Name : "Invalid last name" 135 | - Email : "Invalid email" 136 | - Data of birth : "Invalid date of birth" 137 | 138 | Below is an example when you enter the wrong data into every field. 139 | 140 |

invalid form

141 | 142 | Following is an example when you enter the valid data into every field. In this case, each input field should not show any message. 143 | 144 |

valid form

145 | 146 | If the form does not satisfy the requirements (for example, if Email, First Name, Last Name and Data of Birth fail), the alert message should look like: 147 | 148 | ``` 149 | You must correct: 150 | 151 | First Name 152 | Last Name 153 | Email 154 | Date of Birth 155 | ``` 156 | The first error message "You must correct" is followed by the empty line. Names of incorrect inputs are listed up from the third line. The listing order of those names does not matter. Leading and trailing whitespaces of each line are not taken into account in grading. 157 | 158 | If the form satisfies the requirements, the alert message **must** be `Successfully Submitted!`. No leading and trailing whitespaces are allowed. 159 | 160 | You should set the value of `alertMessage` following the specification above. 161 | Please make sure that you do not trigger the alert function in this function. It is handled in `base.js`. 162 | 163 | The function argument `form` is of `HTMLFormElement`. 164 | This is a DOM element corresponding to the following form tag in `index.html`: 165 | ```html 166 |
167 | ... 168 | 169 |
170 | ``` 171 | You can access the input element of the form using the `name` attribute of the form (e.g. `let field = form['first-name']`). 172 | Then, you can access its value with `field.value`. 173 | All the other input fields can be accessed likewise. 174 | 175 | For more information on HTML Form APIs, 176 | see the documents: [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement) and [HTMLInputElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement). 177 | 178 | 179 | 180 | **Important Note**: 181 | You **must not** manipulate or directly read from HTML elements 182 | except the `form` object given as function argument in `app.js`. 183 | For example, you are not allowed to get the reference to the form 184 | by using `document.getElementById`, `document.querySelector` and other similar APIs. 185 | 186 | 187 | ## Grading 188 | 189 | This assignment is composed of a total of 15 points. 190 | 191 | The functions in `app.js` will be graded. We will see the outputs to validate your answers. 192 | 193 | - `parseAndSave` : 3 points 194 | - `provideYearData` : 5 points 195 | - `provideChartData` : 2 points 196 | - `handleSignUpFormSubmit` : 5 points 197 | - Partial points for minor errors 198 | 199 | 200 | ## Submission 201 | 202 | **Due: 9/18 (Fri) 18:00 (This is a hard deadline)** 203 | 204 | We will check the snapshot of the *master* branch of your Github repository at the deadline and grade it. As you already did in hw1, create a **private** repository with name `swpp-hw2-{USERNAME}` and add TA **`kdh0102`** as your collaborator in the repository settings! Your repository should look like below: 205 | 206 | ```bash 207 | repository_root/ 208 | ├── app.js 209 | ├── base.css 210 | ├── base.js 211 | ├── chart 212 | │ └── chart.min.js 213 | ├── data 214 | │ └── babyname.report.csv 215 | ├── index.html 216 | └── materialize 217 | ├── LICENSE 218 | ├── README.md 219 | ├── css 220 | │ └── materialize.min.css 221 | └── js 222 | └── materialize.min.js 223 | ``` 224 | 225 | Also, make sure to push your work on Github on time. 226 | -------------------------------------------------------------------------------- /hw2/app.js: -------------------------------------------------------------------------------- 1 | // TODO: edit this file 2 | 3 | // This is a list where your records should be stored. See `parseAndSave`. 4 | let records = []; 5 | 6 | // `parseAndSave(text)` is a function called with one argument `text`, the content of the babyname CSV file. 7 | // It is invoked only once at the start of application. 8 | // TODO: parse the csv text and save data records into the global variable `records` properly, 9 | // so that the other functions use them with ease. After calling this function, `records` should 10 | // contain the parsed data of every year like below. 11 | // e.g. records: [{year: 2001, rank: 1, name: "Jacob", gender: "M", rankChange: null}, 12 | // {year: 2001, rank: 2, name: "Michael", gender: "M", rankChange: null}, 13 | // ...] 14 | // Note: a CSV text can end with trailing line-break character '\n'. Whether it exists or not, 15 | // the function should parse `text` correctly. Also, records should be stored in the same order 16 | // in which they appear in a csv text. You can assume that at the first line is always a csv header. 17 | function parseAndSave(text) { 18 | // TODO: Fill this function. (3 points) 19 | 20 | } 21 | 22 | 23 | // `provideYearData(year)` is a function that receives a year and returns an array of data object corresponding to that year. 24 | // Note that male and female record with the same rank should be joined together to form one object. 25 | // TODO: return all data objects of a specific year, that are joined and organized by rank in an ascending order. 26 | // The example of returned array is as follows. 27 | // e.g. [{rank: 1, male: "Jacob", maleRankChange: 0, female: "Isabella", femaleRankChange: 0}, 28 | // {rank: 2, male: "Ethan", maleRankChange: 0, female: "Sophia", femaleRankChange: -2}, 29 | // ..., 30 | // {rank: 1000, male: "Keshawn", maleRankChange: 113, female: "Karley", femaleRankChange: 17}] 31 | function provideYearData(year) { 32 | // TODO: Fill in this function. (5 points) 33 | 34 | // This is just a reference for the return value's format. Delete this and fill your own 35 | // proper code to return the correct data. 36 | return [ 37 | { 38 | rank: 1, 39 | male: "John", 40 | maleRankChange: 0, 41 | female: "Christina", 42 | femaleRankChange: -2 43 | } 44 | ]; 45 | } 46 | 47 | // provideChartData(name, gender) is a function called when a user wants 48 | // to see the chart showing the year-on-year change of rank of a specific name. 49 | // TODO: return a list of all objects from 2001 to 2018 in the format of `{year: , rank: }` 50 | // of a specific name specified by the arguments, name and gender. 51 | // If there are no records with the name and gender for some years, 52 | // either you can set the values of the ranks to `undefined` or not return those records at all. 53 | // The example of return data is as follow. 54 | // e.g. [{year: 2001, rank: undefined}, 55 | // {year: 2002, rank: 613}, 56 | // ..., 57 | // {year: 2018, rank: 380}] 58 | // You can also return data excluding `undefined` value as below. 59 | // e.g. [{year: 2002, rank: 613}, 60 | // ..., 61 | // {year: 2018, rank: 380}] 62 | function provideChartData(name, gender) { 63 | // TODO: Fill in this function. (2 points) 64 | 65 | // This is just a reference for the return value's format. Delete this and fill your own 66 | // proper code to return the correct data. 67 | return [{year: 2001, rank: 3}, {year: 2002, rank: undefined}]; 68 | } 69 | 70 | 71 | // `handleSignUpFormSubmit(form)` is called when a user submits the sign up form. 72 | // `form` is the target HTML form element (L82~ in index.html). 73 | // TODO: validate the form. (5 points) 74 | function handleSignUpFormSubmit(form) { 75 | let alertMessage = 'TODO: Fill in this alert message properly'; 76 | // TODO: Fill in the rest of function to get the HTML form element as above. 77 | 78 | // Hint: you can use the `RegExp` class for matching a string. 79 | 80 | // The return data format is as follows. For the given `form` argument, you should 81 | // correctly process the validation, filling in `alertMessage`, and `validationResults`. 82 | // When you deal with `validationResults`, the values of `message` should be set to `null` 83 | // for the valid input fields. (See the example below.) 84 | // Below is just a reference for the return value's format. Delete this and fill your own 85 | // proper code to return the correct data. 86 | 87 | // IMPORTANT NOTE: You must use the argument `form` rather than directly using APIs such as `document.getElementId` or `document.querySelector`. 88 | // Plus, please do not call `alert` function here. 89 | // For debugging purpose, you can use `console.log`. 90 | return { 91 | alertMessage: alertMessage, 92 | validationResults: [ 93 | {name: "first-name", valid: true, message: null}, 94 | {name: "last-name", valid: false, message: "Invalid last name"}, 95 | {name: "email", valid: true, message: null}, 96 | {name: "date-of-birth", valid: false, message: "Invalid date of birth"} 97 | ] 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /hw2/base.css: -------------------------------------------------------------------------------- 1 | .male-rank-change, .female-rank-change { 2 | vertical-align: middle; 3 | } 4 | header { 5 | background: #1e1e1e; 6 | color: #eee; 7 | padding: 0.5em 1.5em; 8 | } 9 | 10 | #list-section { 11 | padding-top: 2em; 12 | padding-bottom: 2em; 13 | } 14 | 15 | #signup-form { 16 | padding: 2.5em; 17 | } -------------------------------------------------------------------------------- /hw2/base.js: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT THIS FILE!!! 2 | 3 | const YEARS = []; 4 | for (let i = 2001; i <= 2018; i++) YEARS.push(i); 5 | 6 | function whenReady(cb) { 7 | document.addEventListener('DOMContentLoaded', cb); 8 | } 9 | 10 | function initializeChart(element, chartData, name) { 11 | const ctx = element.querySelector('.chart').getContext('2d'); 12 | const rankByYear = {}; 13 | chartData.forEach(c => rankByYear[c.year] = c.rank); 14 | 15 | new Chart(ctx, { 16 | // The type of chart we want to create 17 | type: 'line', 18 | ticks: { reverse: true }, 19 | 20 | // The data for our dataset 21 | data: { 22 | labels: YEARS.map(year => year + "") , 23 | datasets: [{ 24 | label: name + '\'s rank', 25 | borderColor: 'rgb(255, 99, 132)', 26 | data: YEARS.map(year => rankByYear[year]) 27 | }] 28 | }, 29 | // Configuration options go here 30 | options: { 31 | scales: { 32 | yAxes: [{ 33 | display: true, 34 | ticks: { reverse: true } 35 | }] 36 | } 37 | } 38 | 39 | }); 40 | } 41 | 42 | function doHandleYearSelect(year) { 43 | const tableElem = document.querySelector("#ranking-table"); 44 | while (tableElem.lastElementChild && !tableElem.lastElementChild.id) { 45 | tableElem.removeChild(tableElem.lastElementChild); 46 | } 47 | const tempElem = document.querySelector("#template-row-record"); 48 | const data = provideYearData(year); 49 | if (!Array.isArray(data)) { 50 | alert("provideYearData() should return an array!"); 51 | return; 52 | } 53 | data.forEach(record => { 54 | function decorateRank(e, rankChange) { 55 | if (rankChange == null) { 56 | e.querySelector("span").textContent = ""; 57 | e.querySelector("i").textContent = "fiber_new"; 58 | e.classList.add("blue-text", "text-darken-2"); 59 | } else if (rankChange < 0) { 60 | e.querySelector("span").textContent = -rankChange; 61 | e.querySelector("i").textContent = "arrow_upward"; 62 | e.classList.add("teal-text"); 63 | } else if (rankChange > 0) { 64 | e.querySelector("span").textContent = rankChange; 65 | e.querySelector("i").textContent = "arrow_downward"; 66 | e.classList.add("red-text"); 67 | } else { 68 | e.querySelector("span").textContent = ""; 69 | e.querySelector("i").textContent = 'drag_handle'; 70 | e.classList.add("grey-text"); 71 | } 72 | } 73 | function handleClick(e, name, gender) { 74 | e.preventDefault(); 75 | const chartData = provideChartData(name, gender); 76 | if (!Array.isArray(chartData)) { 77 | alert("provideChartData() should return an array!"); 78 | return; 79 | } 80 | 81 | let preventShow = false; 82 | if (newRow.nextElementSibling && 83 | newRow.nextElementSibling.classList.contains("row-chart")) { 84 | // disable chart row 85 | preventShow = newRow.nextElementSibling.getAttribute("data-name") === name; 86 | newRow.parentNode.removeChild(newRow.nextElementSibling); 87 | } 88 | if (!preventShow) { 89 | // enable chart row 90 | const newChartRow = document.querySelector("#template-row-chart").cloneNode(true); 91 | newChartRow.id = ""; 92 | newChartRow.style.display = "table-row"; 93 | newChartRow.setAttribute("data-name", name); 94 | newRow.parentNode.insertBefore( 95 | newChartRow, newRow.nextElementSibling 96 | ); 97 | initializeChart(newChartRow, chartData, name); 98 | } 99 | } 100 | const newRow = tempElem.cloneNode(true); 101 | newRow.id = ""; 102 | newRow.style.display = 'table-row'; 103 | let child = newRow.firstElementChild; 104 | child.textContent = record.rank; 105 | child = child.nextElementSibling; 106 | child.querySelector("a").textContent = record.male; 107 | child.querySelector("a").addEventListener("click", (e) => handleClick(e, record.male, 'M')); 108 | child = child.nextElementSibling; 109 | decorateRank(child, record.maleRankChange); 110 | child = child.nextElementSibling; 111 | child.querySelector("a").textContent = record.female; 112 | child.querySelector("a").addEventListener("click", (e) => handleClick(e, record.female, 'F')); 113 | child = child.nextElementSibling; 114 | decorateRank(child, record.femaleRankChange); 115 | tableElem.appendChild(newRow); 116 | }); 117 | } 118 | 119 | function handleYearSelect() { 120 | const elem = document.querySelector("#year-select"); 121 | const value = parseInt(elem.value); 122 | doHandleYearSelect(value); 123 | } 124 | 125 | whenReady(function () { 126 | const elem = document.querySelector("#year-select"); 127 | 128 | YEARS.forEach(year => { 129 | const optionElem = document.createElement("option"); 130 | optionElem.value = year; 131 | optionElem.textContent = year; 132 | elem.appendChild(optionElem); 133 | }); 134 | elem.value = YEARS[0]; 135 | M.FormSelect.init(elem, {}) 136 | 137 | fetch("data/babyname.report.csv") 138 | .then(resp => resp.text()) 139 | .then(text => parseAndSave(text)) 140 | .then(() => handleYearSelect()); 141 | elem.addEventListener("change", handleYearSelect); 142 | }); 143 | 144 | 145 | whenReady(function () { 146 | document.querySelector("#to-bottom").addEventListener("click", function () { 147 | window.scrollTo(0,document.body.scrollHeight); 148 | }); 149 | document.querySelector("#to-top").addEventListener("click", function () { 150 | window.scrollTo(0,0); 151 | }); 152 | }); 153 | 154 | whenReady(function () { 155 | const form = document.querySelector("#signup-form"); 156 | form.addEventListener("submit", function (e) { 157 | e.preventDefault(); 158 | // e.stopPropagation(); 159 | const form = e.target; 160 | const result = handleSignUpFormSubmit(form); 161 | result.validationResults.forEach(fieldInfo => { 162 | const fieldName = fieldInfo.name; 163 | const fieldElem = form[fieldName]; 164 | const valid = fieldInfo.valid; 165 | const message = fieldInfo.message; 166 | fieldElem.classList.remove("invalid"); 167 | fieldElem.classList.remove("valid"); 168 | if (valid) { 169 | fieldElem.classList.add("valid"); 170 | } else { 171 | fieldElem.classList.add("invalid"); 172 | } 173 | fieldElem.parentNode.querySelector(".helper-text").setAttribute("data-error", message); 174 | }); 175 | alert(result.alertMessage); 176 | M.updateTextFields(); 177 | }); 178 | form.addEventListener("keypress", function (e) { 179 | if (e.keyCode === 13) { 180 | e.preventDefault(); 181 | } 182 | }); 183 | }); 184 | -------------------------------------------------------------------------------- /hw2/images/after_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/after_chart.png -------------------------------------------------------------------------------- /hw2/images/after_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/after_list.png -------------------------------------------------------------------------------- /hw2/images/before_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/before_chart.png -------------------------------------------------------------------------------- /hw2/images/before_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/before_preview.png -------------------------------------------------------------------------------- /hw2/images/disable_live_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/disable_live_server.png -------------------------------------------------------------------------------- /hw2/images/enable_live_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/enable_live_server.png -------------------------------------------------------------------------------- /hw2/images/invalid_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/invalid_example.png -------------------------------------------------------------------------------- /hw2/images/live_server_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/live_server_extension.png -------------------------------------------------------------------------------- /hw2/images/valid_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw2/images/valid_example.png -------------------------------------------------------------------------------- /hw2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Babyname Search Engine! 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | SWPP 2020 20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 | SWPP Babyname Search Engine 28 |
29 |
30 | 31 |
32 |
33 |
34 | 35 | 36 |
37 |
38 | to Bottom 39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
RankMaleswap_vertFemaleswap_vert
70 | 71 |
72 | scroll up 73 |
74 |
75 |
76 |
77 |
78 |
79 |
If you want to tune in, sign up!
80 |
81 |
82 |
83 |
84 |
85 | 86 | 87 | 88 |
89 |
90 | 91 | 92 | 93 |
94 |
95 |
96 |
97 | 98 | 99 | 100 |
101 |
102 |
103 |
104 | 105 | 106 | 107 |
108 |
109 | 113 |
114 |
115 |
116 |
117 |
118 |
Web Development Materials
119 | 124 |
125 |
126 |
127 | 132 |
133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /hw2/materialize/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2018 Materialize 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hw2/materialize/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

MaterializeCSS

8 | 9 |

10 | Materialize, a CSS Framework based on material design. 11 |
12 | -- Browse the docs -- 13 |
14 |
15 | 16 | Travis CI badge 17 | 18 | 19 | npm version badge 20 | 21 | 22 | CDNJS version badge 23 | 24 | 25 | dependencies Status badge 26 | 27 | 28 | devDependency Status badge 29 | 30 | 31 | Gitter badge 32 | 33 |

34 | 35 | ## Table of Contents 36 | - [Quickstart](#quickstart) 37 | - [Documentation](#documentation) 38 | - [Supported Browsers](#supported-browsers) 39 | - [Changelog](#changelog) 40 | - [Testing](#testing) 41 | - [Contributing](#contributing) 42 | - [Copyright and license](#copyright-and-license) 43 | 44 | ## Quickstart: 45 | Read the [getting started guide](http://materializecss.com/getting-started.html) for more information on how to use materialize. 46 | 47 | - [Download the latest release](https://github.com/Dogfalo/materialize/releases/latest) of materialize directly from GitHub. ([Beta](https://github.com/Dogfalo/materialize/releases/)) 48 | - Clone the repo: `git clone https://github.com/Dogfalo/materialize.git` (Beta: `git clone -b v1-dev https://github.com/Dogfalo/materialize.git`) 49 | - Include the files via [cdnjs](https://cdnjs.com/libraries/materialize). More [here](http://materializecss.com/getting-started.html). ([Beta](https://cdnjs.com/libraries/materialize/1.0.0-beta)) 50 | - Install with [npm](https://www.npmjs.com): `npm install materialize-css` (Beta: `npm install materialize-css@next`) 51 | - Install with [Bower](https://bower.io): `bower install materialize` ([DEPRECATED](https://bower.io/blog/2017/how-to-migrate-away-from-bower/)) 52 | - Install with [Atmosphere](https://atmospherejs.com): `meteor add materialize:materialize` (Beta: `meteor add materialize:materialize@=1.0.0-beta`) 53 | 54 | ## Documentation 55 | The documentation can be found at . To run the documentation locally on your machine, you need [Node.js](https://nodejs.org/en/) installed on your computer. 56 | 57 | ### Running documentation locally 58 | Run these commands to set up the documentation: 59 | 60 | ```bash 61 | git clone https://github.com/Dogfalo/materialize 62 | cd materialize 63 | npm install 64 | ``` 65 | 66 | Then run `grunt monitor` to compile the documentation. When it finishes, open a new browser window and navigate to `localhost:8000`. We use [BrowserSync](https://www.browsersync.io/) to display the documentation. 67 | 68 | ### Documentation for previous releases 69 | Previous releases and their documentation are available for [download](https://github.com/Dogfalo/materialize/releases). 70 | 71 | ## Supported Browsers: 72 | Materialize is compatible with: 73 | 74 | - Chrome 35+ 75 | - Firefox 31+ 76 | - Safari 9+ 77 | - Opera 78 | - Edge 79 | - IE 11+ 80 | 81 | ## Changelog 82 | For changelogs, check out [the Releases section of materialize](https://github.com/Dogfalo/materialize/releases) or the [CHANGELOG.md](CHANGELOG.md). 83 | 84 | ## Testing 85 | We use Jasmine as our testing framework and we're trying to write a robust test suite for our components. If you want to help, [here's a starting guide on how to write tests in Jasmine](CONTRIBUTING.md#jasmine-testing-guide). 86 | 87 | ## Contributing 88 | Check out the [CONTRIBUTING document](CONTRIBUTING.md) in the root of the repository to learn how you can contribute. You can also browse the [help-wanted](https://github.com/Dogfalo/materialize/labels/help-wanted) tag in our issue tracker to find things to do. 89 | 90 | ## Copyright and license 91 | Code Copyright 2018 Materialize. Code released under the MIT license. 92 | -------------------------------------------------------------------------------- /hw3/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # dependencies 64 | /node_modules 65 | /.pnp 66 | .pnp.js 67 | 68 | # testing 69 | /coverage 70 | 71 | # production 72 | /build 73 | 74 | # misc 75 | _db.json 76 | .vscode 77 | .idea 78 | .DS_Store 79 | .env.local 80 | .env.development.local 81 | .env.test.local 82 | .env.production.local 83 | 84 | npm-debug.log* 85 | yarn-debug.log* 86 | yarn-error.log* 87 | -------------------------------------------------------------------------------- /hw3/README.md: -------------------------------------------------------------------------------- 1 | # Homework 3 - React Library & Testing 2 | 3 | #### **Due (These are hard deadlines)** 4 | - ##### **Feature Implementation Due: 10/7 (Wed) 18:00** 5 | - ##### **Testing Implementation Due: 10/15 (Thu) 18:00** 6 | 7 | You will implement a front-end for a blogging service using React library. This is an **individual** assignment. 8 | This assignment will help you 9 | 10 | - Make a simple React (version 16.13.1) application before diving into your projects 11 | - Make test suites for the React application you implement 12 | - Let you try out stuff we have learned in our practice sessions 13 | 14 | ## Features 15 | 16 | Our blog will support three models: User, Articles, and Comments. 17 | You are required to create a total of five pages as shown in the below storyboard, meeting the following requirements: 18 | 19 |

live server extension

20 | 21 | - Log In page (`/login`) 22 | - You must have the following fields: 23 | 24 | | JSX Tag type | id | 25 | |---------------|--| 26 | | input | email-input | 27 | | input | pw-input | 28 | | button | login-button | 29 | 30 | - Users should be able to log-in by filling up the email and password inputs and hitting the log-in button. 31 | - As we don't have a proper backend, we don't do real, security-aware authentication yet, but users should only be able to log-in with an account with an email of 'swpp@snu.ac.kr' and password of 'iluvswpp'. 32 | - When a user tries to log-in with invalid inputs, the frontend should emit `Email or password is wrong` message through `alert` command (in Javascript). 33 | - After logging-in, users should find themselves at the article list page. (`/articles`) That is, when the user tries to go login page after logged in, the page should be redirected to article list page. 34 | - This is the only page that unauthorized users will have access to. Unauthorized users trying to access any other pages should be redirected to this page! 35 | - Article list page (`/articles`) 36 | - Users should be able to clearly make out the **whole** list of articles including article id, (full) article title, and author name in this page. 37 | - Also, user must be able to go to article create page (`/articles/create`) by hitting: 38 | 39 | | JSX Tag type | id | 40 | |-------------|--| 41 | | button | create-article-button | 42 | 43 | - Upon clicking on the article title, users should be able to access the article's detail page (`/articles/:id`). These article titles must be `button` but not `link`. 44 | - Article write(create) page (`/articles/create`) 45 | - You must have the following fields in Write tab: 46 | 47 | | JSX Tag type | id | 48 | |-------------|--| 49 | | input or textarea | article-title-input | 50 | | input or textarea | article-content-input | 51 | | button | back-create-article-button | 52 | | button | confirm-create-article-button | 53 | | button | preview-tab-button | 54 | | button | write-tab-button | 55 | 56 | - Also, you must have the following fields in Preview tab: 57 | 58 | | JSX Tag type | id | 59 | |-------------|--| 60 | | any plain text (like h1, h3, p, …) | article-author | 61 | | any plain text (like h1, h3, p, …) | article-title | 62 | | any plain text (like h1, h3, p, …) | article-content | 63 | | button | back-create-article-button | 64 | | button | confirm-create-article-button | 65 | | button | preview-tab-button | 66 | | button | write-tab-button | 67 | 68 | - When users fill up the title and contents under Write tab and hit the confirm button (either in Preview tab or Write tab), a new article having the given title and contents should be posted. 69 | - The created article, of course, should be tagged with your own author id. 70 | - After creating the article, the user should be redirected to the created article's detail page (`/articles/:id`) 71 | - While creating the article, the user should be able to preview the contents (under the Preview tab by hitting `preview-tab-button`), in the way that it will be shown in the details page. The user should stay in `/articles/create` page while seeing the preview. 72 | - After checking the preview, the user should be able to resume writing (under the Write tab by hitting `write-tab-button`). The user should stay in `/articles/create` page after hitting the `write-tab-button`. 73 | - If the title or content input are empty, the confirm button should be disabled. 74 | - The back button will go back to the article list page (`/articles`) (also without any alert). 75 | - Article detail page (`/articles/:id`) 76 | - You must have the following fields: 77 | 78 | | JSX Tag type | id | 79 | |-------------|--| 80 | | any plain text (like h1, h3, p, …) | article-author | 81 | | any plain text (like h1, h3, p, …) | article-title | 82 | | any plain text (like h1, h3, p, …) | article-content | 83 | | input or textarea | new-comment-content-input | 84 | | button | confirm-create-comment-button | 85 | | button | edit-comment-button | 86 | | button | delete-comment-button | 87 | | button | edit-article-button | 88 | | button | delete-article-button | 89 | | button | back-detail-article-button | 90 | 91 | - Users should be able to clearly make out the title, contents, and author name of the article. 92 | - Also, users should be able to see the whole comments for the corresponding article including the author name and contents of each comments. 93 | - Simple comments functionalities (Create for everyone / Edit & Delete for the comment author through the buttons) should work. 94 | - When a user hits the `confirm-create-comment-button`, a new comment with the contents provided through `new-comment-content-input` and the author's name should be added to this detail page (without any alert). However, `confirm-create-comment-button` should be disabled when the `new-comment-content-input` is empty. 95 | - When a user hits the `edit-comment-button`, a `prompt` taking some string input should be popped up (by using Javascript `prompt` command) and initial value of the prompt should be same as current comment's content 96 | - When the user fill the prompt input with new contents and confirm the pop-up, the contents of the comment should be updated with the new contents. 97 | - When the user confirm the pop-up with empty content, the contents should not be modified (consistent to `user cannot create empty comment`) 98 | - When the user canceled the pop-up, the contents should not be modified. 99 | - When a user hits the `delete-comment-button`, the comment should be deleted without any alert. 100 | - The edit and delete button for each comment must be visible for the author only. 101 | - Edit & Delete button for the article should work. 102 | - These buttons must be visible for the author only. 103 | - When a user hit the `edit-article-button`, the user should be redirected to the edit page (`articles/:id/edit`) 104 | - When a user hit the `delete-article-button`, the user should be redirected to the article list page (`articles/`) and the article should be deleted without any alert. 105 | - When a user hits `back-detail-article-button` button, the user should be redirected to the article list page. 106 | - Article edit page (`/articles/:id/edit`) 107 | - You must have the following fields in Write tab: 108 | 109 | | JSX Tag type | id | 110 | |-------------|--| 111 | | input or textarea | article-title-input | 112 | | input or textarea | article-content-input | 113 | | button | back-edit-article-button | 114 | | button | confirm-edit-article-button | 115 | | button | preview-tab-button | 116 | | button | write-tab-button | 117 | 118 | - Also, you must have the following fields in Preview tab: 119 | 120 | | JSX Tag type | id | 121 | |-------------|--| 122 | | any plain text (like h1, h3, p, …) | article-author | 123 | | any plain text (like h1, h3, p, …) | article-title | 124 | | any plain text (like h1, h3, p, …) | article-content | 125 | | button | back-edit-article-button | 126 | | button | confirm-edit-article-button | 127 | | button | preview-tab-button | 128 | | button | write-tab-button | 129 | 130 | - Users should see similar stuffs with article create page: Write tab and Preview tab. All requirements for the plain texts and tab buttons are identical to the create page (except the url). 131 | - However, the `article-title-input` should contains current article's title and `article-content-input` should contains current article's content 132 | - When a user hits `confirm-edit-article-button`, the user should be redirected to the article detail page (`articles/:id`) and the edited title and contents should be saved. Any comment for the article should not be modified or deleted by this process. 133 | - When a user hits `back-edit-article-button`, the following features should be supported: 134 | - If the title and contents have not been modified yet but are the same as the title and contents before editing, just go back to the detail page without any alert. 135 | - If the title or contents has been modified modified, you should make a confirmation pop-up (through Javascript `confirm` command) with message `Are you sure? The change will be lost.` 136 | - If the user accept the confirmation, the user should be redirected to the detail page and the title and contents of the article should not be modified. 137 | - If the user dismiss the confirmation, the user should just stay on the edit page and be able to resume editing. 138 | - If the title or content input are empty, the confirm button should be disabled (consistent to `/create`). 139 | - Common things for **all pages**: 140 | - If the user is logged-in, the user should be able to log-out from any of the pages by clicking `logout-button`. Upon logging-out, the user should end on the initial Log In page (shown as dotted lines on the storyboard). 141 | - The `logout-button` should not be displayed before the login. 142 | 143 | 144 | | JSX Tag type | id | 145 | |---------------|--| 146 | | button | logout-button | 147 | 148 | - Each user should be able to update or delete articles and comments only which they have created. 149 | - **All pages/components should have proper unit tests to test its functionalities, using Jest and Enzyme that are covered in the practice session. Your tests are expected to cover all of your code, and we will give credits according to your coverage results. You can see the coverage information of your application by using `npm test -- --coverage`. Also, all of your tests must pass.** 150 | 151 | We provide a simple [json-server](https://github.com/typicode/json-server) backend with our skeleton code (`skeleton/api`). 152 | Due to its simplicity, we do not go over too much into authentication and security for now, but later on (with HW4 and your project), it should be considered. 153 | 154 | **NOTE: For Windows users** 155 | 156 | To run the mocked json server in Windows environment, please change the following line in `skeleton/package.json`. 157 | 158 | From: 159 | ``` json 160 | "backend": "cp api/db.json api/_db.json && json-server --watch api/_db.json --routes api/routes.json --port 8000" 161 | ``` 162 | To: 163 | ``` json 164 | "backend": "copy /y api₩₩db.json api₩₩_db.json && json-server --watch api₩₩_db.json --routes api₩₩routes.json --port 8000" 165 | ``` 166 | 167 | To run the server, just type following command at the root of the project 168 | ``` 169 | $ yarn install (only for the first time) 170 | $ yarn run backend (or npm run backend) 171 | ``` 172 | 173 | You should be able to implement your redux action creators by sending appropriate http requests to the following URLs: 174 | 175 | | API | GET | POST | PUT | DELETE | 176 | |------------------------|-----|------|-----|--------| 177 | | `api/user` | Get user list | X | X | X | 178 | | `api/user/1` | Get user information containing whether or not the user is logged_in | X | Update user's `logged_in` value to log-in/log-out | X | 179 | | `api/articles` | Get article list | Create new article | X | X | 180 | | `api/articles/:id` | Get specified article | X | Edit specified article | Delete specified article | 181 | | `api/comments` | Get comments | Create new comment | X | X | 182 | | `api/comments/:id` | Get specified comment | X | Edit specified comment | Delete specified comment | 183 | 184 | Articles should have an `id` (number), `author_id` (number), `title` (string), and `content` (string). 185 | 186 | Comments should have an `id` (number), `author_id` (number), `article_id` (number), and `content` (string). 187 | 188 | Users should have an `id` (number), `email` (string), `password` (string), `name` (string), and `logged_in` (boolean). 189 | 190 | Each field names are as specified above. You should be able to implement the pages required with these APIs. 191 | 192 | ## Comments on files 193 | 194 | Files that are created inside the `skeleton` (root) and `src` folder have been already discussed during the practice session (contents in [this link](https://github.com/facebook/create-react-app)). As we have done so in our practice sessions, you are expected to add proper components, containers, redux-store (actions, reducers) under the `src/` directory freely, according to your needs. Nicely refactored code will result in better readability and is recommended. 195 | 196 | The `skeleton/api` directory is added to provide simple backend for you to test out your application, and serves as specified above. **Please do not modify this directory.** You are expected to create `store` that manages redux-store by communicating using HTTP, and `containers` and `components` that produce each pages' requirements. The existing `App.js` is expected to be the root component of the entire application. 197 | 198 | Also, please do not make any un-requested alerts. It will largely harm the e2e test result. 199 | 200 | We will test your code after conducting `yarn install`, so you can import other libraries through `package.json`. However, you should not change the version of already imported dependencies. 201 | 202 | ## Tips 203 | 204 | - Most of things have been covered in the tutorial during the lab session. Please look carefully through the slides and the tips provided. 205 | - It might be useful and more pleasing to the eyes by using a CSS framework like [Bootstrap-React](https://react-bootstrap.github.io/) and [SemanticUI-React](https://react.semantic-ui.com/). However, this is **optional**, please proceed on your own willings. You might be needing them for your projects ahead, so it would be nice to have some head start. 206 | 207 | ## Grading 208 | 209 | This assignment is composed of a total of 80 points. We will score your feature implementation (**lastest commit before 4 October 6:00 PM**) with our e2e test code, having 55 test cases that reflects the requirements given above. You will get 1 point for each passed test case. Also, we will check your unit test coverage (**latest commit before 12 October 6:00 PM**), which is 25 points in total. Grading details are shown below. 210 | 211 | ### Feature Implementation Score 212 | The e2e test consist of cases about: 213 | - Log in page features (4 points) 214 | - Article list page features (5 points) 215 | - Article create page features (10 points) 216 | - Article detail page features when the user is the author of the article (14 points) 217 | - Article detail page features when the user is not the author of the article (13 points) 218 | - Article edit page features (9 points) 219 | 220 | ### Unit Test Score 221 | Your unit test score will be given based on both test coverage and completeness of your feature implementation. 222 | 223 | This is to make sure that your test code is faithful to the feature requirements. 224 | 225 | To be more precise, your unit test score will be given as follows: 226 | (*scorefeature* denotes your feature implementation score) 227 | 228 | |
Coverage
|
Points
| 229 | | :-----------------------: | :---------------------: | 230 | | 90% and above |
25 * (*scorefeature* / 55)
| 231 | | 80% and above |
20 * (*scorefeature* / 55)
| 232 | | 70% and above |
15 * (*scorefeature* / 55)
| 233 | | 60% and above |
10 * (*scorefeature* / 55)
| 234 | | below 60% |
0
| 235 | 236 | *Note: All your test cases must pass* 237 | 238 | 239 | 240 | Since we will automatically score your homework, **please make sure that you follow the features specification (especially, the Tag type and id of JSX fields)**. 241 | 242 | Also, if some basic features are not implemented properly, many other test cases can fail. For example, if the logging-in process works badly, most of the cases above will fail. 243 | We'll try to give some partial points in this case, but it might not be enough. 244 | 245 | It's a good idea to start with the log-in page, and then implement routing features, article list pages, and other features in order. 246 | 247 | Finally, since you have to implement many features, start early! 248 | 249 | ## Submission 250 | 251 | **Due: (These are hard deadlines)** 252 | - **Feature Implementation Due: 10/7 (Wed) 18:00** 253 | - **Testing Implementation Due: 10/15 (Thu) 18:00** 254 | 255 | We will check the snapshot of the *master* branch of your Github repository at the deadline and grade it. 256 | 257 | Please name your repository as `swpp-hw3-YOUR_USERNAME`, and replace YOUR_USERNAME with your own GitHub username. Please note that USERNAME is case-sensitive. 258 | Refer to HW1 to create another private repository. 259 | 260 | Also, make sure to add TA(`kdh0102`) as your collaborator in the repository settings. 261 | 262 | We automatize the grading process, so your homework may not be graded properly if your directory hierarchy doesn't look like this: 263 | 264 | ``` 265 | repository_root/ 266 | api/ 267 | public/ 268 | src/ 269 | App.js 270 | App.test.js 271 | index.js 272 | ... 273 | package.json 274 | ... 275 | ``` 276 | Also, please include proper `.gitignore` file so redundant items are not be pushed to your repository (like `node_modules`). 277 | You can use skeleton code's `.gitignore` file. 278 | 279 | Also, make sure to push your work on Github on time. You won't need to send us an email for submission, but we will pull each repositories at the time specified. 280 | -------------------------------------------------------------------------------- /hw3/api/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "articles": [ 3 | { 4 | "id": 0, 5 | "author_id": 1, 6 | "title": "10 React JS Articles Every Web Developer Should Read", 7 | "content": "Hello Guys, React or React JS is a JavaScript front-end library from Facebook which lets you create HTML based GUI. It makes the task easier by providing a component-based architecture which was only available to languages like Java and C# before." 8 | }, 9 | { 10 | "id": 11, 11 | "author_id": 2, 12 | "title": "React: A JavaScript library for building user interfaces", 13 | "content": "React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes." 14 | }, 15 | { 16 | "id": 12, 17 | "author_id": 1, 18 | "title": "Building the New facebook.com with React, GraphQL and Relay", 19 | "content": "Open source projects like React, GraphQL and Relay are powering more and more Facebook services. In this session, we'll discuss how we use the latest features of these technologies, like React Suspense, to help deliver a high quality, modern web experience at Facebook." 20 | }, 21 | { 22 | "id": 13, 23 | "author_id": 1, 24 | "title": "React State with Hooks: useReducer, useState, useContext", 25 | "content": "If you haven't used state management excessively in React Function Components, this tutorial may help you to get a better understanding of how React Hooks -- such as useState, useReducer, and useContext -- can be used in combination for impressive state management in React applications. In this tutorial, we will almost reach the point where these hooks mimic sophisticated state management libraries like Redux for globally managed state. Let's dive into the application which we will implement together step by step." 26 | }, 27 | { 28 | "id": 14, 29 | "author_id": 3, 30 | "title": "From Redux to Hooks: A Case Study", 31 | "content": "Having a single store in Redux comes with benefits, but at the same time you end up putting everything into global namespace. Most applications profit from this, you can even create nested reducers and use naming conventions for actions. But if your global state is small and you know for sure that specific slices of state will only be used by specific areas of your application, making them global makes you feel.. uneasy." 32 | }, 33 | { 34 | "id": 15, 35 | "author_id": 2, 36 | "title": "Application State Management with React", 37 | "content": "One of the reasons redux was so successful was the fact that react-redux solved the prop drilling problem. The fact that you could share data across different parts of your tree by simply passing your component into some magical connect function was wonderful. Its use of reducers/action creators/etc. is great too, but I'm convinced that the ubiquity of redux is because it solved the prop drilling pain point for developers." 38 | }, 39 | { 40 | "id": 16, 41 | "author_id": 2, 42 | "title": "What I wish I knew when I started to work with React.js", 43 | "content": "After its initial release on May 29, 2013, React.js has taken over the internet. It’s not a secret that myself and many other developers owe their success to this amazing framework." 44 | }, 45 | { 46 | "id": 17, 47 | "author_id": 2, 48 | "title": "React’s Five Fingers of Death. Master these five concepts, then master React.", 49 | "content": "A few years ago, my friend Sean started telling me how this brand new front-end library called React was going to take over the web. At first I dismissed it as just another framework fad. But then I started hearing about React more and more, to the point where I felt like ignoring it just wasn’t an option anymore." 50 | }, 51 | { 52 | "id": 18, 53 | "author_id": 3, 54 | "title": "Semantic UI: User Interface is the language of the web", 55 | "content": "Semantic 2.4 brings a new components for handling content loading ui placeholder, as well as a new type of segment used to reserve space for content: ui placeholder segment." 56 | }, 57 | { 58 | "id": 19, 59 | "author_id": 3, 60 | "title": "Bootstrap: Build responsive, mobile-first projects on the web with the world’s most popular front-end component library", 61 | "content": "Bootstrap is an open source toolkit for developing with HTML, CSS, and JS. Quickly prototype your ideas or build your entire app with our Sass variables and mixins, responsive grid system, extensive prebuilt components, and powerful plugins built on jQuery." 62 | }, 63 | { 64 | "id": 20, 65 | "author_id": 3, 66 | "title": "ReactJS Testing", 67 | "content": "Jest is a delightful JavaScript Testing Framework with a focus on simplicity." 68 | } 69 | ], 70 | "comments": [ 71 | { 72 | "id": 1, 73 | "article_id": 0, 74 | "author_id": 2, 75 | "content": "What do you mean wow?" 76 | }, 77 | { 78 | "id": 2, 79 | "article_id": 0, 80 | "author_id": 3, 81 | "content": "I was surprised" 82 | }, 83 | { 84 | "id": 4, 85 | "article_id": 11, 86 | "author_id": 3, 87 | "content": "I agree with you" 88 | }, 89 | { 90 | "id": 5, 91 | "article_id": 12, 92 | "author_id": 3, 93 | "content": "Haha this is funny" 94 | }, 95 | { 96 | "id": 6, 97 | "article_id": 12, 98 | "author_id": 2, 99 | "content": "Yes, it is hilarious" 100 | }, 101 | { 102 | "id": 7, 103 | "article_id": 13, 104 | "author_id": 1, 105 | "content": "I am sad" 106 | }, 107 | { 108 | "id": 8, 109 | "article_id": 13, 110 | "author_id": 2, 111 | "content": "I do not want to see you sad" 112 | }, 113 | { 114 | "id": 9, 115 | "article_id": 14, 116 | "author_id": 3, 117 | "content": "I do not want to work" 118 | }, 119 | { 120 | "id": 10, 121 | "article_id": 15, 122 | "author_id": 3, 123 | "content": "What time is it?" 124 | }, 125 | { 126 | "id": 11, 127 | "article_id": 15, 128 | "author_id": 2, 129 | "content": "It is 5 in the morning" 130 | }, 131 | { 132 | "id": 12, 133 | "article_id": 16, 134 | "author_id": 1, 135 | "content": "I like it" 136 | }, 137 | { 138 | "id": 13, 139 | "article_id": 17, 140 | "author_id": 1, 141 | "content": "I don't think so" 142 | }, 143 | { 144 | "id": 14, 145 | "article_id": 17, 146 | "author_id": 2, 147 | "content": "Me neither" 148 | }, 149 | { 150 | "id": 15, 151 | "article_id": 18, 152 | "author_id": 2, 153 | "content": "I am so stressed out" 154 | }, 155 | { 156 | "id": 16, 157 | "article_id": 18, 158 | "author_id": 3, 159 | "content": "Stress is bad" 160 | }, 161 | { 162 | "id": 17, 163 | "article_id": 18, 164 | "author_id": 2, 165 | "content": "Yeah, I should try to feel better" 166 | }, 167 | { 168 | "id": 18, 169 | "article_id": 19, 170 | "author_id": 1, 171 | "content": "My dog is cute" 172 | }, 173 | { 174 | "id": 19, 175 | "article_id": 19, 176 | "author_id": 2, 177 | "content": "I think so as well" 178 | }, 179 | { 180 | "id": 20, 181 | "article_id": 20, 182 | "author_id": 3, 183 | "content": "Tornado has hit our town" 184 | }, 185 | { 186 | "id": 21, 187 | "article_id": 20, 188 | "author_id": 1, 189 | "content": "Oh, what a misery" 190 | }, 191 | { 192 | "author_id": 1, 193 | "article_id": 0, 194 | "content": "Wow!", 195 | "id": 22 196 | } 197 | ], 198 | "user": [ 199 | { 200 | "id": 1, 201 | "email": "swpp@snu.ac.kr", 202 | "password": "iluvswpp", 203 | "name": "Software Lover", 204 | "logged_in": false 205 | }, 206 | { 207 | "id": 2, 208 | "email": "alan@turing.com", 209 | "password": "iluvswpp", 210 | "name": "Alan Turing", 211 | "logged_in": false 212 | }, 213 | { 214 | "id": 3, 215 | "email": "edsger@dijkstra.com", 216 | "password": "iluvswpp", 217 | "name": "Edsger Dijkstra", 218 | "logged_in": false 219 | } 220 | ] 221 | } 222 | -------------------------------------------------------------------------------- /hw3/api/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api/*": "/$1" 3 | } -------------------------------------------------------------------------------- /hw3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skeleton", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://localhost:8000", 6 | "dependencies": { 7 | "enzyme": "^3.11.0", 8 | "enzyme-adapter-react-16": "^1.15.4", 9 | "json-server": "^0.16.1", 10 | "react": "^16.13.1", 11 | "react-dom": "^16.13.1", 12 | "react-scripts": "3.4.3", 13 | "react-test-renderer": "^16.13.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "backend": "cp api/db.json api/_db.json && json-server --watch api/_db.json --routes api/routes.json --port 8000" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /hw3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw3/public/favicon.ico -------------------------------------------------------------------------------- /hw3/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /hw3/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw3/public/logo192.png -------------------------------------------------------------------------------- /hw3/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw3/public/logo512.png -------------------------------------------------------------------------------- /hw3/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /hw3/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /hw3/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | .App-header { 12 | background-color: #282c34; 13 | min-height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: calc(10px + 2vmin); 19 | color: white; 20 | } 21 | 22 | .App-link { 23 | color: #61dafb; 24 | } 25 | 26 | @keyframes App-logo-spin { 27 | from { 28 | transform: rotate(0deg); 29 | } 30 | to { 31 | transform: rotate(360deg); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /hw3/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | 4 | function App() { 5 | return ( 6 |
7 |

SWPP HW3!

8 |
9 | ); 10 | } 11 | 12 | export default App; 13 | -------------------------------------------------------------------------------- /hw3/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /hw3/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /hw3/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /hw3/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /hw3/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /hw4/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2020 Software Platform Lab 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /hw4/README.md: -------------------------------------------------------------------------------- 1 | # Homework 4 - Django 2 | 3 | **Due: 10/29 (Thu) 18:00 (This is a hard deadline)** 4 | 5 | ## Django 6 | 7 | In this assignment you will implement a backend service for the blog frontend that you have created in homework 3. 8 | This is an **individual** assignment. 9 | 10 | Through this assignment, you are expected to: 11 | 12 | - Build a RESTful API server with Django (3.1.2) 13 | - Understand how communication between the client and the server occurs 14 | - Test your Django application 15 | 16 | ### Comments on files 17 | 18 | Our `myblog` project consists of a single app, `blog`. 19 | Because we provide the url routing and setting of the project already, you are expected to update the `blog` app mainly. 20 | 21 | ### Features 22 | 23 | As you have seen in homework 3, our blog has three models: User, Article, and Comment. 24 | 25 | - Each user should be able to sign up, sign in and sign out. 26 | - Only those users who are signed in are allowed to read or write articles and comments. 27 | - Users should be able to update or delete articles and comments only which they have created. 28 | 29 | For the user model, you **must** use the [Django default user model](https://docs.djangoproject.com/en/3.1/topics/auth/) and the django authentication system to manage user authentication. 30 | In homework 3, we didn't cover the real user authentication process. 31 | Now with a proper backend, we can manage user sessions and authentication supported by Django. 32 | 33 | For articles, you need to create a model named as `Article` that consists of following fields: 34 | 35 | | Field name | Type | 36 | |------------------------|-----| 37 | | `title` | Char Field (max_length=64) | 38 | | `content` | Text Field | 39 | | `author` | Foreign Key | 40 | 41 | The author must be a (foreign) key referring a User. 42 | 43 | For comments, you need to create a model named as `Comment` that consists of following fields: 44 | 45 | | Field name | Type | 46 | |------------------------|-----| 47 | | `article` | Foreign Key | 48 | | `content` | Text Field | 49 | | `author` | Foreign Key | 50 | 51 | The article and author must be a (foreign) key referring an Article and a User. 52 | 53 | To check whether you implemented your model correctly or not, please check the following code works well with your model implementation (in your test code or somewhere else). 54 | 55 | ``` python 56 | from .models import Article, Comment 57 | 58 | new_user = User.objects.create_user(username='swpp', password='iluvswpp') # Django default user model 59 | new_article = Article(title='I Love SWPP!', content='Believe it or not', author=new_user) 60 | new_article.save() 61 | new_comment = Comment(article=new_article, content='Comment!', author=new_user) 62 | new_comment.save() 63 | ``` 64 | 65 | Detailed specifications of RESTful APIs are as following: 66 | 67 | | API | GET | POST | PUT | DELETE | 68 | |------------------------|-----|------|-----|--------| 69 | | `api/signup` | X | Create new user | X | X | 70 | | `api/signin` | X | Log in | X | X | 71 | | `api/signout` | Log out | X | X | X | 72 | | `api/article` | Get article list | Create new article | X | X | 73 | | `api/article/:article_id` | Get specified article | X | Edit specified article | Delete specified article | 74 | | `api/article/:article_id/comment` | Get comments of specified article | Create comment on specified article | X | X | 75 | | `api/comment/:comment_id` | Get specified comment | X | Edit specified comment | Delete specified comment | 76 | 77 | Note that the APIs are slightly different from that of homework 3. Since we have used simple json server backend, APIs were limited in homework 3. 78 | In this assignment, we will implement a more RESTful API. 79 | 80 | POST and PUT requests should contain data using the JSON format in the body of the request. 81 | For each model, the JSON format should look like: 82 | 83 | - User : `{username: string, password: string}` 84 | - Article : `{title: string, content: string}` 85 | - Comment : `{content: string}` 86 | 87 | Also, the user information will be included in the `request` automatically. Check `request.user`. 88 | 89 | For each API you should respond with the appropriate HTTP response code. 90 | The list of response codes you should use is as follows: 91 | 92 | - `200` (Ok) : Request was responded successfully. 93 | - `201` (Created) : Request has created new resources successfully. 94 | - `204` (No Content) : Request was responded successfully but without any content. 95 | - `400` (Bad Request) : Failed to decode request body or failed to retrieve necessary key-value from json (`KeyError`). 96 | - `401` (Unauthorized) : Request requires authentication. This should be returned if you are requesting without signing in. 97 | - `403` (Forbidden) : Request is forbidden. This should be returned if your request tries to modify resources of which you are not the owner. 98 | - `404` (Not Found) : Requested resource is not found. 99 | - `405` (Method not allowed) : Requested URL does not allow the method of the request. 100 | 101 | Please make sure to implement your request methods under the following global specifications: 102 | 103 | - For all non-allowed requests (X marked in the API table), response with `405` (and any information must not be modified). 104 | - For all requests about article and comment without signing in, response with `401` (and any information must not be modified). 105 | - For all requests about non-existing article and comment, response with `404` (and any information must not be modified). 106 | - For all PUT and DELETE requests from non-author, response with `403` (and any information must not be modified). 107 | 108 | Among these global specifications, the prior specification has the higher priority. For example, if someone requests for a non-existing article without signing in, response with `401` instead of `404`. 109 | 110 | Also, please make sure to implement your request methods under the following detailed specifications (in your `views.py`). When the server successfully handles a request, it responds with a JSONResponse for GET (except `api/signout`) and responds with a HTTPResponse for POST, PUT, and DELETE. 111 | 112 | - POST `api/signup`: 113 | Create a user with the information given by request JSON body and response with `201` 114 | 115 | - POST `api/signin`: 116 | Authenticate with the information given by request JSON body. If success, log-in (the authentication info should be changed properly) and response with `204`. If fail, response with `401`. 117 | 118 | - GET `api/signout`: 119 | If the user is authenticated, log-out (the authentication info should be changed properly) and response with `204`. If not, response with `401`. 120 | 121 | - GET `api/article`: 122 | Response with a JSON having a list of dictionaries for each article's `title`, `content`, and `author`. The value of the `author` must be the `id` of the author but not her `username`. 123 | 124 | - POST `api/article`: 125 | Create an article with the information given by request JSON body and response with `201`. Posted article (with it's assigned id) should be included in response's content as JSON format. 126 | 127 | - GET `api/article/:article_id`: 128 | Response with a JSON having a dictionary for the target article's `title`, `content`, and `author`. The value of the `author` must be the `id` of the author but not her `username`. 129 | 130 | - PUT `api/article/:article_id`: 131 | Update the target article with the information given by request JSON body and response with `200`. Updated article (with it's id) should be included in response's content as JSON format. 132 | 133 | - DELETE `api/article/:article_id`: 134 | Delete the target article and response with `200`. When deleting an article, all comments under the target article (but not any comments under other articles, of course) **must** be deleted also. 135 | 136 | - GET `api/article/:article_id/comment`: 137 | Response with a JSON having a list of dictionaries for each comment's `article`, `content`, and `author`. The value of the `article` and the `author` must be the `id` of the article and the author but not the `title` and her `username`. 138 | 139 | - POST `api/article/:article_id/comment`: 140 | Create a comment with the information given by request JSON body and response with `201`. Posted comment (with it's assigned id) should be included in response's content as JSON format. 141 | 142 | - GET `api/comment/:comment_id`: 143 | Response with a JSON having a dictionary for the target comment's `article`, `content`, and `author`. The value of the `article` and the `author` must be the `id` of the article and the author but not the `title` and her `username`. 144 | 145 | - PUT `api/comment/:comment_id`: 146 | Update the target comment with the information given by request JSON body and response with `200`. Updated comment (with it's id) should be included in response's content as JSON format. 147 | 148 | - DELETE `api/comment/:comment_id`: 149 | Delete the target comment and response with `200`. When deleting a comment, other users, articles and comments **must** not be deleted also. 150 | 151 | ### Testing 152 | 153 | You should also write tests to verify that your blog backend is implemented correctly. 154 | Your tests should reach **100%** of both the statement coverage and the branch coverage. 155 | 156 | You can run tests and gather coverage data by: 157 | 158 | - Statement coverage : `coverage run --source='./blog' manage.py test` 159 | - Branch coverage : `coverage run --branch --source='./blog' manage.py test` 160 | 161 | Use `coverage report -m` to report on the results. 162 | Use `coverage html` for a nicer presentation. 163 | 164 | ### Tips 165 | 166 | In Django, it is rather complex to send request other than GET method with RESTful API due to the [CSRF token](https://docs.djangoproject.com/en/3.1/ref/csrf/). 167 | To successfully handle such requests, try the following steps: 168 | 169 | 1. Before sending the request, send GET request to `/api/token`. The response will come with an empty content and will set the cookie `csrftoken` in your browser. 170 | 2. Send the POST request with a header containing `HTTP_X_CSRFTOKEN` as the value of the `csrftoken`. 171 | 172 | For more detail, see `test_csrf` of the `blog/test.py` file in the skeleton code. 173 | You may change this part if you have a better way of handling the CSRF token, but disabling the protection itself is **NOT** permitted. 174 | 175 | ## Grading 176 | 177 | This assignment is composed of a total of 80 points. 178 | We will test your Django code under Python 3.7, and Django 3.1.2. (You can check your Django version with `pip freeze | grep Django`) 179 | 180 | - User APIs (15 points) 181 | - Article APIs (20 points) 182 | - Comment APIs (20 points) 183 | - Django Testing (25 points) 184 | 185 | Like HW 3, if some basic features are not implemented properly, many other test cases can fail. 186 | For example, if the signing-in process works badly, most of the cases above will fail. 187 | We'll try to give some partial points in this case, but it might not be enough. 188 | 189 | ## Submission 190 | 191 | **Due: 10/29 (Thu) 18:00 (This is a hard deadline)** 192 | 193 | We will check the snapshot of the *master* branch of your Github repository at the deadline and grade it. 194 | Please name your repository as `swpp-hw4-YOUR_USERNAME`, and replace YOUR_USERNAME with you own GitHub username. 195 | Refer to HW1 to create another private repository. (Make sure to push your work on Github on time and add `kdh0102` as a collaborator in your repository settings.) 196 | Also, make sure to push your work on Github on time. 197 | You won't need to send us an email for submission, but we will pull each repositories at the time specified. 198 | 199 | Please put your django projects in the root folder (not inside another `hw4` or `skeleton` folder) appropriately. 200 | Your directory hierarchy should look like this: 201 | 202 | ``` bash 203 | repository_root/ 204 | README.md (this file) 205 | blog/ 206 | ... 207 | myblog/ 208 | ... 209 | manage.py 210 | ... 211 | ``` 212 | -------------------------------------------------------------------------------- /hw4/blog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw4/blog/__init__.py -------------------------------------------------------------------------------- /hw4/blog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /hw4/blog/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BlogConfig(AppConfig): 5 | name = 'blog' 6 | -------------------------------------------------------------------------------- /hw4/blog/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw4/blog/migrations/__init__.py -------------------------------------------------------------------------------- /hw4/blog/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /hw4/blog/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase, Client 2 | import json 3 | 4 | 5 | class BlogTestCase(TestCase): 6 | def test_csrf(self): 7 | # By default, csrf checks are disabled in test client 8 | # To test csrf protection we enforce csrf checks here 9 | client = Client(enforce_csrf_checks=True) 10 | response = client.post('/api/signup', json.dumps({'username': 'chris', 'password': 'chris'}), 11 | content_type='application/json') 12 | self.assertEqual(response.status_code, 403) # Request without csrf token returns 403 response 13 | 14 | response = client.get('/api/token') 15 | csrftoken = response.cookies['csrftoken'].value # Get csrf token from cookie 16 | 17 | response = client.post('/api/signup', json.dumps({'username': 'chris', 'password': 'chris'}), 18 | content_type='application/json', HTTP_X_CSRFTOKEN=csrftoken) 19 | self.assertEqual(response.status_code, 201) # Pass csrf protection 20 | -------------------------------------------------------------------------------- /hw4/blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from blog import views 3 | 4 | urlpatterns = [ 5 | path('signup', views.signup, name='signup'), 6 | path('token', views.token, name='token'), 7 | ] 8 | -------------------------------------------------------------------------------- /hw4/blog/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse, HttpResponseNotAllowed 2 | from django.contrib.auth.models import User 3 | from django.views.decorators.csrf import ensure_csrf_cookie 4 | import json 5 | 6 | 7 | def signup(request): 8 | if request.method == 'POST': 9 | req_data = json.loads(request.body.decode()) 10 | username = req_data['username'] 11 | password = req_data['password'] 12 | User.objects.create_user(username, password) 13 | return HttpResponse(status=201) 14 | else: 15 | return HttpResponseNotAllowed(['POST']) 16 | 17 | 18 | @ensure_csrf_cookie 19 | def token(request): 20 | if request.method == 'GET': 21 | return HttpResponse(status=204) 22 | else: 23 | return HttpResponseNotAllowed(['GET']) 24 | -------------------------------------------------------------------------------- /hw4/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myblog.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /hw4/myblog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/hw4/myblog/__init__.py -------------------------------------------------------------------------------- /hw4/myblog/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for myblog project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myblog.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /hw4/myblog/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for myblog project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.1.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.1/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '=-0r^%f83$ko7i1-bqo^r6x-*+mk3bz^_irr#tl@8vy44oyin0' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'blog.apps.BlogConfig', 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'myblog.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'myblog.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 76 | 77 | DATABASES = { 78 | 'default': { 79 | 'ENGINE': 'django.db.backends.sqlite3', 80 | 'NAME': BASE_DIR / 'db.sqlite3', 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 91 | }, 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/3.1/topics/i18n/ 106 | 107 | LANGUAGE_CODE = 'en-us' 108 | 109 | TIME_ZONE = 'UTC' 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 120 | 121 | STATIC_URL = '/static/' 122 | -------------------------------------------------------------------------------- /hw4/myblog/urls.py: -------------------------------------------------------------------------------- 1 | """myblog URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | path('api/', include('blog.urls')), 22 | ] 23 | -------------------------------------------------------------------------------- /hw4/myblog/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for myblog project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myblog.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /project/Add Badge in README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/Add Badge in README.pdf -------------------------------------------------------------------------------- /project/README.md: -------------------------------------------------------------------------------- 1 | The project will follow this process: 2 | 3 | - Project team formation 4 | - [Project proposal](projectproposal.md) 5 | - Project sprint 1 _(Features + Setup)_ 6 | - [Requirements and specification](project-requirements-and-specification.md) 7 | - Project sprint 2 _(Proof-of-Concept)_ 8 | - Revised requirements and specification 9 | - [Design and planning](design-and-planning.md) 10 | - Working code up to project sprint 2 11 | - Project sprint 3 _(Feature Development)_ 12 | - Revised requirements and specification 13 | - Revised design and planning 14 | - Working code up to project sprint 3 15 | - Project sprint 4 _(Exception Handling + Testing)_ 16 | - Revised requirements and specification 17 | - Revised design and planning 18 | - Working code up to project sprint 4 19 | - Project sprint 5 _(Deployment + Optimization)_ 20 | - Revised requirements and specification 21 | - Revised design and planning 22 | - Working code up to project sprint 5 23 | - [Final demo poster session](postersession.md) & final report 24 | 25 | Note : Every document for each milestones must be written in **English**! 26 | 27 | [Sprint Instructions](sprint-instructions.md) 28 | 29 | ## Resources 30 | [Code review developer guide](https://google.github.io/eng-practices/review/) 31 | 32 | ## Project Timeline 33 | | | Start | End | TA meeting | 34 | |-|-------|-----|------------| 35 | | Sprint 1 | Oc1. 5 (Mon)| Oct. 17 (Sat), 6pm(report due) | TH/F | 36 | | Sprint 2 | Oct. 19 (Mon)| Oct. 31 (Sat), 6pm(report due) | TH/F | 37 | | Sprint 3 | Nov. 2 (Mon)| Nov. 14 (Sat), 6pm(report due) | TH/F | 38 | | Project progress presentation | Nov. 11, Nov. 12 | | | 39 | | Sprint 4 | Nov. 16 (Mon)| Nov. 28 (Sat), 6pm(report due) | TH/F| 40 | | Sprint 5 | Nov. 30 (Mon)| Dec. 12 (Sat), 6pm(report due) | TH/F | 41 | | Final poster | Dec. 17 (Thu) | | | 42 | | Final report | | Dec. 18 (Thu) 6pm | | 43 | 44 | -------------------------------------------------------------------------------- /project/design-and-planning-examples/Design_and_Planning_example.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/design-and-planning-examples/Design_and_Planning_example.pdf -------------------------------------------------------------------------------- /project/design-and-planning.md: -------------------------------------------------------------------------------- 1 | ## Design and Planning Document 2 | 3 | Project Name
4 | Design and Planning Document
5 | ??/??/??,
6 | Version major.minor
7 | 8 | **Instructions**
9 | This is a design and planning document template for SWPP. Please fill out this template carefully. 10 | 11 | This will be a living document. For the first sprint you will fill in the document with the design details as you can see them before the first sprint. In subsequent sprints you will expand this document. 12 | 13 | You have to use Github wiki for this document. 14 | 15 | **Submission**
16 | You must send an email to swpp-staff@spl.snu.ac.kr with a PDF version of your document by the deadline. 17 | 18 | You must send the email by the deadline in the class calendar. This is a HARD deadline. 19 | 20 | **Document Revision History**
21 | Your first version of this document is version 1.0. After that minor changes increment the minor version number (e.g., 1.1, 1.2, …) and major changes increment the major version number and set the minor number to zero (e.g., 2.0, 3.0, …). We will follow this convention with other documents as well. 22 | 23 | Rev. 1.0 YYYY-MM-DD - initial version 24 | 25 | **System Architecture**
26 | Here you should describe the **high-level architecture** of your system: the major pieces and how they fit together. Use graphical notations as much as possible in preference to English sentences. For example, you could describe the architecture using UML, if your system lends itself to these descriptions. Try to use standard architectural elements (e.g., pipe-and-filter, client-server, event-based, model-view-controller). Make sure to describe the major interfaces between components, which you will describe in more detail in the "Design details" section below. 27 | 28 | **Design Details**
29 | In this section go those important facets that are not at the level of “architecture,” such as descriptions of critical algorithms, protocols, and key invariants. Also, wherever possible items should be linked back to your specification. Ideally, you can match up everything in the specification with where it is implemented in the design. 30 | 31 | We expect that once this document is completed you will split into subteams to implement the various major components. To be ready for such a split, you need to have a precise idea of how the components are interacting, and how you are going to start implementing them. A complete class-level design might not be always possible so early, but you need to specify at least the API among the major components. Use UML when appropriate. 32 | 33 | If there are messages sent between clients and servers, you should identify **what messages and what data they contain, and in what format, and in what order they should be sent.** 34 | 35 | We expect to see a more refined design for the features to be included in the current sprint, and perhaps a more rough design for the features to be implemented in future sprints. 36 | 37 | If you have considered alternative designs, please describe briefly your reasons for choosing the final design. 38 | 39 | **Implementation Plan**
40 | Break down each user story described in your requirements document into programming tasks. Determine the difficulty of each task, and try to estimate the number of developer-days that the tasks should take. Try to also determine dependencies among tasks. Then, you should list all of the tasks to be done in the current sprint, a preliminary assignment of tasks to people in the group, estimates of the time for each task, dependencies between tasks, and a preliminary division into sprints (e.g., which features are implemented in the first sprint, second sprint, and so on). The plan should be designed to get some prototype system running as quickly as possible and then growing towards to the full project over a sequence of sprints. Please pay extra attention to the dependency relationships between tasks; you will almost certainly run into the situation where one bit isn't done but everything else is waiting for it. In that case, you want to know exactly where resources need to go, and how urgent each bit is (hint: NOT proportional to its size or importance in the whole system). 41 | 42 | Try to identify the major risks for the project in general and the plan in particular. Explain what the risks are and discuss how you plan to mitigate them. 43 | 44 | **Testing Plan**
45 | In this section goes a brief description of how you plan to test the system. Thought should be given to how mostly automatic testing can be carried out, so as to maximize the limited number of human hours you will have for testing your system. The effort you put early on on automated testing will pay off when you have to ensure that you are not breaking existing functionality in future sprints. 46 | Consider the following kinds of testing: 47 | 48 | - **Unit testing**: explain for what modules you plan to write unit tests, and what framework you plan to use.
49 | - **Functional testing**: What APIs you plan to test? How will you test them? What tools you will use? Will you write mocks?
50 | - **Acceptance & integration testing**: how do you plan to test the user interface and scenarios?
51 | 52 | **Registering Issues**
53 | You have to register Github issues regarding tasks for design, implementation, and testing and mark them with milestones. 54 | 55 | **Design and planning document grading guidelines**
56 | These are the grading guidelines that staff will use to evaluate your document. 57 | 58 | | Max points | Design | 59 | |-------------|--------| 60 | | 8 | Are all parts of the document in agreement with the product requirements? | 61 | | 10 | Is the architecture of the system described, with the major components and their interfaces? | 62 | | 10 | Are all the external interfaces to the system specified in detail? | 63 | | 10 | Are the major internal interfaces (e.g., client-server) specified in detail? | 64 | | 8 | Is there sufficient detail in the design to start Iteration 1? | 65 | | 4 | Is there reasonable detail in the design for features in future iterations? | 66 | | | **Planning** | 67 | | 8 | Is the plan for Iteration 1 sufficiently complete to start the implementation ? | 68 | | 4 | Are the subteams identified and has enough thought been given to parallelization of effort and team dependencies? | 69 | | 4 | Is there a discussion of the risks associated with this plan? | 70 | | 4 | Are the testing activities scheduled at the appropriate times? | 71 | | | **Testing** | 72 | | 5 | Does the design take into account testability of the various units, components, and subsystems ? | 73 | | 4 | Is there a discussion of how unit testing will be done? | 74 | | 6 | Is there a discussion of how functional (API) testing will be done automatically? | 75 | | 4 | Is there a discussion of how acceptance/integration testing will be done? | 76 | | | **Clarity** | 77 | | 4 | Is the solution at a fairly consistent and appropriate level of detail? | 78 | | 4 | Is the solution clear enough to be turned over to an independent group for implementation and still be understood? | 79 | | 5 | Is the document making good use of semi-formal notation (UML, diagrams, etc) | 80 | | 4 | Is the document identifying common architectural or design patterns, where appropriate? | 81 | | 4 | Is the document carefully written, without typos and grammatical errors? | 82 | -------------------------------------------------------------------------------- /project/final-report-examples/team11_final_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/final-report-examples/team11_final_report.pdf -------------------------------------------------------------------------------- /project/final-report-examples/team2_final_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/final-report-examples/team2_final_report.pdf -------------------------------------------------------------------------------- /project/poster-examples/Team21_poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/poster-examples/Team21_poster.pdf -------------------------------------------------------------------------------- /project/poster-examples/Team3_poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/poster-examples/Team3_poster.pdf -------------------------------------------------------------------------------- /project/poster-examples/Team6_poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/poster-examples/Team6_poster.pdf -------------------------------------------------------------------------------- /project/poster-examples/Team7_poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/poster-examples/Team7_poster.pdf -------------------------------------------------------------------------------- /project/postersession.md: -------------------------------------------------------------------------------- 1 | ## Poster Session 2 | 3 | A check list for the poster. 4 | - Catchy name 5 | - Examples 6 | - LinkedIn: Connect, share ideas, and discover opportunities 7 | - Uber: Moving people 8 | - Facebook: Connect with friends and the world around you on Facebook 9 | - Github: Build software better, together 10 | 11 | - Descriptions (2-4 sentences, max 60 words) 12 | - Use the poster template 13 | - Prepare well for your demo 14 | 15 | -------------------------------------------------------------------------------- /project/project-requirements-and-specification.md: -------------------------------------------------------------------------------- 1 | ## Project Requirements and Specification 2 | 3 | **Project Requirements and Specification**
4 | (Borrowed and Adapted from UCB CS169) 5 | 6 | Your Project Name
7 | Requirements and Specification Document
8 | ??/??/????, version major.minor 9 | 10 | **Instructions**
11 | This is a requirements and specification document template for Software Development Principles and Practices. Please follow these directions for filling out this template. Items in italics are information that you are to fill in, beginning with the project name, date, and version number above. See below for an explanation of version numbers. 12 | 13 | There is no standard for requirements or specifications documents and in fact many organizations blend aspects of requirements, specification, and design in a single document. We will use a simple template for requirements and specification. Inevitably in preparing this document you will discuss design and planning, but limit this document to requirements, a description of how the system should interact with the outside world. 14 | 15 | This will be a living document. For the first sprint you will fill in the overview sections (Abstract, Customer, Competitive Landscape), and a few of the requirements that you plan to implement in the first sprint. In subsequent sprints you will expand the applicable sections. 16 | 17 | You have to use the Github wiki of your private team repo. 18 | 19 | **Submission**
20 | You must send an email to swpp-staff@spl.snu.ac.kr with a PDF version of your document. 21 | You must send the email by the deadline in the class calendar. This is a HARD deadline. 22 | 23 | 24 | **Project Abstract**
25 | A one paragraph summary (about 200 words) of what the software will do. (**Must include in the first version.**) 26 | 27 | **Document Revision History**
28 | Your first version of this document is version 1.0. When you evolve this document for future sprints of your project you will increment the minor version number (e.g., 1.1, 1.2, ...). We do not expect that you will have to increment the major version number in the course of this semester. For every version after the initial version, you should list a short bullet list with the main changes and extensions that you made to the document: 29 | 30 | Rev. 1.0 YYYY-MM-DD - initial version 31 | 32 | **Customer**
33 | A brief description of the customer for this software, both in general (the population who might eventually use such a system) and specifically for this document (the customer(s) who informed this document). (**Must include in the first version**) 34 | 35 | 36 | **Competitive Landscape**
37 | Briefly identify the competitors in this market, and list the main ways in which your project is going to be different. 38 | (**Must include in the first version**) 39 | 40 | **User Stories**
41 | This section will include the specification for your project in the form of user stories. For each user story, you should have at least a Feature and one or more Scenarios, each of which can have one or more Acceptance Tests. Acceptance Tests list one or more acceptance tests with concrete values for the parameters, and concrete assertions that you will make to verify the postconditions. Each user story should also have a field called "Sprint" where you specify in which sprint you implemented or plan to implement this feature. 42 | You should list only the user stories for the previous sprints and those for the current sprint. 43 | 44 | At the end of this section you should maintain a bullet list of user stories that you plan to get to in future sprints, with only minimal detail for each such story. We expect that in future sprints some of these items will be promoted to full fledged user stories. 45 | (**Must include in the first version, and must be expanded for future sprints**) 46 | 47 | **User Interface Requirements**
48 | Describes any customer user interface requirements including graphical user interface requirements. Here you should have sketches or mockups for the main parts of the interface. To save time you may want to use scanned drawings of interface mockups here, instead of Photoshop drawings. 49 | 50 | Just like for the User Stories section, you need to list here only the parts of the user interface that are applicable to the previous sprints and the current one. 51 | (**Must include in the first version, and must be expanded for future sprints**) 52 | 53 | 54 | **Requirements grading guidelines:**
55 | These are the grading guidelines that staff will use to evaluate your document. 56 | 57 | 58 | | Max Points | Content | 59 | |------------|---------| 60 | | 5 | Do the requirements state the customers needs? | 61 | | 5 | Competitive analysis | 62 | | 5 | Do the requirements avoid specifying a design (customer-specified design elements are allowed) ? | 63 | | 5 | Do you believe all parts are possible to implement? | 64 | | 5 | Is the project scope big enough? | 65 | | | **Completeness** | 66 | | 20| Are the user stories written in sufficient detail to allow for design and planning? | 67 | | 5 | Do the user stories have acceptance tests ? | 68 | | 5 | Do the user stories mention error conditions and required behavior ? | 69 | | 5 | Are there sufficient user stories for the first iteration? | 70 | | 5 | Is there a discussion of the stories for future iterations ? | 71 | | 20 | Are the User Interface Requirements given with some detail? Are there some sketches, mockups?| 72 | | | **Clarity** | 73 | | 5 | Is the document carefully written, without typos and grammatical errors? | 74 | | 5 | Is each part of the document in agreement with all other parts? | 75 | | 5 | Are all items clear and not ambiguous? (Minor document readability issues should be handled off-line, not in the review, e.g. spelling, grammar, and organization).| 76 | -------------------------------------------------------------------------------- /project/projectproposal.md: -------------------------------------------------------------------------------- 1 | ## Project proposal guideline 2 | 3 | Write a 2-page project proposal on a web service software system you would like to build this semester. Think ambitiously. Imagine that you create a new start-up. Who knows that you may actually start a new company with the service you build during this course! 4 | 5 | You can propose a new web service that innovates e-commerce, social network service, education, recommendation, multi-player gaming, location-based service, etc. In the proposal, you have to justify why the service is an interesting system to build and what new feature(s) it brings to market. 6 | 7 | In particular, your proposal should include: 8 | - the proposer names (team information) 9 | - a title (it can be changed later) 10 | - your target customers 11 | - what feature the system would add or what problem the system would solve and why the system is important and exciting (at least one of the features must make use of machine learning) 12 | - what the system would do 13 | - how you will test and demo the system 14 | 15 | Send your proposal in pdf or doc to swpp-staff at spl.snu.ac.kr via email. 16 | 17 | Please write your proposal in *English*! 18 | 19 | For your reference: [Proposal Examples](proposal-examples) 20 | -------------------------------------------------------------------------------- /project/proposal-examples/Team1 - BusyWrite.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/proposal-examples/Team1 - BusyWrite.pdf -------------------------------------------------------------------------------- /project/proposal-examples/Team8 - You&Meet - X.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swsnu/swppfall2020/966043dcf33e7daefd17067de5c779c1889360fa/project/proposal-examples/Team8 - You&Meet - X.pdf -------------------------------------------------------------------------------- /project/sprint-instructions.md: -------------------------------------------------------------------------------- 1 | ## Sprint Instructions 2 | 3 | The main part of the project will be done in several sprints. In each sprint you will have to work on several tasks: 4 | - An update of your specification and design documents (Requirements and Design). In Sprint 1 and Sprint 2 you will have extra time to write the first version of the Design document. 5 | - A completion of a running version of your code with a partial set of features 6 | - Testing support and tests for the implemented features 7 | - A (short) progress report 8 | 9 | 10 | **Revised requirements/spec and design documents**
11 | Update the specification and design documents based on the progress you made so far. 12 | - Update the implementation and testing plans (in the design document) to reflect what you have already accomplished. 13 | - Identify the features you are going to implement in the current sprint. Add user stories (to the requirements document), task breakdowns (design doc), and assignments (design doc). You may need to update tasks that weren't completed in the last sprint. Reassess the task difficulties if necessary. 14 | - Flesh out your list of possible features for future sprints (requirements document). Ideally, you should have a rough outline in place for all sprints. You will change this as we do each sprint, but having a general plan in place will be helpful to everyone involved. 15 | - Identify any changes in the requirements, system architecture and design details. 16 | - Ensure the design document is current for the set of features you've implemented to date and will implement in the next sprint. 17 | - Discuss design decisions that affect testing and describe any test interfaces built into the system (in the testing plan section). 18 | 19 | You must mark clearly the parts of the document that were changed. Use the Changes section at the start of the document and use a different font color for the changed parts. 20 | 21 | 22 | **Sprint Progress Report**
23 | Write a short (1~2 pages) summary for sprint and submit it along with the revised documents. 24 | 25 | - What were the main difficulties so far? You should consider both technical and organization issues. 26 | - Were there any features you did not implement as planned, and why? Are you pushing some features to later sprints, and if so, why? 27 | - What tests did you prepare for this sprint, and what are they covering? Did the tests you wrote deviate from your plan? What features are you not testing yet? Did you use any test frameworks? 28 | - You must include in your progress report a test coverage report. The coverage metric must be over 80%. If it's below 80%, you will lose 10% of the overall score of that sprint. This number may change each sprint. 29 | You must specify what tool did you use, and you must include one or more screenshots showing: 30 | - The overall coverage metric 31 | - The list of classes with lowest coverage. Explain why is the coverage low, and what (if anything) you plan to do about it 32 | - Contribution of team members 33 | - Tag a branch or revision in your repository to identify the code you want to submit for sprintN (replace N with 1, 2, ...). The date and time of this revision should be before the deadline. This branch should include a README file with instructions on how to run the application and tests. 34 | 35 | **Submission**
36 | Each team must send an email to swpp-staff@spl.snu.ac.kr by the deadline. The subject line of the email message must start with the word “[SWPP] sprintN” (replace N with 1, 2, ...) followed by your project's name. The email should contain: 37 | Pointers to the (previously submitted) requirements document and design document. 38 | The sprint summary progress report, as described above. 39 | Instructions for your TA to be able to check out a tagged version of the code that constitutes your sprint release. 40 | A url and instructions for using the app in the current stage. 41 | 42 | Additionally, in the project progress presentation you will have to demonstrate a running version of your sprint release. 43 | --------------------------------------------------------------------------------