Skip to content

DAZHAdazha/todolist

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Development notes

Description of design and architecture

This project is aiming at implementing a web application offers to do list functionality. It supports sign up and log in for users, each task consists of a title, description, status (whether completed or not), creating time, finished time. Once they have logged in, they are able to create task, remove task, change task by titles and descriptions, change task status, browse user information, all tasks information, completed task information, uncompleted task information and specific task details, searching for results for user's own tasks and log out are also workable.

File architecture

The file architecture of this project is defined as below (Figure 1). The root directory includes 3 folders: "migrations", "static" and "templates" and 8 Python Flask scripts: "todolist.py", "config.py", "db_script.py", "decorator.py", "exts.py", "manage.py", "models.py" and "utils.py". In the folder of "migrations", it contains database model history of all versions, generated by flask-migrate module. In the folder of "static", it stores all the static resource such as CSS files, JavaScript files, image files and font files. In the folder of "templates" folder places all HTML files. The main running script is "todolist.py" which includes all view functions for business logic. The file "config.py" configs database URL and debugging mode. The file "db_script.py" provides manager to run flask-migrate for database. The file "decorator.py" define customized hook function to require user's log-in action. The file "exts.py" provide a solution to solve recursively reference (a typical flask bug). The file "manage.py" implements flask-migrate command to manage. The file "models.py" implement classes of database models. The file "utils.py" is used for store log information.

images

Figure 1

Route

"@app.route" function is used to dynamically interpret Uniform Resource Locator (URL), for example (Figure 2), if user type in http://127.0.0.1:5000/ this URL in browser to visit root home page of the website (if the user running in local server), then this decorator will bind its request to bellowing view function, which will return a web page in "HTML/index.html".

images

Figure 2

Cookies and Sessions

Cookies and Sessions are important application of web technology, which allows users to have a better experience to manipulate the system. Generally, cookies are log-in and other information that stored in user's browser whilst sessions are corresponding information stored in the server. For instance, many websites (including this project) has a option for log-in procedure as "remember me", when user ticks this option, the website will normally stores the user's log-in status for about a month, which means once they logged in, they would not need to type password again in the next time.

Flask Template

Flask provide template technology called jinja2 template engine and werkzeug. The main purpose of adaopting this technique is to avoid HTML code redundancy, all HTML file could inherit parent template which had already designed a basic framework (such as navigation bar, footer and other container) and import relative CSS files and JavaScript scripts. In this project, a "template.html" file is created, offering main navigation system and required resource (CSS and JavaScript files), all other 9 HTML files are inherited from this template, which largely curtail repeated code.

g object

g object is an import technology provided by Flask. It is a global namespace for holding any data to store during a single app context cookies and sessions. This is usually implemented along with the using of cookies and sessions. Once user's browser sending pre-stored user information to server, if every redirect requires retrieving data from cookies and sessions again would be a curtailment in sever performance. Therefore, global variables g object is suited to use in this situation. (Figure 3)

In the code below, executing sequence is defined in Flask as @before_request => view function => contect_processor, after that a request is returned where "@before_request" and "contect_processor" are built-in decorators just like "@app.router" in Flask.

It is noticeable that a decorator is defined wrapping by "@app.before_request", this decorator will work before all the requests, to retrieve any possible storage in sessions, then will search it from the database, once it is done, the information will be stored in g object, which could enhance server performance to avoid repeating using of cookies and sessions.

images

Figure 3

Another decorator is created working as a hook function after every end of view function, its main purpose will check whether g object has attribute of "user", if it has, then we retrieve it and could use it after that, if not give empty value.(Figure 4)

images

Figure 4

Migrate

Migrate is important in using database which provide version control for previous database model. All the previous versions of models are stored in the folders of "migrations/versions", simply run the command "python manage.py db upgrade" or "python manage.py db downgrade" to execute these processes.

Database

In this project, MySQL is chosen for database. The reason is that MySQL is a light-weight database but offers high response speed as long as its wide flexibility and compatibility. More details about database model could see chapter "data types in the model" below. To configure and running the whole application system, just simply change database URL below would be feasible, in the file of "config.py": (Figure 5)

The format: dialect+driver://username:password@host:port/database

images

Figure 5

In "user" table, a default user is created (username="admin", email="[email protected]", password="admin"), for user to log-in this web application.

Decorator

In flask, executing sequence is defined as @before_request => view function => context_processor. At first, user post a request to the web server, decorator @before_request will be running, then Flask view function, finally executes context_processor.

