49
itucsdb Documentation Release 1.0 Team Name December 30, 2016

itucsdb Documentation - Read the Docs

  • Upload
    others

  • View
    6

  • Download
    0

Embed Size (px)

Citation preview

Page 1: itucsdb Documentation - Read the Docs

itucsdb DocumentationRelease 1.0

Team Name

December 30, 2016

Page 2: itucsdb Documentation - Read the Docs
Page 3: itucsdb Documentation - Read the Docs

Contents

1 User Guide 3

2 Developer Guide 19

3 Installation Guide 45

i

Page 4: itucsdb Documentation - Read the Docs

ii

Page 5: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Team ShakeSpace

Members

• Ipek Karakurt

• Mumtaz Cem Eris

• Emre Can Agatay

• Member 4

• Member 5

ShakeSpace

ShakeSpace allows people to share their opinions in any topic with a wide audience. Anyone can sign up as amoderator. After having signed up, you can post image posts, topics and you can comment about these topics.Moderators can also add events , announcements and hashtags.

Contents:

Contents 1

Page 6: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

2 Contents

Page 7: itucsdb Documentation - Read the Docs

CHAPTER 1

User Guide

It is quite easy to sign up to ShakeSpace, login and logout too. ShakeSpace provides all the links that user needsand using the advantage of its simple and clean design any user can use ShakeSpace comfortably. For more detail,you can check our members’ work and technical guide.

Fig. 1.1: Sign up page of ShakeSpace.

1.1 Parts Implemented by Ipek Karakurt

1.1.1 Events

Adding an Event

If there is an event coming up that you wish to advertise on ShakeSpace, it is possible to add that event on thewebsite by filling in its name, date and the place that the event is going to take place in. If the place of the newlyadded event is not present as an option to select on the dropdown list, it is possible to add that place yourself. Howto do so will be explained in this document.

3

Page 8: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

After clicking on the Save button, the added event is displayed on the page.

Deleting an Event

If an event that was added to the website is cancelled and the user wishes to delete the event entry on the website,it is possible to do so by going to the Delete Event page by clicking on the link on Events page, and then enteringthe name of the event and clicking on the Delete button.

After the deletion, it can be seen that the event entry is removed from the list on the events page.

Updating an Event

If the name, date or the place of an event changes and the user wishes to update the event entry on the website, itis possible to do so by going to the Update Event page by clicking on the link on Events page, and then entering

4 Chapter 1. User Guide

Page 9: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

the event’s old name as well as the new information about the event, and then clicking on the Update button.

After the update operation, it can be seen that the event entry is updated on the list on the events page.

1.1.2 Place

In the case that the place that an event is going to place in not being in the list of already added places, it is possibleto add a new place. The newly added place will be displayed on the dropdown list of events on the event addingpage. It is possible to go to the event adding page by clicking on the link provided on events page.

1.1. Parts Implemented by Ipek Karakurt 5

Page 10: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Adding a Place

A list of already added places is displayed on top of the page. The user needs to type in the name of the place tobe added.

After typing in the place to be added and clicking on the Save button, the added place is displayed on the placespage.

6 Chapter 1. User Guide

Page 11: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

It is also displayed now as an option on the events page.

Deleting a Place

If the user wishes to delete the place entry on the website, it is possible to do so by going to the Delete Place pageby clicking on the link provided on Places page, and then entering the name of the place and clicking on the Deletebutton.

1.1. Parts Implemented by Ipek Karakurt 7

Page 12: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

After the deletion, it can be seen that the event entry is removed from the list on the places page.

The place is also removed from the events page as an option on the dropdown list of places.

Updating a Place

If the name of a place changes and the user wishes to update the place entry on the website, it is possible to do soby going to the Update Place page by clicking on the link on Places page, and then entering the place’s old nameas well its new name, and then clicking on the Update button.

8 Chapter 1. User Guide

Page 13: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

After the update operation, it can be seen that the event place is updated on the list on the places page.

The place is also updated on the events page as an option on the dropdown list of places.

1.1. Parts Implemented by Ipek Karakurt 9

Page 14: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

1.1.3 Text Posts

Adding a Text Post

A list of text posts added by the user is displayed on top of the page.

After typing in the text post to be added and clicking on the Save button, the added text post is displayed on thepage.

Deleting a Text Post

If the user wishes to delete the text post they have previously written on the website, it is possible to do so bygoing to the Remove Text Post page by clicking on the link provided on Text Posts page, and then the text postcontent and clicking on the Delete button.

After the deletion, it can be seen that the text post entry is removed from the list on the text posts page.

10 Chapter 1. User Guide

Page 15: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Updating a Text Post

If the the user wishes to update the text post they posted previously on the website, it is possible to do so by goingto the Update Text Post page by clicking on the link on Text Posts page, and then entering the text post’s oldcontent as well its new content, and then clicking on the Update button.

After the update operation, it can be seen that the text post is updated on the list on the text posts page.

1.2 Parts Implemented by Mumtaz Cem Eris

Logins and Moderators (users)

First of all, to be able to enter website, you must sign up as a moderator. If you have already signed up, then you

1.2. Parts Implemented by Mumtaz Cem Eris 11

Page 16: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

may login. Without having logged in, you can not access to the website. All passwords are stored in the databaseand they are all hashed. For more technical detail go to the developer guide.

Fig. 1.2: Sign up page of ShakeSpace.

Below, you will see two screenshots from ShakeSpace. In the first one, you can see that someone tries to access toimageposts page without having logged in. After that, ShakeSpace would redirect this person to the sign up pageas it can be seen from the second screenshot.

Fig. 1.3: Unsuccessful access.

Fig. 1.4: Redirection to the signup page.

Having signed up with the nickname ‘mumtaz’, after login ShakeSpace will welcome you. Now the visitor canpost some posts, create events, check his/her profile and so on.

Let’s check mumtaz’s profile. Here, the nickname of the current user and the other moderators are displayed.

Apart from logins, I have created ImagePosts page as well. Well, you can not post an image, instead you can onlypost text unfortunately. In this page, you can observe the image post feed that is all the image posts that have beenposted.

12 Chapter 1. User Guide

Page 17: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Let’s add an image post as ‘mumtaz’.

‘report.jpg’ has been added by mumtaz. In the image post feed, you can see which moderator posted what alongwith their ids.

Update operation of ‘report.jpg’ to ‘report.png’.

It has been updated as ‘report.png’.

Now, let’s delete ‘report.png’.

It has been deleted.

If a moderator decides to log out, he/she can successfully log out by clicking Log Out link. To be able to enterthe site again, the moderator can click the link in the photo and it will direct to log in page. The moderator shouldlogin again as well.

On the left screenshot, say the moderator ‘mumtaz’ decided to login again and ‘mumtaz’ is about to enter thenickname incorrectly. On the right screenshot, it can be seen that the website handled it successfully. It would bethe same case if the moderator has typed his/her password incorrect as well.

If you are logged in as ‘admin’, then you can access the admin panel of moderators. Other moderators can not seethat nor access that.

In AdminPanel, admin can see all the moderators and can do operations on them such as adding, deleting orupdating. If a moderator who has posted an image post or image posts will be deleted, all of the image posts thathas been posted by the moderator will be deleted as well. Let’s delete ‘mumtaz’ and see what happens.

1.2. Parts Implemented by Mumtaz Cem Eris 13

Page 18: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

14 Chapter 1. User Guide

Page 19: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

1.2. Parts Implemented by Mumtaz Cem Eris 15

Page 20: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Image posts before deletion operation of mumtaz.

Removing mumtaz.

mumtaz has been deleted.

And after mumtaz has been deleted, all the image posts that posted by mumtaz have been deleted as well.

Let’s update ‘shakespeare’.

It has been updated.

1.3 Parts Implemented by Emre Can Agatay

1.4 Parts Implemented by Member Name

16 Chapter 1. User Guide

Page 21: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

1.4. Parts Implemented by Member Name 17

Page 22: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

18 Chapter 1. User Guide

Page 23: itucsdb Documentation - Read the Docs

CHAPTER 2

Developer Guide

2.1 Database Design

The database contains the following 9 tables:

• MODERATORS

• PLACES

• EVENTS

• ANNOUNCEMENTS

• TEXTPOSTS

• IMGPOSTS

• HASHTAGS

• HASHTAGCONTENTS

• TOPICS

