diff --git a/.gitignore b/.gitignore
index 723ef36..361103f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,15 @@
-.idea
\ No newline at end of file
+*.pyc
+*~
+__pycache__
+.DS_Store
+.idea
+env
+envs
+errors.txt
+log.txt
+traceback.txt
+*.rpyc
+saves
+cache
+tl
+more.md
\ No newline at end of file
diff --git a/README.md b/README.md
index 47f75e8..4b85a82 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,36 @@
-# Welcome to the world of Python!
-
-[](https://gitter.im/WWCodeManila/Python)
-
-For beginners, we will learn the basic concepts of
-programming using Python. This can be your jump off towards web development,
-scripting, data processing and more!
-
-For immediate/advanced Pythonistas, let's spread our knowledge of Python and learn more!
-
-If you have questions, please feel free to ask and participate on our [Gitter group](https://gitter.im/WWCodeManila/Python).
+# Welcome, Pythonistas!
+!> Please go to our official [GitHub Page](https://wwcodemanila.github.io/WWCodeManila-Python/#/)
+if you're viewing this from the source code repository.
## About this study group
-!> Read this [document](wwcodemanila/study_groups.md) first to know what study groups are.
+!> If you're not yet familiar with study groups, read this [document](wwcodemanila/study_groups.md)
+first to know what they are.
-This study group is held once a month and is led by our volunteer leaders. However, everyone is welcome to join, learn, and share their knowledge in the community.
+This study group is held once a month and is led by our volunteer leaders.
-Everyone joining this study group and other Women Who Code events must follow our [code of conduct](https://github.com/WomenWhoCode/guidelines-resources/blob/master/code_of_conduct.md).
+Everyone is welcome to join, learn, and share their knowledge in the community
+as long as they abide by our [code of conduct](https://github.com/WomenWhoCode/guidelines-resources/blob/master/code_of_conduct.md).
### Setup
-* We currently subgroup into:
- - Python beginners
- - Flask beginners
- - Advanced Pythonistas
-
-* Spend the hours randomly into:
- - Getting to know first-timers
- - Discuss questions/issues raised last meetup or on Gitter
- - Lightning Talk (anyone who wants to share/chikka something!)
- - Self-paced or mini study session
- - Today I Learned (TIL)
- - Announcements
+During the study groups, we usually spend the time:
+
+- getting to know first-timers,
+- discussing questions/issues raised during the last meetup,
+- presenting lightning talks (anyone who wants to share/chika something!),
+- doing self-paced and/or mini study sessions
+- sharing TILS (Today I Learned), and
+- communicating announcements.
## Relevant links
-- [Gitter Chat](https://gitter.im/WWCodeManila/Python): Interact with the community. You can share something about Python, or ask help if you're stuck on a problem.
-- [Github](https://github.com/wwcodemanila/WWCodeManila-Python): View study group code
-- [Meetup](https://bit.ly/wwcodemanilameetups): Get updated with the upcoming study group and other event schedules.
-- [Facebook](https://facebook.com/wwcodemanila): Get updated about what's happening with the community.
-- [Twitter](https://twitter.com/wwcodemanila): Get updates about current and future events.
+- [GitHub](https://github.com/wwcodemanila/WWCodeManila-Python): View our study group resources.
+- [Meetup](https://bit.ly/wwcodemanilameetups): Get updates about our upcoming study groups and events.
+- [Facebook](https://facebook.com/wwcodemanila): Get updates about what's happening in the community.
+- [Twitter](https://twitter.com/wwcodemanila): Get updates about our current and future events.
## Helping out
@@ -52,19 +41,20 @@ Attend one of our [events](https://bit.ly/wwcodemanilameetups) and talk to us! :
### Donate
-Even a little amount is a big help for us to achieve our mission of inspiring women to excel in tech careers. :heart:
+Any amount will greatly help us achieve our mission of inspiring women to excel in tech careers. :heart:
-You can donate at our [Open Collective](https://opencollective.com/wwcodemanila).
+Donations are accepted via [Open Collective](https://opencollective.com/wwcodemanila).
### Hosting
-Do you want to host one of our study groups? Email us at **manila@womenwhocode.com**.
+Do you want to help us host one of our study groups? Email us at **manila@womenwhocode.com**.
-## Contributors
+## Mentors
-- Nicole Tibay
+- Alysson Alvaran
+- Angelica Lapastora
- Marylette Roa
-- Clau Yagyagan
-- Micaela Reyes
+- Issa Tingzon
- Iris Uy
-- Alysson Alvaran
+- Clau Yagyagan
+- Nicole Tibay
diff --git a/_coverpage.html b/_coverpage.html
index 9640903..eda94af 100644
--- a/_coverpage.html
+++ b/_coverpage.html
@@ -1,9 +1,5 @@
diff --git a/_media/bg.jpg b/_media/bg.jpg
index 7c5a7b5..3cf5c92 100644
Binary files a/_media/bg.jpg and b/_media/bg.jpg differ
diff --git a/_media/gitter.png b/_media/gitter.png
deleted file mode 100644
index 66813aa..0000000
Binary files a/_media/gitter.png and /dev/null differ
diff --git a/_sidebar.md b/_sidebar.md
index 95db90f..28c59c7 100644
--- a/_sidebar.md
+++ b/_sidebar.md
@@ -1,43 +1,65 @@
-- Women Who Code Manila
- - [About WWCode Manila](wwcodemanila/about.md)
- - [Study Groups](wwcodemanila/study_groups.md)
-
-- Getting Started
- - [Installation](getting_started/installation_guide.md)
- - [Run a basic Python program](getting_started/warm_up.md)
- - [Sharing your work](getting_started/exercise_upload_step.md)
-
-- Basic Concepts
- - [Variables, Arithmetic Operations, Keyboard Input](basic_concepts/variables.md)
- - [Let's go Git!](git/README.md)
- - [Strings pa more!](basic_concepts/strings.md)
- - [PEP8](basic_concepts/pep8.md)
- - [Lists](basic_concepts/lists.md)
- - [Conditional Statements](basic_concepts/conditional_statements.md)
- - [Loops](basic_concepts/loops.md)
- - [Challenge: I survived Hangman!](basic_concepts/exercises/hangman/README.md)
- - [Dictionaries](basic_concepts/dictionaries.md)
- - [Functions](basic_concepts/functions.md)
- - [Classes and Module](http://introtopython.org/classes.html)
- - [Error Handling](basic_concepts/error_handling.md)
- - [Challenge: It's 2048!](basic_concepts/exercises/2048/README.md)
-
-- Advanced Concepts
- - [List comprehension](https://hackernoon.com/list-comprehension-in-python-8895a785550b)
- - [Generators](https://anandology.com/python-practice-book/iterators.html)
- - [Decorators](http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/)
-
-- Flask
- - [Introduction](flask/discussions/01_introduction.md)
- - [Setting up Flask and virtual environments](flask/discussions/02_setup.md)
- - [Building my first Flask app](flask/discussions/03_my_first_flask_app.md)
- - [Creating URL routes](flask/discussions/04_url_routes.md)
- - [Styling templates and passing variables](flask/discussions/05_templates_and_variables.md)
- - Integrating databases using SQLite :soon:
- - Forming forms :soon:
- - Using cookies and sessions :soon:
- - Deploying my app :soon:
- - Wrapping up :soon:
-
-- Resources
- - [References](resources/references.md)
+- Women Who Code Manila
+ - [About WWCode Manila](wwcodemanila/about.md)
+ - [Study Groups](wwcodemanila/study_groups.md)
+
+- Getting Started
+ - [Installation](getting_started/installation_guide.md)
+ - [Run a basic Python program](getting_started/warm_up.md)
+ - [Sharing your work](getting_started/exercise_upload_step.md)
+
+- Basic Concepts
+ - [Variables, Arithmetic Operations, Keyboard Input](basic_concepts/variables.md)
+ - [Let's go Git!](git/README.md)
+ - [Strings pa more!](basic_concepts/strings.md)
+ - [PEP8](basic_concepts/pep8.md)
+ - [List, tuple, set](basic_concepts/lists.md)
+ - [Conditional Statements](basic_concepts/conditional_statements.md)
+ - [Loops](basic_concepts/loops.md)
+ - [Challenge: I survived Hangman!](basic_concepts/exercises/hangman/README.md)
+ - [Dictionaries](basic_concepts/dictionaries.md)
+ - [Functions](basic_concepts/functions.md)
+ - [Classes and Module](http://introtopython.org/classes.html)
+ - [Error Handling](basic_concepts/error_handling.md)
+ - [Challenge: It's 2048!](basic_concepts/exercises/2048/README.md)
+
+- Advanced Concepts
+ - [List comprehension](https://hackernoon.com/list-comprehension-in-python-8895a785550b)
+ - [Generators](https://anandology.com/python-practice-book/iterators.html)
+ - [Decorators](http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/)
+
+- Flask
+ - [Introduction](flask/discussions/01_introduction.md)
+ - [Setting up Flask and virtual environments](flask/discussions/02_setup.md)
+ - [Building my first Flask app](flask/discussions/03_my_first_flask_app.md)
+ - [Creating URL routes](flask/discussions/04_url_routes.md)
+ - [Styling templates and passing variables](flask/discussions/05_templates_and_variables.md)
+ - Integrating databases using SQLite :soon:
+ - Forming forms :soon:
+ - Using cookies and sessions :soon:
+ - Deploying my app :soon:
+ - Wrapping up :soon:
+
+- Ren'Py
+ - [Introduction](ren'py/introduction.md)
+ - [Installation and setting up](ren'py/installation.md)
+ - [Creating a new game](ren'py/create-new-game.md)
+ - [Aling Nena VN](ren'py/aling-nena-vn.md)
+ - [Tutorial Part 1](ren'py/scene1.md)
+ - [Tutorial Part 2](ren'py/scene2.md)
+ - [Tutorial Part 3](ren'py/scene3.md)
+ - [Tutorial Part 4](ren'py/scene4.md)
+ - [Tutorial Part 5 - Ending](ren'py/scene567.md)
+
+- Django
+ - [Introduction](django/01_introduction.md)
+ - [Setting up Django and virtual environments](django/02_setup.md)
+ - [Creating your first Django application](django/03_start_project.md)
+ - [Creating Users module](django/04_create_users_module.md)
+ - [Creating Users endpoints](django/05_endpoints.md)
+ - [Creating Questions module](django/06_create_questions_module.md)
+ - [Creating the frontend of the application](django/07_create_frontend.md)
+ - [Adding VueJS to the application](django/08_add_vue.md)
+ - [Adding VueJS to the application (continuation)](django/09_vue_cont.md)
+
+- Resources
+ - [References](resources/references.md)
diff --git a/basic_concepts/exercises/exercises03/exercise03_01.py b/basic_concepts/exercises/exercises03/exercise03_01.py
deleted file mode 100644
index 6ed0f89..0000000
--- a/basic_concepts/exercises/exercises03/exercise03_01.py
+++ /dev/null
@@ -1,42 +0,0 @@
-''' Lists Exercise 1: Share and tell us the output '''
-
-my_list = ['w', 'o', 'm', 'e', 'n', 'w', 'h', 'o', 'c', 'o', 'd', 'e']
-aTuple = (123, 'C++', 'Java', 'Python')
-
-
-max(my_list)
-
-## Output:
-
-min(my_list)
-
-## Output:
-
-list(aTuple)
-
-## Output:
-
-my_list.count('o')
-
-## Output:
-
-my_list.index('o')
-
-## Output:
-
-my_list.sort()
-
-## my_list
-## Output:
-
-my_list.reverse()
-
-## my_list
-## Output:
-
-copied_list = my_list.copy()
-
-## copied_list
-## Output:
-
-
diff --git a/basic_concepts/exercises/exercises03/exercise03_02.py b/basic_concepts/exercises/exercises03/exercise03_02.py
deleted file mode 100644
index 97ccf58..0000000
--- a/basic_concepts/exercises/exercises03/exercise03_02.py
+++ /dev/null
@@ -1,18 +0,0 @@
-''' Lists Exercise 2: Working list '''
-
-# Make a list that includes four careers, such as 'programmer', 'truck driver', etc....
-
-
-# Use the list.index() function to find the index of one career in your list.
-
-
-# Use the in function to show that this career is in your list.
-
-
-# Use the append() function to add a new career to your list.
-
-
-# Use the insert() function to add a new career at the beginning of the list.
-
-
-# Use a loop to show all the careers in your list.
\ No newline at end of file
diff --git a/basic_concepts/exercises/exercises03/exercise03_03.py b/basic_concepts/exercises/exercises03/exercise03_03.py
deleted file mode 100644
index e9b58b2..0000000
--- a/basic_concepts/exercises/exercises03/exercise03_03.py
+++ /dev/null
@@ -1,7 +0,0 @@
-''' List Exercise 3
-Write a program that accepts a series of numbers, outputs all odd numbers in a list in ascending order,
-and all even numbers in a list in descending order.
-Sample Input: 1,2,35,45,7,8,912,23,1
-Sample Output: [1, 1, 7, 23, 35, 45] # odd
- [912, 8, 2] # even
-'''
\ No newline at end of file
diff --git a/basic_concepts/exercises/exercises03/exercise03_04.py b/basic_concepts/exercises/exercises03/exercise03_04.py
deleted file mode 100644
index e089f55..0000000
--- a/basic_concepts/exercises/exercises03/exercise03_04.py
+++ /dev/null
@@ -1,35 +0,0 @@
-''' List Exercise 4
-
-Suppose you and your friend Sally goes to the supermarket. You have the following items on your shopping list:
- tomatoes, carrots, beans, milk, cabbage, corn, papaya, orange
-Sally has the following on her list:
- milk, tacos, cabbage, fish, beans, tuna, corn, pasta
-
-You and Sally decide to accomplish grocery shopping together.
-
-1. Write a program that outputs a new shopping list containing both of your lists, without the repeated entries, in alphabetical order
-'''
-
-# your solution here
-
-
-'''
-While shopping, you bumped into your friend Kris. Kris saw your list, and advised you not to get the fifth item as they ran out already.
-A little while later, your other friend Nat called and asked you to get peanut butter and ice cream
-
-2. Output the item that ran out, and your updated groceries list which includes Nat's, in alphabetical order
-'''
-
-# your solution here
-
-
-
-'''
-You were finally heading to the checkout counters; however, you noticed that the lines were too long. You and Sally decided to split the
-groceries alternatively, with you lining up with the first item, Sally the second item, you the third, and so on.
-
-3. Output your final list, and output Sally's list
-'''
-
-
-# your solution here
diff --git a/basic_concepts/exercises/lists/list.py b/basic_concepts/exercises/lists/list.py
new file mode 100644
index 0000000..258a091
--- /dev/null
+++ b/basic_concepts/exercises/lists/list.py
@@ -0,0 +1,10 @@
+# Write a program that accepts 3 integers and separate these into odd and even numbers.
+# Duplicates are permited in the input but not in the output.
+
+# Example
+
+# >>> Enter first number: 3567
+# >>> Enter second number: 789
+# >>> Enter third number: 1234
+# odd: {789, 3567}
+# even: {1234}
diff --git a/basic_concepts/exercises/lists/list_solution.py b/basic_concepts/exercises/lists/list_solution.py
new file mode 100644
index 0000000..a0daa52
--- /dev/null
+++ b/basic_concepts/exercises/lists/list_solution.py
@@ -0,0 +1,38 @@
+# Write a program that accepts 3 integers and separate these into odd and even numbers.
+# Duplicates are permited in the input but not in the output.
+
+# Example
+
+# >>> Enter first number: 3567
+# >>> Enter second number: 789
+# >>> Enter third number: 1234
+# odd: {789, 3567}
+# even: {1234}
+
+
+odd = []
+even = []
+
+num1 = int(input("Enter first number: "))
+
+if num1 % 2 == 1:
+ odd.append(num1)
+else: even.append(num1)
+
+num2 = int(input("Enter second number: "))
+
+if num2 % 2 == 1:
+ odd.append(num2)
+else: even.append(num2)
+
+
+num3 = int(input("Enter third number: "))
+
+if num3 % 2 == 1:
+ odd.append(num3)
+else: even.append(num3)
+
+
+
+print(f"odd: {set(odd)}")
+print(f"even: {set(even)}")
\ No newline at end of file
diff --git a/basic_concepts/lists.md b/basic_concepts/lists.md
index cc29abc..0dfa492 100644
--- a/basic_concepts/lists.md
+++ b/basic_concepts/lists.md
@@ -1,2 +1,446 @@
-## Slide Reference
-Please view [here](https://docs.google.com/presentation/d/1901VMP5cIUHjmeAdDATMFgTQ_1l0k0gtV4DdJOfQ0eM/edit?usp=sharing)
+So far, we have talked about some of the different data types in Python:
+
+* integer
+* float
+* string
+* boolean
+
+ In the succeeding sections, we'll cover other commonly used data types in Python: list, tuples, sets, and dictionaries.
+
+## List
+
+A list stores a collection of elements in Python. Actually, it may not contain any element at all. A list starts and ends with square brackets `[` and `]` and uses commas `,` to separate each element. These elements could be of the same, or different data types.
+
+### Creating lists
+
+To create a list, simply put comma-separated elements inside square brackets, as shown below:
+
+```python
+# a list containing the same data types (strings)
+a = ["hey", "there", "you"]
+
+# list of mixed data types
+b = [1, 2, "papaya"]
+
+# list of lists
+c = [[1,2], [3, 4]]
+
+# list of dictionaries (dictionaries will be covered in the succeeding section)
+d = [{"a": "apple"}, {"b": "ball"}]
+```
+
+You can also create an empty list just by typing two square brackets:
+
+```python
+x = [ ]
+```
+
+Or using the `list()` function
+
+```python
+e = list()
+print(e)
+```
+
+You can use the same function to convert strings into list
+
+```python
+w = "womenwhocode"
+w_list = list(w)
+print(w_list)
+```
+
+!> Try `list()` on other data types.
+
+Lists can be created from other lists by slicing (see below).
+
+
+### Accessing and slicing lists
+
+Accessing elements in a list is the same as accessing elements in strings: you use integer indices
+
+```python
+colors = ["red", "blue", "green"]
+print(colors[0])
+print(colors[2])
+print(colors[-2])
+```
+
+```shell
+red
+green
+blue
+```
+
+Slicing elements involves specifying a range of indices using `:`.
+
+```python
+rhyme = [1,2,"buckle","my", "shoe", 3, 4]
+words = rhyme[2:-2]
+print(words)
+```
+
+Notice that slicing a list produces a list as well.
+
+```python
+print(type(words))
+```
+
+```
+
+```
+
+### Updating lists
+
+Once created, a list can be changed. That is, we consider them **mutable** objects in Python. Thus, one way to update a list is to re-assign a value to a particular index or indices.
+
+```python
+odd = [2, 4, 6, 8] # not odd at all
+
+odd[0] = 1 # change the first value
+print(odd)
+
+odd[1:] = [3, 5, 7] # change the rest of the values
+print(odd)
+
+```
+
+```
+[1, 4, 6, 8]
+[1, 3, 5, 7]
+```
+
+Elements can also be added to lists
+
+```python
+odd.append(9) # add to a number to end of list
+print(odd)
+
+odd.extend([11, 15]) # append the contents of a sequence ([11, 15]) to list
+print(odd)
+
+odd.insert(-1, 13) # insert object (13) at position (-1)
+print(odd)
+```
+
+```
+[1, 3, 5, 7, 9]
+[1, 3, 5, 7, 9, 11, 15]
+[1, 3, 5, 7, 9, 11, 13, 15]
+```
+
+!> Notice that applying these methods changes the list itself. Think of ways in which this could be useful. Think of where this may not be ideal for your code or application.
+
+Deleting elements in a list can be done in several ways:
+
+```python
+my_list = ["p", "r", "o", "b", "l", "e", "m"]
+
+del my_list[2] # remove the element in my_list[2]
+print(my_list)
+
+my_list.remove("p") # remove the first occurence of "p"
+print(my_list)
+
+my_list[2:3] = [] # substitute element in [2:3] thereby removing them
+print(my_list) # from list.
+
+my_list.pop() # remove the last element
+print(my_list)
+
+my_list.pop(1) # remove the element in index 1
+print(my_list)
+
+my_list.clear() # remove all elements
+print(my_list)
+```
+
+```
+['p', 'r', 'b', 'l', 'e', 'm']
+['r', 'b', 'l', 'e', 'm']
+['r', 'b', 'e', 'm']
+['r', 'b', 'e']
+['r', 'e']
+[]
+```
+
+!> Note that, out of the above methods, only `pop` returns a value. What happens when we use a single index instead of range in the second example above? i.e. `my_list[2] = []`
+
+An entire list can also be deleted using the `del` statement. Printing the list results in an error, confirming that the list has been deleted.
+
+```python
+del my_list
+print(my_list)
+```
+```
+Traceback (most recent call last):
+ File "test.py", line 21, in
+ print(my_list)
+NameError: name 'my_list' is not defined
+```
+
+
+### Basic operations
+
+Like strings, the operations `*` and `+` can be applied to lists. The results of these are new lists.
+
+```python
+x = ["a", "b", "c"] + [1, 2, 3]
+y = ["hello"] * 3
+z = 3 * ["world"]
+
+print(x)
+print(y)
+print(z)
+```
+```
+['a', 'b', 'c', 1, 2, 3]
+['hello', 'hello', 'hello']
+['world', 'world', 'world']
+```
+
+!> Remember that `+` is permissible only on two or more *lists* while `*` is permissible between a *list* and an *integer*.
+
+
+Here are other functions and methods that can be applied to a list (e.g. `list`). Try them for your self to see what they can do:
+
+- `len()`
+- `max()`
+- `min()`
+- `list.count()`
+- `list.index()`
+- `list.reverse()`
+- `list.sort()`
+
+
+## Tuple
+
+Tuples are similar to lists in that they contain zero or more comma-separated elements of the same or different data types. However, one important thing to remember about tuples is that they are **immutable**; that is, unlike lists (and similar to strings) once a tuple is created, it cannot be changed.
+
+Tuples are bounded by parentheses `(` and `)`:
+
+```python
+tup1 = ("t", ) # tuple with one element
+tup2 = ("hello", "world") # tuple containing the same data type i.e. strings
+tup3 = (1, "a", 1.5) # tuple containing different data types
+tup_e = () # an empty tuple
+tup4 = ([1, 2, 3], "4") # a tuple containing a list
+tup5 = (("x", "y", "z"), (1, 2, 3)) # a tuple containing tuples
+```
+
+!> Notice that there is a trailing comma when we defined the tuple `tup1 = ("t", )` containing a single element. Why do you think is this necessary? What happens if we leave out the comma at the end?
+
+
+Accessing and slicing elements in a tuple also makes use of integer indices and ranges of indices `:`:
+
+```python
+print(tup2[1])
+print(tup3[0:2])
+```
+
+```
+'world'
+(1, 'a')
+```
+
+!> Try slicing a tuple and check the data type of the result.
+
+Similar to list, a tuple can be created from a string using the function `tuple()`:
+
+```python
+t = tuple("code")
+
+print(t)
+```
+
+You can convert a list to a tuple using `list()`, and convert a tuple to a list using `list()`
+
+```python
+m = tuple(["make", "me", "a", "tuple"])
+n = list(("make", "me", "a", "list"))
+
+print(m)
+print(n)
+```
+
+```
+('make', 'me', 'a', 'tuple')
+['make', 'me', 'a', 'list']
+```
+
+
+### Updating tuples
+
+Consider the following:
+
+```python
+x = (1, 2, 4)
+
+x[-1] = 3
+```
+
+What happens when we tried to substitute a value?
+
+Since tuples are immutable, updating and deleting a value within a tuple is not possible and will result in an error. We can, however, think of another way to come up with the desired elements. In the example above, we want a tuple with three consecutive numbers `(1, 2, 3)`. While updating an immutable object is not possible, we can define new tuples instead and perform a combination of operations.
+
+```python
+z = x[0:-1]
+y = (3, )
+
+t_cons = z + y
+
+print(t_cons)
+```
+
+```
+(1, 2, 3)
+```
+
+An entire tuple can be deleted using the `del` statement. Printing the tuple after it has been deleted results in an error (`NameError`), confirming that the deletion has taken place.
+
+```python
+del t_cons
+
+print(t_cons)
+```
+
+```
+Traceback (most recent call last):
+ File "", line 1, in
+NameError: name 't_cons' is not defined
+```
+
+### Basic operations
+
+As illustrated in a previous example, `+` is permitted on tuples. Likewise, `*` can be used, resulting in new tuples.
+
+```
+a = ("hi",) + ("hello",)
+b = ("bye",) * 3
+
+print(a)
+print(b)
+```
+
+```
+('hi', 'hello')
+('bye', 'bye', 'bye')
+```
+
+A similar logic applies to tuples as list when using these operations: `+` can be applied to two or more *tuples*, `*` works with a *tuple* and an *integer*.
+
+Here are other functions that work with tuples. Try the following and see what they can do:
+
+- `len()`
+- `max()`
+- `min()`
+
+## Set
+
+A set is an *unordered* collection of data that has *no duplicate elements*. A set in Python is what it sounds like from your math lessons: it can be used to carry out operations like union, intersection, and differences.
+
+We will not dwell too much on sets during this study group, but in case you might have a use for sets and set operations in the future, you can read more about these [here](https://www.programiz.com/python-programming/set).
+
+A set consists of zero or more comma-separated elements within curly braces `{` and `}`. The values may or may not be of the same data types. Likewise, the `set()` function can be used to make an empty set, or convert a list or tuple to a set.
+
+The following are examples of sets
+
+```python
+set1 = {1, 2, 3}
+set2 = {("a", "b",), 3.0}
+```
+
+!> Simply using `{}` does not result in a set (e.g. `e = {}`). What would the type be?
+
+The following will result in an error
+
+```python
+x = {[1, 2, 3], 4}
+y = {{1, 2, 3 }, 4}
+```
+This is because both lists and sets can change over time (they are mutable ~ unhashable), unlike tuples and strings which are immutable and therefore permitted in sets. Dictionaries, discussed in the succeeding section, are also mutable and therefore cannot be contained in sets.
+
+As mentioned, sets are *unordered*, hence accessing an element using integers and slicing using a range of indices do not make sense in a set and will therefore result in an error.
+
+
+```python
+set1[0]
+```
+
+Also the elements in sets are *deduplicated*. Even though you intentionally wrote a set with duplicate elements, these will still be stored as a single entities.
+
+```python
+set3 = {1,1,1,1}
+print(set3)
+```
+
+
+### Updating a set
+
+While indices have no meaning in sets, sets are still mutable. Hence, elements in a set can be updated and/or removed.
+
+```python
+my_set = {1, 3, 6}
+print(my_set)
+
+my_set.add(2)
+print(my_set)
+
+my_set.update([4, 5])
+print(my_set)
+
+my_set.update([6,7], {8,9})
+print(my_set)
+```
+
+```
+{1, 3, 6}
+{1, 2, 3, 6}
+{1, 2, 3, 4, 5, 6}
+{1, 2, 3, 4, 5, 6, 7, 8, 9}
+```
+
+To remove elements or the set itself:
+
+```python
+
+another_set = {1, 2, 3, "a", "b", "c"}
+print(another_set)
+
+another_set.discard(3) # discard 3
+print(another_set)
+
+another_set.remove(2) # remove 2
+print(another_set)
+
+another_set.discard(3) # discard 3 again
+print(another_set)
+
+another_set.remove(3) # remove 3
+
+another_set.clear() # remove all elements in set
+print(another_set)
+
+del another_set # delete the set itself
+print(another_set)
+```
+
+!> From running the code above, what do you think is the difference between `.remove()` and `.discard()`?
+
+
+## Challenge
+
+
+!> Please use the template below
+
+[filename](exercises/lists/list.py ':include :type=code python')
+
+[challenge_partial](../challenge_partial.md ':include')
+
+
+## Put your thinking cap on!
+
+- How is an **array** in Python different from a list?
+- We've briefly covered **mutable** (e.g. list, sets) and **immutable** (e.g. strings) objects in Python. What other objects are considered mutable? How about immutable?
+- Strings, lists, and tuples have plenty in common in terms of the methods and operations that can be done to them. Why do you think this is so?
+
diff --git a/django/01_introduction.md b/django/01_introduction.md
new file mode 100644
index 0000000..de55653
--- /dev/null
+++ b/django/01_introduction.md
@@ -0,0 +1,39 @@
+# Web Development with Django
+
+Welcome to Python - Django Web Framework Study Group. Have a little sip of your :coffee: first before we start building. :computer:
+
+Before we deep dive into the world of Django, check these things first in your local machine:
+
+- [x] I have a text editor installed in my computer (e.g. Notepad++, Sublime).
+- [x] I have Python installed in my computer.
+- [x] I'm familiar with the basics concepts of Python.
+
+## What will you learn?
+
+Once you have finished our study group, you will be able to create a Quora/Reddit-like web application. You will also learn the following in the process:
+- Django, DRF, and VueJS
+- How to use Vue CLI and Vue Router
+
+## What is Django?
+
+[Django](https://www.djangoproject.com/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. It’s free and open source.
+
+## Why You Should Learn Django?
+- It is written in Python (obviously).
+- You don't need to rely on external libraries / packages.
+- It has an in-depth [documentation](https://www.djangoproject.com/).
+- You can ask for help by joining to their [community](https://stackoverflow.com/questions/tagged/django).
+
+## Applications powered by Django
+- Spotify
+- Pinterest
+- Instagram
+- Mozilla
+- Disqus
+- National Geograpic
+- Knight Foundation
+
+## References
+- [Django Girls Tutorial](https://tutorial.djangogirls.org/en/)
+- [The Complete Guide to Django REST Framework and Vue JS](https://www.udemy.com/course/the-complete-guide-to-django-rest-framework-and-vue-js/learn/lecture/14198644?start=0#overview)
+- [10 Popular Websites Built With Django](https://djangostars.com/blog/10-popular-sites-made-on-django/)
\ No newline at end of file
diff --git a/django/02_setup.md b/django/02_setup.md
new file mode 100644
index 0000000..c6a7886
--- /dev/null
+++ b/django/02_setup.md
@@ -0,0 +1,125 @@
+## Goals
+
+- [ ] Understand the importance of virtual environments
+- [ ] Install and create a virtual environment
+- [ ] Install and use `virtualenvwrapper`
+- [ ] Install Django and Django Rest Framework through pip
+
+## Virtual Environments
+
+A **virtual environment** is a self-contained directory tree that contains a Python installation for a particular version of Python, plus a number of additional packages. It is used to isolate a particular project to avoid conflicts with the requirements of another project. **`virtualenv`** is a tool used to create Python virtual environments.
+
+## Installing 'virtualenv'
+
+Check if `virtualenv` already exists in your system through your terminal or console:
+
+```shell
+$ virtualenv --version
+```
+
+If you see a version number, that means you already have it installed and may now proceed to the next step. Otherwise, install `virtualenv` through the Python package manager, a.k.a. `pip`:
+
+```shell
+$ pip install virtualenv
+```
+
+Visit this [link](https://packaging.python.org/guides/installing-using-pip-and-virtualenv/) for more info on how to make sure you have the latest version of pip installed.
+
+## Working with 'virtualenv'
+
+To create a virtual environment, go to your project's directory and run `virtualenv`:
+
+```shell
+cd path/to/directory
+virtualenv env
+```
+
+The second argument refers to the location where you want to create the `virtualenv`. Generally, you can just create this in your project and call it `env`. `virtualenv` will create a virtual Python installation in the `env` folder.
+
+**Note**: Don't forget to exclude your `virtualenv` directory from your version control system using .gitignore or similar.
+
+To activate your `virtualenv`:
+
+```shell
+Windows
+$ .\env\Scripts\activate
+
+Linux and macOS
+$ source env/bin/activate
+```
+
+To confirm if you're in the `virtualenv` by checking the location of your Python interpreter, which should point to the `env` directory:
+
+```shell
+Windows
+$ where python
+
+Linux and macOS
+$ which python
+```
+
+As long as your `virtualenv` is activated, pip will install packages into that specific environment and you'll be able to import and use packages in your Python application.
+
+To leave your `virtualenv`, simply run:
+
+```shell
+$ deactivate
+```
+
+## Bonus: virtualenvwrapper
+
+To make working with virtual environments easier (especially for Windows uers), you can use [virtualenvwrapper](http://virtualenvwrapper.readthedocs.io/en/latest/), which is a set of extensions to the `virtualenv` tool. It also places all of your virtual environments in a single location.
+
+```shell
+Mac:
+$ pip install virtualenvwrapper
+$ export WORKON_HOME=~/Envs
+$ source /usr/local/bin/virtualenvwrapper.sh
+
+Windows:
+$ pip install virtualenvwrapper-win
+```
+
+Once you've installed `virtualenvwrapper`, create a new isolated development environment:
+
+```shell
+mkvirtualenv portfolio
+```
+
+This produced a folder named `portolio` inside the `Envs` folder with a clean copy of Python inside.
+
+To activate `virtualenv` with `virtualenvwrapper`:
+
+```shell
+$ workon portfolio
+```
+
+You may also check the list of existing virtual environments in your `Envs` folder by typing:
+
+```shell
+$ workon
+```
+
+## Installing Django and Django Rest Framework
+
+To install Django through `pip`, just run:
+
+```shell
+$ pip install django djangorestframework
+```
+
+## Saving packages
+
+To save the list of packages installed in your environment for easier reproduction:
+
+```shell
+$ pip freeze > requirements.txt
+```
+
+This will save what you've installed so far in a text file named `requirements.txt`.
+
+To install the packages listed in a `requirements.txt` file:
+
+```shell
+$ pip install -r requirements.txt
+```
diff --git a/django/03_start_project.md b/django/03_start_project.md
new file mode 100644
index 0000000..4a32ff2
--- /dev/null
+++ b/django/03_start_project.md
@@ -0,0 +1,61 @@
+## Goals
+
+- [ ] Starting the project
+- [ ] Setting things up
+- [ ] Running the server
+
+## Starting the project
+
+Once you've installed the `Django` web framework inside your `env` virtual environment, start the project by typing this command:
+```
+django-admin startproject forumapp .
+```
+
+Your directory structure should look like this:
+```
+django
+C:.
+│ manage.py
+│ requirements.txt
+│
+└───forumapp
+ asgi.py
+ settings.py
+ urls.py
+ wsgi.py
+ __init__.py
+```
+These files are:
+
+- The outer `django/` root directory is a container for your project. Its name doesn’t matter to Django; you can rename it to anything you like.
+- `manage.py`: A command-line utility that lets you interact with this Django project in various ways.
+- The inner `forumapp/` directory is the actual Python package for your project. Its name is the Python package name you’ll need to use to import anything inside it (e.g. forumapp.urls).
+- `forumapp/__init__.py`: An empty file that tells Python that this directory should be considered a Python package. If you’re a Python beginner, read [more about packages](https://docs.python.org/3/tutorial/modules.html#tut-packages) in the official Python docs.
+- `forumapp/settings.py`: Settings/configuration for this Django project. Django settings will tell you all about how settings work.
+- `forumapp/urls.py`: The URL declarations for this Django project; a “table of contents” of your Django-powered site.
+- `forumapp/asgi.py`: An entry-point for ASGI-compatible web servers to serve your project. See [How to deploy with ASGI](https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/) for more details.
+- `forumapp/wsgi.py`: An entry-point for WSGI-compatible web servers to serve your project. See [How to deploy with WSGI](https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/) for more details.
+
+## Setting things up
+
+Using your favorite text editor, go to `forumapp/settings.py`. The first thing that we need to consider editing is the `TIME_ZONE`. You may get the relevant time zone in the [Wikipedia's list of time zone.](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
+
+The code should look like this:
+```
+TIME_ZONE = 'Asia/Manila'
+```
+
+The second thing to check is the language. If English is not your native language, you can add this to change the default buttons and notifications from Django to be in your language.
+```
+LANGUAGE_CODE = 'en-us'
+```
+
+## Running the server
+Once checked and ensured that the directory or the project that you are working on has the same structure stated above, run the server by typing this command:
+```
+python manage.py migrate
+python manage.py runserver
+```
+
+Check your browser, then go to `localhost:8000` or `http://127.0.0.1:8000/`.
+
diff --git a/django/04_create_users_module.md b/django/04_create_users_module.md
new file mode 100644
index 0000000..743be8b
--- /dev/null
+++ b/django/04_create_users_module.md
@@ -0,0 +1,346 @@
+## Goals
+- [ ] Create users module
+ - [ ] Adding a model
+ - [ ] Registering users module to admin
+- [ ] Application Settings
+- [ ] Create a superuser
+- [ ] Install other required libraries
+- [ ] Adding to urls
+- [ ] Settings and Templates
+
+## Create `users` module
+```
+python manage.py startapp users
+```
+
+```
+django
+C:.
+│ manage.py
+│ requirements.txt
+│
+├───forumapp
+│ │ asgi.py
+│ │ settings.py
+│ │ urls.py
+│ │ wsgi.py
+│ |____init__.py
+│
+└───users
+ │ admin.py
+ │ apps.py
+ │ models.py
+ │ tests.py
+ │ views.py
+ │ __init__.py
+ │
+ └───migrations
+ __init__.py
+```
+
+In order to use the newly-created module (`users`), and the library `rest_framework`,
+add them to the file `settings.py`. `rest_framework.authtoken` should also be added for token authentication.
+```
+INSTALLED_APPS = [
+ 'rest_framework',
+ 'rest_framework.authtoken',
+ 'users'
+]
+```
+### Adding a model
+We are not going to add new fields to the model, but it is also a good idea to think about the future of the project. We are going to use the User model of django.
+Go to `users/models.py`, enter this command:
+```
+from django.contrib.auth.models import AbstractUser
+
+class CustomUser(AbstractUser):
+ pass
+```
+
+The user model is important for the authentication. We need to explicitly state that we're using a custom model. In `settings.py`, add this at the end:
+```
+AUTH_USER_MODEL = "users.CustomUser"
+```
+
+### Registering users module to admin
+Add the custom model user. Go to `users/admin.py`, enter this command:
+```
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin
+from users.models import CustomUser
+
+class CustomUserAdmin(UserAdmin):
+ model = CustomUser
+ list_display = ["username", "email", "is_staff"]
+
+
+admin.site.register(CustomUser, CustomUserAdmin)
+```
+
+Then make migrations, migrate:
+
+```
+> python manage.py makemigrations
+> python manage.py migrate
+```
+
+## Create a superuser
+
+Now, we already have the users module, but we don't have an actual `user`. What are we going to do is to create a `superuser`.
+
+```
+python manage.py createsuperuser
+```
+
+Enter your information and press enter.
+
+```
+Username: admin123
+Email address: admin123@example.com
+Password: admin123
+Password (again): admin123
+Superuser created successfully.
+```
+
+Now, let's run again the server.
+
+```
+python manage.py runserver
+```
+
+Access the admin interface by entering this url in your browser:
+```
+http://localhost:8000/admin
+```
+
+## Install other required libraries
+We need to enable creation of new accounts and authenticate them, so we will be needing a couple of packages in our project.
+```
+pip install django-rest-auth django-allauth django-registration django-crispy-forms
+pip freeze > requirements.txt
+```
+
+- django-rest-auth enables user registration with activation, login/logout, etc.
+- django-allauth enables signup of both local and social accounts
+- django-registration enables two-phase(with confirmation email)/one-phase(immediately active and logged in)registration
+- django-crispy-forms used to create good looking forms
+
+We also need to activate our installed libraries in `settings.py`:
+```
+'django.contrib.sites'
+'allauth',
+'allauth.account',
+'allauth.socialaccount',
+'rest_auth',
+'rest_auth.registration'
+
+'crispy_forms'
+```
+
+Next is setting important variables to the libraries the we added.
+```
+# Custom User Model
+AUTH_USER_MODEL= 'users.CustomUser'
+
+# django-crispy-forms
+CRISPY_TEMPLATE_PACK = 'bootstrap4'
+
+# django.contrib.sites
+SITE_ID = 1
+
+# django.allauth
+ACCOUNT_EMAIL_VERIFICATION = 'none'
+ACCOUNT_EMAIL_REQUIRED = (True)
+```
+
+Then make migrations, migrate:
+
+```
+> python manage.py makemigrations
+> python manage.py migrate
+```
+
+## Adding to urls
+Add new urls to our application. In `forumapp/urls.py`:
+```
+from django.urls import include, path, re_path
+
+urlpatterns = [
+ path('admin/', admin.site.urls),
+
+ # Login and registration paths provided by django
+ path('accounts/', include('django.contrib.auth.urls')),
+
+ # Login via browsable API
+ path('api-auth/', include('rest_framework.urls')),
+
+ # Login via rest
+ path('api/rest-auth/', include('rest_auth.urls')),
+
+ # Registration via rest
+ path('api/rest-auth/registration/', include('rest_auth.registration.urls')),
+
+]
+```
+
+We are going to use the views provided by the Django registration package, even though those views expect us to use a default user model. Since we are using a custom user, we also need to create a custom form.
+
+In users folder, create `forms.py`. Also enter this script:
+```
+from django_registration.forms import RegistrationForm
+from users.models import CustomUser
+
+
+class CustomUserForm(Registration Form):
+
+ class Meta(RegistrationForm.Meta):
+ model = CustomUser
+```
+
+Now, import the file to `forumapp/urls.py`.
+```
+# Skip email verification
+from django_registration.backends.one_step.views import RegistrationView
+
+from users.forms import CustomUserForm
+
+# Custom version of the registration provided by django registration
+path('accounts/register/',
+ RegistrationView.as_view(
+ form_class=CustomUserForm,
+ success_url = '/',
+ ), name='django_registration_register'),
+
+# Other URLs provided by django registration package
+path('accounts/', include('django_registration.backends.one_step.urls')),
+```
+For more info about RegistrationView, go to this [link.](https://django-registration.readthedocs.io/en/3.1.2/one-step-workflow.html)
+
+
+After that, go to `settings.py`, add the following. This will help us redirect after logging in, logging out.
+```
+LOGIN_URL = 'accounts/login/'
+LOGIN_REDIRECT_URL = '/'
+LOGOUT_REDIRECT_URL = '/'
+```
+
+## Settings and Templates
+The next step that we are going to do is to create templates that are needed to authenticate users via browser.
+
+Go to `forumapp/settings.py`, set this command:
+```
+# Django-REST-Framework
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'rest_framework.authentication.TokenAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ),
+ 'DEFAULT_PERMISSION_CLASSES': (
+ 'rest_framework.permissions.IsAuthenticated',
+ ),
+}
+```
+
+Then create a templates directory. Still inside the `settings.py` file, add the directory inside the dictionary `TEMPLATES` like this. Also add the templates folder in the root directory.
+```
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [BASE_DIR / 'templates'], # this should be added
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+```
+
+Inside the templates folder, add two more folders: `django_registration` `registration`.
+
+- `django_registration` - to cater the templates required by Django Registration package.
+- `registration` - store all the login related templates used by Django.
+
+Now, we have to make sure that the authetication-related templates share a common layout. Let's create a new file called `templates/auth_layout.html`.
+```
+
+
+
+
+
+
+
+
+ Forum App
+
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
+
+
+```
+
+We can add a new template called `templates/registration/login.html`. This html file will call the main design `auth_layout`, then add new style such as the login form container.
+```
+{% extends 'auth_layout.html' %}
+{% load crispy_forms_tags %}
+
+{% block content %}
+
Login
+
+
+
+{% endblock %}
+```
+
+Now, let's define the style `login-form-container` in `templates/auth_layout.html`. The style tag should be inside the `` tag.
+```
+
+```
+
+Next is to create a template called `templates/django_registration/registration_form.html`. Just copy from `templates/registration/login.html`, then change necessary information.
+```
+{% extends 'auth_layout.html' %}
+{% load crispy_forms_tags %}
+
+{% block content %}
+
Create your Account
+
+
+
+{% endblock %}
+```
+
+Don't forget also to add the style in `templates/auth_layout.html`.
+```
+
+```
+
+Now reload, then check your browser! Access this URL:
+`localhost:8000/accounts/register`
\ No newline at end of file
diff --git a/django/05_endpoints.md b/django/05_endpoints.md
new file mode 100644
index 0000000..beddd5c
--- /dev/null
+++ b/django/05_endpoints.md
@@ -0,0 +1,117 @@
+## Goals
+- [ ] Create a homepage endpoint
+- [ ] Create endpoints for users module
+
+## Create a homepage endpoint
+We are now going to create our endpoint. Endpoints provide a useful information about the user that is currently connected to the application.
+
+We saw in the `settings.py` file that the URLs are set to these. Now, we are going to create a path that points to homepage('/') and link a view to render the template.
+```
+LOGIN_REDIRECT_URL = '/'
+```
+
+Create a folder in the root directory called `core`. Inside the folder create `__init__.py` and `views.py`. Inside the view, add this script.
+```
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.views.generic.base import TemplateView
+
+
+class IndexTemplateView(LoginRequiredMixin, TemplateView):
+
+ # overwrite the method
+ def get_template_names(self):
+ template_name = 'index.html'
+ return template_name
+```
+We have the the `template_name` to `index.html`, but we don't have it yet, so we are going to create it inside the folder `templates`.
+```
+
+
+
+
+
+
+ Forum App
+
+
+
+
Vue JS
+
+
+
+
+
+```
+
+Create a path for the class `IndexTemplateView`. Import it inside the `forumapp/urls.py`.
+
+```
+from core.views import IndexTemplateView
+
+# Put this at the end of the urlpatterns
+re_path(r"^.*$", IndexTemplateView.as_view(), name='entry-point'),
+```
+
+Check again your browser, then go to `localhost:8000` or `http://127.0.0.1:8000/`. You should be seeing Vue JS text.
+
+## Create endpoints for users module
+We will continue creating endpoints for the users module. Create the folder `api` in `users` module. Inside it, create the ff folders:
+```
+- serializers.py
+- urls.py
+- views.py
+```
+
+Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes, then rendered to `JSON`, or `XML`.
+
+In `users/api/serializers.py`, add this set of code:
+```
+from rest_framework import serializers
+from users.models import CustomUser
+
+
+class UserDisplaySerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = CustomUser
+ fields = ["username"]
+```
+
+The view function, takes a web request and returns a web response. It is important to know that views handles the logic that gets processed each time a different URL is visited.
+
+In `users/api/views.py`, add this block of code:
+
+```
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from users.api.serializers import UserDisplaySerializer
+
+
+class CurrentUserAPIView(APIView):
+
+ def get(self, request):
+ serializer = UserDisplaySerializer(request.user)
+ return Response(serializer.data)
+```
+
+We need to ensure that our created view is registered in `users/api/urls.py`.
+```
+from django.urls import path
+from users.api.views import CurrentUserAPIView
+
+urlpatterns = [
+ path("user/", CurrentUserAPIView.as_view(), name="current-user")
+]
+```
+
+And in `forumapp/urls.py`, register the api under users.
+```
+ path("api/", include("users.api.urls")),
+```
+
+Again reload and check your browser! Access this URL:
+`localhost:8000/api/user/`
+
+As you can see in the browser, the `username` is shown. It is because of what we did earlier in the serializer and view.
+
+
diff --git a/django/06_create_questions_module.md b/django/06_create_questions_module.md
new file mode 100644
index 0000000..a20ff35
--- /dev/null
+++ b/django/06_create_questions_module.md
@@ -0,0 +1,404 @@
+## Goals
+- [ ] Create questions module
+- [ ] Create questions and answers serializer
+- [ ] Create questions views
+- [ ] Create answers views
+- [ ] Create detail endpoints
+- [ ] Create like endpoint
+
+## Create `questions` module
+```
+python manage.py startapp questions
+```
+
+After starting the application, register the app in `forumapp/settings.py`
+```
+INSTALLED_APPS = [
+ ...
+ 'questions'
+]
+```
+
+Define the model that we will be using for the `questions` and `anwsers`. In `questions/models.py`:
+```
+from django.db import models
+from django.conf import settings
+
+
+class Question(models.Model):
+ created_at = models.DateTimeField(auto_now_add=True)
+ updated_at = models.DateTimeField(auto_now=True)
+ content = models.CharField(max_length=240)
+ slug = models.SlugField(max_length=255, unique=True)
+ author = models.ForeignKey(settings.AUTH_USER_MODEL,
+ on_delete=models.CASCADE,
+ related_name="questions")
+
+ def __str__(self):
+ return self.content
+
+
+class Answer(models.Model):
+ created_at = models.DateTimeField(auto_now_add=True)
+ updated_at = models.DateTimeField(auto_now=True)
+ body = models.TextField()
+ question = models.ForeignKey(Question,
+ on_delete=models.CASCADE,
+ related_name="answers")
+ author = models.ForeignKey(settings.AUTH_USER_MODEL,
+ on_delete=models.CASCADE)
+ voters = models.ManyToManyField(settings.AUTH_USER_MODEL,
+ related_name="votes")
+
+ def __str__(self):
+ return self.author.username
+```
+
+Then make migrations, migrate:
+
+```
+> python manage.py makemigrations
+> python manage.py migrate
+```
+
+Let us also register the models that we created to `questions/admin.py`.
+```
+from django.contrib import admin
+from questions.models import Answer, Question
+
+admin.site.register(Answer)
+admin.site.register(Question)
+
+```
+
+We have defined a `SlugField` in the model. A slug is a short label for something, containing only letters, numbers or hyphens. A SlugField is a field that is automatically created. In this case, we have to find a way to create it, as it also has to be unique. We are going to use `signal`, that is used with the model.
+
+Create the file `questions/signals.py`:
+```
+from django.db.models.signals import pre_save
+from django.dispatch import receiver
+from django.utils.text import slugify
+
+from core.utils import generate_random_string
+from questions.models import Question
+```
+
+Then create a `generate_random_string` function inside `core/utils.py`:
+```
+import random
+import string
+
+ALPHANUMERIC_CHARS = string.ascii_lowercase + string.digits
+STRING_LENGTH = 6
+
+def generate_random_string(chars=ALPHANUMERIC_CHARS, length=STRING_LENGTH):
+ return "".join(random.choice(chars) for _ in range(length))
+```
+
+After that, go back to `questions/signals.py`. Add the function `add_slug_to_question`. This will also call the function that we created in `core/utils.py`
+```
+@receiver(pre_save, sender=Question)
+def add_slug_to_question(sender, instance, *args, **kwargs):
+ if instance and not instance.slug:
+ slug = slugify(instance.content)
+ random_string = generate_random_string()
+ instance.slug = slug + "-" + random_string
+```
+
+Complete the setup by registering this to `questions/apps.py`.
+```
+from django.apps import AppConfig
+
+
+class QuestionsConfig(AppConfig):
+ name = 'questions'
+
+ def ready(self):
+ import questions.signals
+```
+
+And in `questions/__init__.py`, register the `QuestionsConfig`.
+```
+default_app_config = "questions.apps.QuestionsConfig"
+```
+
+Check that everything works fine in your terminal by typing
+```
+$ python manage.py shell
+
+>>> from django.contrib.auth import get_user_model
+>>> custom_user = get_user_model()
+>>> u = custom_user.objects.first()
+>>> u
+
+>>> from questions.models import Question
+>>> q = Question.objects.create(author=u, content="First, question! Does it work?")
+>>> q
+
+>>> q.slug
+'first-question-does-it-work-ea8vb1'
+```
+
+## Create questions and answers serializer
+In the previous part of this chapter, we created a model for `Questions` and `Answers`. Now we need to create a seralizer for them.
+
+Create folder `questions/api` then create `serializers.py`.
+```
+from rest_framework import serializers
+from questions.models import Answer, Question
+
+
+class AnswerSerializer(serializers.ModelSerializer):
+ author = serializers.StringRelatedField(read_only=True)
+ created_at = serializers.SerializerMethodField()
+ likes_count = serializers.SerializerMethodField()
+ user_has_voted = serializers.SerializerMethodField()
+ question_slug = serializers.SerializerMethodField()
+
+ class Meta:
+ model = Answer
+ exclude = ["question", "voters", "updated_at"]
+
+ def get_created_at(self, instance):
+ return instance.created_at.strftime("%B %d, %Y")
+
+ def get_likes_count(self, instance):
+ return instance.voters.count()
+
+ def get_user_has_voted(self, instance):
+ request = self.context.get("request")
+ return instance.voters.filter(pk=request.user.pk).exists()
+
+ def get_question_slug(self, instance):
+ return instance.question.slug
+
+
+class QuestionSerializer(serializers.ModelSerializer):
+ author = serializers.StringRelatedField(read_only=True)
+ created_at = serializers.SerializerMethodField()
+ slug = serializers.SlugField(read_only=True)
+ answers_count = serializers.SerializerMethodField()
+ user_has_answered = serializers.SerializerMethodField()
+
+ class Meta:
+ model = Question
+ exclude = ["updated_at"]
+
+ def get_created_at(self, instance):
+ return instance.created_at.strftime("%B %d, %Y")
+
+ def get_answers_count(self, instance):
+ return instance.answers.count()
+
+ def get_user_has_answered(self, instance):
+ request = self.context.get("request")
+ return instance.answers.filter(author=request.user).exists()
+```
+
+## Create `questions` views
+In this part we will create a viewset for module `questions`. It is a class-based view without method handlers such as `get` or `post`, but it provides actions such as `.list()` and `.create()`.
+questions/api/views.py
+
+```
+from rest_framework import serializers, viewsets
+from rest_framework.permissions import IsAuthenticated
+
+from questions.api.serializers import QuestionSerializer
+from questions.api.permissions import IsAuthorOrReadOnly
+from questions.models import Question
+
+
+class QuestionViewSet(viewsets.ModelViewSet):
+ queryset = Question.objects.all()
+ lookup_field = 'slug'
+ serializer_class = QuestionSerializer
+ permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
+
+ def perform_create(self, serializer):
+ serializer.save(author=self.request.user)
+
+
+```
+
+Then we will create a `permissions.py` file in `questions/api` folder to secure the api and restrict other users from accessing the apis. We will create a class `IsAuthorOrReadOnly`.
+
+```
+from rest_framework import permissions
+
+
+class IsAuthorOrReadOnly(permissions.BasePermission):
+
+ def has_object_permission(self, request, view, obj):
+ if request.method in permissions.SAFE_METHODS:
+ return True
+ return obj.author == request.user
+```
+
+
+In `questions/api/urls.py`, register the view.
+```
+from django.urls import include, path
+from rest_framework import routers, urlpatterns
+from rest_framework.routers import DefaultRouter
+
+from questions.api import views as qv
+
+router = DefaultRouter()
+router.register(r"questions", qv.QuestionViewSet)
+
+
+urlpatterns = [
+ path("", include(router.urls))
+]
+```
+
+After that, register the `questions` url in the `forumapp/urls.py` file. Register this inside the urlpatterns.
+```
+ path('api/', include('questions.api.urls')),
+```
+
+Now, try the newly created api by accessing it in your browser then by typing `localhost:8000/api/questions/`. You may access the api with the question instance. You may also try to accessor update an individual questions by adding the slug value at the end of the search bar.
+```
+example:
+http://localhost:8000/api/questions/first-question-does-it-work-7ue6r8/
+```
+
+## Create `answers` views
+In `questions/api/views.py`, we are going to create two separate views, first to let the users answer the question, and second to list all of the answers for the questions.
+
+```
+from rest_framework import generics, viewsets
+from rest_framework.exceptions import ValidationError
+from rest_framework.generics import get_object_or_404
+
+
+from questions.api.serializers import AnswerSerializer, QuestionSerializer
+from questions.models import Answer, Question
+
+
+class AnswerCreateAPIView(generics.CreateAPIView):
+ queryset = Answer.objects.all()
+ serializer_class = AnswerSerializer
+ permission_classes = [IsAuthenticated]
+
+ def perform_create(self, serializer):
+ request_user = self.request.user
+ kwarg_slug = self.kwargs.get("slug")
+ question = get_object_or_404(Question, slug=kwarg_slug)
+
+ if question.answers.filter(author=request_user).exists():
+ raise ValidationError("You have already answered this question!")
+ serializer.save(author=self.request.user, question=question)
+```
+
+Then register the View to `questions/api/urls.py`
+```
+path("questions//answer/",
+ qv.AnswerCreateAPIView.as_view(), name='answer-create')
+```
+
+Now, go back to your browser then check whether you see an answer field, like in the example below:
+```
+http://localhost:8000/api/questions/do-you-like-guitars-norzk0/answer/
+
+```
+
+Once you accessed the link in your browser, you may notice that you cannot access a list of all of the answers in each question. The next thing that we need to do is to create a view that will list them all.
+
+In `questions/api/views.py`, create another class called `AnswerListAPIView`.
+
+```
+class AnswerListAPIView(generics.ListAPIView):
+ serializer_class = AnswerSerializer
+ permission_classes = [IsAuthenticated]
+
+ def get_queryset(self):
+ kwarg_slug = self.kwargs.get('slug')
+ return Answer.objects.filter(question__slug=kwarg_slug).order_by('-created_at')
+```
+
+Same as what we did earlier, register the view `AnswerListAPIView` to `questions/api/urls.py`.
+
+```
+path("questions//answers/",
+ qv.AnswerListAPIView.as_view(), name='answer-list')
+```
+
+Perfect! Now, go back to your browser, then access this link `http://localhost:8000/api/questions//answers/`. You may now check the list of the answers in each question. :tada:
+
+
+## Create detail endpoint
+To finish our backend setup, we need two more endpoints; one for Retrieving, Updating and Deleting answers, and one for the "like" feature.
+
+In `questions/api/views.py`, add a class called `AnswerRUDApiView`.
+
+```
+class AnswerRUDApiView(generics.RetrieveUpdateDestroyAPIView):
+ queryset = Answer.objects.all()
+ serializer_class = AnswerSerializer
+ permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
+```
+Then register again in `questions/api/urls.py`.
+```
+path("answers//",
+ qv.AnswerRUDApiView.as_view(), name='answer-detail')
+```
+Try accessing the url `http://localhost:8000/api/questions//answers/`, you can see a list of answers with id. After that, get the id, then use that id in url `http://localhost:8000/api/answers//`. You may see the details of the answer. Try also updating and deleting the answer.
+
+## Create like endpoint
+Let us now create the last endpoint - the like endpoint.
+In `questions/api/views.py`, add a class called `AnswerLikeAPIView`.
+```
+from rest_framework import generics, status, viewsets
+
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
+
+class AnswerLikeAPIView(APIView):
+ serializer_class = AnswerSerializer
+ permission_classes = [IsAuthenticated]
+
+ def delete(self, request, pk):
+ answer = get_object_or_404(Answer, pk=pk)
+ user = request.user
+
+ answer.voters.remove(user)
+ answer.save()
+
+ serializer_context = {'request': request}
+ serializer = self.serializer_class(answer, context=serializer_context)
+
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ def post(self, request, pk):
+ answer = get_object_or_404(Answer, pk=pk)
+ user = request.user
+
+ answer.voters.add(user)
+ answer.save()
+
+ serializer_context = {'request': request}
+ serializer = self.serializer_class(answer, context=serializer_context)
+
+ return Response(serializer.data, status=status.HTTP_200_OK)
+```
+
+Register the view in `questions/api/urls.py`.
+
+```
+path("answers//like/",
+ qv.AnswerLikeAPIView.as_view(), name='answer-like')
+```
+
+Access the created endpoint by typing in `http://localhost:8000/api/answers//like/`.
+
+The last thing that we need to do is to set the pagination in `settings.py` file.
+```
+REST_FRAMEWORK = {
+ ...
+ 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
+ 'PAGE_SIZE': 2
+}
+```
+
diff --git a/django/07_create_frontend.md b/django/07_create_frontend.md
new file mode 100644
index 0000000..3b1d7f0
--- /dev/null
+++ b/django/07_create_frontend.md
@@ -0,0 +1,103 @@
+## Goals
+- [ ] Create a Registration Link on the Login Page
+- [ ] Create a Login Link on the Registration Page
+- [ ] Adding Margins to Layout
+- [ ] Adding a Border
+
+## Create a Registration Link on the Login Page
+Yay! We are done setting-up the backend of our application! Now, we will be focusing on the front-end side of the app. We want to make it more `user-friendly` that is why we are going to modify or add a few things.
+
+Open `templates/registration/login.html`. Add this line of code after `
Login
`:
+
+```
+
Share Your Knowledge!
+```
+
+Also, add the registration link inside the class `login-form-container` after the `form` tag:
+
+```
+
+{% endblock %}
+```
+
+Go to url `http://localhost:8000/accounts/login/` and see the changes.
+
+## Create a Login Link on the Registration Page
+In this part, we are going to do the same steps. In `templates/django_registration/registration_form.html`, add this code inside the class `registration-form-container`, under the `form` tag.
+
+```
+
+{% endblock %}
+```
+
+Then check the changes by accessing this url: `http://localhost:8000/accounts/register/`.
+
+## Adding Margins to Layout
+In this part, we are going to add some margins to our `registration` page and `login` page.
+In `templates/auth_layout.html`, add a css class called `outer-area`. The style would look as follows:
+```
+ .outer-area {
+ margin-top: 100px;
+ }
+```
+Then add a new div tag outside the div class `auth-box text-center`. In the newly added div tag, call the `outer-area` class. It should look like this:
+
+```
+
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
+
+
+```
+
+## Adding a Border
+To add a border on registration and login page, add a css class named `auth-box`. Then add these other details:
+```
+.auth-box {
+ border: 3px solid lightgray;
+ border-radius: 10px;
+ padding-top: 25px;
+ padding-bottom: 25px;
+ width: 600px;
+ margin: auto;
+}
+```
\ No newline at end of file
diff --git a/django/08_add_vue.md b/django/08_add_vue.md
new file mode 100644
index 0000000..ca9efa6
--- /dev/null
+++ b/django/08_add_vue.md
@@ -0,0 +1,195 @@
+## Goals
+- [ ] Install NPM
+- [ ] Install VueCLI
+- [ ] Add Vue to application
+
+## Install NPM
+Go to `https://nodejs.org/en/download/`, download the latest or the LTS version of Node. Install node with the recommended settings. Check if Node and NPM is installed by checking in your terminal:
+
+```
+> npm --version
+6.14.14
+> node --version
+v14.17.4
+```
+
+## Install VueCLI
+Once the NodeJS has been installed in your computer, you may now install the VueCLI. In your terminal, type this command:
+```
+sudo npm i -g @vue/cli
+```
+Then test if its working by typing
+```
+vue create hello-vue
+Vue CLI v4.5.13
+? Please pick a preset: Manually select features
+? Check the features needed for your project: Choose Vue version, Babel, Router, Linter
+? Choose a version of Vue.js that you want to start the project with 2.x
+? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
+? Pick a linter / formatter config: Prettier
+? Pick additional lint features: Lint on save
+? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
+? Save this as a preset for future projects? (y/N) N
+```
+
+Wait for a few minutes, then once completed, will show a prompt like this:
+
+```
+📄 Generating README.md...
+
+🎉 Successfully created project hello-vue2.
+👉 Get started with the following commands:
+
+ $ cd hello-vue
+ $ npm run serve
+```
+
+Go to `hello-vue` and type `npm run serve`. It will remind you to access to `http://localhost:/`. Go to that url and check if Vue is showing. This indicates that Vue is properly installed to your computer.
+
+
+## Add Vue to application
+We are going to create a simple Vue project in our Django project. Ensure that the project name is `frontend`, like so:
+```
+vue create frontend
+```
+
+Set the config like the ones below:
+```
+vue create hello-vue
+Vue CLI v4.5.13
+? Please pick a preset: Manually select features
+? Check the features needed for your project: Choose Vue version, Babel, Router, Linter
+? Choose a version of Vue.js that you want to start the project with 2.x
+? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
+? Pick a linter / formatter config: Prettier
+? Pick additional lint features: Lint on save
+? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
+? Save this as a preset for future projects? (y/N) N
+```
+
+Then, we're going to install a package `webpack-bundle-tracker` to keep track of the code in our frontend application.
+Go to `frontend` folder then install the webpack tracker:
+```
+cd frontend
+
+npm install --save-dev webpack-bundle-tracker@0.4.3
+```
+
+Next, create a file inside the folder `frontend`. Name it `vue.config.js`.
+```
+const BundleTracker = require("webpack-bundle-tracker");
+
+module.exports = {
+ // on Windows you might want to set publicPath: "http://127.0.0.1:8080/"
+ publicPath: "http://127.0.0.1:8081/",
+ outputDir: './dist/',
+
+ chainWebpack: config => {
+
+ config
+ .plugin('BundleTracker')
+ .use(BundleTracker, [{filename: './webpack-stats.json'}])
+
+ config.output
+ .filename('bundle.js')
+
+ config.optimization
+ .splitChunks(false)
+
+ config.resolve.alias
+ .set('__STATIC__', 'static')
+
+ config.devServer
+ // the first 3 lines of the following code have been added to the configuration
+ .public('http://127.0.0.1:8080')
+ .host('127.0.0.1')
+ .port(8080)
+ .hotOnly(true)
+ .watchOptions({poll: 1000})
+ .https(false)
+ .disableHostCheck(true)
+ .headers({"Access-Control-Allow-Origin": ["\*"]})
+
+ },
+
+ // uncomment before executing 'npm run build'
+ // css: {
+ // extract: {
+ // filename: 'bundle.css',
+ // chunkFilename: 'bundle.css',
+ // },
+ // }
+
+};
+
+```
+
+Next, we are going to install a webpack loader in Django environment. This will help us read the changes given by the bundle tracker.
+```
+pip install django-webpack-loader==0.7.0
+```
+
+Update the requirements.txt file.
+```
+pip freeze > requirements.txt
+```
+
+Then add the webpack_loader library to `settings.py`.
+```
+INSTALLED_APPS = [
+ 'webpack_loader',
+]
+
+// on the bottom of settings.py file
+WEBPACK_LOADER = {
+ 'DEFAULT': {
+ 'BUNDLE_DIR_NAME': 'dist/',
+ 'STATS_FILE': BASE_DIR / 'frontend' / 'webpack-stats.json'
+ }
+}
+```
+
+Add this code inside the file `templates/index.html`, on the first line of the code:
+```
+{% load render_bundle from webpack_loader %}
+```
+Also, add this inside the body of the code, after the div id app:
+```
+{% render_bundle 'app' %}
+```
+
+The index file should look this this:
+```
+{% load render_bundle from webpack_loader %}
+
+
+
+
+
+
+ Forum App
+
+
+
+
Vue JS
+
+
+
+ {% render_bundle 'app' %}
+
+
+
+```
+
+Run server of both django and vue separately.
+```
+python manage.py runserver
+
+npm run serve
+```
+
+
+(Optional) You may also explore the vue project by typing this command.
+```
+vue ui
+```
\ No newline at end of file
diff --git a/django/09_vue_cont.md b/django/09_vue_cont.md
new file mode 100644
index 0000000..8e023db
--- /dev/null
+++ b/django/09_vue_cont.md
@@ -0,0 +1,507 @@
+## Goals
+- [ ] Additional setup
+- [ ] Add navbar component
+- [ ] Initialize csrf token
+- [ ] Add home component and questions list
+- [ ] Add single question component
+
+## Additional Setup
+Add this script to be shown whenever there is an error with JavaScript in `templates/index.html`, above the div id app.
+```
+
+```
+
+Comment out the base key-value in `src/router/index.js`, `const router`. This will remove the extended baseurl that we saw in our browser.
+```
+const router = new VueRouter({
+ mode: "history",
+ // base: process.env.BASE_URL,
+ routes,
+});
+```
+
+## Add navbar component
+Now, we are going to create a navbar component to our application.
+
+New file `templates/base.html`. Code should be like this.
+```
+
+
+
+
+
+
+ Forum Application
+
+
+ {% block style %}
+ {% endblock %}
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
+
+```
+
+Change the `auth-layout` page to this:
+```
+{% extends "base.html" %}
+{% block style %}
+
+{% endblock %}
+
+
+{% block content %}
+
+
+ {% block auth %}
+ {% endblock %}
+
+
+{% endblock %}
+```
+
+As you can see here in the code, it has several blocks called liquid tags. It basically helps to override the specific parts of the template.
+
+Now, we should change some block names in files `templates/django_registration/registration_form.html` and `templates/registration/login.html`.
+```
+{% block auth %}
+ ...
+{% endblock %}
+```
+
+We should add add an import of the `auth-layout` to the same files mentioned. This should be added on the first line of the code.
+```
+{% extends 'auth_layout.html' %}
+```
+
+Now we are going to add a file `frontend/src/components/Navbar.vue` to add the navigation bar.
+```
+
+
+
+
+
+
+
+```
+
+Then, import this navbar component in `frontend/src/App.vue`. Under the template tag, add a new tag called script.
+```
+
+```
+
+Remove the following as it is no longer needed
+```
+
+ Home |
+ About
+
+```
+
+Then, replace it with ``. The code should look like this:
+```
+
+
+
+
+
+
+
+
+
+
+```
+
+Check the changes in your broswer!
+
+Let's add a container in our html code in `frontend/src/views/Home.vue`.
+```
+
+
+
+
+```
+
+## Initialize csrf token
+In `frontend` folder, create a folder named `common`. Add two files named `csrf_token.js` and `api.service.js`
+
+Add this script in `csrf_token.js`.
+```
+function getCookie(name) {
+ let cookieValue = null;
+ if (document.cookie && document.cookie !== '') {
+ const cookies = document.cookie.split(';');
+ for (let i = 0; i < cookies.length; i++) {
+ const cookie = cookies[i].trim();
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) === (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+}
+const CSRF_TOKEN = getCookie('csrftoken');
+
+export { CSRF_TOKEN };
+```
+
+On the other hand, add this script inside `api.service.js`.
+```
+import { CSRF_TOKEN } from "./csrf_token.js"
+
+async function getJSON(response) {
+ if (response.status === 204) return '';
+ return response.json()
+}
+
+function apiService(endpoint, method, data) {
+ const config = {
+ method: method || "GET",
+ body: data !== undefined ? JSON.stringify(data) : null,
+ headers: {
+ 'content-type': 'application/json',
+ 'X-CSRFTOKEN': CSRF_TOKEN
+ }
+ };
+ return fetch(endpoint, config)
+ .then(getJSON)
+ .catch(error => console.log(error))
+}
+
+export { apiService };
+```
+
+Then remove the VueJS logo by deleting or commenting this line of code in file `frontend/src/views/Home.vue`
+```
+
+```
+
+## Add home component and questions list
+Let's do a little bit of cleanup. Delete this code in `frontend/src/views/Home.vue`
+```
+
+
+```
+
+In `frontend/src/views/Home.vue`, import apiService
+```
+
+
+
+```
+
+Import the Questions.vue file to `frontend/src/router/index.js`.
+```
+import Question from "../views/Question.vue";
+...
+
+ {
+ path: "/question/:slug",
+ name: "question",
+ component: Question,
+ props: true
+ },
+```
+
+Then add another router-link in `frontend/src/views/Home.vue`. Add also a style for the question link class.
+```
+
+ {{ question.content }}
+
+
+```
+```
+
+```
+
+After the changes, the file `frontend/src/views/Home.vue` should look like this:
+```
+
+
+
+
+
Posted by:
+ {{ question.author }}
+
+
+ {{ question.content }}
+
+
+
Answers: {{ question.answers_count }}
+
+
+
+
+
+
+
+
+
+```
+
+
diff --git a/flask/discussions/09_deployment.md b/flask/discussions/09_deployment.md
index f4c62c3..4f3990c 100644
--- a/flask/discussions/09_deployment.md
+++ b/flask/discussions/09_deployment.md
@@ -1,3 +1,57 @@
## Goals
-- [ ]
+- [ ] Deploy your first Flask application to Heroku via GitHub
+
+## Heroku
+
+Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud.
+
+While they have a free plan that is ideal for experimenting with cloud applications in a limited sandbox, they also have good pricing options based on dynos consumed. Dynos are smart, lightweight containers built for modern languages and developer productivity. Basically, you only pay for what you use, prorated to the second.
+
+## Creating a Heroku application
+
+After signing up and logging in, you will be redirected to the dashboard.
+
+It displays your account type, where the default is Personal. You can also create teams to collaborate with other people, although you need to add a valid credit card to do so. Below that, it lists all the applications that you have so far, its name (alyssonalvaran), application type (Python), stack type (heroku-16), and region (United States).
+
+To create a new app, click the New button across your account type located on the upper right portion of your dashboard and select Create new app.
+
+You will be asked to provide an app name and region. For demonstration purposes, I created an app named pineapples-on-pizza.
+
+Once you select Create app, the next screen that will appear is the deploy tab of your newly created application. At this point, you can already access your app by selecting the Open app button on the upper right portion of your dashboard or going to .herokuapp.com, which is my case is https://pineapples-on-pizza.herokuapp.com/.
+
+Now that you’re done creating a Heroku app, the next thing that you have to do is to prepare your Flask app.
+
+## Green unicorn
+
+Green unicorn, or simply Gunicorn, is a Python Web Server Gateway Interface (WSGI) HTTP server for UNIX, ported from Ruby’s Unicorn project. Basically, this lightweight pre-fork worker model will enable us to deploy our Flask app to Heroku.
+
+You can install this easily through pip.
+
+```
+$ pip3 install gunicorn
+```
+
+After installing, create a file named Procfile (yes, without an extension) and add this line here inside:
+
+```
+web: gunicorn app:app
+```
+
+## requirements.txt
+
+That’s it! The final preparation that you have to do is to make sure that all of your packages are frozen to the text file requirements.txt. You can do this by simply entering this in the command line:
+
+```
+$ pip3 freeze > requirements.txt
+```
+
+## Connecting your Heroku app to a GitHub repository
+
+Assuming that you have already pushed your latest changes to your GitHub repository, go back to the deploy tab of your Heroku application. In the Deployment method section, select GitHub. This opens the Connect to GitHub where you can search for a repository to connect to. Just search the name of your repository, which in my case is pineapples-on-pizza, and select the connect button.
+
+Congratulations! Your Flask app is now deployed at pineapples-on-pizza.herokuapp.com!
+
+## Bonus: Automatic deploys
+
+You can automatically deploy your Heroku app everytime you push something to your GitHub repository by simply choosing a branch to deploy (default is Master) and selecting the Enable Automatic Deploys button in the Automatic deploys section of your Heroku app’s deploy tab.
\ No newline at end of file
diff --git a/flask/exercises/WebForm/app/app.py b/flask/exercises/WebForm/app/app.py
deleted file mode 100644
index b4aecba..0000000
--- a/flask/exercises/WebForm/app/app.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from flask import Flask, render_template, flash, request
-from wtforms import Form, TextField, TextAreaField, validators, StringField, SubmitField
-
-# App config.
-DEBUG = True
-app = Flask(__name__)
-app.config.from_object(__name__)
-app.config['SECRET_KEY'] = '7d441f27d441f27567d441f2b6176a'
-
-class ReusableForm(Form):
- name = TextField('Name:', validators=[validators.required()])
- email = TextField('Email:', validators=[validators.required(), validators.Length(min=6, max=35)])
- password = TextField('Password:', validators=[validators.required(), validators.Length(min=3, max=35)])
-
-@app.route("/", methods=['GET', 'POST'])
-def hello():
- form = ReusableForm(request.form)
-
- print(form.errors)
- if request.method == 'POST':
- name=request.form['name']
- password=request.form['password']
- email=request.form['email']
- print(name, " ", email, " ", password)
-
- if form.validate():
- # Save the comment here.
- flash('Thank you, ' + name)
- else:
- flash('Error: All the form fields are required. ')
-
- return render_template('hello.html', form=form)
-
-if __name__ == "__main__":
- app.run()
\ No newline at end of file
diff --git a/flask/exercises/WebForm/app/templates/hello.html b/flask/exercises/WebForm/app/templates/hello.html
deleted file mode 100644
index 3c2f359..0000000
--- a/flask/exercises/WebForm/app/templates/hello.html
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
- Reusable Form Demo
-
-
-
-
-
-
-
-
-
-
Flask Web Form
-
-
- {% with messages = get_flashed_messages(with_categories=true) %}
- {% if messages %}
- {% for message in messages %}
- {% if "Error" not in message[1]: %}
-
- Success! {{ message[1] }}
-
- {% endif %}
-
- {% if "Error" in message[1]: %}
-