diff --git a/backend/19/Folder2/nested.py b/backend/19/Folder2/nested.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ddfc8f8590cc02ffa25d601ed760a7429b5d482
--- /dev/null
+++ b/backend/19/Folder2/nested.py
@@ -0,0 +1 @@
+print('hello world!')
\ No newline at end of file
diff --git a/backend/19/app.py b/backend/19/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..f579e6f8fcda7ae8245520403ea9c07877544954
--- /dev/null
+++ b/backend/19/app.py
@@ -0,0 +1,14 @@
+""" Doc string """
+
+print('Hello world!')
+
+for i in range(0, 1):
+    print('Hello world!')
+print('Hello!')
+
+print('Hello!')
+
+for i in range(0, 1):
+    test_var = '10'
+
+TST = 2
diff --git a/backend/19/database_helper.py b/backend/19/database_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..023d63f9ecccb018140d50b607daaa2633193387
--- /dev/null
+++ b/backend/19/database_helper.py
@@ -0,0 +1,441 @@
+import os
+
+from __init__ import app
+
+import json
+from datetime import datetime
+from flask import Flask, request, jsonify
+from flask_sqlalchemy import SQLAlchemy
+from flask_marshmallow import Marshmallow, fields
+
+basedir = os.path.abspath(os.path.dirname(__file__))
+
+# Database
+app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'db.sqlite')
+app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
+
+db = SQLAlchemy(app)
+ma = Marshmallow(app)
+
+# EditorSettings
+class EditorSettings(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    fontSize = db.Column(db.Integer, nullable=False)
+    margin = db.Column(db.Integer, nullable=False)
+    tabSize = db.Column(db.Integer, nullable=False)
+
+    def __init__(self, font_size, margin, tab_size):
+        self.fontSize = font_size
+        self.margin = margin
+        self.tabSize = tab_size
+
+class EditorSettingsSchema(ma.Schema):
+    class Meta:
+        model = EditorSettings
+        fields = ('id', 'fontSize', 'margin', 'tabSize')
+
+editor_settings_schema = EditorSettingsSchema(strict=True)
+
+# User
+class User(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    email = db.Column(db.String(100), unique=True, nullable=False)
+    username = db.Column(db.String(50), unique=True, nullable=False)
+    password = db.Column(db.Text, nullable=False)
+    editorSettingsId = db.Column(db.Integer, db.ForeignKey(EditorSettings.id))
+    editorSettings = db.relationship("EditorSettings")
+
+    def __init__(self, email, username, password):
+        self.email = email
+        self.username = username
+        self.password = password
+
+class UserSchema(ma.Schema):
+    editorSettings = ma.Nested(EditorSettingsSchema)
+    class Meta:
+        model = User
+        fields = ('id', 'email', 'username', 'editorSettingsId', 'editorSettings')
+
+user_schema = UserSchema(strict=True)
+users_schema = UserSchema(many=True, strict=True)
+
+# Project
+class Project(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    title = db.Column(db.String(50), nullable=False)
+    creatorId = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
+    creator = db.relationship('User')
+    created = db.Column(db.String(50), default=datetime.now, nullable=False)
+    edited = db.Column(db.String(50), onupdate=datetime.now)
+    archived = db.Column(db.Boolean, default=False, nullable=False)
+
+    def __init__(self, title, creator_id, created=None, edited=None, archived=False):
+        self.title = title
+        self.creatorId = creator_id
+        self.created = created
+        self.edited = edited
+        self.archived = archived
+
+class ProjectSchema(ma.Schema):
+    creator = ma.Nested(UserSchema)
+    class Meta:
+        model = Project
+        fields = ('id', 'title', 'creatorId', 'creator', 'created', 'edited', 'archived')
+
+project_schema = ProjectSchema(strict=True)
+projects_schema = ProjectSchema(many=True, strict=True)
+
+# File
+class File(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    projectId = db.Column(db.Integer, db.ForeignKey(Project.id), nullable=False)
+    project = db.relationship('Project', lazy='subquery')
+    name = db.Column(db.String(100), nullable=False)
+    isFolder = db.Column(db.Boolean, nullable=False)
+    parent = db.Column(db.String(100))
+    content = db.Column(db.Text)
+
+    def __init__(self, project_id, name, is_folder, parent, content):
+        self.projectId = project_id
+        self.name = name
+        self.isFolder = is_folder
+        self.parent = parent
+        self.content = content
+
+class FileSchema(ma.Schema):
+    project = ma.Nested(ProjectSchema)
+    class Meta:
+        model = File
+        fields = ('id', 'projectId', 'project', 'name', 'isFolder', 'parent', 'content')
+
+file_schema = FileSchema(strict=True)
+files_schema = FileSchema(many=True, strict=True)
+
+# Collaborator
+class Collaborator(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    projectId = db.Column(db.Integer, db.ForeignKey(Project.id), nullable=False)
+    project = db.relationship('Project', lazy='subquery')
+    userId = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
+    user = db.relationship('User', lazy='subquery')
+    permission = db.Column(db.Text, nullable=False)
+
+    def __init__(self, project_id, user_id, permission):
+        self.projectId = project_id
+        self.userId = user_id
+        self.permission = permission
+
+class CollaboratorSchema(ma.Schema):
+    project = ma.Nested(ProjectSchema)
+    user = ma.Nested(UserSchema)
+    class Meta:
+        fields = ('id', 'projectId', 'project', 'userId', 'user', 'permission')
+
+collaborator_schema = CollaboratorSchema(strict=True)
+collaborators_schema = CollaboratorSchema(many=True, strict=True)
+
+# Message
+class Message(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    projectId = db.Column(db.Integer, db.ForeignKey(Project.id), nullable=False)
+    project = db.relationship('Project')
+    authorId = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
+    author = db.relationship('User')
+    message = db.Column(db.Text)
+    time = db.Column(db.String(50), default=datetime.now, nullable=False)
+
+    def __init__(self, project_id, author_id, message, time=None):
+        self.projectId = project_id
+        self.authorId = author_id
+        self.message = message
+        self.time = time
+
+class MessageSchema(ma.Schema):
+    project = ma.Nested(ProjectSchema)
+    author = ma.Nested(UserSchema)
+    class Meta:
+        fields = ('id', 'projectId', 'project', 'authorId', 'author', 'message', 'time')
+
+message_schema = MessageSchema(strict=True)
+messages_schema = MessageSchema(many=True, strict=True)
+
+### USER ###
+def add_user(new_user):
+    db.session.add(new_user)
+    db.session.commit()
+
+def get_users():
+    all_users = User.query.all()
+    result = users_schema.dump(all_users)
+    return result.data
+
+def get_user(id):
+    user = User.query.get(id)
+    return user
+
+def update_user(id, updated_user):
+    user = User.query.get(id)
+    user.email = updated_user.email
+    user.username = updated_user.username
+    user.password = updated_user.password
+    user.editorSettingsId = updated_user.editorSettingsId
+    db.session.commit()
+    return user
+
+def delete_user(id):
+    user = User.query.get(id)
+    db.session.delete(user)
+    db.session.commit()
+    return user
+
+def delete_all_users():
+    num_users_deleted = db.session.query(User).delete()
+    db.session.commit()
+    return num_users_deleted
+
+def is_user(user):
+    return user != None and 'id' in user.keys() and bool(User.query.get(user['id']))
+
+def is_user_id(userId):
+    return userId != None and bool(User.query.get(userId))
+
+def is_unregistered_email(email):
+    return not bool(get_user_from_email(email))
+
+def is_unregistered_username(username):
+    return not bool(get_user_from_username(username))
+
+def get_user_from_email(email):
+    return User.query.filter_by(email=email).first()
+
+def get_user_from_username(username):
+    return User.query.filter_by(username=username).first()
+
+def get_username_from_email(email):
+    return get_user_from_email(email).username
+
+def is_valid_email_password(email, password):
+    user = get_user_from_email(email)
+    valid_password = user.password if user else None
+    return password != None and password == valid_password
+
+def is_valid_username_password(username, password):
+    user = get_user_from_username(username)
+    valid_password = user.password if user else None
+    return password != None and password == valid_password
+
+def change_password(username, newPassword):
+    user = get_user_from_username(username)
+    user.password = newPassword
+    db.session.commit()
+    return user
+
+### EDITORSETTINGS ###
+def add_editor_settings(editor_settings):
+    db.session.add(editor_settings)
+    db.session.commit()
+
+def get_editor_settings(user_id):
+    return EditorSettings.query.filter_by(userId=user_id).first()
+
+def update_editor_settings(updated_editor_settings):
+    editor_settings = EditorSettings.query.get(updated_editor_settings.id)
+    editor_settings.fontSize = updated_editor_settings.fontSize
+    editor_settings.margin = updated_editor_settings.margin
+    editor_settings.tabSize = updated_editor_settings.tabSize
+    db.session.commit()
+    return editor_settings
+
+def delete_editor_settings(id):
+    editor_settings = EditorSettings.query.get(id)
+    db.session.delete(editor_settings)
+    db.session.commit()
+    return editor_settings
+
+def delete_all_editor_settings():
+    num_editor_settings_deleted = db.session.query(EditorSettings).delete()
+    db.session.commit()
+    return num_editor_settings_deleted
+
+### PROJECT ###
+def add_project(project):
+    db.session.add(project)
+    db.session.commit()
+
+def get_projects():
+    all_projects = Project.query.all()
+    result = projects_schema.dump(all_projects)
+    return result.data
+
+def get_user_projects(user_id):
+    """ Returns all projects where user is the creator or a collaborator """
+    user_projects = Project.query.filter_by(creatorId=user_id).all()
+    collaborator_project_ids = [id for id, in db.session.query(Collaborator.projectId).filter_by(userId=user_id)]
+    user_projects += Project.query.filter(Project.id.in_(collaborator_project_ids)).all()
+    result = projects_schema.dump(user_projects)
+    return result.data
+
+def get_project(id):
+    project = Project.query.get(id)
+    return project
+
+def update_project(updated_project):
+    project = Project.query.get(updated_project.id)
+    project.title = updated_project.title
+    project.creatorId = updated_project.creatorId
+    project.archived = updated_project.archived
+    db.session.commit()
+    return project
+
+def delete_project(id):
+    project = Project.query.get(id)
+    db.session.delete(project)
+    db.session.commit()
+    return project
+
+def delete_user_projects(user_id):
+    """ Deletes all projects owned by user with id. """
+    user = User.query.get(user_id)
+    user_projects_query = Project.query.filter_by(creatorId=user_id)
+
+    user_projects = user_projects_query.all()
+    for project in user_projects:
+        delete_project_files(project.id)
+        delete_project_collaborators(project.id)
+        delete_project_messages(project.id)
+        delete_project(project.id)
+
+    num_projects_deleted = user_projects_query.delete()
+    db.session.commit()
+    return num_projects_deleted
+
+def delete_all_projects():
+    num_projects_deleted = db.session.query(Project).delete()
+    db.session.commit()
+    return num_projects_deleted
+
+### FILE ###
+def add_file(file):
+    db.session.add(file)
+    db.session.commit()
+
+def get_file(id):
+    return File.query.get(id)
+
+def get_files():
+    all_files = File.query.all()
+    result = files_schema.dump(all_files)
+    return result.data
+
+def get_project_files(project_id):
+    project_files = File.query.filter_by(projectId=project_id).all()
+    result = files_schema.dump(project_files)
+    return result.data
+
+def update_file(updated_file):
+    file = File.query.get(updated_file.id)
+    file.projectId = updated_file.projectId
+    file.name = updated_file.name
+    file.isFolder = updated_file.isFolder
+    file.parent = updated_file.parent
+    file.content = updated_file.content
+    db.session.commit()
+    return file
+
+def delete_file(file_id):
+    file = File.query.filter_by(id=file_id).first()
+    db.session.delete(file)
+    db.session.commit()
+    return file
+
+def delete_project_files(project_id):
+    num_project_files_deleted = File.query.filter_by(projectId=project_id).delete()
+    db.session.commit()
+    return num_project_files_deleted
+
+def delete_all_files():
+    num_files_deleted = db.session.query(File).delete()
+    db.session.commit()
+    return num_files_deleted
+
+### COLLABORATOR ###
+def add_collaborator(collaborator):
+    db.session.add(collaborator)
+    db.session.commit()
+
+def get_collaborator(id):
+    collaborator = Collaborator.query.get(id)
+    return collaborator
+
+def get_collaborators():
+    all_collaborators = Collaborator.query.all()
+    result = collaborators_schema.dump(all_collaborators)
+    return result.data
+
+def get_project_collaborators(project_id):
+    project_collaborators = Collaborator.query.filter_by(projectId=project_id).all()
+    result = collaborators_schema.dump(project_collaborators)
+    return result.data
+
+def update_collaborator(updated_collaborator):
+    collaborator = Collaborator.query.get(updated_collaborator.id)
+    collaborator.projectId = updated_collaborator.projectId
+    collaborator.userId = updated_collaborator.userId
+    collaborator.permission = updated_collaborator.permission
+    db.session.commit()
+    return collaborator
+
+def delete_collaborator(collaborator_id):
+    collaborator = Collaborator.query.filter_by(id=collaborator_id).first()
+    db.session.delete(collaborator)
+    db.session.commit()
+    return collaborator
+
+def delete_project_collaborators(project_id):
+    num_project_collaborators_deleted = Collaborator.query.filter_by(projectId=project_id).delete()
+    db.session.commit()
+    return num_project_collaborators_deleted
+
+def delete_all_collaborators():
+    num_collaborators_deleted = db.session.query(Collaborator).delete()
+    db.session.commit()
+    return num_collaborators_deleted
+
+### Message ###
+def add_message(message):
+    db.session.add(message)
+    db.session.commit()
+
+def get_message(id):
+    message = Message.query.get(id)
+    return message
+
+def get_messages():
+    all_messages = Message.query.all()
+    result = messages_schema.dump(all_messages)
+    return result.data
+
+def get_project_messages(project_id):
+    project_messages = Message.query.filter_by(projectId=project_id).all()
+    result = messages_schema.dump(project_messages)
+    return result.data
+
+def update_message(updated_message):
+    message = Message.query.get(updated_message.id)
+    message.projectId = updated_message.projectId
+    message.autorId = updated_message.autorId
+    message.time = updated_message.time
+    db.session.commit()
+    return message
+
+def delete_project_messages(project_id):
+    num_project_messages_deleted = Message.query.filter_by(projectId=project_id).delete()
+    db.session.commit()
+    return num_project_messages_deleted
+
+def delete_all_messages():
+    num_messages_deleted = db.session.query(Message).delete()
+    db.session.commit()
+    return num_messages_deleted
+
+print('Hello world!')
\ No newline at end of file
diff --git a/backend/19/napp.py b/backend/19/napp.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/backend/2/Folder/app.py b/backend/2/Folder/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9204ee55f1fd86c5e5f407cdcd1ec3d0f072d66
--- /dev/null
+++ b/backend/2/Folder/app.py
@@ -0,0 +1 @@
+print('Hello!')
\ No newline at end of file
diff --git a/backend/2/Folder/heh.py b/backend/2/Folder/heh.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/backend/2/Folder/hello.py b/backend/2/Folder/hello.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/backend/2/app.py b/backend/2/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..63fe476518fedf45c96e340c20b0c566e0c7e4e0
--- /dev/null
+++ b/backend/2/app.py
@@ -0,0 +1,592 @@
+import os, sys, subprocess, shutil
+import random
+
+import logging
+from subprocess import Popen, PIPE
+from datetime import datetime
+from flask import Flask, request, jsonify, Response, json
+from flask_jwt_extended import JWTManager
+from flask_jwt_extended import (create_access_token, create_refresh_token,
+                                jwt_required, jwt_refresh_token_required, get_jwt_identity)
+from flask_bcrypt import Bcrypt
+from flask_socketio import emit
+from base64 import b64decode
+from simpleflock import SimpleFlock
+
+from __init__ import app
+import database_helper as db
+from socketio_helper import socketio, SIMPLE_FLOCK_TIMEOUT
+
+flask_bcrypt = Bcrypt(app)
+jwt = JWTManager(app)
+
+# Http status codes
+OK_STATUS_CODE = 200
+BAD_REQUEST_STATUS_CODE = 400
+CONFLICT_STATUS_CODE = 409
+UNAUTHORIZED_STATUS_CODE = 401
+
+DEFAULT_EDITOR_SETTINGS = {'fontSize': 16, 'margin': 80, 'tabSize': 4}
+
+### GENERAL ###
+def generate_response(response, status, headers={}):
+    response.status_code = status
+    response.headers = {**{
+        'Content-Type': 'application/json',
+        'Access-Control-Expose-Headers' : 'X-Auth-Token',
+        }, **headers}
+    return response
+
+### Authentication ###
+def is_valid_credentials(usernameEmail, password):
+    if '@' in usernameEmail:
+        user = db.get_user_from_email(usernameEmail)
+    else:
+        user = db.get_user_from_username(usernameEmail)
+    return user and flask_bcrypt.check_password_hash(user.password,
+        b64decode(password).decode("utf-8"))
+
+def is_valid_password(password):
+    return len(password) >= 8 and len(password) <= 100
+
+def is_valid_collaborators(collaborators):
+    if not collaborators:
+        return True
+    acceptedPermissions = ['View-only', 'May edit']
+    for collaborator in collaborators:
+        if (not collaborator or not db.is_user_id(collaborator['userId']) or
+        not collaborator['permission'] in acceptedPermissions):
+            return False
+    return True
+
+@jwt.unauthorized_loader
+def unauthorized_response(callback):
+    return jsonify({
+        'ok': False,
+        'message': 'Missing Authorization Header'
+    }), 401
+
+@app.route('/api/auth', methods=["GET"])
+@jwt_required
+def auth():
+    return generate_response(jsonify({
+        'success': True,
+        'message': 'Authorization successsful'
+        }), OK_STATUS_CODE)
+
+@app.route('/api/refresh', methods=['POST'])
+@jwt_refresh_token_required
+def refresh():
+    current_user = get_jwt_identity()
+    return generate_response(jsonify({
+        'success': True,
+        'message': 'Successfully refreshed token',
+        'jwt': create_access_token(identity=current_user, fresh=False)
+        }), OK_STATUS_CODE)
+
+### USER ###
+@app.route('/api/sign_up', methods=['POST'])
+def sign_up():
+    email = request.json['email']    
+    username = request.json['username']
+    password = b64decode(request.json['password']).decode("utf-8")
+    if (not email or not username or not password):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'Bad request.'
+        }), BAD_REQUEST_STATUS_CODE)
+    elif (not db.is_unregistered_email(email.lower())):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'User with that email already exists'
+        }), CONFLICT_STATUS_CODE)
+    elif (not db.is_unregistered_username(username.lower())):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'User with that username already exists'
+        }), CONFLICT_STATUS_CODE)
+    elif (not is_valid_password(password)):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'Password does not fulfill requirements'
+        }), BAD_REQUEST_STATUS_CODE)
+    else:
+        new_user = db.User(
+            email.lower(),
+            username.lower(),
+            flask_bcrypt.generate_password_hash(password).decode('utf-8'))
+        db.add_user(new_user)
+
+        new_editor_settings = db.EditorSettings(
+            DEFAULT_EDITOR_SETTINGS['fontSize'],
+            DEFAULT_EDITOR_SETTINGS['margin'],
+            DEFAULT_EDITOR_SETTINGS['tabSize'])
+        db.add_editor_settings(new_editor_settings)
+
+        new_user.editorSettingsId = new_editor_settings.id
+        db.update_user(new_user.id, new_user)
+        return generate_response(db.user_schema.jsonify(new_user),
+            OK_STATUS_CODE)
+
+@app.route('/api/sign_in', methods=['GET'])
+def sign_in():
+    auth = request.authorization
+    usernameEmail = auth.username
+    password = auth.password
+
+    if not usernameEmail or not password:
+        message = 'Missing credentials!'
+        status = BAD_REQUEST_STATUS_CODE
+    elif not is_valid_credentials(usernameEmail.lower(), password):
+        message = 'Incorrect username/email or password'
+        status = UNAUTHORIZED_STATUS_CODE
+    else:
+        if '@' in usernameEmail:
+            username = db.get_username_from_email(usernameEmail.lower())
+        else:
+            username = usernameEmail
+
+        db_user = db.get_user_from_username(username.lower())
+        user = {
+            'id': db_user.id,
+            'email': db_user.email,
+            'username': db_user.username,
+            'editorSettingsId': db_user.editorSettingsId,
+            'editorSettings': db.editor_settings_schema.dumps(db_user.editorSettings),
+            'jwt': create_access_token(identity=username),
+            'refreshToken': create_refresh_token(identity=username)
+        }
+        return generate_response(jsonify(user), OK_STATUS_CODE)
+
+    return generate_response(jsonify({
+        'success': False,
+        'message': message
+        }), status)
+
+@app.route('/api/change_password', methods=['POST', 'PUT'])
+@jwt_required
+def change_password():
+    username = request.json['username']
+    user = db.get_user_from_username(username)
+    oldPassword = request.json['oldPassword']
+    newPassword = request.json['newPassword']
+    if (not user):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'No user with that username'
+        }), BAD_REQUEST_STATUS_CODE)
+    elif not is_valid_credentials(username, oldPassword):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'Old password was incorrect'
+        }),UNAUTHORIZED_STATUS_CODE)
+    elif (not is_valid_password(newPassword)):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'New password does not fulfill requirements'
+        }), BAD_REQUEST_STATUS_CODE)
+    else:
+        updated_user = db.change_password(
+            username,
+            flask_bcrypt.generate_password_hash(newPassword).decode('utf-8')
+            )
+        return generate_response(db.user_schema.jsonify(updated_user), OK_STATUS_CODE)
+
+@app.route('/api/delete_account', methods=['DELETE'])
+def delete_user():
+    auth = request.authorization
+    username = auth.username
+    password = auth.password
+    user = db.get_user_from_username(username)
+    if not is_valid_credentials(username, password):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'Password was incorrect'
+        }),UNAUTHORIZED_STATUS_CODE)
+    elif (not user):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'No user with that username'
+        }), BAD_REQUEST_STATUS_CODE)
+    else:
+        db.delete_user_projects(user.id)
+        db.delete_editor_settings(user.editorSettingsId)
+        db.delete_user(user.id)
+        return generate_response(jsonify({
+            'success': True,
+            'message': 'Successfully deleted account'
+        }), OK_STATUS_CODE)
+
+@app.route('/api/get_users', methods=['GET'])
+@jwt_required
+def get_users_request():
+    return generate_response(db.users_schema.jsonify(db.get_users()),
+        OK_STATUS_CODE)
+
+@app.route('/api/get_user/<id>', methods=['GET'])
+@jwt_required
+def get_user_request(id):
+    return generate_response(db.user_schema.jsonify(db.get_user(id)),
+        OK_STATUS_CODE)
+
+### EDITOR SETTINGS ###
+@app.route('/api/update_editor_settings/<user_id>', methods=['PUT'])
+@jwt_required
+def update_editor_settings_request(user_id):
+    editor_settings = request.json['editorSettings']
+    updated_editor_settings = db.EditorSettings(
+        editor_settings['fontSize'],
+        editor_settings['margin'],
+        editor_settings['tabSize'],
+    )
+    updated_editor_settings.id = editor_settings['id']
+    return generate_response(db.editor_settings_schema.jsonify(
+        db.update_editor_settings(updated_editor_settings)), OK_STATUS_CODE)
+
+### PROJECT ###
+@app.route('/api/add_project', methods=['POST'])
+@jwt_required
+def new_project():
+    title = request.json['title']
+    creatorId = request.json['creatorId']
+    collaborators = request.json['collaborators'] if request.json['collaborators'] != [] else None
+    if (not title or not creatorId):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'Bad request.'
+        }), BAD_REQUEST_STATUS_CODE)
+    elif (not db.is_user_id(creatorId)):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'User does not exist'
+        }), BAD_REQUEST_STATUS_CODE)
+    elif (not is_valid_collaborators(collaborators)):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'Invalid collaborators'
+        }), BAD_REQUEST_STATUS_CODE)
+    else:
+        new_project = db.Project(title, creatorId)
+        db.add_project(new_project)
+
+        if collaborators:
+            for collaborator in collaborators:
+                new_collaborator = db.Collaborator(
+                    project_id=new_project.id,
+                    user_id=collaborator['userId'],
+                    permission=collaborator['permission'])
+                db.add_collaborator(new_collaborator)
+        
+        
+        return generate_response(db.project_schema.jsonify(new_project), OK_STATUS_CODE)
+
+@app.route('/api/duplicate_project/<project_id>', methods=['POST'])
+@jwt_required
+def duplicate_project(project_id):
+    title = request.json['title']
+    creator_id = request.json['creatorId']
+    if (not title or not creator_id):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'Bad request.'
+        }), BAD_REQUEST_STATUS_CODE)
+    elif (not db.is_user_id(creator_id)):
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'User does not exist'
+        }), BAD_REQUEST_STATUS_CODE)
+    else:
+        old_project = db.get_project(project_id)
+        duplicated_project = db.Project(
+            title,
+            creator_id,
+            archived=old_project.archived
+        )
+        db.add_project(duplicated_project)
+
+        collaborators = db.get_project_collaborators(project_id)
+        if collaborators:
+            for collaborator in collaborators:
+                if collaborator['userId'] != creator_id:
+                    duplicated_collaborator = db.Collaborator(
+                        project_id=duplicated_project.id,
+                        user_id=collaborator['userId'],
+                        permission=collaborator['permission'])
+                    db.add_collaborator(duplicated_collaborator)
+
+        if old_project.creatorId != creator_id:
+            old_creator_collaborator = db.Collaborator(
+                project_id=duplicated_project.id,
+                user_id=old_project.creatorId,
+                permission='May edit')
+            db.add_collaborator(old_creator_collaborator)
+
+        files = db.get_project_files(project_id)
+        if files:
+            for file in files:
+                duplicated_file = db.File(
+                    project_id=duplicated_project.id,
+                    name=file['name'],
+                    is_folder=file['isFolder'],
+                    parent=file['parent'],
+                    content=file['content'])
+                db.add_file(duplicated_file)
+
+        return generate_response(db.project_schema.jsonify(duplicated_project), OK_STATUS_CODE)
+
+@app.route('/api/get_projects', methods=['GET'])
+@jwt_required
+def get_projects_request():
+    return generate_response(db.projects_schema.jsonify(db.get_projects()),
+        OK_STATUS_CODE)
+
+@app.route('/api/get_projects/<user_id>', methods=['GET'])
+@jwt_required
+def get_user_projects_request(user_id):
+    return generate_response(db.projects_schema.jsonify(db.get_user_projects(user_id)),
+        OK_STATUS_CODE)
+
+@app.route('/api/get_project/<id>', methods=['GET'])
+@jwt_required
+def get_project_request(id):
+    return generate_response(db.project_schema.jsonify(db.get_project(id)),
+        OK_STATUS_CODE)
+
+@app.route('/api/update_project/<id>', methods=['POST', 'PUT'])
+@jwt_required
+def update_project_request(id):
+    project = json.loads(request.json['project'])
+    updated_project = db.Project(
+        title = project['title'],
+        creator_id = project['creatorId'],
+        archived = project['archived'])
+    updated_project.id = project['id']
+
+    return generate_response(db.project_schema.jsonify(
+        db.update_project(updated_project)), OK_STATUS_CODE)
+
+@app.route('/api/set_archived_project/<project_id>', methods=['POST', 'PUT'])
+@jwt_required
+def set_archived_project_request(project_id):
+    project = db.get_project(project_id)
+    project.archived = request.json['archived']
+
+    return generate_response(db.project_schema.jsonify(
+        db.update_project(project)), OK_STATUS_CODE)
+
+@app.route('/api/delete_project/<project_id>', methods=['DELETE'])
+@jwt_required
+def delete_project_request(project_id):
+    # TODO: Check that the user trying to delete the project is the creator!
+    db.delete_project_files(project_id)
+    db.delete_project_collaborators(project_id)
+    db.delete_project_messages(project_id)
+    db.delete_project(project_id)
+    return generate_response(jsonify({
+            'success': True,
+            'message': 'Successfully deleted project'
+        }), OK_STATUS_CODE)
+
+@app.route('/api/parse_project_files/<project_id>', methods=['GET'])
+@jwt_required
+def parse_file_request(project_id):
+    if db.get_project(project_id):
+        if os.path.exists(str(project_id)):
+            find_cmd = 'find ' + str(project_id) + '/ -type f -name "*.py"'
+            find = Popen(find_cmd, shell=True, stdout=PIPE)
+            files = find.communicate()[0].decode('utf-8')
+            files = ' '.join(files.splitlines())
+            
+            pylint_cmd = 'pylint --output-format=json ' + files
+            pylint = Popen(pylint_cmd, shell=True, stdout=PIPE)
+            result = pylint.communicate()[0].decode('utf-8')
+        else:
+            result = '[]'
+        return generate_response(jsonify({
+            'success': True,
+            'message': 'Successfully parsed files.',
+            'result': result
+        }), OK_STATUS_CODE)
+    else:
+        return generate_response(jsonify({
+            'success': False,
+            'message': 'No such project'
+        }), BAD_REQUEST_STATUS_CODE)
+
+### FILE ###
+@app.route('/api/create_file', methods=['POST'])
+@jwt_required
+def create_file_request():
+    file = request.json['file']
+    new_file = db.File(
+        file['projectId'],
+        file['name'],
+        file['isFolder'],
+        file['parent'],
+        file['content'],
+    )
+    db.add_file(new_file)
+
+    # Update edited date in project
+    project = db.get_project(new_file.projectId)
+    project.edited = datetime.now()
+    db.update_project(project)
+
+    # Add file locally on server if project is open
+    open_project_path = str(new_file.projectId) + '/'
+    if os.path.exists(open_project_path):
+        if new_file.parent:
+            file_path = open_project_path + new_file.parent + '/' + new_file.name
+        else:
+            file_path = open_project_path + new_file.name
+
+        if new_file.isFolder:
+            os.makedirs(file_path)
+        else:
+            f = open(file_path, 'w+')
+            f.write(new_file.content)
+            f.close()
+
+    return generate_response(db.file_schema.jsonify(
+        new_file), OK_STATUS_CODE)
+
+@app.route('/api/update_file/<file_id>', methods=['PUT'])
+@jwt_required
+def update_file_request(file_id):
+    file = request.json['file']
+    old_file = db.get_file(file['id'])
+
+    updated_file = db.File(
+        file['projectId'],
+        file['name'],
+        file['isFolder'],
+        file['parent'],
+        file['content'])
+    updated_file.id = file['id']
+
+    # Update edited date in project
+    project = db.get_project(updated_file.projectId)
+    project.edited = datetime.now()
+    db.update_project(project)
+
+    # Update file locally on server if project is open
+    open_project_path = str(project.id) + '/'
+    if old_file.parent:
+        old_path = open_project_path + old_file.parent + '/' + old_file.name
+    else:
+        old_path = open_project_path + old_file.name
+    if os.path.exists(old_path):
+        with SimpleFlock(str(project.id) + 'lock', SIMPLE_FLOCK_TIMEOUT):
+            if os.path.exists(old_path):
+                if file['parent']:
+                    new_path = open_project_path + file['parent'] + '/' + file['name']
+                else:
+                    new_path = open_project_path + file['name']
+                os.rename(old_path, new_path)
+
+    return generate_response(db.file_schema.jsonify(
+        db.update_file(updated_file)), OK_STATUS_CODE)
+
+@app.route('/api/delete_file/<file_id>', methods=['DELETE'])
+@jwt_required
+def delete_file_request(file_id):
+    # Update edited date in project
+    file = db.get_file(file_id)
+    project = db.get_project(file.projectId)
+    project.edited = datetime.now()
+    db.update_project(project)
+
+    # Remove file locally on server if project is open
+    open_project_path = str(file.projectId) + '/'
+    if file.parent:
+        path = open_project_path + file.parent + '/' + file.name
+    else:
+        path = open_project_path + file.name
+    if os.path.exists(path):
+        with SimpleFlock(str(file.projectId) + 'lock', SIMPLE_FLOCK_TIMEOUT):
+            if os.path.exists(path):
+                if file.isFolder:
+                    shutil.rmtree(path)
+                else:
+                    os.remove(path)
+
+    return generate_response(db.file_schema.jsonify(
+        db.delete_file(file_id)), OK_STATUS_CODE)
+
+@app.route('/api/get_project_files/<project_id>', methods=['GET'])
+@jwt_required
+def get_project_files_request(project_id):
+    return generate_response(db.files_schema.jsonify(
+        db.get_project_files(project_id)), OK_STATUS_CODE)
+
+### COLLABORATOR ###
+@app.route('/api/add_collaborator', methods=['POST'])
+@jwt_required
+def add_collaborator_request():
+    collaborator = request.json['collaborator']
+    new_collaborator = db.Collaborator(
+        collaborator['projectId'],
+        collaborator['userId'],
+        collaborator['permission'],
+    )
+    db.add_collaborator(new_collaborator)
+
+    # Update edited date in project
+    project = db.get_project(new_collaborator.projectId)
+    project.edited = datetime.now()
+    db.update_project(project)
+
+    return generate_response(db.collaborator_schema.jsonify(
+        new_collaborator), OK_STATUS_CODE)
+
+
+@app.route('/api/update_collaborator/<collaborator_id>', methods=['PUT'])
+@jwt_required
+def update_collaborator_request(collaborator_id):
+    # Update collaborator
+    collaborator = request.json['collaborator']
+    updated_collaborator = db.Collaborator(
+        collaborator['projectId'],
+        collaborator['userId'],
+        collaborator['permission']
+        )
+    updated_collaborator.id = collaborator['id']
+
+    # Update edited date in project
+    project = db.get_project(updated_collaborator.projectId)
+    project.edited = datetime.now()
+    db.update_project(project)
+
+    return generate_response(db.collaborator_schema.jsonify(
+        db.update_collaborator(updated_collaborator)), OK_STATUS_CODE)
+
+@app.route('/api/delete_collaborator/<collaborator_id>', methods=['DELETE'])
+@jwt_required
+def delete_collaborator_request(collaborator_id):
+    # Update edited date in project
+    collaborator = db.get_collaborator(collaborator_id)
+    project = db.get_project(collaborator.projectId)
+    project.edited = datetime.now()
+    db.update_project(project)
+
+    return generate_response(db.collaborator_schema.jsonify(
+        db.delete_collaborator(collaborator_id)), OK_STATUS_CODE)
+
+@app.route('/api/get_project_collaborators/<project_id>', methods=['GET'])
+@jwt_required
+def get_project_collaborators_request(project_id):
+    return generate_response(db.collaborators_schema.jsonify(
+        db.get_project_collaborators(project_id)), OK_STATUS_CODE)
+
+### MESSAGE ###
+@app.route('/api/get_project_messages/<project_id>', methods=['GET'])
+@jwt_required
+def get_project_messages_request(project_id):
+    return generate_response(db.messages_schema.jsonify(
+        db.get_project_messages(project_id)), OK_STATUS_CODE)
+
+if __name__ == '__main__':
+    socketio.run(app, host='0.0.0.0')
+
+    """ If you deleted the db, run the following to re-initialize it. """
+    # db.db.create_all()
\ No newline at end of file
diff --git a/backend/2/database_helper.py b/backend/2/database_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..8bfe3a7cc18136540240475bf855010289d980b5
--- /dev/null
+++ b/backend/2/database_helper.py
@@ -0,0 +1,439 @@
+import os
+
+from __init__ import app
+
+import json
+from datetime import datetime
+from flask import Flask, request, jsonify
+from flask_sqlalchemy import SQLAlchemy
+from flask_marshmallow import Marshmallow, fields
+
+basedir = os.path.abspath(os.path.dirname(__file__))
+
+# Database
+app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'db.sqlite')
+app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
+
+db = SQLAlchemy(app)
+ma = Marshmallow(app)
+
+# EditorSettings
+class EditorSettings(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    fontSize = db.Column(db.Integer, nullable=False)
+    margin = db.Column(db.Integer, nullable=False)
+    tabSize = db.Column(db.Integer, nullable=False)
+
+    def __init__(self, font_size, margin, tab_size):
+        self.fontSize = font_size
+        self.margin = margin
+        self.tabSize = tab_size
+
+class EditorSettingsSchema(ma.Schema):
+    class Meta:
+        model = EditorSettings
+        fields = ('id', 'fontSize', 'margin', 'tabSize')
+
+editor_settings_schema = EditorSettingsSchema(strict=True)
+
+# User
+class User(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    email = db.Column(db.String(100), unique=True, nullable=False)
+    username = db.Column(db.String(50), unique=True, nullable=False)
+    password = db.Column(db.Text, nullable=False)
+    editorSettingsId = db.Column(db.Integer, db.ForeignKey(EditorSettings.id))
+    editorSettings = db.relationship("EditorSettings")
+
+    def __init__(self, email, username, password):
+        self.email = email
+        self.username = username
+        self.password = password
+
+class UserSchema(ma.Schema):
+    editorSettings = ma.Nested(EditorSettingsSchema)
+    class Meta:
+        model = User
+        fields = ('id', 'email', 'username', 'editorSettingsId', 'editorSettings')
+
+user_schema = UserSchema(strict=True)
+users_schema = UserSchema(many=True, strict=True)
+
+# Project
+class Project(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    title = db.Column(db.String(50), nullable=False)
+    creatorId = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
+    creator = db.relationship('User')
+    created = db.Column(db.String(50), default=datetime.now, nullable=False)
+    edited = db.Column(db.String(50), onupdate=datetime.now)
+    archived = db.Column(db.Boolean, default=False, nullable=False)
+
+    def __init__(self, title, creator_id, created=None, edited=None, archived=False):
+        self.title = title
+        self.creatorId = creator_id
+        self.created = created
+        self.edited = edited
+        self.archived = archived
+
+class ProjectSchema(ma.Schema):
+    creator = ma.Nested(UserSchema)
+    class Meta:
+        model = Project
+        fields = ('id', 'title', 'creatorId', 'creator', 'created', 'edited', 'archived')
+
+project_schema = ProjectSchema(strict=True)
+projects_schema = ProjectSchema(many=True, strict=True)
+
+# File
+class File(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    projectId = db.Column(db.Integer, db.ForeignKey(Project.id), nullable=False)
+    project = db.relationship('Project', lazy='subquery')
+    name = db.Column(db.String(100), nullable=False)
+    isFolder = db.Column(db.Boolean, nullable=False)
+    parent = db.Column(db.String(100))
+    content = db.Column(db.Text)
+
+    def __init__(self, project_id, name, is_folder, parent, content):
+        self.projectId = project_id
+        self.name = name
+        self.isFolder = is_folder
+        self.parent = parent
+        self.content = content
+
+class FileSchema(ma.Schema):
+    project = ma.Nested(ProjectSchema)
+    class Meta:
+        model = File
+        fields = ('id', 'projectId', 'project', 'name', 'isFolder', 'parent', 'content')
+
+file_schema = FileSchema(strict=True)
+files_schema = FileSchema(many=True, strict=True)
+
+# Collaborator
+class Collaborator(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    projectId = db.Column(db.Integer, db.ForeignKey(Project.id), nullable=False)
+    project = db.relationship('Project', lazy='subquery')
+    userId = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
+    user = db.relationship('User', lazy='subquery')
+    permission = db.Column(db.Text, nullable=False)
+
+    def __init__(self, project_id, user_id, permission):
+        self.projectId = project_id
+        self.userId = user_id
+        self.permission = permission
+
+class CollaboratorSchema(ma.Schema):
+    project = ma.Nested(ProjectSchema)
+    user = ma.Nested(UserSchema)
+    class Meta:
+        fields = ('id', 'projectId', 'project', 'userId', 'user', 'permission')
+
+collaborator_schema = CollaboratorSchema(strict=True)
+collaborators_schema = CollaboratorSchema(many=True, strict=True)
+
+# Message
+class Message(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    projectId = db.Column(db.Integer, db.ForeignKey(Project.id), nullable=False)
+    project = db.relationship('Project')
+    authorId = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
+    author = db.relationship('User')
+    message = db.Column(db.Text)
+    time = db.Column(db.String(50), default=datetime.now, nullable=False)
+
+    def __init__(self, project_id, author_id, message, time=None):
+        self.projectId = project_id
+        self.authorId = author_id
+        self.message = message
+        self.time = time
+
+class MessageSchema(ma.Schema):
+    project = ma.Nested(ProjectSchema)
+    author = ma.Nested(UserSchema)
+    class Meta:
+        fields = ('id', 'projectId', 'project', 'authorId', 'author', 'message', 'time')
+
+message_schema = MessageSchema(strict=True)
+messages_schema = MessageSchema(many=True, strict=True)
+
+### USER ###
+def add_user(new_user):
+    db.session.add(new_user)
+    db.session.commit()
+
+def get_users():
+    all_users = User.query.all()
+    result = users_schema.dump(all_users)
+    return result.data
+
+def get_user(id):
+    user = User.query.get(id)
+    return user
+
+def update_user(id, updated_user):
+    user = User.query.get(id)
+    user.email = updated_user.email
+    user.username = updated_user.username
+    user.password = updated_user.password
+    user.editorSettingsId = updated_user.editorSettingsId
+    db.session.commit()
+    return user
+
+def delete_user(id):
+    user = User.query.get(id)
+    db.session.delete(user)
+    db.session.commit()
+    return user
+
+def delete_all_users():
+    num_users_deleted = db.session.query(User).delete()
+    db.session.commit()
+    return num_users_deleted
+
+def is_user(user):
+    return user != None and 'id' in user.keys() and bool(User.query.get(user['id']))
+
+def is_user_id(userId):
+    return userId != None and bool(User.query.get(userId))
+
+def is_unregistered_email(email):
+    return not bool(get_user_from_email(email))
+
+def is_unregistered_username(username):
+    return not bool(get_user_from_username(username))
+
+def get_user_from_email(email):
+    return User.query.filter_by(email=email).first()
+
+def get_user_from_username(username):
+    return User.query.filter_by(username=username).first()
+
+def get_username_from_email(email):
+    return get_user_from_email(email).username
+
+def is_valid_email_password(email, password):
+    user = get_user_from_email(email)
+    valid_password = user.password if user else None
+    return password != None and password == valid_password
+
+def is_valid_username_password(username, password):
+    user = get_user_from_username(username)
+    valid_password = user.password if user else None
+    return password != None and password == valid_password
+
+def change_password(username, newPassword):
+    user = get_user_from_username(username)
+    user.password = newPassword
+    db.session.commit()
+    return user
+
+### EDITORSETTINGS ###
+def add_editor_settings(editor_settings):
+    db.session.add(editor_settings)
+    db.session.commit()
+
+def get_editor_settings(user_id):
+    return EditorSettings.query.filter_by(userId=user_id).first()
+
+def update_editor_settings(updated_editor_settings):
+    editor_settings = EditorSettings.query.get(updated_editor_settings.id)
+    editor_settings.fontSize = updated_editor_settings.fontSize
+    editor_settings.margin = updated_editor_settings.margin
+    editor_settings.tabSize = updated_editor_settings.tabSize
+    db.session.commit()
+    return editor_settings
+
+def delete_editor_settings(id):
+    editor_settings = EditorSettings.query.get(id)
+    db.session.delete(editor_settings)
+    db.session.commit()
+    return editor_settings
+
+def delete_all_editor_settings():
+    num_editor_settings_deleted = db.session.query(EditorSettings).delete()
+    db.session.commit()
+    return num_editor_settings_deleted
+
+### PROJECT ###
+def add_project(project):
+    db.session.add(project)
+    db.session.commit()
+
+def get_projects():
+    all_projects = Project.query.all()
+    result = projects_schema.dump(all_projects)
+    return result.data
+
+def get_user_projects(user_id):
+    """ Returns all projects where user is the creator or a collaborator """
+    user_projects = Project.query.filter_by(creatorId=user_id).all()
+    collaborator_project_ids = [id for id, in db.session.query(Collaborator.projectId).filter_by(userId=user_id)]
+    user_projects += Project.query.filter(Project.id.in_(collaborator_project_ids)).all()
+    result = projects_schema.dump(user_projects)
+    return result.data
+
+def get_project(id):
+    project = Project.query.get(id)
+    return project
+
+def update_project(updated_project):
+    project = Project.query.get(updated_project.id)
+    project.title = updated_project.title
+    project.creatorId = updated_project.creatorId
+    project.archived = updated_project.archived
+    db.session.commit()
+    return project
+
+def delete_project(id):
+    project = Project.query.get(id)
+    db.session.delete(project)
+    db.session.commit()
+    return project
+
+def delete_user_projects(user_id):
+    """ Deletes all projects owned by user with id. """
+    user = User.query.get(user_id)
+    user_projects_query = Project.query.filter_by(creatorId=user_id)
+
+    user_projects = user_projects_query.all()
+    for project in user_projects:
+        delete_project_files(project.id)
+        delete_project_collaborators(project.id)
+        delete_project_messages(project.id)
+        delete_project(project.id)
+
+    num_projects_deleted = user_projects_query.delete()
+    db.session.commit()
+    return num_projects_deleted
+
+def delete_all_projects():
+    num_projects_deleted = db.session.query(Project).delete()
+    db.session.commit()
+    return num_projects_deleted
+
+### FILE ###
+def add_file(file):
+    db.session.add(file)
+    db.session.commit()
+
+def get_file(id):
+    return File.query.get(id)
+
+def get_files():
+    all_files = File.query.all()
+    result = files_schema.dump(all_files)
+    return result.data
+
+def get_project_files(project_id):
+    project_files = File.query.filter_by(projectId=project_id).all()
+    result = files_schema.dump(project_files)
+    return result.data
+
+def update_file(updated_file):
+    file = File.query.get(updated_file.id)
+    file.projectId = updated_file.projectId
+    file.name = updated_file.name
+    file.isFolder = updated_file.isFolder
+    file.parent = updated_file.parent
+    file.content = updated_file.content
+    db.session.commit()
+    return file
+
+def delete_file(file_id):
+    file = File.query.filter_by(id=file_id).first()
+    db.session.delete(file)
+    db.session.commit()
+    return file
+
+def delete_project_files(project_id):
+    num_project_files_deleted = File.query.filter_by(projectId=project_id).delete()
+    db.session.commit()
+    return num_project_files_deleted
+
+def delete_all_files():
+    num_files_deleted = db.session.query(File).delete()
+    db.session.commit()
+    return num_files_deleted
+
+### COLLABORATOR ###
+def add_collaborator(collaborator):
+    db.session.add(collaborator)
+    db.session.commit()
+
+def get_collaborator(id):
+    collaborator = Collaborator.query.get(id)
+    return collaborator
+
+def get_collaborators():
+    all_collaborators = Collaborator.query.all()
+    result = collaborators_schema.dump(all_collaborators)
+    return result.data
+
+def get_project_collaborators(project_id):
+    project_collaborators = Collaborator.query.filter_by(projectId=project_id).all()
+    result = collaborators_schema.dump(project_collaborators)
+    return result.data
+
+def update_collaborator(updated_collaborator):
+    collaborator = Collaborator.query.get(updated_collaborator.id)
+    collaborator.projectId = updated_collaborator.projectId
+    collaborator.userId = updated_collaborator.userId
+    collaborator.permission = updated_collaborator.permission
+    db.session.commit()
+    return collaborator
+
+def delete_collaborator(collaborator_id):
+    collaborator = Collaborator.query.filter_by(id=collaborator_id).first()
+    db.session.delete(collaborator)
+    db.session.commit()
+    return collaborator
+
+def delete_project_collaborators(project_id):
+    num_project_collaborators_deleted = Collaborator.query.filter_by(projectId=project_id).delete()
+    db.session.commit()
+    return num_project_collaborators_deleted
+
+def delete_all_collaborators():
+    num_collaborators_deleted = db.session.query(Collaborator).delete()
+    db.session.commit()
+    return num_collaborators_deleted
+
+### Message ###
+def add_message(message):
+    db.session.add(message)
+    db.session.commit()
+
+def get_message(id):
+    message = Message.query.get(id)
+    return message
+
+def get_messages():
+    all_messages = Message.query.all()
+    result = messages_schema.dump(all_messages)
+    return result.data
+
+def get_project_messages(project_id):
+    project_messages = Message.query.filter_by(projectId=project_id).all()
+    result = messages_schema.dump(project_messages)
+    return result.data
+
+def update_message(updated_message):
+    message = Message.query.get(updated_message.id)
+    message.projectId = updated_message.projectId
+    message.autorId = updated_message.autorId
+    message.time = updated_message.time
+    db.session.commit()
+    return message
+
+def delete_project_messages(project_id):
+    num_project_messages_deleted = Message.query.filter_by(projectId=project_id).delete()
+    db.session.commit()
+    return num_project_messages_deleted
+
+def delete_all_messages():
+    num_messages_deleted = db.session.query(Message).delete()
+    db.session.commit()
+    return num_messages_deleted
\ No newline at end of file
diff --git a/backend/2/socketio_helper.py b/backend/2/socketio_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..6dfe599e7c1c9a707e16d90db91418c483f5c8d9
--- /dev/null
+++ b/backend/2/socketio_helper.py
@@ -0,0 +1,301 @@
+import os, shutil
+
+from flask import json
+from flask_socketio import SocketIO, send, emit, join_room, leave_room
+import diff_match_patch as dmp_module
+from simpleflock import SimpleFlock
+
+from __init__ import app
+import database_helper as db
+
+SIMPLE_FLOCK_TIMEOUT = 10
+
+socketio = SocketIO(app)
+dmp = dmp_module.diff_match_patch()
+
+project_viewers = dict()
+
+def has_viewer(username, project_id):
+    for viewer in project_viewers[str(project_id)]:
+        if viewer['username'] == username:
+            return True
+    return False
+
+def set_viewer(viewer, project_id):
+    filtered_viewers = []
+    for curr_viewer in project_viewers[str(project_id)]:
+        if curr_viewer['username'] != viewer['username']:
+            filtered_viewers.append(curr_viewer)
+    project_viewers[str(project_id)].append(viewer)
+    project_viewers[str(project_id)] = filtered_viewers
+
+def add_project_viewer(project_id, username):
+    viewer = {
+        'username': username,
+        'cursor': {
+            'fileId': None,
+            'position': None,
+            'added': False
+        }
+    }
+    if not str(project_id) in project_viewers.keys():
+        project_viewers[str(project_id)] = [viewer]
+    elif not has_viewer(username, project_id):
+        project_viewers[str(project_id)].append(viewer)
+    else:
+        set_viewer(viewer, project_id)
+
+def remove_project_viewer(project_id, username):
+    if str(project_id) in project_viewers.keys() and \
+    has_viewer(username, project_id):
+        viewers = []
+        for viewer in project_viewers[str(project_id)]:
+            if viewer['username'] != username:
+                viewers.append(viewer)
+        project_viewers[str(project_id)] = viewers
+
+
+## Join user room
+@socketio.on('joinUserRoom')
+def on_join_user_room(data):
+    data = json.loads(data)
+    user_id = data['userId']
+    room = 'user' + str(user_id)
+    join_room(room)
+    emit('joinUserRoom', {
+        'on': 'joinUserRoom',
+        'userId': user_id
+        }, room=room)
+
+@socketio.on('leaveUserRoom')
+def on_leave_user_room(data):
+    data = json.loads(data)
+    user_id = data['userId']
+    room = 'user' + str(user_id)
+    leave_room(room)
+    emit('leaveUserRoom', {
+        'on': 'leaveUserRoom',
+        'userId': user_id
+        }, room=room)
+
+## Join project room
+@socketio.on('joinProjectRoom')
+def on_join_project_room(data):
+    data = json.loads(data)
+    username = data['username']
+    project_id = data['projectId']
+    room = 'project' + str(project_id)
+    join_room(room)
+    emit('joinProjectRoom', {
+        'on': 'joinProjectRoom',
+        'username': username,
+        'projectId' : project_id
+        }, room=room)
+
+@socketio.on('leaveProjectRoom')
+def on_leave_project_room(data):
+    data = json.loads(data)
+    username = data['username']
+    project_id = data['projectId']
+    room = 'project' + str(project_id)
+    leave_room(room)
+    emit('leaveProjectRoom', {
+        'on': 'leaveProjectRoom',
+        'username': username,
+        'projectId' : project_id
+        }, room=room)
+
+## Project handling ##
+@socketio.on('projectCreated')
+def handle_project_created(data):
+    project = json.loads(data)
+    emit('projectCreated', {
+        'on': 'projectCreated',
+        'project': project
+        }, room = 'project' + str(project['id']))
+
+@socketio.on('projectChanged')
+def handle_project_changed(data):
+    project = json.loads(data)
+    emit('projectChanged', {
+        'on': 'projectChanged',
+        'project': project
+        }, room = 'project' + str(project['id']))
+
+@socketio.on('projectDeleted')
+def handle_project_deleted(data):
+    projectId = json.loads(data)
+    emit('projectDeleted', {
+        'on': 'projectDeleted',
+        'projectId': projectId
+        }, room = 'project' + str(projectId))
+
+@socketio.on('collaboratorAdded')
+def handle_collaborator_added(data):
+    collaborator = json.loads(data)
+    emit('collaboratorAdded', {
+        'on': 'collaboratorAdded',
+        'collaborator': collaborator
+        }, room = 'user' + str(collaborator['userId']))
+
+@socketio.on('collaboratorChanged')
+def handle_collaborator_changed(data):
+    collaborator = json.loads(data)
+    emit('collaboratorChanged', {
+        'on': 'collaboratorChanged',
+        'collaborator': collaborator
+        }, room = 'project' + str(collaborator['projectId']))
+
+## Join open project room
+@socketio.on('joinOpenProjectRoom')
+def on_join_open_project_room(data):
+    data = json.loads(data)
+    username = data['username']
+    project_id = data['projectId']
+    room = 'openProject' + str(project_id)
+    join_room(room)
+    add_project_viewer(project_id, username)
+
+    if not os.path.exists(str(project_id)):
+        os.mkdir(str(project_id))
+        for file in db.get_project_files(project_id):
+            dir_name = str(project_id) + ('/' + file['parent'] if file['parent'] else '')
+            if not os.path.exists(dir_name):
+                os.makedirs(dir_name)
+            if not file['isFolder']:
+                f = open(dir_name + '/' + file['name'], "a+")
+                f.write(file['content'])
+                f.close()
+
+    emit('joinOpenProjectRoom', {
+        'on': 'joinOpenProjectRoom',
+        'username': username,
+        'viewers': project_viewers[str(project_id)]
+        }, room=room)
+
+@socketio.on('leaveOpenProjectRoom')
+def on_leave_open_project_room(data):
+    data = json.loads(data)
+    username = data['username']
+    project_id = data['projectId']
+    room = 'openProject' + str(project_id)
+    leave_room(room)
+    remove_project_viewer(project_id, username)
+
+    if not project_viewers[str(project_id)] and os.path.exists(str(project_id)):
+        shutil.rmtree(str(project_id))
+
+    emit('leaveOpenProjectRoom', {
+        'on': 'leaveOpenProjectRoom',
+        'username': username,
+        'viewers': project_viewers[str(project_id)]
+        }, room=room)
+
+## File handling ##
+@socketio.on('fileCreated')
+def handle_file_created(data):
+    file = json.loads(data)
+    emit('fileCreated', {
+        'on': 'fileCreated',
+        'file': file
+        }, room = 'openProject' + str(file['projectId']))
+
+@socketio.on('fileChanged')
+def handle_file_changed(data):
+    file = json.loads(data)
+    emit('fileChanged', {
+        'on': 'fileChanged',
+        'file': file
+        }, room = 'openProject' + str(file['projectId']))
+
+
+@socketio.on('fileContentChanged')
+def handle_file_content_changed(data):
+    data = json.loads(data)
+    project_id = data['projectId']
+    file_id = data['fileId']
+    patches_text = data['patchesText']
+    patches = dmp.patch_fromText(patches_text)
+
+    file = db.get_file(file_id)
+    file.content = dmp.patch_apply(patches, file.content)[0]
+    db.update_file(file)
+
+    # Update file locally on server if project is open
+    open_project_path = str(file.projectId) + '/'
+    if os.path.exists(open_project_path):
+        if file.parent:
+            file_path = open_project_path + file.parent + '/' + file.name
+        else:
+            file_path = open_project_path + file.name
+        if os.path.exists(file_path):
+            with SimpleFlock(str(file.projectId) + 'lock', SIMPLE_FLOCK_TIMEOUT):
+                if os.path.exists(file_path):
+                    f = open(file_path, 'w')
+                    f.write(file.content)
+                    f.close()
+
+    emit('fileContentChanged', {
+        'on': 'fileContentChanged',
+        'projectId': project_id,
+        'fileId': file_id,
+        'patchesText' : patches_text
+        }, room = 'openProject' + str(project_id), include_self=False)
+
+@socketio.on('fileDeleted')
+def handle_file_deleted(data):
+    file = json.loads(data)
+    emit('fileDeleted', {
+        'on': 'fileDeleted',
+        'file': file
+        }, room = 'openProject' + str(file['projectId']))
+
+## Chat ##
+@socketio.on('message')
+def handle_message(data):
+    data = json.loads(data)
+    
+    projectId = data['projectId']
+    username = data['username']
+    message = data['message']
+
+    author = db.get_user_from_username(username)
+    new_message = db.Message(
+        projectId,
+        author.id,
+        message
+    )
+    db.add_message(new_message)
+    
+    emit('message', {
+        'on': 'message',
+        'author': db.user_schema.dumps(author),
+        'time': new_message.time,
+        'message': message
+        }, room = 'openProject' + str(projectId))
+
+## Editor ##
+@socketio.on('viewerCursorChanged')
+def handle_viewer_cursor_changed(data):
+    data = json.loads(data)
+    project_id = data['projectId']
+    file_id = data['fileId']
+    username = data['username']
+    cursor_position = data['cursorPosition']
+
+    if not str(project_id) in project_viewers.keys() \
+            or not has_viewer(username, project_id):
+        add_project_viewer(project_id, username)
+
+    for viewer in project_viewers[str(project_id)]:
+        if viewer['username'] == username:
+            viewer['cursor'] = {
+                'fileId': file_id,
+                'position': cursor_position
+        }
+    emit('viewerCursorChanged', {
+        'on': 'viewerCursorChanged',
+        'viewer': username,
+        'fileId': file_id,
+        'cursorPosition': cursor_position
+        }, room = 'openProject' + str(project_id), include_self=False)
\ No newline at end of file
diff --git a/backend/2/test.py b/backend/2/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..735bdcc87b647754c06a0e86535f7bc47694dd8e
--- /dev/null
+++ b/backend/2/test.py
@@ -0,0 +1 @@
+print('Hello world!')
\ No newline at end of file
diff --git a/backend/Pipfile b/backend/Pipfile
index 2132e1525c3bc907ee7495a61c14ba536b7d95b0..8f2ac692ecc9fb06a457e1eaf75178fa336109fa 100644
--- a/backend/Pipfile
+++ b/backend/Pipfile
@@ -17,6 +17,7 @@ pyjwt = "*"
 bson = "*"
 flask-socketio = "*"
 diff-match-patch = "*"
+simpleflock = "*"
 
 [requires]
 python_version = "3.6"
diff --git a/backend/Pipfile.lock b/backend/Pipfile.lock
index 4aca4b4834766ab23c9a2505803556b3a1b315a9..dc7c95c197e6a58dc5c0167d5cfc25d470d2149d 100644
--- a/backend/Pipfile.lock
+++ b/backend/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "c40b8fdfa182b209d5d3d2e5c2cac67b40cbc77ff95f0ae845d722a57fae950b"
+            "sha256": "0cbc6707a54e77c6ff91fec870c927dfba819c9aaa97d84191e5ebef71e9cfba"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -96,11 +96,11 @@
         },
         "flask": {
             "hashes": [
-                "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
-                "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
+                "sha256:ad7c6d841e64296b962296c2c2dabc6543752985727af86a975072dea984b6f3",
+                "sha256:e7d32475d1de5facaa55e3958bc4ec66d3762076b074296aa50ef8fdc5b9df61"
             ],
             "index": "pypi",
-            "version": "==1.0.2"
+            "version": "==1.0.3"
         },
         "flask-bcrypt": {
             "hashes": [
@@ -134,11 +134,11 @@
         },
         "flask-socketio": {
             "hashes": [
-                "sha256:8d8f9f104db5ddff1b06ba322d8e158881d590144199c993fe26cf53218c7edd",
-                "sha256:f9b9c95c82b62381862fd3bc55cea7fcc08e787f6bb63fdc79a2f258df2bdc9a"
+                "sha256:841dc9636fcf0659223745434b7ceef56f716fede2311ec26cb97af30648ca22",
+                "sha256:f44b2fc91149c1b2b522c8fd64824d10d4e38d535c2cb76623c22b1e9eeb9de9"
             ],
             "index": "pypi",
-            "version": "==3.3.2"
+            "version": "==4.0.0"
         },
         "flask-sqlalchemy": {
             "hashes": [
@@ -233,17 +233,24 @@
         },
         "python-engineio": {
             "hashes": [
-                "sha256:a89d2cff7f9b447d37c450c2666101d97043b4b22524bf4efaafcb9784c9f7f4",
-                "sha256:fd14357f0b854de729cc2dba960ceba1d20da973c91f289110807f0bb2f69935"
+                "sha256:06bd817c0ea50df62e0ea31b89f427ba4ef6f954e09cb0f9036bcfce8193e304",
+                "sha256:0886831a4ce9f0e8293b927c4b8e4a3dc6dba4a55830abb9c170b12b34d453e9"
             ],
-            "version": "==3.5.1"
+            "version": "==3.6.0"
         },
         "python-socketio": {
             "hashes": [
-                "sha256:aa702157694d55a743fb6f1cc0bd1af58fbfda8a7d71d747d4b12d6dac29cab3",
-                "sha256:cd225eb0bb3b348665727cfaafad1e455ee13b8fd9ea9ff2691082b88b9a9444"
+                "sha256:80810f40a5b276937a95d4944229fa9c20ee61963365387549fd943589661a6f",
+                "sha256:c128d320b23ce3c4323ca5b0e20f602a075ceea56f368f6b9f52bed48d97df71"
             ],
-            "version": "==3.1.2"
+            "version": "==4.0.3"
+        },
+        "simpleflock": {
+            "hashes": [
+                "sha256:0dfebcbe4ae574fdd01466be002dc8cd274ba350b90d11ef910c4bc9766c59eb"
+            ],
+            "index": "pypi",
+            "version": "==0.0.3"
         },
         "six": {
             "hashes": [
diff --git a/backend/app.py b/backend/app.py
index d631cf6b23f3eb50d88093ffb20d081f10ac9092..d2bda1516ceb7243ac270dcbdc48e501d694f367 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -2,17 +2,20 @@ import os, sys, subprocess, shutil
 import random
 
 import logging
+from subprocess import Popen, PIPE
+from datetime import datetime
 from flask import Flask, request, jsonify, Response, json
 from flask_jwt_extended import JWTManager
 from flask_jwt_extended import (create_access_token, create_refresh_token,
                                 jwt_required, jwt_refresh_token_required, get_jwt_identity)
 from flask_bcrypt import Bcrypt
+from flask_socketio import emit
 from base64 import b64decode
-from datetime import datetime
+from simpleflock import SimpleFlock
 
 from __init__ import app
 import database_helper as db
-from socketio_helper import socketio
+from socketio_helper import socketio, SIMPLE_FLOCK_TIMEOUT
 
 flask_bcrypt = Bcrypt(app)
 jwt = JWTManager(app)
@@ -30,7 +33,8 @@ def generate_response(response, status, headers={}):
     response.status_code = status
     response.headers = {**{
         'Content-Type': 'application/json',
-        'Access-Control-Expose-Headers' : 'X-Auth-Token',
+        'Access-Control-Expose-Headers': 'X-Auth-Token',
+        'Access-Control-Allow-Origin': '*'
         }, **headers}
     return response
 
@@ -265,7 +269,6 @@ def new_project():
     else:
         new_project = db.Project(title, creatorId)
         db.add_project(new_project)
-        app.logger.error(new_project.id)
 
         if collaborators:
             for collaborator in collaborators:
@@ -389,27 +392,27 @@ def delete_project_request(project_id):
 @jwt_required
 def parse_file_request(project_id):
     if db.get_project(project_id):
-        os.mkdir(str(project_id))
-        for file in db.get_project_files(project_id):
-            dir_name = str(project_id) + ('/' + file['parent'] if file['parent'] else '')
-            if not os.path.exists(dir_name):
-                os.makedirs(dir_name)
-            if not file['isFolder']:
-                f = open(dir_name + '/' + file['name'], "a+")
-                f.write(file['content'])
-                f.close()
-        result = subprocess.run(['pylint', '--output-format=json', str(project_id) + '/'],\
-            stdout=subprocess.PIPE).stdout.decode('utf-8')
-        shutil.rmtree(str(project_id))
+        if os.path.exists(str(project_id)):
+            with SimpleFlock(str(project_id) + 'lock', SIMPLE_FLOCK_TIMEOUT):
+                find_cmd = 'find ' + str(project_id) + '/ -type f -name "*.py"'
+                find = Popen(find_cmd, shell=True, stdout=PIPE)
+                files = find.communicate()[0].decode('utf-8')
+                files = ' '.join(files.splitlines())
+
+                pylint_cmd = 'pylint --output-format=json ' + files
+                pylint = Popen(pylint_cmd, shell=True, stdout=PIPE)
+                result = pylint.communicate()[0].decode('utf-8')
+        else:
+            result = '[]'
         return generate_response(jsonify({
             'success': True,
-            'message': 'Successfully parsed file.',
+            'message': 'Successfully parsed files.',
             'result': result
         }), OK_STATUS_CODE)
     else:
         return generate_response(jsonify({
             'success': False,
-            'message': 'No such file'
+            'message': 'No such project'
         }), BAD_REQUEST_STATUS_CODE)
 
 ### FILE ###
@@ -431,14 +434,30 @@ def create_file_request():
     project.edited = datetime.now()
     db.update_project(project)
 
+    # Add file locally on server if project is open
+    open_project_path = str(new_file.projectId) + '/'
+    if os.path.exists(open_project_path):
+        if new_file.parent:
+            file_path = open_project_path + new_file.parent + '/' + new_file.name
+        else:
+            file_path = open_project_path + new_file.name
+
+        if new_file.isFolder:
+            os.makedirs(file_path)
+        else:
+            f = open(file_path, 'w+')
+            f.write(new_file.content)
+            f.close()
+
     return generate_response(db.file_schema.jsonify(
         new_file), OK_STATUS_CODE)
 
 @app.route('/api/update_file/<file_id>', methods=['PUT'])
 @jwt_required
 def update_file_request(file_id):
-    # Update file
     file = request.json['file']
+    old_file = db.get_file(file['id'])
+
     updated_file = db.File(
         file['projectId'],
         file['name'],
@@ -452,6 +471,21 @@ def update_file_request(file_id):
     project.edited = datetime.now()
     db.update_project(project)
 
+    # Update file locally on server if project is open
+    open_project_path = str(project.id) + '/'
+    if old_file.parent:
+        old_path = open_project_path + old_file.parent + '/' + old_file.name
+    else:
+        old_path = open_project_path + old_file.name
+    if os.path.exists(old_path):
+        with SimpleFlock(str(project.id) + 'lock', SIMPLE_FLOCK_TIMEOUT):
+            if os.path.exists(old_path):
+                if file['parent']:
+                    new_path = open_project_path + file['parent'] + '/' + file['name']
+                else:
+                    new_path = open_project_path + file['name']
+                os.rename(old_path, new_path)
+
     return generate_response(db.file_schema.jsonify(
         db.update_file(updated_file)), OK_STATUS_CODE)
 
@@ -464,6 +498,20 @@ def delete_file_request(file_id):
     project.edited = datetime.now()
     db.update_project(project)
 
+    # Remove file locally on server if project is open
+    open_project_path = str(file.projectId) + '/'
+    if file.parent:
+        path = open_project_path + file.parent + '/' + file.name
+    else:
+        path = open_project_path + file.name
+    if os.path.exists(path):
+        with SimpleFlock(str(file.projectId) + 'lock', SIMPLE_FLOCK_TIMEOUT):
+            if os.path.exists(path):
+                if file.isFolder:
+                    shutil.rmtree(path)
+                else:
+                    os.remove(path)
+
     return generate_response(db.file_schema.jsonify(
         db.delete_file(file_id)), OK_STATUS_CODE)
 
diff --git a/backend/db.sqlite b/backend/db.sqlite
index 487f58053c2e48871332198feaef47f8ecd16bd9..8877bab2689f0f7d27a34cbe7a6def330c44a19f 100644
Binary files a/backend/db.sqlite and b/backend/db.sqlite differ
diff --git a/backend/socketio_helper.py b/backend/socketio_helper.py
index 1b2b7245047dd11954a7136e9f554114fc82a6df..6dfe599e7c1c9a707e16d90db91418c483f5c8d9 100644
--- a/backend/socketio_helper.py
+++ b/backend/socketio_helper.py
@@ -1,9 +1,14 @@
-from __init__ import app
-import database_helper as db
+import os, shutil
 
 from flask import json
 from flask_socketio import SocketIO, send, emit, join_room, leave_room
 import diff_match_patch as dmp_module
+from simpleflock import SimpleFlock
+
+from __init__ import app
+import database_helper as db
+
+SIMPLE_FLOCK_TIMEOUT = 10
 
 socketio = SocketIO(app)
 dmp = dmp_module.diff_match_patch()
@@ -150,6 +155,18 @@ def on_join_open_project_room(data):
     room = 'openProject' + str(project_id)
     join_room(room)
     add_project_viewer(project_id, username)
+
+    if not os.path.exists(str(project_id)):
+        os.mkdir(str(project_id))
+        for file in db.get_project_files(project_id):
+            dir_name = str(project_id) + ('/' + file['parent'] if file['parent'] else '')
+            if not os.path.exists(dir_name):
+                os.makedirs(dir_name)
+            if not file['isFolder']:
+                f = open(dir_name + '/' + file['name'], "a+")
+                f.write(file['content'])
+                f.close()
+
     emit('joinOpenProjectRoom', {
         'on': 'joinOpenProjectRoom',
         'username': username,
@@ -164,6 +181,10 @@ def on_leave_open_project_room(data):
     room = 'openProject' + str(project_id)
     leave_room(room)
     remove_project_viewer(project_id, username)
+
+    if not project_viewers[str(project_id)] and os.path.exists(str(project_id)):
+        shutil.rmtree(str(project_id))
+
     emit('leaveOpenProjectRoom', {
         'on': 'leaveOpenProjectRoom',
         'username': username,
@@ -200,6 +221,20 @@ def handle_file_content_changed(data):
     file.content = dmp.patch_apply(patches, file.content)[0]
     db.update_file(file)
 
+    # Update file locally on server if project is open
+    open_project_path = str(file.projectId) + '/'
+    if os.path.exists(open_project_path):
+        if file.parent:
+            file_path = open_project_path + file.parent + '/' + file.name
+        else:
+            file_path = open_project_path + file.name
+        if os.path.exists(file_path):
+            with SimpleFlock(str(file.projectId) + 'lock', SIMPLE_FLOCK_TIMEOUT):
+                if os.path.exists(file_path):
+                    f = open(file_path, 'w')
+                    f.write(file.content)
+                    f.close()
+
     emit('fileContentChanged', {
         'on': 'fileContentChanged',
         'projectId': project_id,