MODID attribute of MODERATORS table is referenced by TEXTPOSTS, IMGPOSTS and TOPICStables. AREA_ID attribute of PLACES table is referenced by EVENTS and ANNOUNCEMENTStables. ID attribute of HASHTAGS table is referenced by the HASHTAGCONTENTS table.

Any further details can be found in the following descriptions of team members.

2.2 Code

The technical structure of the code is described in the following sections.

2.2.1 Parts Implemented by Ipek Karakurt

Events

Events Table Initialization

The events table is initialized in server.py as follows

1 def init_events_db():2 with dbapi2.connect(app.config['dsn']) as connection:3 cursor = connection.cursor()4 query = """DROP TABLE IF EXISTS EVENTS"""

19

Page 24: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

5 cursor.execute(query)6

7 query = """CREATE TABLE EVENTS (8 ID SERIAL NOT NULL,9 CONTENT VARCHAR(300),

10 EVENT_DATE DATE,11 AREA_ID INTEGER,12 PRIMARY KEY(ID)13 )"""14 cursor.execute(query)15

16 query = """ALTER TABLE EVENTS17 ADD FOREIGN KEY(AREA_ID)18 REFERENCES PLACES(AREA_ID)19 ON DELETE CASCADE"""20 cursor.execute(query)21

22 return redirect(url_for('site.home_page'))

Events entity has four attributes.

• ID: ID is a serial value that is incremented as new events are added. It is the primary key for thisentity.

• CONTENT: Content is of type VARCHAR with a limit of 300 characters. It is designed to hold thename of the event.

• EVENT_DATE: This attribute is of type DATE, it holds the date of the event in the YYYYMMDDformat.

• AREA_ID: This attribute is a foreign key referencing the AREA_ID attribute from the PLACES table.This is done so that when a new place is added to the places table, it is automatically included as anoption for an event’s place.

20 Chapter 2. Developer Guide

Page 25: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Event Class Definition

The event class is defined in event.py as follows

1 class Event:2 def __init__(self, content, event_date, place):3 self.content = content4 self.event_date = event_date5 self.place = place6

7 def update_event(self, new_content, new_place, new_date):8 self.content = new_content9 self.event_date = new_date

10 self.place = new_place

Event class has content, event_date and place attributes. When an event needs to be updated, update_event()function takes care of this operation and takes new_content, new_place and new_date as parameters, which rep-resent the newly updated values for the event object. These parameters are then used to set the object’s attributesaccording to the updated event attributes.

Event List Class Definition

The event list class is used to keep a list of events that are currently kept in the database. It is defined in eventlist.pyas follows:

1 class EventList:2 def __init__(self):3 self.events = {}4 self.last_event_id = 05

6 def add_event(self, event):7 self.last_event_id += 18 self.events[self.last_event_id] = event9 event._id = self.last_event_id

10

11 def delete_event(self, event_id):12 del self.events[event_id]13

14 def get_event(self, event_id):15 return self.events[event_id]16

17 def get_events(self):18 return self.events

Initially, this list is empty with the last_event_id being 0. As events are added, last_event_id is incremented, newlyadded event is added to the list and its ID is set. delete_event() function takes the event’s ID as parameter anddeletes the event with this ID from the list of events. get_event() function also takes the event’s ID as parameterand returns the event with the given ID from the list of events. Lastly, get_events() function returns the whole listof events.

Event Adding Operation

The event adding operation is defined in the blueprint eventsB.py as follows

1 @events.route('/events', methods = ['GET', 'POST'])2 def events_page():3 if request.method == 'GET':4 events = current_app.eventlist.get_events()5 places = current_app.placelist.get_places()6 return render_template('events.html', events=sorted(events.items()), places=sorted(places.items()))

2.2. Code 21

Page 26: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

7 else:8 content = str(request.form['content'])9 place = str(request.form['option'])

10 event_date = str(request.form['event_date'])11 with dbapi2.connect(app.config['dsn']) as connection:12 cursor = connection.cursor()13 statement = """SELECT AREA_ID, AREA FROM PLACES WHERE (AREA=(%s))"""14 cursor.execute(statement, (place,))15 connection.commit()16 for row in cursor:17 area_id, place = row18 statement ="""INSERT INTO EVENTS (CONTENT, EVENT_DATE, AREA_ID) VALUES (%s, %s, %s)"""19 cursor.execute(statement, (content, event_date, area_id,))20 connection.commit()21

22 event = Event(content, event_date, place)23

24 current_app.eventlist.add_event(event)25 return redirect(url_for('events.events_page', event_id=event._id))

When the user goes to the events page, they can both see the list of current events and the form that prompts theuser to user to enter a new event. If the HTTP request sent by the user is of type GET, “events.html” page isrendered with the additional parameters places and events, which contain the list of places and the list of eventsin the database respectively. The list of places is passed as a parameter to the render function so that the placescan be displayed as options in the dropdown list included in the form to add a new event. If the HTTP requestsent by the user is of type POST, then the user filled in the form on the events page and wants to add a new event.Content, event_date and place variables are initialized with values from the form. The place variable is a textvalue, however its ID in the PLACES table is needed to add an event to the EVENTS table, as the event entitytakes area ID as its foreign key. Therefore, a database query is needed. In this query, the place with the givenAREA attribute (which corresponds to city name) is searched for its ID. This ID is kept in area_id variable. Then,a new event is inserted into the EVENTS table in the database with the content and event_date values from theform and the area_id value from the result of the previous database query. Then, a new event object is createdwith content, event_date and place parameters and this object is added to the event list. The user is redirected tothe events page, where they can see the newly added event on the bottom of the list.

Event Deleting Operation

The event deleting operation is defined in the blueprint eventsB.py as follows

1 @events.route('/events/delete', methods=['GET', 'POST'])2 def delete_event():3 if request.method == 'GET':4 return render_template('delete_event.html')5 else:6 content = str(request.form['content'])7 with dbapi2.connect(app.config['dsn']) as connection:8 cursor = connection.cursor()9 statement ="""SELECT ID, CONTENT FROM EVENTS WHERE (CONTENT = (%s))"""

10 cursor.execute(statement, (content,))11 connection.commit()12 for row in cursor:13 id, content = row14 statement ="""DELETE FROM EVENTS WHERE (ID = (%s))"""15 cursor.execute(statement, (id,))16 connection.commit()17 current_app.eventlist.delete_event(id)18 return redirect(url_for('events.events_page'))

When the user goes to the /events/delete page, they see a form that prompts them to enter an event’s name to deleteit. If the HTTP request sent by the user is of type GET, “delete_event.html” page is rendered which contains thesaid form. If the HTTP request sent by the user is of type POST, then the user filled in the form on the page

22 Chapter 2. Developer Guide

Page 27: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

and wants to delete an existing event. Content is initialized with the value from the form. The event’s ID in theEVENTS table is needed. Therefore, a database query is done. In this query, the event with the given CONTENTattribute is searched for its ID. This ID is kept in id variable. Then, the event is deleted from the EVENTS tablein the database with the id value obtained from the result of the previous database query. Then, the event objectwith the said id is deleted from the event list. The user is redirected to the events page, where they can see that theevent they deleted disappeared from the list of events displayed on that page.

Event Updating Operation

The event updating operation is defined in the blueprint eventsB.py as follows

1 @events.route('/events/update', methods = ['GET', 'POST'])2 def update_event():3 if request.method == 'GET':4 events = current_app.eventlist.get_events()5 places = current_app.placelist.get_places()6 return render_template('update_event.html', events=sorted(events.items()), places=sorted(places.items()))7 else:8 content = str(request.form['content'])9 new_content = str(request.form['new_content'])

10 new_place = str(request.form['option'])11 new_date = str(request.form['new_date'])12 with dbapi2.connect(app.config['dsn']) as connection:13 cursor = connection.cursor()14 statement = """SELECT AREA_ID, AREA FROM PLACES WHERE (AREA=(%s))"""15 cursor.execute(statement, (new_place,))16 connection.commit()17 for row in cursor:18 area_id, place = row19

20 cursor = connection.cursor()21 statement = """UPDATE EVENTS22 SET CONTENT = (%s),23 EVENT_DATE = (%s),24 AREA_ID = (%s)25 WHERE (CONTENT=(%s))"""26 cursor.execute(statement, (new_content, new_date, area_id, content))27 connection.commit()28

29 cursor = connection.cursor()30 statement = """SELECT ID, CONTENT FROM EVENTS WHERE (CONTENT = (%s))"""31 cursor.execute(statement, (new_content,))32 connection.commit()33 for row in cursor:34 id, content = row35