In the Figure 6, "my_before_quest" decorator is created with the wrapper of "@app.before_request", this function is used to detect whether user is logged in, once the email information is founded in session, we search the user information in database, and pass it to local g object.

images

Figure 6

In the Figure 7, "my_context_processor" is created with the wrapper of "@app.context_processor", this function is used for detect whether g object has user information (passed by "my_before_quest" function), once there is, we could dump this data to HTML template, where flask template could just use {{ g.user }}.

images

Figure 7

In the Figure 8, a customized decorator is defined named "login_required", this function will work before view function if it is wrapped by this decorator. Its main function is to check whether user's email information is stored in session, if does, means this user has already logged in, otherwise it should redirect user to log-in page. This function is normally decorated those view function require user to log-in (such as see and change this user's tasks and other information).

images

Figure 8

Validator

In file of "scripts.js", several validators are defined to detect whether user's inputs are valid before request (such as whether they post null values or right format for email address). If invalid inputs are given by users, system will prompt them to refill the fields, this will enhance server performance and security by filtering unnecessary requests.

Bootstrap and jQuery

Bootstrap is an open-source web page framework which provide a plenty of useful components and layouts to embedded to web application while jQuery is a set of JavaScript function library which could simplify basic operation between JavaScript and HTML. In this project, these two technologies are both embedded into HTML, CSS and JavaScript code, to provide functionalities such as responsive web page, input validator, prompt and interaction between website and user and so on.

Demonstration of main features of HTTP