36 updated_event = current_app.eventlist.get_event(id)37 updated_event.update_event(new_content, new_place, new_date)38 return redirect(url_for('events.events_page'))

When the user goes to the /events/update page, they see a form that prompts them to enter an event’s name toupdate it. The form also contains the updated name, the updated date and the updated place input fields. If theHTTP request sent by the user is of type GET, “update_event.html” page is rendered which contains the said form.This function takes the parameters events and places lists, the former will be used to display the current list ofevents on the top of the page and the latter to display the place names as options in a dropdown list in the updateform. If the HTTP request sent by the user is of type POST, then the user filled in the form on the page and wantsto update an existing event. new_content, new_date and new_place variables which are used to keep the updatedattributes of the event, and the content variable which is used to keep the name of the event to be updated areinitialized with the values from the form. The new_place variable is a text value, however its ID in the PLACEStable is needed to update an event on the EVENTS table, as the event entity takes area ID as its foreign key.Therefore, a database query is needed. In this query, the place with the given AREA attribute (which corresponds

2.2. Code 23

Page 28: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

to city name) is searched for its ID. This ID is kept in area_id variable. Then, the event is updated in the EVENTStable in the database where the content of the previously added event matches the content variable initialized fromthe form value. That event’s content, date and area attributes are set to their new values. The ID of the currentlyupdated event is needed to pass as an argument to the get_event() function, in order to update the event object. Adatabase query is done in order to get to ID from the EVENTS table with a set content attribute. Then, the eventobject with the said id selected as updated_event from the event list. update_event() method is invoked on thisobject with new_content, new_place and new_date arguments so that the object is updated with these values. Theuser is redirected to the events page, where they can see that the event they updated is changed as wanted in thelist of events displayed on that page.

Places

Places Table Initialization

The places table is initialized in the blueprint placesB.py as follows

1 @places.route('/initplaces')2 def init_places_db():3 with dbapi2.connect(app.config['dsn']) as connection:4 cursor = connection.cursor()5

6 query = """DROP TABLE IF EXISTS PLACES CASCADE"""7 cursor.execute(query)8

9 query = """CREATE TABLE PLACES (10 AREA_ID SERIAL,11 AREA VARCHAR(300),12 PRIMARY KEY(AREA_ID)13 )"""14 cursor.execute(query)15 connection.commit()16 return redirect(url_for('site.home_page'))

Places entity has two attributes.

• AREA_ID: Area ID is a serial value that is incremented as new places are added. It is the primary keyfor this entity.

Area ID is referenced as a foreign key in the EVENTS table as well as the ANNOUNCEMENTS table. -AREA: Area is of type VARCHAR with a limit of 300 characters. It is designed to hold the name of theplace.

Place Class Definition

The place class is defined in place.py as follows

1 class Place:2 def __init__(self, area):3 self.area = area4

5 def update_place(self, new_area):6 self.area = new_area

Place class has the area attribute. When a place needs to be updated, update_place() function takes care of thisoperation and takes new_area, which represents the newly updated area name value for the place object. Thisparameter is then used to set the place object’s attributes according to the updated place’s attributes.

24 Chapter 2. Developer Guide

Page 29: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Place List Class Definition

The place list class is used to keep a list of places that are currently kept in the database. This list is beneficial tokeep in order to print out the place options for an event in a dropdown list. It is defined in placelist.py as follows:

1 class PlaceList:2 def __init__(self):3 self.places = {}4 self.last_place_id = 05

6 def add_place(self, place):7 self.last_place_id += 18 self.places[self.last_place_id] = place9 place._id = self.last_place_id

10

11 def delete_place(self, place_id):12 del self.places[place_id]13

14 def get_place(self, place_id):15 return self.places[place_id]16

17 def get_places(self):18 return self.places

Initially, this list is empty with the last_event_id being 0. As places are added, last_place_id is incremented, newlyadded place is added to the list and its ID is set. delete_place() function takes the place’s ID as parameter anddeletes the place with this ID from the list of events. get_place() function also takes the place’s ID as parameterand returns the place with the given ID from the list of places. Lastly, get_places() function returns the whole listof places.

Place Adding Operation

The place adding operation is defined in the blueprint placesB.py as follows

1 @places.route('/places', methods = ['GET', 'POST'])2 def places_page():3 if request.method == 'GET':4 places = current_app.placelist.get_places()5 return render_template('places.html', places=sorted(places.items()))6 else:7 area = str(request.form['area'])8 with dbapi2.connect(app.config['dsn']) as connection:9 cursor = connection.cursor()

10

11 statement ="""INSERT INTO PLACES (AREA) VALUES (%s)"""12 cursor.execute(statement, [area])13 connection.commit()14

15 place = Place(area)16

17 current_app.placelist.add_place(place)18 return redirect(url_for('places.places_page', place_id=place._id))

When the user goes to the places page, they can both see the list of current places and the form that prompts theuser to user to enter a new place. If the HTTP request sent by the user is of type GET, “places.html” page isrendered with the additional parameter places, which contains the list of places in the database. This is passed asa parameter to the render function so that the list of places can be displayed on the page. If the HTTP request sentby the user is of type POST, then the user filled in the form on the places page and wants to add a new place. Areavariable is initialized with its value from the form. Then, a new place is inserted into the PLACES table in thedatabase with the this area value. Then, a new place object is created with the area value and this object is added

2.2. Code 25

Page 30: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

to the place list. The user is redirected to the places page, where they can see the newly added place on the bottomof the list.

Place Deleting Operation

The place deleting operation is defined in the blueprint placesB.py as follows

1 @places.route('/places/delete', methods=['GET', 'POST'])2 def delete_place():3 if request.method == 'GET':4 return render_template('delete_place.html')5 else:6 area = str(request.form['area'])7 with dbapi2.connect(app.config['dsn']) as connection:8 cursor = connection.cursor()9 statement ="""SELECT AREA_ID, AREA FROM PLACES WHERE (AREA = (%s))"""

10 cursor.execute(statement, (area,))11 connection.commit()12 for row in cursor:13 id, area = row14 statement ="""DELETE FROM PLACES WHERE (AREA_ID = (%s))"""15 cursor.execute(statement, (id,))16 connection.commit()17 current_app.placelist.delete_place(id)18 return redirect(url_for('places.places_page'), place_id=place._id)

When the user goes to the /places/delete page, they see a form that prompts them to enter a place to delete it. Ifthe HTTP request sent by the user is of type GET, “delete_place.html” page is rendered which contains the saidform. If the HTTP request sent by the user is of type POST, then the user filled in the form on the page and wantsto delete an existing place. area is initialized with the value from the form. The place’s ID in the PLACES tableis needed. Therefore, a database query is conducted. In this query, the event with the given AREA attribute issearched for its AREA_ID. This ID is kept in id variable. Then, the place is deleted from the PLACES table in thedatabase with the id value obtained from the result of the previous database query. Then, the place object with thesaid id is deleted from the place list. The user is redirected to the places page, where they can see that the placethey deleted disappeared from the list of places displayed on that page.

Place Updating Operation

The place updating operation is defined in the blueprint placesB.py as follows

1 @places.route('/places/update', methods=['GET', 'POST'])2 def update_place():3 if request.method == 'GET':4 return render_template('update_place.html')5 else:6 area= str(request.form['area'])7 new_area = str(request.form['new_area'])8 with dbapi2.connect(app.config['dsn']) as connection:9 cursor = connection.cursor()

10 statement ="""UPDATE PLACES11 SET AREA = (%s)12 WHERE AREA = (%s)"""13 cursor.execute(statement, (new_area, area,))14 connection.commit()15

16 cursor = connection.cursor()17 statement = """SELECT AREA_ID, AREA FROM PLACES WHERE (AREA = (%s))"""18 cursor.execute(statement, (new_area,))19 connection.commit()20 for row in cursor:

26 Chapter 2. Developer Guide

Page 31: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

21 area_id, area = row22

23 updated_place = current_app.placelist.get_place(area_id)24 updated_place.update_place(new_area)25 return redirect(url_for('places.places_page'), place_id=place._id)