Hyper Text Transfer protocol (HTTP) is a protocol which describe rules that how a client should request a webpage from a server through web browser and how a sever should respond. Therefore, two parties are involved, the client and the server. To be more specifically, all requests are posted by clients, by asking the server to respond and return a series of resources as a form of Uniform Resource Locator (URL). To transfer this data, connections by Internet Protocol (IP) address on both sides are required, however, the IP address is indirectly obtained by the using of Domain Naming Service (DNS). Moreover, HTTP is a stateless protocol, meaning that no state will be recorded during each request. In each HTTP request, a variety of methods are defined, such as GET (to retrieve resource) , POST (to request resource within given client's message), PUT (normally used as updating resource) , DELETE (usually used for removing resource) and so on. Once a request is done, the server will send the status code and resource that client requested (if the request is successfully executed).

On the server side, language choices are varied, for instance, Java, PHP, Python and so on. In this project, Python Flask was chosen, for its wide flexibility and light weight framework size. All the HTTP requests in this project could be handled by WTF-form, Flask built-in router methods or jQuery (based on JavaScript) post methods.

Security: cookies session, database encryption

Since the invention of Internet, there were many cybercrimes came along with it, such as Cross-site request forgery (CSRF) and other Cross-site scripting (XSS) related security vulnerability. More importantly, directly storing or transmitting cleartext password of customer in database, cookies and sessions is not safe. Thus, encryption is required during these processes above.

Cross-site request forgery (CSRF), a security vulnerability could be used by attackers especially for websites that often use cookies and sessions to store temporary data in user's web browser. Hackers will forge malicious site that aims at stealing passwords, by taking advantages of vulnerable submitting forms. To handle this tricky problem, a secret token is including in this project within the Flask code (Figure 9), which is automatically provided by the server when client requesting eh page.

images

Figure 9

Cross-site scripting (XSS) threat often happened when people intended to undermine the web application system by submit unauthorized JavaScript make web server to run which made users to redirect to malicious website to get their private information. To avoid this issue is simply do not return unauthorized content to user. Fortunately, Flask templating engine is already built such defense system by default.

Cookies, sessions and private information of users (passwords) that stored in database is also target of attackers. This project has implemented encryption for those data by import library named "werkzeug.security" (Figure 10)

images

Figure 10

After that, the table model of User is defined below (Figure 11), an extra __init__ function is defined to generate password with hash method (to encrypt), and check password function is also defined, which will using the same encryption process to handle the raw password, then comparing the encrypted password, to avoid cleartext password storage. When Flask script need to check password when user log in, just simply call this function (Figure 12). For result, the password stored in database is encrypted, which means even database managers could not access user's password! (Figure 13) (e.g. a default user (username="admin", email="[email protected]", password="admin") ,but in database password is encrypted.)

images

Figure 11

images

Figure 12

images

Figure 13

3 choices of requests

In flask, there are generally 3 ways of handing request, they are through URL, Form and using "url_for(view function)" respectively. In my personally understanding, each way has its own features and fortes.

URL: In HTML file, just create a form with an input tag given an attribute called "name=q", then when posts are requesting, arguments will be captured by the form of URL (e.g. http://127.0.0.1:5000/search/?q=newtask means user type in "newtask" as keyword for searching. Similarly, http://127.0.0.1:5000/task/1 is also an alternative for URL to pass arguments, which "1" is the task id passing to the server where view function could be defined as "@app.route('/task/<task_id>)". This method is normally used for searching while arguments could be passed without causing any security issues (not like submitting passwords), it is a convenient way to post requests.

Form: just simply create a form which could include one or more inputs, once submitted, flask scripts could capture those inputs through "request.form", then attribute could be called by this object. This method is usually used to post log-in, sign-in and other form information which need more than one filed to fill in, sensitive data could also be used in this method.

url_for(view function): this method is the most simple way to post request, just use a tag with its "href=url_for(view function)", and just implement corresponding flask function in python scripts(do not necessarily need template, just a view function would be ok). This method is normally used as a quick way to post request with easy-to-get argument or no argument situation.

Handling requests

Basically, there are in total 13 different requests defined in this application:

  1. Sign-up

This router function receives two methods: POST and GET, GET request is for users to access the sign-up.html page while POST request if for users to submit forms to sign up in this website. The code is shown below: (Figure 14)

If the user is posting sign-up request, the function will check whether this username is already registered in database, if so, "This email had been registered" will be returned for JavaScript file "scripts.js" to capture this and handle, prompt out alert to inform user this fact. If not, new user information will be stored in database and also set sessions for current username. Moreover, "scripts.js" also serve as validator to check whether valid sign up information is typed in by users (whether all fields are filled, whether email format is correct) before post requests to sever, in order to avoid malicious attempts attacking the server and also enhance server performance.

images

Figure 14

  1. Log-in

This router function also receives two methods: POST and GET, GET request is for users to access the login.html page while POST request if for users to submit forms to log-in in this website. The code is shown below: (Figure 15)

If the user is posting log-in request, if the user is not registered in database, "Wrong email address, please try again", if the user password is wrong, "Wrong password, please try again" will be returned for JavaScript file "scripts.js" to handle, prompting user about these information. If this username exists, function "check_password" function will be called which defined in the "model.py" file, using hash to check the encrypted password comparing to raw password, to achieve data security purpose (see chapter Security above). After that, username session will also be set in user's browser (but after user close the browser, temporary session will be deleted automatically). Additionally, if user chooses "remember me" option, the session will be stored for a month.

images

Figure 15

  1. Search

This router function is used for return user search result for tasks they had created. The code is shown below: (Figure 16)

It is noticeable that "@login_required" decorator is added to this function, since a user can only search his/her own tasks data. Arguments will captured by the form of URL (e.g. http://127.0.0.1:5000/search/?q=newtask means user type in "newtask" as keyword for searching. As a result, the number of matching results will be counted and returned as well as the content themselves (searching will related to title, description and date created, tag and date finished, but only for corresponding user's own tasks). In the file of "scripts.js", validator function used for checking empty input are defined, to avoid post null results, in order to enhance server performance.

images

Figure 16

  1. Add task

This router function is used for adding new task that user just created. The code is shown below: (Figure 17)

Similarly, "@login_required" decorator is required for only log-in user could create new tasks. In the HTML page of "create_task.html", user could submit their new task through a form by typing title, tag(optional) and description. New task will be submitted and stored in database, and it will return to the page user made the post. Additionally, null values are also invalid, and will be checked in the file of "scripts.js", where validator function used for checking empty input are defined, to avoid post null results, in order to enhance server performance.

images

Figure 17

  1. Change task

This router function is used for changing the task that already existed. The code is shown below: (Figure 18)

Similarly, "@login_required" decorator is required for only log-in user could change tasks. The function will get the task id through URL (e.g. http://127.0.0.1:5000/changeTask/1 meaning that requested task id is "1"). Then, the database will first search corresponding task id, then will retrieve data from the form user had updated, to make changes about task title, tag and description. Additionally, null values are also invalid, and will be checked in the file of "main.js", where functions used for checking empty input are defined, to avoid post null results, in order to enhance server performance. If the record is not found, "error.html" will be returned to prompt user this information.

images

Figure 18

  1. Delete task

This router function is used for removing the task that already existed. The code is shown below: (Figure 19)

Similarly, "@login_required" decorator is required for only log-in user could remove tasks. The function will get the task id through URL (e.g. http://127.0.0.1:5000/removeTask/1 meaning that requested task id is "1"). Then, the database will first search corresponding task id, then will delete the record of this task in database. If this record is not founded, "error.html" will be returned to users to prompt this information, otherwise, it will return the requested page that user posted.

images

Figure 19

7. View task

This router function is used for displaying specific task by giving task id that already existed. The code is shown below: (Figure 20)

Similarly, "@login_required" decorator is required for only log-in user could view specific tasks. The function will get the task id through URL (e.g. http://127.0.0.1:5000/viewTask/1 meaning that requested task id is "1"). Then, the database will first search corresponding task id, then will return the record object that user requires. If the record is not found, "error.html" will be returned to prompt user this information.

images

Figure 20

  1. Task status

This router function is used for changing the task status if it is already existed. The code is shown below: (Figure 21)

Similarly, "@login_required" decorator is required for only log-in user could change specific task status. The function will get the task id through URL (e.g. http://127.0.0.1:5000/taskStatus/1 meaning that requested task id is "1"). Then the database will first search corresponding task id, then will return the record object that user requires. If the record is not found, "error.html" will be returned to prompt user this information. If the record is confirmed to be existed, then its status will be changed, if it is completed already, then it will be switched to uncompleted, otherwise, it will be changed to completed as along as current time will be recorded as its finished time.

images

Figure 21

  1. View All

This router function is used for displaying all tasks referring the user. The code is shown below: (Figure 22)

Similarly, "@login_required" decorator is required for only log-in user could see all tasks. The database will first return the count of all tasks belonged to this user as long as the iterator of all tasks, allow HTML page "result.html" to display.

images

Figure 22

  1. View Completed

This router function is used for displaying all completed tasks referring the user. The code is shown below: (Figure 23)

Similarly, "@login_required" decorator is required for only log-in user could see all completed tasks. The database will first return the count of all tasks belonged to this user as long as the iterator of all tasks, allow HTML page "result.html" to display.

images

Figure 23

  1. View Uncompleted

This router function is used for displaying all uncompleted tasks referring the user. The code is shown below: (Figure 24)

Similarly, "@login_required" decorator is required for only log-in user could see all uncompleted tasks. The database will first return the count of all tasks belonged to this user as long as the iterator of all tasks, allow HTML page "result.html" to display.

images

Figure 24

  1. User

This router function is used for displaying user personal storage about tasks. The code is shown below: (Figure 25)

Similarly, "@login_required" decorator is required for only log-in user could see user storage. The database will compute and return the count of all tasks, completed tasks, uncompleted tasks counts that belonged to this user respectively, allow HTML page "user.html" to display.

images

Figure 25

  1. Log out

This router function is used to log out current user. The code is shown below: (Figure 26)

Similarly, "@login_required" decorator is required for only log-in user could log out. The function will delete current username that stored in local session from user's browser, then will redirect to the login view function.

images

Figure 26

  1. Tag

A tag is abled to be added to specific task, however it is not mandatory but optional, this is attribute could also be searched, in order to provide customized categories for users to organize their tasks conveniently.

3-tier architecture web application

Three tiers in web application architecture are presentation, business logic and data access respectively.

Presentation: this layer normally represents the display of code that generate and manage HTML response, including decoding of HTTP request and other form data. In this layer, the concept of Forms Templating is adopted, in particular Flask embodiment is Flask-WTForm, Jinjia2 templating engine. Each HTML page is sending to user through function "render_template" or "redirect (url_fro(...))". On the one hand, this method could take advantage of templating engine to avoid code redundancy. On the other hand, it prevents unauthorized resources sending to customers, enhancing web application security.

Business Logic: this layer implements logical functionality that this application providing, such as data validation and so on. In this layer, the concept of Views is adopted, in particular Flask embodiment is Flask.route. The basic executing order of web application logic is defined below:

HTML template control statements (execute basic logic) => JavaScript functions (if there are callback functions exist, then waiting for server to respond) => Flask backend scripts running in the server (return resources to users or to JavaScript function).

Data Access: this layer is used for exchanging data through database or other forms of API's. In this layer, the concept of Models is adopted, in particular Flask embodiment is module called "SQLAlchemy". In this project, MySQL is adopted due to its wide popularity, relatively small size but fast respond speed, data security is also putted into consideration, by implementing encryption of passwords (see chapter Security above).

Data types in the model

The database model and graph are defined as below (Figure 27, 28 29):

images

Figure 27

images

Figure 28

images

Figure 29

According to Figure 27, there are 3 tables in total: "alembic_version", "users" and "record" respectively.

Table "alembic_version" is automatically generated by Flask migration, for upgrade and downgrade database model while server is running, and also serve as backup for previous version.

Table "users" is used for store basic user information where 4 fields are defined: "id", "email", "username" and "password", and another field "record" is the foreign key that in "record" table. The type of "id" is Integer and serve as primary key of this table, it cannot be null and will increase automatically. The type of "email" is String with maximum length 32, it also cannot be null and unique, since no identical email is allowed. The type of "username" is String with maximum length 16, it also cannot be null. The type of "password" is also String with maximum length 256, it also cannot be null, and by encryption the length will be relatively long.

Additionally, to achieve data security, password filed is encrypted. (more details see section "Security" above)

Table "record" is used for store tasks that each user created where 7 fields are defined: "id", "title", "description", "status", "date", "tag" and "finish_time", and another field "user_id" is the foreign key that in "users" table.

The type of "id" is Integer and serve as primary key of this table, it cannot be null and will increase automatically. The type of "title" is Text with maximum length 50, it also cannot be null. The type of "description" is Text with maximum length 200, it also cannot be null. The type of "status" is Boolean, denoting whether this task is finished or not, it cannot be null and also the default value is False as new task is always unfinished. The type of "date" is DateTime, stores the exact time of task creating time, it also cannot be null. The type of "tag" is Text with maximum length 20, stores the tag class of this task. The type of "finish_time" is DateTime, stores the exact of last task finished time, since it could be not finished, it could be null.

Evaluation

Debugging and testing

In all development of programming project, testing and debugging always have vital importance in the process, it is great way to evaluate whether error will be properly handled and avoid severe accidents happening. Black box testing and white box testing are generally adopted in procedures, the former, black box testing is also known as functionality testing, which case codes cannot be seen for testers while the latter, white testing, is the opposite, which testers should analyze program structure and logic to carry on all possible faults. In this project, both types of testing are implemented in order to enhance software quality.

Black box testing:

Browser

To test adaptivity among mainstream browsers, tests are carried on as below to see whether displaying are identical.

Chrome:

images

Figure 30

Microsoft Edge:

images

Figure 31

360 Safe Browser:

images

Figure 32

Firefox:

images

Figure 33

Responsive

To test wide adaption among different sizes of screens, console tools provided by chrome are used to simulate different screen sizes.

iphone 6/7/8 Plus (414 * 736):

images

Figure 34

Pixel 2 XL(411*823):

images

Figure 35

iPad (768 * 1024):

images

Figure 36 iPad pro(1024*1366):

images

Figure 37

Regular user

To implement black box testing, several regular users are invited to test this web application by directly using it. They had no idea of code structure, implement details and design choices, by testing the basic functionalities like sign-up, log-in, log-out, create task, change task, delete task, search task and view task specific information and so on, while there are no evidence error or dissatisfaction occur yet.

White box testing:

URL testing

Dead links would always be frustrating for users, thus during the tests, all possible links are tested to avoid dead links appearance. Besides, to avoid user accidentally enter some invalid URLs to trigger errors, a prevention measure is introduced in Figure 9.2

First, a 404 handler is designed for wrong URLs that user request, moreover, a try and except section is added to view function which need to relocate templates. With this method, all possible wrong URLs would be captured and redirect to customized page "error.html".

images

Figure 38

Techniques and tools for testing:

Debugging mode of Flask

In the config file of Flask, debugging mode could be turned on, automatically refresh for loading new-saved scripts will be feasible while server errors will be displayed to the browser in order to debug.

Browser console

It is always useful to use browser console (by pressing F12 in browser) to check whether error occurs for reference CSS files, JavaScript files or other static resources, it is also could check bugs in JavaScript file, or simply use it to print out variables by using "console.log(message)", in this project, no browser console error related to source code(possibly there are some errors triggered by Chrome-extension or settings).

Flash

During the testing procedure, several useful tools are used to detect bugs, such as flash module in Flask, which is a module to conveniently display messages for debugging, though it could only respond to request while invalid if the page is refreshed. Just using this in HTML template like Figure 39, and in flask script, simply using 'flash("message")' just as print will be fine, and the messages will be returned to HTML template.

images

Figure 39

Log

It is useful to use log to record what is happening and other important information, or some errors occur during execution of program. In the file of "exts.py", function to record log is defined.

Selenium

Selenium is a portable framework for testing web applications, which provides a playback tool for authoring functional tests for a variable of programming language (such as C#, PHP, JAVA, Python and so on)

images

Postman

Postman is a collaboration platform for API development, it is a useful testing tool to REST, SOAP and other forms of requests quickly, it is often used during collaboration where developers could just test their APIs simply.

images

Challenges

During the development of this web application, there are some challenges that had been encountered as below:

Web-page design

At the first iteration of development, a relatively bad user interface was designed, which lacks enough interactions between users and systems, more importantly, it did not have an appealing web-page design appearance. Additionally, responsive framework is not properly embedded to the old version of web application system. To tackle this problem, I referenced and searched a lot of open-source HTML framework template online, by legitimately using component and reference their partial design, a better design web application was created as the latest version. It has relative plain design which aiming at provide user a simple-to-use feature, plus more user interactions were added to the system (such as validator for input filed, transition animation for waiting redirecting, anchor and to-top button to relocate location in single page). What's more, responsive page design for all sizes of screens is also implemented which could allow desktop, mobile phone and tablets terminals to browse.

Balance between implement and document-writing cycle

Another tricky problem that I encountered during the developing it to find the balance between implementing phase and document or report-writing cycle. Both of the two stages have a vital importance in software development, however, a balance to prepare proper time schedule is rather difficult for me due to the lack of software development experience. If more time is devoted to code development, more features would be implemented but rough documentation will cause misunderstanding even misusing of software system, and vice versa. In this project, many additional features could not be implemented because the lack of time, it is a pity that happens, reminding me to do better time management before carrying on development for next project. Furthermore, a plan should be drafted in advance to avoid lack of progress.

Difference between design and implementation or future improvement

When designing this web application, some other useful features are considered, such as giving each task tags to classify them, or setting different priority to different task to identify their important levels. Moreover, features like adding optional deadline or count-down clock to some particular task would be feasible. However, due to the relatively short limited developing time, those features could not be implemented on time, thus these features were abandoned. In next updating versions, those will be putted into consideration.

Possibly more advanced security techniques

Additionally, some advanced security techniques are also in developing planning stage, such as flask-security, which is an extension for role management and provide authentication for verifying sessions. Furthermore, HTTPS is the secure version of HTTP, it is safe and feasible to storage encrypted data within this technology.

Reconstruction of database

At the first stage of development, database model for the "user" table only contains 3 fields: id, username and password, where username should be unique, and id is the primary key. However, with deep consideration, I recognized that there are plenty of websites require email to sign-up which is feasible for user to verify and find back their accounts. Thus, a new model for user "table" is redesigned which consists of 4 fields: id, username, email and password, where email should be unique, and the id is still the primary key. Moreover, changes were also taken place in the table "records", which is used to store user's task information. Two additional fields are considered to be added as extensions named "finish_time" and "tag" which denote the last time of this task to become status "finished" and the class tag of the task, while "finish_time" could be null since it have never been finished yet as well as tag. These two changes make the database model more useful and closer to reality usage.

Reflection

During this project, I had learned the concept of embedding 3-tire web architecture, which is particularly useful in the collaboration work and has a clear framework of design, moreover, testing and security issues are also important, automated tools and security modules should also be utilized during development (such as Selenium and flask-security or other encryption methods). Additionally, separating API from front and back end is also vital in the process of implementing software application, which is also easy to test. Generally speaking, though Flask is a light-weight web application framework, its basic structure and developing process are similar to other completed frameworks and easy to learn and transfer to new models. I had learned a lot about how clients, browsers and servers interact among each other which is the most valuable knowledge that I had learned.

Old version

In the folder of "old version" contains previous design along with HTML, CSS, JavaScript, image files, it is possible to compare designs with older version.

Version control

To implement proper version control of this project, all the source codes were uploaded to Github: https://github.com/DAZHAdazha/todolist

While there are total 12 commits for history version. (Figure 40)

images

Figure 40

Reference

Icon:

https://www.iconfont.cn/

HTML template:

https://onepagelove.com/templates/free-templates

http://www.bootstrapmb.com/muban

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published