When the user goes to the /places/update page, they see a form that prompts them to enter an place’s name toupdate it. The form also contains the updated place input fields. If the HTTP request sent by the user is of typeGET, “update_place.html” page is rendered which contains the said form. If the HTTP request sent by the user isof type POST, then the user filled in the form on the page and wants to update an existing place. new_area variablewhich is used to keep the updated attributes of the area, and the area variable which is used to keep the old nameof the area to be updated are initialized with the values from the form. Then, the event is updated in the PLACEStable in the database where the content of the previously added place matches the area variable initialized from theform value. That place’s AREA attribute is set to its new value, new_area. The ID of the currently updated placeis needed to pass as an argument to the get_place() function, in order to update the place object. A database queryis done in order to get the AREA_ID from the PLACES table with a set area attribute. Then, the event object withthe said area ID is selected as updated_place from the event list. update_place() method is invoked on this objectwith new_area argument so that the object is updated with these values. The user is redirected to the places page,where they can see that the place they updated is changed as wanted in the list of places displayed on that page.

Text Posts

Text Posts Table Initialization

The text posts table is initialized in the server.py as follows

1 @app.route('/inittextposts')2 def init_posts():3 with dbapi2.connect(app.config['dsn']) as connection:4 cursor = connection.cursor()5

6 query = """DROP TABLE IF EXISTS TEXTPOSTS CASCADE"""7 cursor.execute(query)8

9 query = """CREATE TABLE TEXTPOSTS(10 POSTID SERIAL,11 CONTENT VARCHAR(300),12 WRITER INTEGER,13 POSTTOPIC VARCHAR(40),14 PRIMARY KEY(POSTID),15 FOREIGN KEY (WRITER) REFERENCES MODERATORS (ID)16 ON DELETE CASCADE17 ON UPDATE CASCADE18 )"""19 cursor.execute(query)20

21 connection.commit()22 return redirect(url_for('site.home_page'))

Text post entity has four attributes.

• POSTID: Post ID is a serial value that is incremented as new text posts are added. It is the primarykey for this entity.

• CONTENT: Content is of type VARCHAR with a limit of 300 characters. It is designed to hold thetext content of the entry.

• WRITER: Writer is of type INTEGER. It is designed to hold the ID of the writer of the entry. Writerattribute reference the MODERATORS table as its foreign key.

• POSTTOPIC: Post topic is of type VARCHAR with a limit of 40 characters. It is designed to holdthe topic of the entry. This feature is not fully implemented therefore it does not reference the TOPICS

2.2. Code 27

Page 32: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

table, this attribute is NULL for every tuple in the TEXTPOSTS table.

Text Post Class Definition

The text post class is defined in textpost.py as follows

1 class TextPost:2 def __init__(self, content, writer):3 self.content = content4 self.writer = writer5 def change_textpost(self, new_content):6 self.content = new_content

Text post class has the content and writer attributes. When a text post needs to be updated, change_textpost()function takes care of this operation and takes new_content, which represents the newly updated content valuefor the text post object. This parameter is then used to set the text post object’s content attribute according to theupdated text post’s attribute. Writer does not change with update operation.

Text Post List Class Definition

The text post list class is used to keep a list of text posts that are currently kept in the database. This list is beneficialto keep in order to print out the text post list for the user to see. It is defined in textpostlist.py as follows:

1 class TextPostList:2 def __init__(self):3 self.last_key = None4

5 def add_TextPost(self, textPost):6 with dbapi2.connect(app.config['dsn']) as connection:7 cursor = connection.cursor()8 query = """INSERT INTO TEXTPOSTS (CONTENT, WRITER) VALUES (%s, %s)"""9 cursor.execute(query, (textPost.content, textPost.writer))

10 connection.commit()11

12 def delete_TextPost(self, TextPost_id):13 with dbapi2.connect(app.config['dsn']) as connection:14 cursor = connection.cursor()15 statement ="""DELETE FROM TEXTPOSTS WHERE (POSTID = (%s))"""16 cursor.execute(statement, (TextPost_id,))17 connection.commit()18

19 def update_TextPost(self, TextPost_id, new_content):20 with dbapi2.connect(app.config['dsn']) as connection:21 cursor = connection.cursor()22 statement ="""UPDATE TEXTPOSTS23 SET CONTENT = (%s)24 WHERE (POSTID = (%s))"""25 cursor.execute(statement, (new_content, TextPost_id,))26 connection.commit()27

28 def get_TextPost(self, content):29 with dbapi2.connect(app.config['dsn']) as connection:30 cursor = connection.cursor()31 query = """SELECT POSTID FROM TEXTPOSTS WHERE (CONTENT = (%s))"""32 cursor.execute(query, (content, ))33 TextPost_id = cursor.fetchone()34 connection.commit()35 return TextPost_id36

37 def get_TextPostList(self):38 with dbapi2.connect(app.config['dsn']) as connection:

28 Chapter 2. Developer Guide

Page 33: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

39 cursor = connection.cursor()40 query = """SELECT POSTID, CONTENT, WRITER, NICKNAME FROM TEXTPOSTS JOIN41 MODERATORS ON WRITER=ID42 ORDER BY POSTID"""43 cursor.execute(query)44 TextPostTable = [(id, TextPost(content, writer), modname)45 for id, content, writer, modname in cursor]46 return TextPostTable47

48 def get_TextPostListofMod(self, writer):49 with dbapi2.connect(app.config['dsn']) as connection:50 cursor = connection.cursor()51 query = """SELECT POSTID, CONTENT, WRITER, NICKNAME FROM TEXTPOSTS JOIN52 MODERATORS ON WRITER=ID53 WHERE WRITER = (%s)54 ORDER BY POSTID"""55 cursor.execute(query, (writer,))56 TextPostTableofMod = [(id, TextPost(content, writer), modname)57 for id, content, writer, modname in cursor]58 return TextPostTableofMod

Initially, this list is empty with the last_key being None. As text posts are added, text post objects are created inthe textpostsB.py blueprint, where they are also passed to the add_TextPost() function as a parameter. Here theyare inserted to the database. delete_TextPost() function takes the post’s ID as parameter and deletes the place withthis ID from the database. update_TextPost() function takes the new content of the post in addition to the post’sID as parameters and sets the content of the text post to the new content in the database. get_TextPost() functionalso takes the post’s content as parameter and returns the post with the given content from the TEXTPOSTS tablein the database. get_TextPostList() function returns the posts from the TEXTPOSTS table in the database. Thewriter’s ID is referenced as a foreign key from the MODERATORS table and kept in the TEXTPOSTS tabletherefore in order to get and display the writer’s name, a JOIN operation is done with the MDOERATORS table.Lastly, get_TextPostListofMod() function works in a similar fashion to get_TextPostList() but it returns the textposts from the currently logged in writer only.

Text Post Adding Operation

In addition to the add operation described in Text Post List Class Definition section, adding operation is done inthe blueprint textpostsB.py as follows

1 @TextPosts.route('/textposts/add_text_posts', methods=['GET', 'POST'])2 @login_required3 def textpost_add_page():4 if request.method == 'GET':5 return render_template('textpost_add.html')6 else:7 content = str(request.form['content'])8 writer = app.moderatorlist.get_moderator(current_user.nickname)9 textPost = TextPost(content, writer)

10 current_app.textpostlist.add_TextPost(textPost)11 return redirect(url_for('TextPosts.textposts_page'))

When the user goes to the /textposts/add_text_posts page, they see the form that prompts the user to user to entera new post. If the HTTP request sent by the user is of type POST, then the user filled in the form on the text postspage and wants to add a new post. Content variable is initialized with its value from the form. Writer is initializedby making use of the current_user proxy, to get the ID of this user get_moderator() function is used. Then, anew TextPost object is created with the content and writer variables and this object is sent as a parameter to theadd_TextPost() function described in the previous section. The user is redirected to the text posts page, where theycan see the newly added post on the bottom of the list.

2.2. Code 29

Page 34: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Text Post Deleting Operation

In addition to the delete operation described in Text Post List Class Definition section, the text post deletingoperation is defined in the blueprint textpostsB.py as follows

1 @TextPosts.route('/textposts/textpost_remove', methods=['GET', 'POST'])2 @login_required3 def textpost_remove_page():4 if request.method == 'GET':5 return render_template('textpost_remove.html')6 else:7 content = str(request.form['content'])8 postid = current_app.textpostlist.get_TextPost(content)9 current_app.textpostlist.delete_TextPost(postid)

10 return redirect(url_for('TextPosts.textposts_page'))

When the user goes to the /textposts/textpost_remove page, they see a form that prompts them to enter a post’scontent to delete it. If the HTTP request sent by the user is of type GET, “textpost_remove.html” page is renderedwhich contains the said form. If the HTTP request sent by the user is of type POST, then the user filled in the formon the page and wants to delete an existing post. Content variable is initialized with the value from the form. Thepost’s ID in the TEXTPOSTS table is needed in order to be passed to the delete_TextPost() function. In order toget the post ID, the content is passed to the get_TextPost() function which returns the ID of the post as describedabove. This ID is kept in postid variable. Then, the post with the given postid is deleted from the TEXTPOSTStable in the database when it is passed to the delete_TextPost() function. The user is redirected to the text postspage, where they can see that the post they deleted disappeared from the list of posts displayed on that page.

Text Post Updating Operation

In addition to the update operation described in Text Post List Class Definition section, the text post updatingoperation is defined in the blueprint textpostsB.py as follows

1 @TextPosts.route('/textposts/textpost_update', methods=['GET', 'POST'])2 @login_required3 def textpost_update_page():4 if request.method == 'GET':5 return render_template('textpost_update.html')6 else:7 content = str(request.form['content'])8 new_content = str(request.form['new_content'])9 postid = current_app.textpostlist.get_TextPost(content)

10 current_app.textpostlist.update_TextPost(postid, new_content)11 return redirect(url_for('TextPosts.textposts_page'))

When the user goes to the /textposts/textpost_update page, they see a form that prompts them to enter a text post’scontent to update it. The form also contains the updated text post input field. If the HTTP request sent by the useris of type GET, “textpost_update.html” page is rendered which contains the said form. If the HTTP request sentby the user is of type POST, then the user filled in the form on the page and wants to update an existing text post.new_content variable which is used to keep the updated value of the post content, and the content variable whichis used to keep the old content of the post to be updated are initialized with the values from the form. The post’sID in the TEXTPOSTS table is needed in order to be passed to the update_TextPost() function. In order to get thepost ID, the content is passed to the get_TextPost() function which returns the ID of the post as described above.This ID is kept in postid variable. Then, the post with the given postid is updated in the TEXTPOSTS table in thedatabase when it is passed to the update_TextPost() function along with the parameters postid and new_content asdescribed in the previous sections. The user is redirected to the text posts page, where they can see that the postthey updated is changed as wanted in the list of posts displayed on that page.

30 Chapter 2. Developer Guide

Page 35: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Displaying Text Posts

In order to display the text posts written by the current user on the text posts page, the following logic is used inthe Blueprint textpostsB.py

@TextPosts.route('/textposts')@login_requireddef textposts_page():

writer = app.moderatorlist.get_moderator(current_user.nickname)textposts = app.textpostlist.get_TextPostListofMod(writer)return render_template('textposts.html', textposts=textposts)

The writer, which is the currently logged in user is initialized by making use of the current_user proxy, to get theID of this user get_moderator() function is used. Then, textposts variable is populated with the return value ofget_TextPostListofMod() function with the writer variable as a parameter. This variable now contains the list ofposts written by the currently logged in user. Then, textposts.html file is rendered with the additional textpostsvariable, which allows for displaying the text posts of the currently logged in user on the page.

2.2.2 Parts Implemented by Mumtaz Cem Eris

Tables

There are two tables that are implemented by me. Tables can be seen from the ‘/initmods/’ route which initializes‘MODERATORS’ and ‘IMGPOSTS’. IMGPOSTS’s MODID references MODERATORS’s ID.

@mods.route('/initmods')def init_mod_db():

with dbapi2.connect(app.config['dsn']) as connection:cursor = connection.cursor()query = """DROP TABLE IF EXISTS IMGPOSTS"""cursor.execute(query)query = """DROP TABLE IF EXISTS MODERATORS"""cursor.execute(query)

query = """CREATE TABLE MODERATORS (ID SERIAL,NICKNAME VARCHAR(20) UNIQUE NOT NULL,PASSWORD VARCHAR(300) NOT NULL,IS_ADMIN VARCHAR(6),PRIMARY KEY(ID))"""cursor.execute(query)

query = """CREATE TABLE IMGPOSTS (IMGID SERIAL,IMGNAME VARCHAR(20),IMGTYPE VARCHAR(10),PRIMARY KEY(IMGID),MODID INTEGER,FOREIGN KEY (MODID) REFERENCES MODERATORS (ID)

ON DELETE RESTRICTON UPDATE CASCADE

)"""cursor.execute(query)

connection.commit()#flash('Moderators db is initialized.')return redirect(url_for('site.signup_page'))

Logins

Starting from server.py, blueprints below and login manager are designed by me. It would be much clearer as you

2.2. Code 31

Page 36: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

go further down. Important note, users was going to be handled by Basar but since he left the project, users partis also implemented by me. Moderators and users are merged after this situation which means that every user isactually a moderator at the same time, users are all moderators and they will be called as moderators.

login_manager = LoginManager()

@login_manager.user_loaderdef load_user(user_id):

mod = app.moderatorlist.get_moderatorObj(user_id)return mod

def create_app():app = Flask(__name__)app.register_blueprint(site)app.register_blueprint(mods)app.register_blueprint(imgPosts)...app.moderatorlist = ModeratorList()app.imgpostlist = ImgPostList()...app.secret_key = "secret key"login_manager.init_app(app)login_manager.login_view = 'site.signup_page'return app

Continuing with the blueprint ‘site’, it has 4 significant routes which are ‘/home’ home page, ‘/’ the signup page,‘/login’ login page, and finally ‘/logout’ page. ‘/home’ is the page that ShakeSpace welcomes the moderator whosigned up before, and logged in. The other routes will be explained one by one below.

@site.route('/home')def home_page():

message = 'You have successfully logged in'return render_template('home.html', message=message)

#---- Login ----

@site.route('/', methods=['GET', 'POST'])def signup_page():

if request.method == 'GET':return render_template('signup.html')

else:nickname = str(request.form['nickname'])password = str(request.form['password'])hashed = pwd_context.encrypt(password)moderator = Moderator(nickname, hashed)app.moderatorlist.add_moderator(moderator)modid = app.moderatorlist.get_moderator(nickname)

login_user(moderator)return redirect(url_for('site.home_page'))

@site.route('/login', methods=['GET', 'POST'])def login_page():

if request.method == 'GET':return render_template('login.html')

else:nickname = str(request.form['nickname'])mod = app.moderatorlist.get_moderatorObj(nickname)if mod is not None:

password = str(request.form['password'])if pwd_context.verify(password, mod.password):

login_user(mod)message = 'You have logged in.'

32 Chapter 2. Developer Guide

Page 37: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

next_page = request.args.get('next', url_for('site.home_page'))return redirect(next_page)

else:message = 'Invalid credentials.'return render_template('login.html', message=message)

else:message = 'Invalid credentials.'return render_template('login.html', message=message)

@site.route('/logout')def logout_page():

logout_user()message = 'You have logged out.'return render_template('logout.html', message=message)

#---- Login end ----

Route ‘/’ is the signup page. That is the first page the visitor would see. When a visitor who has not loggedin, will be redirected to this page. In this route, nickname and password information is received from the visi-tor, the password will be hashed immediately and Moderator object will be created. This object will be sent toadd_moderator() function of moderatorlist, and with the given information the new moderator will be added to thedatabase by add_moderator(). After that, this moderator will be logged in, and the website redirects this moderatorto the home page.

@site.route('/', methods=['GET', 'POST'])def signup_page():

if request.method == 'GET':return render_template('signup.html')

else:nickname = str(request.form['nickname'])password = str(request.form['password'])hashed = pwd_context.encrypt(password)moderator = Moderator(nickname, hashed)app.moderatorlist.add_moderator(moderator)

login_user(moderator)return redirect(url_for('site.home_page'))

Let’s have a quick look at add_moderator. It gets the moderator object and inserts it into moderators table.

def add_moderator(self, moderator):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """INSERT INTO MODERATORS (NICKNAME, PASSWORD) VALUES (%s, %s)"""cursor.execute(query, (moderator.nickname, moderator.password))connection.commit()

Route ‘/login’ is for the visitor who has an account already. login_page() would first get the nickname, and usingget_moderatorObj(), the moderator object ‘mod’ with given nickname would be returned. If there is no such‘mod’ with given nickname, then ‘mod’ would be ‘None’. If it is ‘None’ then error message ‘Invalid credentials.’will be returned to the user. If not, then the password would be checked. If it matches with the hashed passwordin the database, then that moderator would be logged in. If the password is incorrect, same error message will besent to the visitor.

@site.route('/login', methods=['GET', 'POST'])def login_page():

if request.method == 'GET':return render_template('login.html')

else:nickname = str(request.form['nickname'])mod = app.moderatorlist.get_moderatorObj(nickname)if mod is not None:

password = str(request.form['password'])

2.2. Code 33

Page 38: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

if pwd_context.verify(password, mod.password):login_user(mod)message = 'You have logged in.'next_page = request.args.get('next', url_for('site.home_page'))return redirect(next_page)

else:message = 'Invalid credentials.'return render_template('login.html', message=message)

else:message = 'Invalid credentials.'return render_template('login.html', message=message)

Route ‘/logout’ is pretty straightforward, the current moderator would be logged out and redirected to the logoutpage with the message below.

@site.route('/logout')def logout_page():

logout_user()message = 'You have logged out.'return render_template('logout.html', message=message)

Admin

There is only one admin in ShakeSpace, that is the moderator with the nickname of ‘admin’. Admin can basicallyaccess to the ‘moderators_page’ ,namely the admin panel, in which the admin can add, remove or update modera-tors. It is a bit mentioned in ‘User Manual’ of my part with visual materials. Here, the three methods add, delete,and update will be discussed.

@mods.route('/moderators/add', methods=['GET', 'POST'])@login_requireddef mod_add_page():

if not current_user.nickname == 'admin':abort(401)

if request.method == 'GET':return render_template('modedit.html')

else:nickname = str(request.form['nickname'])password = str(request.form['password'])hashed = pwd_context.encrypt(password)moderator = Moderator(nickname, hashed)current_app.moderatorlist.add_moderator(moderator)modid = current_app.moderatorlist.get_moderator(moderator.nickname)message = 'Moderator is added.'return redirect(url_for('mods.moderators_page'))

@mods.route('/profile/remove', methods=['GET', 'POST'])@login_requireddef mod_remove_page():

if not current_user.nickname == 'admin':abort(401)

if request.method == 'GET':return render_template('modremove.html')

else:nickname = str(request.form['nickname'])mod_id = current_app.moderatorlist.get_moderator(nickname)current_app.moderatorlist.delete_moderator(mod_id)message = 'You have deleted your account.'return redirect(url_for('mods.moderators_page'))

@mods.route('/moderators/update', methods=['GET', 'POST'])@login_requireddef mod_update_page():

if not current_user.nickname == 'admin':

34 Chapter 2. Developer Guide

Page 39: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

abort(401)if request.method == 'GET':

return render_template('modupdate.html')else:

nickname = str(request.form['nickname'])newnickname = str(request.form['newnickname'])mod_id = current_app.moderatorlist.get_moderator(nickname)current_app.moderatorlist.update_moderator(mod_id, newnickname)#message = 'You have changed your name.'return redirect(url_for('mods.moderators_page'))

Since there is only one admin and its nickname is ‘admin’, there is only one if condition to authorize the currentuser as admin, as seen below. There is possibility of having ‘abort(401)’, although no moderators can see thispage in the navigation bar. Admin can add a moderator just like the moderator is added to the database in signuppage with hashing.

@mods.route('/moderators/add', methods=['GET', 'POST'])@login_requireddef mod_add_page():

if not current_user.nickname == 'admin':abort(401)

if request.method == 'GET':return render_template('modedit.html')

else:nickname = str(request.form['nickname'])password = str(request.form['password'])hashed = pwd_context.encrypt(password)moderator = Moderator(nickname, hashed)current_app.moderatorlist.add_moderator(moderator)modid = current_app.moderatorlist.get_moderator(moderator.nickname)message = 'Moderator is added.'return redirect(url_for('mods.moderators_page'))

Admin can remove a moderator with typing its nickname. This function below would call get_moderator() whichreturns moderator id of the given nickname of the moderator. Then it calls delete_moderator() with given id, andthis function would find the corresponding moderator and deletes it from the database. You can check ‘Moderators’header for more detail about moderator functions.

@mods.route('/profile/remove', methods=['GET', 'POST'])@login_requireddef mod_remove_page():

if not current_user.nickname == 'admin':abort(401)

if request.method == 'GET':return render_template('modremove.html')

else:nickname = str(request.form['nickname'])mod_id = current_app.moderatorlist.get_moderator(nickname)current_app.moderatorlist.delete_moderator(mod_id)return redirect(url_for('mods.moderators_page'))

Finally, admin can update moderators’ nicknames. The function gets the current and the new nickname. Afterhaving returned the moderator id, it sends id and the new nickname to update_moderator() and this method updatesthe moderator in the database.

@mods.route('/moderators/update', methods=['GET', 'POST'])@login_requireddef mod_update_page():

if not current_user.nickname == 'admin':abort(401)

if request.method == 'GET':return render_template('modupdate.html')

else:

2.2. Code 35

Page 40: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

nickname = str(request.form['nickname'])newnickname = str(request.form['newnickname'])mod_id = current_app.moderatorlist.get_moderator(nickname)current_app.moderatorlist.update_moderator(mod_id, newnickname)return redirect(url_for('mods.moderators_page'))

Moderators

It is crucial to know moderators since all users are treated as moderators. First, Moderator class will be examined.It uses features of ‘UserMixin’, and plus it has ‘nickname’ and ‘password’ variables.

class Moderator(UserMixin):def __init__(self, nickname, password):

self.nickname = nicknameself.password = passwordself.active = Trueself.is_admin = False

def get_id(self):return self.nickname

@propertydef is_active(self):

return self.active

Then ModeratorList class comes. This class has all the methods which are discussed in Logins part such as adding,deleting and updating. First, add_moderator(self, moderator) will be examined. It gets a moderator object andinserts the nickname and the password (hashed) to the table.

def add_moderator(self, moderator):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """INSERT INTO MODERATORS (NICKNAME, PASSWORD) VALUES (%s, %s)"""cursor.execute(query, (moderator.nickname, moderator.password))connection.commit()

delete_moderator(self, mod_id) gets id of the moderator, and first deletes the imageposts that have been posted bythis moderator. Below, ‘MODID’ references ‘ID’ of the ‘MODERATORS’. It is mentioned in the user manual aswell. Then it deletes the moderator using its id.

def delete_moderator(self, mod_id):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""DELETE FROM IMGPOSTS WHERE (MODID = (%s))"""cursor.execute(statement, (mod_id,))connection.commit()statement ="""DELETE FROM MODERATORS WHERE (ID = (%s))"""cursor.execute(statement, (mod_id,))connection.commit()cursor.close()

In update method, id of the moderator and the updated nickname is received and the moderator is updated relatedto the information.

def update_moderator(self, mod_id, newName):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""UPDATE MODERATORSSET NICKNAME = (%s)WHERE (ID = (%s))"""cursor.execute(statement, (newName, mod_id))connection.commit()cursor.close()

36 Chapter 2. Developer Guide

Page 41: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

Below, four get methods can be seen. get_moderator() gets nickname of the moderator and it returns all the image-posts that the corresponding moderator has been posted. get_moderatorName(self, modid) gets id of the moderatordoes the same thing as get_moderator(). get_moderators(self) returns all the moderators. get_moderatorObj(self,mod_name) returns the moderator object with the given moderator nickname ‘mode_name’.

def get_moderator(self, modName):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """SELECT ID FROM MODERATORS WHERE (NICKNAME = (%s))"""cursor.execute(query, (modName, ))mod_id = cursor.fetchone()#imgid, imgname, modidconnection.commit()cursor.close()

return mod_id

def get_moderatorName(self, modid):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """SELECT NICKNAME FROM MODERATORS WHERE (ID = (%s))"""cursor.execute(query, (modid, ))modName = cursor.fetchone()#imgid, imgname, modidconnection.commit()cursor.close()

return modName

def get_moderators(self):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """SELECT ID, NICKNAME, PASSWORD FROM MODERATORSORDER BY ID"""cursor.execute(query)modTable = [(id, Moderator(nickname, password))

for id, nickname, password in cursor]connection.commit()cursor.close()

return modTable

def get_moderatorObj(self, mod_name): # def get_userwith dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """SELECT PASSWORD FROM MODERATORS WHERE (NICKNAME = (%s))"""cursor.execute(query, (mod_name,))mod_pass = cursor.fetchone()connection.commit()if mod_pass is None:

mod = Noneelse:

mod = Moderator(mod_name, mod_pass[0]) #[0]cursor.close()return mod

There is also profile page of the moderator. It uses get_moderators() method to get all the moderators. In profilepage, the current moderator can see other moderators as well. See user manual for screenshots.

@mods.route('/profile', methods=['GET', 'POST'])@login_requireddef profile_page():

moderators = current_app.moderatorlist.get_moderators()return render_template('profile.html', moderators=moderators)

‘/moderators’ route is admin panel. This page is explained in ‘Admin’ part. moderators_page() method usesget_moderators() to post all the moderators.

2.2. Code 37

Page 42: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

@mods.route('/moderators')@login_requireddef moderators_page():

moderators = current_app.moderatorlist.get_moderators()return render_template('moderators.html', moderators=moderators)

Image Posts

Class of the image post is below. It includes name of the image, and foreign key id of the moderator.

class ImgPost:def __init__(self, imgname, modid):

self.imgname = imgnameself.modid = modid

def change_nickname(self, newimgname):self.imgname = newimgname

ImgPostList class is below. add_imgPost(self, imgPost) inserts the given image post object to the database.delete_imgPost(self, imgPost_id) deletes the image post from the database using its id. update_imgPost(self,imgPost_id, newName) works similarly as update_moderator(self, mod_id, newName). get_imgPost(self, img-Name) returns the id, name and id of the moderator of the given image post. get_imgPostList(self) returns all theimage posts along with the moderators who posted them.

class ImgPostList:def __init__(self):

self.last_key = None

def add_imgPost(self, imgPost):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """INSERT INTO IMGPOSTS (IMGNAME, MODID) VALUES (%s, %s)"""cursor.execute(query, (imgPost.imgname, imgPost.modid))connection.commit()

def delete_imgPost(self, imgPost_id):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()#toBeDeleted = ImgPostList.get_imgPost(imgPost_id)#for row in cursor:# id, nickname = rowstatement ="""DELETE FROM IMGPOSTS WHERE (IMGID = (%s))"""cursor.execute(statement, (imgPost_id,))connection.commit()

def update_imgPost(self, imgPost_id, newName):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""UPDATE IMGPOSTSSET IMGNAME = (%s)WHERE (IMGID = (%s))"""cursor.execute(statement, (newName, imgPost_id))connection.commit()

def get_imgPost(self, imgName):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """SELECT IMGID FROM IMGPOSTS WHERE (IMGNAME = (%s))"""cursor.execute(query, (imgName, ))imgid = cursor.fetchone()#imgid, imgname, modidconnection.commit()

return imgid

38 Chapter 2. Developer Guide

Page 43: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

def get_imgPostList(self):#modid = app.get_moderator(current_user.nickname)with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()query = """SELECT IMGID, IMGNAME, MODID, NICKNAME FROM IMGPOSTS JOINMODERATORS ON MODID=IDORDER BY IMGID"""cursor.execute(query)imgPostTable = [(id, ImgPost(imgname, modid), modname)

for id, imgname, modid, modname in cursor]return imgPostTable

Routes for the image posts are given below. Their approach is pretty similar to moderators routes’ approach.imgposts_page() provides image posts feed using get_imgPostList(). imgpost_add_page() adds the image postwith given name and using current_user it validates the information of the moderator who posted it and cre-ates image post object according to that. imgpost_remove_page() deletes the image post with given name usingget_imgPost(imgname) and delete_imgPost(imgid). Finally, imgpost_update_page() updates corresponding im-age post using get_imgPost(imgname) and update_imgPost(imgid, newimgname).

@imgPosts.route('/imageposts')@login_requireddef imgposts_page():

imgposts = app.imgpostlist.get_imgPostList()return render_template('imgposts.html', imgposts=imgposts)

@imgPosts.route('/imageposts/add_image_posts', methods=['GET', 'POST'])@login_requireddef imgpost_add_page():

if request.method == 'GET':return render_template('imgpost_add.html')

else:imgname = str(request.form['imgname'])modid = app.moderatorlist.get_moderator(current_user.nickname)imgPost = ImgPost(imgname, modid)current_app.imgpostlist.add_imgPost(imgPost)return redirect(url_for('imgPosts.imgposts_page'))

@imgPosts.route('/imageposts/imgpost_remove', methods=['GET', 'POST'])@login_requireddef imgpost_remove_page():

if request.method == 'GET':return render_template('imgpost_remove.html')

else:imgname = str(request.form['imgname'])imgid = current_app.imgpostlist.get_imgPost(imgname)current_app.imgpostlist.delete_imgPost(imgid)return redirect(url_for('imgPosts.imgposts_page'))

@imgPosts.route('/imageposts/imgpost_update', methods=['GET', 'POST'])@login_requireddef imgpost_update_page():

if request.method == 'GET':return render_template('imgpost_update.html')

else:imgname = str(request.form['imgname'])newimgname = str(request.form['newimgname'])imgid = current_app.imgpostlist.get_imgPost(imgname) # to be updatedcurrent_app.imgpostlist.update_imgPost(imgid, newimgname)return redirect(url_for('imgPosts.imgposts_page'))

2.2. Code 39

Page 44: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

2.2.3 Parts Implemented by Emre Can Agatay

Tables

There are two tables that are implemented by me. Tables can be seen from the ‘/init_hashtags/’ route which initial-izes ‘HASHTAGS’ and ‘HASHTAGCONTENTS’. HASHTAGCONTENTS’s HASHTAGID references HASH-TAGS’s ID.

@hashtags.route(‘/init_hashtags’)

def init_hashtag_db():

with dbapi2.connect(app.config[’dsn’]) as connection: cursor = connection.cursor()

query = “”“DROP TABLE IF EXISTS HASHTAGCONTENTS”“” cur-sor.execute(query) query = “”“DROP TABLE IF EXISTS HASHTAGS CASCADE”“”cursor.execute(query)

query = “”“CREATE TABLE HASHTAGS ( ID SERIAL NOT NULL, NAME VAR-CHAR(50), PRIMARY KEY(ID) )”“” cursor.execute(query)

query = “”“CREATE TABLE HASHTAGCONTENTS ( ID SERIAL, HASHTAGIDINTEGER, CONTENT VARCHAR(300), PRIMARY KEY(HASHTAGID, ID), FOR-EIGN KEY(HASHTAGID) REFERENCES HASHTAGS(ID) ON DELETE CAS-CADE ON UPDATE CASCADE )”“” cursor.execute(query)

connection.commit() return redirect(url_for(‘site.home_page’))

Hashtags

Firstly, Hashtag class will be examined. It has only ‘name’ variable and ‘update_name’ method to change thename of the hashtag.

class Hashtag:def __init__(self, name):

self.name = name

def update_name(self, updatedHashtag):self.name = updatedHashtag

Hashtags class is below. This class have the methods of adding, deleting, updating and getting one or all hashtags.First, let’s see the add_hashtag(self, hashtag) method. It inserts the given hashtag object to the database.

def add_hashtag(self, hashtag):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""INSERT INTO HASHTAGS (NAME) VALUES (%s)"""cursor.execute(statement, (hashtag.name,))connection.commit()

delete_hashtag(self, hashtag_id) gets id of the hashtag as an argument and it deletes the hashtag from the databaseusing its id, after it deletes the contents that belongs to that hashtag.

def delete_hashtag(self, hashtag_id):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""DELETE FROM HASHTAGCONTENTS WHERE (HASHTAGID = (%s))"""cursor.execute(statement, (hashtag_id,))connection.commit()statement ="""DELETE FROM HASHTAGS WHERE (ID = (%s))"""cursor.execute(statement, (hashtag_id,))connection.commit()cursor.close()

40 Chapter 2. Developer Guide

Page 45: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

In update method, id of the hashtag and the new hashtag name is received and the hashtag is updated according tothat information.

def update_hashtag(self, hashtag_id, newHashtag):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""UPDATE HASHTAGS SET NAME = (%s) WHERE (ID = (%s))"""cursor.execute(statement, (newHashtag, hashtag_id))connection.commit()cursor.close()

Below, 4 get methods can be seen. get_hashtag(self, name) returns the id of the hashtag with the given name.get_hashtagName(self, hashtag_id) returns the name of the hashtag with the given id. get_hashtags(self) returnsall the hashtag’s id and their name ordered by id. get_hashtagObj(self, hashtag_name) returns the hashtag objectitself with the given name.

def get_hashtag(self, name):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement = """SELECT ID FROM HASHTAGS WHERE (NAME = (%s))"""cursor.execute(statement, (name,))hashtag_id = cursor.fetchone()connection.commit()cursor.close()return hashtag_id

def get_hashtagName(self, hashtag_id):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement = """SELECT NAME FROM HASHTAGS WHERE (ID = (%s))"""cursor.execute(statement, (hashtag_id, ))hashtagName = cursor.fetchone()connection.commit()cursor.close()return hashtagName

def get_hashtags(self):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement = """SELECT ID, NAME FROM HASHTAGS ORDER BY ID"""cursor.execute(statement)hashtagTable = [(id, Hashtag(name))

for id, name in cursor]connection.commit()cursor.close()return hashtagTable

def get_hashtagObj(self, hashtag_name):hashtag = Hashtag(hashtag_name)return hashtag

Routes for the hashtags are given below. hashtags_page() provides hashtags feed using get_hashtags(). hash-tag_page(hashtag_id) provides hashtagContents feed using get_contents(). hashtag_add_page() adds the hashtagwith given name and creates hashtag object. hashtag_remove_page() deletes the hashtag with given name usingget_hashtag(name) and delete_hashtag(hashtagid). Finally, hashtag_update_page() updates corresponding hash-tag using get_hashtag(imgname) and update_hashtag(hashtag_id, newhashtag).

@hashtags.route('/hashtags')def hashtags_page():

hashtags = current_app.hashtags.get_hashtags()return render_template('hashtags.html', hashtags = hashtags)

@hashtags.route('/hashtag/<int:hashtag_id>')

2.2. Code 41

Page 46: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

def hashtag_page(hashtag_id):hashtag_name = current_app.hashtags.get_hashtagName(hashtag_id)hashtag = current_app.hashtags.get_hashtagObj(hashtag_name)hashtagContents = current_app.hashtagContents.get_contents(hashtag_id)return render_template('hashtag.html', hashtag=hashtag, hashtagContents = hashtagContents, hashtagid = hashtag_id)

@hashtags.route('/hashtags/add', methods=['GET', 'POST'])def hashtag_add_page():

if request.method == 'GET':return render_template('hashtag_add.html')

else:name = str(request.form['name'])hashtag = Hashtag(name)current_app.hashtags.add_hashtag(hashtag)hashtag_id = current_app.hashtags.get_hashtag(hashtag.name)return redirect(url_for('hashtags.hashtags_page'))

@hashtags.route('/hashtags/update', methods=['GET', 'POST'])def hashtag_update_page():

if request.method == 'GET':return render_template('hashtag_update.html')

else:name = str(request.form['name'])newhashtag = str(request.form['newhashtag'])hashtag_id = current_app.hashtags.get_hashtag(name)current_app.hashtags.update_hashtag(hashtag_id, newhashtag)return redirect(url_for('hashtags.hashtags_page'))

@hashtags.route('/hashtags/remove', methods=['GET', 'POST'])def hashtag_delete_page():

if request.method == 'GET':return render_template('hashtag_delete.html')

else:name = str(request.form['name'])hashtag_id = current_app.hashtags.get_hashtag(name)current_app.hashtags.delete_hashtag(hashtag_id)return redirect(url_for('hashtags.hashtags_page'))

HashtagContents

After we examine the hashtag class, we can proceed with the HashtagContent class which represents the commentsabout the hashtags. This class has ‘content’ variable and forign key id of the hashtag.

HashtagContents class is below. This class has the similar methods with the Hashtags class such as add, delete,update and get content. add_content(self, hashtagcontent) inserts the given content object to the database.delete_content(self, hashtagid, contentid) deletes the content from the database using its id and hashtag id.update_content(self, hashtagid, content_id, newcontent) changes the content with the given id and hashtagid.get_contents(self, hashtagid) returns all contents of a hashtag with their content id and hashtag id.

def add_content(self, hashtagcontent):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""INSERT INTO HASHTAGCONTENTS (HASHTAGID, CONTENT) VALUES (%s, %s)"""cursor.execute(statement, (hashtagcontent.hashtagid, hashtagcontent.content))connection.commit()

def delete_content(self, hashtagid, contentid):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""DELETE FROM HASHTAGCONTENTS WHERE (ID = (%s) AND HASHTAGID = (%s))"""cursor.execute(statement, (contentid, hashtagid))connection.commit()

42 Chapter 2. Developer Guide

Page 47: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

def update_content(self, hashtagid, content_id, newcontent):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement ="""UPDATE HASHTAGCONTENTS SET CONTENT = (%s) WHERE (ID = (%s) AND HASHTAGID = (%s))"""cursor.execute(statement, (newcontent, content_id, hashtagid))connection.commit()

def get_contents(self, hashtagid):with dbapi2.connect(app.config['dsn']) as connection:

cursor = connection.cursor()statement = """SELECT ID, HASHTAGID, CONTENT FROM HASHTAGCONTENTS WHERE HASHTAGID = (%s) ORDER BY ID"""cursor.execute(statement, (hashtagid,))contents = [(id, HashtagContent(hashtagid, content))

for id, hashtagid, content in cursor]return contents

Routes for the hashtag contents are given below. Their approach is pretty similar to hashtags routes’ approach.hashtagContent_add_page(hashtag_id) creates the content with given name and hashtag id and adds it usingadd_content(hashtag_content). hashtagContent_delete_page(hashtag_id) deletes the hashtag content with givenhashtag id using delete_content(hashtag_id, contentid). Finally, hashtagContent_update_page(hashtag_id) up-dates corresponding hashtag content using update_content(hashtag_id, contentid, newContent).

@hashtagContents.route('/hashtag/addContent/<hashtag_id>', methods=['GET', 'POST'])def hashtagContent_add_page(hashtag_id):

if request.method == 'GET':return render_template('hashtagContent_add.html')

else:content = str(request.form['content'])hashtag_content = HashtagContent(content, hashtag_id)current_app.hashtagContents.add_content(hashtag_content)return redirect(url_for('hashtags.hashtag_page', hashtag_id=hashtag_id))

@hashtagContents.route('/hashtag/updateContent/<hashtag_id>', methods=['GET', 'POST'])def hashtagContent_update_page(hashtag_id):

if request.method == 'GET':return render_template('hashtagContent_update.html')

else:contentid = str(request.form['id'])newContent = str(request.form['newContent'])current_app.hashtagContents.update_content(hashtag_id, contentid, newContent)return redirect(url_for('hashtags.hashtag_page', hashtag_id=hashtag_id))

@hashtagContents.route('/hashtag/deleteContent/<hashtag_id>', methods=['GET', 'POST'])def hashtagContent_delete_page(hashtag_id):

if request.method == 'GET':return render_template('hashtagContent_delete.html')

else:contentid = str(request.form['id'])current_app.hashtagContents.delete_content(hashtag_id, contentid)return redirect(url_for('hashtags.hashtag_page', hashtag_id=hashtag_id))

2.2.4 Parts Implemented by Member Name

2.2.5 Parts Implemented by Member Name

2.2. Code 43

Page 48: itucsdb Documentation - Read the Docs

itucsdb Documentation, Release 1.0

44 Chapter 2. Developer Guide

Page 49: itucsdb Documentation - Read the Docs

CHAPTER 3

Installation Guide

Following programs and extensions must be downloaded in order to run this project.

Python

To download Python 3.5 go to https://www.python.org/downloads/.

Flask

Flask framework is used in this project. You can install Flask by typing:

pip install flask

Some packages of the Flask and Python are needed to be downloaded as well. These are psycopg2, flask-loginand passlib. You can use the command above to download these.

PostgreSQL

To download PostgreSQL go to https://www.postgresql.org/download/.

After having downloaded all above, you must setup local database. When the setup of local database is done,make sure you arrange ‘port’, ‘dbname’, ‘user’ and ‘password’ according to your choice.

if __name__ == '__main__':VCAP_APP_PORT = os.getenv('VCAP_APP_PORT')if VCAP_APP_PORT is not None:

port, debug = int(VCAP_APP_PORT), Falseelse:

port, debug = 5000, True

VCAP_SERVICES = os.getenv('VCAP_SERVICES')if VCAP_SERVICES is not None:

app.config['dsn'] = get_elephantsql_dsn(VCAP_SERVICES)else:

app.config['dsn'] = """user='vagrant' password='vagrant'host='localhost' port=5432 dbname='itucsdb'"""

app.run(host='0.0.0.0', port=port, debug=debug)

Running the project

Finally, you can run the project by following command in Python:

python server.py

45