'''
Created on 31.03.2018
@author: Sercan Turkmen
         Moamen Ibrahim
         Mina Ghobrial
    REFERENCEs:
    -   [1] Programmable Web Project, Exercise3, resources.py
'''

import json

from urllib.parse import unquote

from flask import Flask, request, Response, g, _request_ctx_stack, redirect, send_from_directory
from flask_restful import Resource, Api, abort
from werkzeug.exceptions import NotFound, UnsupportedMediaType

from app.utils import RegexConverter
from app import database
from app.model.mason import MasonObject

# Constants for hypermedia formats and profiles
MASON = "application/vnd.mason+json"
JSON = "application/json"
CRITIQUE_USER_PROFILE = "/critique/profiles/user-profile/"
CRITIQUE_POST_PROFILE = "/critique/profiles/post_profile/"
CRITIQUE_RATING_PROFILE = "/critique/profiles/rating-profile/"
ERROR_PROFILE = "/critique/profiles/error-profile"

# Fill these in
# Fill with the correct Apiary url"
APIARY_PROJECT = "https://critique.docs.apiary.io"
APIARY_PROFILES_URL = APIARY_PROJECT+"/#reference/profiles/"
APIARY_RELATIONS_URL = APIARY_PROJECT+"/#reference/link-relations/"

CREATE_USER_SCHEMA = json.load(open('app/schema/create_user.json'))
CREATE_RATING_SCHEMA = json.load(open('app/schema/create_rating.json'))
EDIT_USER_SCHEMA = json.load(open('app/schema/edit_user.json'))
CREATE_POSTS_SCHEMA = json.load(open('app/schema/create_posts.json'))
CREATE_REPLY_SCHEMA = json.load(open('app/schema/create_reply.json'))
EDIT_RATING_SCHEMA = json.load(open('app/schema/edit_rating.json'))
EDIT_POST_SCHEMA = json.load(open('app/schema/edit_post.json'))

LINK_RELATIONS_URL = "/critique/link-relations/"

# Borrowed from lab exercises [1]
# Define the application and the api
app = Flask(__name__, static_folder="static", static_url_path="/.")
app.debug = True
# Set the database Engine. In order to modify the database file (e.g. for
# testing) provide the database path   app.config to modify the
# database to be used (for instance for testing)
app.config.update({"Engine": database.Engine()})
# Start the RESTFUL API.
api = Api(app)

class CritiqueObject(MasonObject):  # Borrowed from lab exercises [1]
    '''
    A convenience subclass of MasonObject that defines a bunch of shorthand
    methods for inserting application specific objects into the document. This
    class is particularly useful for adding control objects that are largely
    context independent, and defining them in the resource methods would add a
    lot of noise to our code - not to mention making inconsistencies much more
    likely!

    In the code this object should always be used for root document as
    well as any items in a collection type resource.
    '''

    def __init__(self, **kwargs):  # Borrowed from lab exercises [1]
        '''
        Calls dictionary init method with any received keyword arguments. Adds
        the controls key afterwards because hypermedia without controls is not
        hypermedia.
        '''

        super(CritiqueObject, self).__init__(**kwargs)
        self["@controls"] = {}

    def add_control_up(self, href):
        '''
        This adds the up link to an object. Intended for the document object.
        '''

        self["@controls"]["up"] = {
            "href": href,
            "title": "Parent document"
        }

    def add_control_users_collection(self):
        '''
        This adds the user collection link to an object. Intended for the document object.
        '''

        self["@controls"]["collection"] = {
            "href": api.url_for(Users),
            "title": "List users"
        }

    def add_control_all_posts(self):
        '''
        This adds the posts collection link to an object. Intended for the document object.
        '''

        self["@controls"]["critique:all-posts"] = {
            "href": api.url_for(Posts),
            "title": "List posts"
        }

    def add_control_all_users(self):
        '''
        This adds the users collection link to an object. Intended for the document object.
        '''

        self["@controls"]["critique:all-users"] = {
            "href": api.url_for(Users),
            "title": "List users"
        }

    def add_control_add_user(self):
        '''
        This adds the add-user control to an object. Intended for the
        document object.
        '''

        self["@controls"]["critique:add-user"] = {
            "href": api.url_for(Users),
            "title": "Create a new user",
            "encoding": "json",
            "method": "POST",
            "schema": CREATE_USER_SCHEMA
        }

    def add_control_user_inbox(self, nickname):
        '''
        This adds the user-inbox control to an object. Intended for the
        document object.

        : param str nickname: The nickname of the user
        '''

        self["@controls"]["critique:user-inbox"] = {
            "href": api.url_for(UserInbox, nickname=nickname),
        }

    def add_control_user_river(self, nickname):
        '''
        This adds the user-river control to an object. Intended for the
        document object.

        : param str nickname: The nickname of the user
        '''

        self["@controls"]["critique:user-river"] = {
            "href": api.url_for(UserRiver, nickname=nickname),
        }

    def add_control_user_ratings(self, nickname):
        '''
        This adds the user-ratings control to an object. Intended for the
        document object.

        : param str nickname: The nickname of the user
        '''

        self["@controls"]["critique:user-ratings"] = {
            "href": api.url_for(UserRatings, nickname=nickname),
        }

    def add_control_add_reply(self, postId):
        '''
        This adds the reply to a post control to an object. Intended for the
        document object.

        : param str postId: The postId of the post to be replied
        '''

        self["@controls"]["critique:add-reply"] = {
            "href": api.url_for(Post, postId=postId),
            "title": "Add reply",
            "encoding": "json",
            "method": "POST",
            "schema": CREATE_REPLY_SCHEMA
        }

    def add_control_delete_user(self, nickname):
        '''
        Adds the delete control to an object. This is intended for any
        object that represents a user.

        : param str nickname: The nickname of the user to remove
        '''

        self["@controls"]["critique:delete"] = {
            "href": api.url_for(User, nickname=nickname),
            "title": "Delete a user",
            "method": "DELETE"
        }

    def add_control_delete_post(self, post_id):
        '''
        Adds the delete control to an object. This is intended for any
        object that represents a user's post.

        : param integer post_id : The id of the post to be deleted
        '''

        self["@controls"]["critique:delete"] = {
            "href": api.url_for(Post, postId=post_id),
            "title": "Delete a user's post",
            "method": "DELETE"
        }

    def add_control_edit_user(self, nickname):
        '''
        Adds the edit control to an object. This is intended for any
        object that represents a user.

        : param str nickname: The nickname of the user to edit
        '''

        self["@controls"]["edit"] = {
            "href": api.url_for(User, nickname=nickname),
            "title": "Edit this user",
            "method": "PUT",
            "encoding": "json",
            "schema": EDIT_USER_SCHEMA
        }

    def add_control_add_rating(self, nickname):
        '''
        This adds the add-rating control to an object. Intended for the
        document object.
        '''

        self["@controls"]["critique:add-rating"] = {
            "href": api.url_for(UserRatings, nickname=nickname),
            "title": "Create a new rating",
            "encoding": "json",
            "method": "POST",
            "schema": CREATE_RATING_SCHEMA
        }

    def add_control_sender(self, nickname):
        '''
        This adds the sender control to an object. Intended for the
        document object.
        '''

        self["@controls"]["critique:sender"] = {
            "href": api.url_for(User, nickname=nickname),
            "title": "Sender of the resource",
        }

    def add_control_receiver(self, nickname):
        '''
        This adds the receiver control to an object. Intended for the
        document object.
        '''

        self["@controls"]["critique:receiver"] = {
            "href": api.url_for(User, nickname=nickname),
            "title": "Receiver of the resource",
        }

    def add_control_edit_rating(self, nickname, ratingId):
        '''
        This adds the link to edit a given rating for the
        document object.
        '''
        self["@controls"]["edit"] = {
            "href": api.url_for(Rating, nickname=nickname, ratingId=ratingId),
            "title": "Edit Rating",
            "method": "PUT",
            "encoding": "json",
            "schema": EDIT_RATING_SCHEMA
        }

    def add_control_delete_rating(self, nickname, ratingId):
        '''
        This adds the link to delete a given rating for the
        document object.
        '''
        self["@controls"]["critique:delete"] = {
            "href": api.url_for(Rating, nickname=nickname, ratingId=ratingId),
            "method": "DELETE"
        }

    def add_control_edit_post(self, postId):
        '''
        Adds the edit control to an object. Intended for any object
        that represents a post.

        :param str postId: ID of a specific post.
        '''
        self["@controls"]["edit"] = {
            "href": api.url_for(Post, postId=postId),
            "title": "Edit a post",
            "method": "PUT",
            "encoding": "json",
            "schema": EDIT_POST_SCHEMA
        }

# Borrowed from lab exercises [1]
def create_error_response(status_code, title, message=None):
    '''
    Creates a: py: class:`flask.Response` instance when sending back an
    HTTP error response

    : param integer status_code: The HTTP status code of the response
    : param str title: A short description of the problem
    : param message: A long description of the problem
    : rtype:: py: class:`flask.Response`
    '''

    resource_url = None
    # We need to access the context in order to access the request.path
    ctx = _request_ctx_stack.top
    if ctx is not None:
        resource_url = request.path
    envelope = MasonObject(resource_url=resource_url)
    envelope.add_error(title, message)

    # +";"+ERROR_PROFILE)
    return Response(json.dumps(envelope), status_code, mimetype=MASON)

@app.errorhandler(404)
def resource_not_found(error):  # Borrowed from lab exercises [1]
    return create_error_response(404, "Resource not found",
                                 "This resource url does not exit")

@app.errorhandler(400)
def resource_not_found(error):  # Borrowed from lab exercises [1]
    return create_error_response(400, "Malformed input format",
                                 "The format of the input is incorrect")

@app.errorhandler(500)
def unknown_error(error):  # Borrowed from lab exercises [1]
    return create_error_response(500, "Error",
                                 "The system has failed. Please, contact the administrator")

# Borrowed from lab exercises [1]
# HOOKS
@app.before_request
def connect_db():  # Borrowed from lab exercises [1]
    '''
    Creates a database connection before the request is proccessed.

    The connection is stored in the application context variable flask.g .
    Hence it is accessible from the request object.
    '''

    g.con = app.config["Engine"].connect()

@app.teardown_request
def close_connection(exc):  # Borrowed from lab exercises [1]
    '''
    Closes the database connection
    Check if the connection is created. It might be exception appear before
    the connection is created.
    '''

    if hasattr(g, "con"):
        g.con.close()

class Users(Resource):

    def get(self):
        '''
        Gets a list of all the users in the database.

        It returns always status code 200.

        RESPONSE ENTITY BODY:

         OUTPUT:
            * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
            * Profile: User
                /profiles/user-profile

        Link relations used in items: self, profile

        Semantic descriptions used in items: nickname, givenName, familyName, bio, avatar

        Link relations used in links: self, profile, add-user, up, all-posts

        Semantic descriptors used in template: items

        NOTE:
         * The attribute givenName is obtained from the column users_profile.firstname
         * The attribute familyName is obtained from the column users_profile.lastname
         * The rest of attributes match one-to-one with column names in the
           database.
        '''
        # PERFORM OPERATIONS
        # create users list
        users_db = g.con.get_users()

        # FILTER AND GENERATE THE RESPONSE
        # Create the envelope
        envelope = CritiqueObject()

        items = envelope["items"] = []

        for user in users_db:
            item = CritiqueObject(
                nickname=user["nickname"],
                givenName=user['firstname'],
                familyName=user['lastname'],
                avatar=user['avatar'],
                bio=user['bio']
            )
            items.append(item)
            item.add_control("self", href=api.url_for(
                User, nickname=user["nickname"]))
            item.add_control("profile", href=CRITIQUE_USER_PROFILE)
            item.add_control_up(api.url_for(Users))

        envelope.add_namespace("critique", LINK_RELATIONS_URL)
        envelope.add_control_all_posts()

        envelope.add_control_add_user()
        envelope.add_control("self", href=api.url_for(Users))

        # RENDER
        # +";" + CRITIQUE_USER_PROFILE)
        return Response(json.dumps(envelope), 200, mimetype=MASON)

    def post(self):
        '''
        Adds a new user in the database.

        REQUEST ENTITY BODY:
         * Media type: JSON
         * Profile: Critique_User

        Semantic descriptors used in template: nickname(mandatory), givenName(mandatory),
        email(mandatory), bio(optional), avatar(optional), familyName(optional),
        birthDate(optional), telephone(optional), gender(optional)

        RESPONSE STATUS CODE:
         * Returns 201 + the url of the new resource in the Location header
         * Return 400 User info is not well formed or entity body is missing.
         * Return 415 if it receives a media type != application/json
         * Return 422 Conflict if there is another user with the same nickname, email, mobile

        NOTE:
         * The attribute givenName is obtained from the column users_profile.firstname
         * The attribute familyName is obtained from the column users_profile.lastname
         * The rest of attributes match one-to-one with column names in the
           database.

        NOTE:
        The: py: method:`Connection.create_user()` receives as a parameter a
        dictionary with the following format.

            {
                'summary': {
                    'nickname': '',
                    'registrationdate': ,
                    'bio': '',
                    'avatar': ''
                },
                'details': {
                    'lastlogindate': ,
                    'firstname': '',
                    'lastname': '',
                    'email': '',
                    'mobile': '',
                    'gender': '',
                    'birthdate': '',
                }
            }

        '''

        if JSON != request.headers.get("Content-Type", ""):
            abort(415)

        # PARSE THE REQUEST:
        request_body = request.get_json(force=True)
        if not request_body:
            return create_error_response(415,
                                         "Unsupported Media Type")

        # pick up nickname, email, and firstName so we can check for conflicts
        try:
            nickname = request_body["nickname"]
            firstName = request_body["givenName"]
            email = request_body["email"]
        except KeyError:
            return create_error_response(400,
                                         "Wrong request format")
        # optional fields

        # Conflict if user already exist
        if g.con.contains_user_extended(nickname, email):
            return create_error_response(422,
                                         "Nickname, email, or mobile already exist in the users list.")

        user = {
            'summary': {
                'nickname': nickname,
                'bio': request_body.get('bio', None),
                'avatar': request_body.get('avatar', None)
            },
            'details': {
                'firstname': firstName,
                'lastname': request_body.get('familyName', None),
                'email': email,
                'mobile': request_body.get('telephone', None),
                'gender': request_body.get('gender', None),
                'birthdate': request_body.get('birthDate', None),
            }
        }

        try:
            nickname = g.con.create_user(nickname, user)
        except ValueError:
            return create_error_response(400,
                                         "Wrong request format")

        # CREATE RESPONSE AND RENDER
        return Response(status=201,
                        headers={"Location": api.url_for(User, nickname=nickname)})

class User(Resource):
    '''
    Resource has the basic information of a specific user.
    Bio is where the user should enter information about himself/herself,
    so it should be text. Email and telephone are unique texts as they
    will not repeat for other users. Gender, avatar and birthdate are texts.
    '''

    def get(self, nickname):
        '''
        Extract information of a user.

        INPUT PARAMETER:

        :param str nickname: Nickname of the required user.

        OUTPUT:
         * Return 200 if the nickname exists.
         * Return 404 if the nickname is not stored in the system.

        RESPONSE ENTITY BODY:

        OUTPUT:
            * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
            * Profile: User
                /critique/profiles/user-profile/

        Link relations used: self, collection, delete, user-inbox, edit,
        user-river, user-ratings, profile, up

        Semantic descriptors used: nickname, givenName, familyName, avatar,
        bio, email, birthday, telephone, gender

        NOTE:
         * The attribute givenName is obtained from the column users_profile.firstname
         * The attribute familyName is obtained from the column users_profile.lastname
         * The attribute telephone is obtained from the column users_profile.mobile
         * The rest of attributes match one-to-one with column names in the
           database.

        NOTE:
        The: py: method:`Connection.get_user()` returns a dictionary with the
        the following format.

            {
                'summary': {
                    'nickname': '',
                    'registrationdate': ,
                    'bio': '',
                    'avatar': ''
                },
                'details': {
                    'lastlogindate': ,
                    'firstname': '',
                    'lastname': '',
                    'email': '',
                    'mobile': '',
                    'gender': '',
                    'birthdate': '',
                }
            }
        '''

        # PERFORM OPERATIONS
        user_db = g.con.get_user(nickname)
        if not user_db:
            return create_error_response(404, "User not found.")

        summary = user_db['summary']
        details = user_db['details']

        # FILTER AND GENERATE RESPONSE
        # Create the envelope:
        envelope = CritiqueObject(
            nickname=nickname,
            givenName=details.get('firstname', None),
            familyName=details.get('lastname', None),
            avatar=summary.get('avatar', None),
            bio=summary.get('bio', None),
            email=details.get('email', None),
            birthdate=details.get('birthdate', None),
            telephone=details.get('mobile', None),
            gender=details.get('gender', None)
        )

        envelope.add_namespace("critique", LINK_RELATIONS_URL)

        envelope.add_control("self", href=api.url_for(User, nickname=nickname))
        envelope.add_control("profile", href=CRITIQUE_USER_PROFILE)
        envelope.add_control("collection", href=api.url_for(Users))
        envelope.add_control_edit_user(nickname)
        envelope.add_control_delete_user(nickname)
        envelope.add_control_user_inbox(nickname)
        envelope.add_control_user_river(nickname)
        envelope.add_control_user_ratings(nickname)
        envelope.add_control_up(api.url_for(Users))

        # +";" + CRITIQUE_USER_PROFILE)
        return Response(json.dumps(envelope), 200, mimetype=MASON)

    def put(self, nickname):
        '''
        Modifies mutable attributes of the specified user.

        REQUEST ENTITY BODY:
         * Media type: JSON
         * Profile: Critique_User

        Semantic descriptors used in template: givenName(optional), familyName(optional),
        avatar(optional), bio(optional), email(optional), birthdate(optional), telephone(optional),
        gender(optional)

        RESPONSE STATUS CODE:
         * Returns 204 + the url of the edited resource in the Location header
         * Return 400 User info is not well formed or entity body is missing.
         * Return 404 If user not found with given nickname.
         * Return 415 if it receives a media type != application/json
         * Return 422 Conflict if there is another user with the same nickname, email, mobile

        NOTE:
         * The attribute givenName is obtained from the column users_profile.firstname
         * The attribute familyName is obtained from the column users_profile.lastname
         * The attribute telephone is obtained from the column users_profile.mobile
         * The rest of attributes match one-to-one with column names in the
           database.

        NOTE:
        The: py: method:`Connection.modify_user()` receives as a parameter a
        dictionary with the following format.

            {
                'summary': {
                    'nickname': '',
                    'registrationdate': ,
                    'bio': '',
                    'avatar': ''
                },
                'details': {
                    'lastlogindate': ,
                    'firstname': '',
                    'lastname': '',
                    'email': '',
                    'mobile': '',
                    'gender': '',
                    'birthdate': '',
                }
            }
        '''

        if JSON != request.headers.get("Content-Type", ""):
            return create_error_response(415, "UnsupportedMediaType",
                                         "Use a JSON compatible format")

        user_db = g.con.get_user(nickname)
        if not user_db:
            return create_error_response(404, "User not found.")

        summary = user_db['summary']
        details = user_db['details']

        request_body = request.get_json()
        if not request_body:
            return create_error_response(415, "Format of the input is not json.")

        email = request_body.get('email', None)
        if email is not None and details['email'] != email and g.con.contains_user_email(email):
            return create_error_response(
                422, "Nickname, email, or mobile already exist in the users list.")

        summary['avatar'] = request_body.get(
            'avatar', summary['avatar'])
        summary['bio'] = request_body.get(
            'bio', summary['bio'])

        details['firstname'] = request_body.get(
            'givenName', details['firstname'])
        details['lastname'] = request_body.get(
            'familyName', details['lastname'])
        details['mobile'] = request_body.get(
            'telephone', details['mobile'])
        details['email'] = request_body.get(
            'email', details['email'])
        details['birthdate'] = request_body.get(
            'birthdate', details['birthdate'])
        details['gender'] = request_body.get(
            'gender', details['gender'])

        if not g.con.modify_user(nickname, summary, details):
            return create_error_response(500, "The system has failed. Please, contact the administrator.")

        return Response(status=204,
                        headers={"Location": api.url_for(User, nickname=nickname)})

    def delete(self, nickname):
        '''
        Deletes the specified user.

        :param str nickname: The nickname of the user. Example: Scott.

        RESPONSE STATUS CODE:

            * If the user is deleted returns 204.
            * If the nickname does not exist return 404
            * If there was a db error return 500

        '''
        # PEROFRM OPERATIONS

        userExist = g.con.contains_user(nickname)
        if not userExist:
            return create_error_response(404, "User not found.")

        # Try to delete the user. If it could not be deleted, the database
        # returns None.
        try:
            if g.con.delete_user(nickname):
                # RENDER RESPONSE
                return Response('', 204)
            else:
                # GENERATE ERROR RESPONSE
                return create_error_response(404, "User not found.")
        except:
            return create_error_response(500,
                                         "The system has failed. Please, contact the administrator.")

class UserRatings(Resource):
    '''
    Contains the ratings list with ratings from all other users to this specific user.
    It should have a track of the ratings made by users to each other including the person
    who made the rating and that who received it. Also, it contains the rating id to keep
    track of the ratings.
    '''

    def post(self, nickname):
        '''
        Adds a new rating in the database.

        REQUEST ENTITY BODY:
         * Media type: JSON
         * Profile: rating-profile

        Semantic descriptors used in template: ratingId (mandatory), sender (mandatory), receiver (mandatory),
                            bestRating(optional), ratingValue(mandatory)

        RESPONSE STATUS CODE:
         * Returns 201 + the url of the new resource in the Location header if the rating was successfully created
         * Return 400 if Rating info is not well formed or entity body is missing.
         * Return 404 if User not found.
         * Return 415 if it receives a media type != application/json
         * Return 422 if Sender or reciever not found
         * Return 500 in case of system failure.

        NOTE:
         * The attribute givenName is obtained from the column users_profile.firstname
         * The attribute familyName is obtained from the column users_profile.lastname
         * The rest of attributes match one-to-one with column names in the
           database.

        NOTE:
        The: py: method:`Connection.create_rating()` receives as a parameter a
        dictionary with the following format.

            {
                'rating_id': '',
                'timestamp': '',
                'sender': '',
                'receiver': '',
                'rating': ''
            }
        '''

        if JSON != request.headers.get("Content-Type", ""):
            abort(415)

        # PARSE THE REQUEST:
        request_body = request.get_json(force=True)
        if not request_body:
            return create_error_response(415,
                                         "Unsupported Media Type")

        userExist = g.con.contains_user(nickname)
        if not userExist:
            return create_error_response(404, "User not found.")

        # pick up rating_id so we can check for conflicts
        try:
            sender = request_body["sender"]
            rating = request_body["ratingValue"]
        except KeyError:
            return create_error_response(400,
                                         "Wrong request format")
        # optional fields

        userExist = g.con.contains_user(sender)
        if not userExist:
            return create_error_response(404, "Sender not found.")

        try:
            rating_id = g.con.create_rating(sender, nickname, rating)
        except ValueError:
            return create_error_response(400,
                                         "Wrong request format")

        # CREATE RESPONSE AND RENDER
        return Response(status=201,
                        headers={"Location": api.url_for(Rating,
                                                         nickname=nickname, ratingId=rating_id)})

    def get(self, nickname):
        '''
        Extracts the ratings given to the user.

        INPUT PARAMETER:

        :param str nickname: The nickname of the user. Example: `Scott`.

        OUTPUT:
            * Return 200 if the nickname exists.
            * Return 404 if the nickname not found.

        RESPONSE ENTITY BODY:

        OUTPUT:
            * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
            * Profile: Rating
                /profiles/rating-profile

        Link relations used in items: self, profile, sender, receiver, up

        Semantic descriptions used in items: ratingId, bestRating, ratingValue, sender, receiver

        Link relations used in links: self, add-rating, up

        Semantic descriptors used in template: items

        NOTE:
            * The attribute ratingId is obtained from the column ratings.rating_id
            * The attribute ratingValue is obtained from the column ratings.rating

        NOTE:
            * Database returns in this format
                {
                    rating_id: "string",
                    timestamp: "number",
                    sender: "string",
                    receiver: "string",
                    rating: "string",
                }
        '''

        userExist = g.con.contains_user(nickname)
        if not userExist:
            return create_error_response(404, "User not found.")

        # PERFORM OPERATIONS
        # create ratings list
        ratings_db = g.con.get_ratings(receiver=nickname)

        # FILTER AND GENERATE THE RESPONSE
        # Create the envelope
        envelope = CritiqueObject()

        items = envelope["items"] = []

        for rating in ratings_db:
            item = CritiqueObject(
                ratingId=rating["rating_id"],
                bestRating=10,
                ratingValue=rating['rating'],
                sender=rating['sender'],
                receiver=rating['receiver']
            )

            items.append(item)
            item.add_control_sender(rating['sender'])
            item.add_control_receiver(rating['receiver'])
            item.add_control("self",
                             href=api.url_for(Rating, nickname=nickname, ratingId=rating["rating_id"]))
            item.add_control("profile", href=CRITIQUE_RATING_PROFILE)
            item.add_control_up(api.url_for(UserRatings, nickname=nickname))

        envelope.add_namespace("critique", LINK_RELATIONS_URL)

        envelope.add_control("self", href=api.url_for(
            UserRatings, nickname=nickname))
        envelope.add_control_add_rating(nickname)
        envelope.add_control_up(api.url_for(User, nickname=nickname))

        # RENDER
        # +";" + CRITIQUE_RATING_PROFILE)
        return Response(json.dumps(envelope), 200, mimetype=MASON)

class UserInbox(Resource):

    '''
    Contains private posts sent to the user, it should include text as the actual post,
    however, rating is optional in the message.
    '''

    def get(self, nickname):
        '''
        Get posts sent to the user which are currently not public

        INPUT PARAMETER:

       :param user_id: default is None, takes the user id of the user
            that you want the posts of. if the parameter is None, it
            will raise a ValueError exception.
        :type nickname: nickname of the user

        OUTPUT:
            * Return 200 if the nickname exists.
            * Return 404 if the nickname not found.

        RESPONSE ENTITY BODY:

        OUTPUT:
            * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
            * Profile: Inbox
                /profiles/post_profile

        Link relations used in items: self, profile, sender, receiver, up, delete, edit

        Semantic descriptions used in items: sender, receiver, timestamp, postId, replyTo, body, anonymous, public

        Link relations used in links: self, add-post, add-reply, up

        Semantic descriptors used in template: items

        '''

        userExist = g.con.contains_user(nickname)
        if not userExist:
            return create_error_response(404, "User not found.")

        # FILTER AND GENERATE THE RESPONSE
        # Create the envelope
        envelope = CritiqueObject()

        items = envelope["items"] = []

        # PEFORM OPERATIONS INITIAL CHECKS
        # Get the post from db
        post_db = g.con.get_posts_by_user(nickname, False)
        if not post_db:
            return create_error_response(404, "Posts not found",
                                         "There is no posts for %s" % nickname)

        for post in post_db:
            item = CritiqueObject(
                postId=post["post_id"],
                sender=post['sender'],
                timestamp=post['timestamp'],
                bestRating=10,
                ratingValue=post['rating'],
                receiver=post['receiver'],
                replyTo=post['reply_to'],
                body=post['post_text'],
                anonymous=post['anonymous'],
                public=post['public']
            )
            if (post['public'] == 0):
                # check if the post is not public and then append
                items.append(item)
                item.add_control("self",
                                    href=api.url_for(Post, postId=post['post_id']))
                item.add_control("profile", href=CRITIQUE_POST_PROFILE)
                item.add_control_delete_post(post['post_id'])
                item.add_control_edit_post(post['post_id'])
                item.add_control_sender(post['sender'])
                if post["receiver"] is not None:
                    item.add_control_receiver(post["receiver"])
                item.add_control_add_reply(post['post_id'])
                item.add_control_up(api.url_for(UserInbox, nickname=nickname))

        envelope.add_namespace("critique", LINK_RELATIONS_URL)
        envelope.add_control("profile", href=CRITIQUE_POST_PROFILE)
        envelope.add_control("self", href=api.url_for(
            UserInbox, nickname=nickname))
        envelope.add_control_up(api.url_for(User, nickname=nickname))

        # RENDER
        # +";" + CRITIQUE_POST_PROFILE)
        return Response(json.dumps(envelope), 200, mimetype=MASON)

    def post(self, nickname):
        '''
        Creates a new post to the list of posts. Returns the post URI.

        INPUT PARAMETERS:
        :param nickname: nickname of the user who is sending.
        :type nickname: string
        :param receiver_nickname: nickname of the user that we are sending to.
        :type receiver_nickname: string

        REQUEST ENTITY BODY:
        * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
        * Profile: Post Profile
                /critique/profiles/post_profile

        Semantic descriptors used in template: post_text(mandatory),
        anonymous(mandatory)

        RESPONSE STATUS CODE:
         * Returns 201 + the url of the new resource in the Location header
         * Return 400 Post info is not well formed or entity body is missing.
         * Return 415 if it receives a media type != application/json
         * Return 422 Conflict if the sender or receiver are not found

        NOTE:
        The: py: method:`Connection.create_post()` receives as a parameter
        a dictionary with the following format:
            {
                'post_id': '',
                'receiver': '',
                'timestamp': '',
                'reply_to': '',
                'post_text': '',
                'rating': '',
                'anonymous': '',
                'public': ''
            }
         '''
        # CONTENT TYPE CHECK
        if JSON != request.headers.get("Content-Type", ""):
            abort(415)

        # PARSE REQUEST
        request_body = request.get_json(force=True)
        if not request_body:
            return create_error_response(415,
                                         "Unsupported Media Type")

        # CHECK IF USER EXISTS
        userExist = g.con.contains_user(nickname)
        if not userExist:
            return create_error_response(404, "User not found")

        # check mandatory fields
        try:
            post_text = request_body["body"]
            sender_nickname = request_body["sender"]
            anonymous = request_body["anonymous"]
        except KeyError:
            return create_error_response(400, "Wrong request format")

        userExist = g.con.contains_user(sender_nickname)
        if not userExist:
            return create_error_response(404, "Sender user not found")

        new_post_id = g.con.create_post(sender_nickname=sender_nickname,
                                        receiver_nickname=nickname,
                                        reply_to=None,
                                        post_text=post_text,
                                        anonymous=anonymous,
                                        public=0,
                                        rating=None)
        if not new_post_id:
            return create_error_response(500, "Problem with database",
                                         "can not access database.")

        url = api.url_for(UserInbox, nickname=nickname, postId=new_post_id)

        return Response(status=201,  headers={"Location": url})

class UserRiver(Resource):
    '''
    Contains public posts sent to the user, it should include text as the actual post,
    however, rating is optional in the posts.
    '''

    def get(self, nickname):
        '''
        Get posts sent to the user which are currently public

        INPUT PARAMETER:

       :param user_id: default is None, takes the user id of the user
            that you want the posts of. if the parameter is None, it
            will raise a ValueError exception.
        :type nickname: nickname of the user

        OUTPUT:
            * Return 200 if the nickname exists.
            * Return 404 if the nickname not found.

        RESPONSE ENTITY BODY:

        OUTPUT:
            * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
            * Profile: River
                /profiles/post_profile

        Link relations used in items: self, profile

        Semantic descriptions used in items: sender, receiver, timestamp, postId, replyTo, body, anonymous, public

        Link relations used in links: self, add-post, add-reply

        Semantic descriptors used in template: items

        '''

        userExist = g.con.contains_user(nickname)
        if not userExist:
            return create_error_response(404, "User not found.",
                                         "can't find a user to retrieve their river")

        # FILTER AND GENERATE THE RESPONSE
        # Create the envelope
        envelope = CritiqueObject()

        items = envelope["items"] = []

        # PEFORM OPERATIONS INITIAL CHECKS
        # Get the post from db and create posts list
        post_db = g.con.get_posts_by_user(nickname, False)
        if not post_db:
            return create_error_response(404, "Message not found",
                                         "There is no a message with id %s" % nickname)

        for post in post_db:
            item = CritiqueObject(
                postId=post["post_id"],
                ratingValue=post['rating'],
                receiver=post['receiver'],
                replyTo=post['reply_to'],
                body=post['post_text'],
                timestamp=post['timestamp'],
                bestRating=10,
                anonymous=post['anonymous'],
                public=post['public'],
                sender=post["sender"]
            )

            if (post['public'] == 1):
                # check if the post is public and then append
                items.append(item)
                item.add_control("self",
                                 href=api.url_for(UserRiver, nickname=post['receiver']))
                item.add_control("profile", href=CRITIQUE_POST_PROFILE)
                item.add_control_delete_post(post['post_id'])
                item.add_control_edit_post(post['post_id'])
                item.add_control_sender(post['sender'])
                item.add_control_receiver(post['receiver'])
                item.add_control_up(api.url_for(UserRiver, nickname=post['receiver']))

        envelope.add_namespace("critique", LINK_RELATIONS_URL)
        envelope.add_control_user_river(nickname)
        envelope.add_control_add_reply(nickname)
        envelope.add_control_up(api.url_for(User, nickname=nickname))
        envelope.add_control("profile", href=CRITIQUE_POST_PROFILE)
        envelope.add_control("self", href=api.url_for(
            UserRiver, nickname=nickname))

        # if post['reply_to']:
        #     envelope.add_control("atom-thread:in-reply-to",
        #                          href=api.url_for(Post, postId=post['post_id']))
        # else:
        #     envelope.add_control("atom-thread:in-reply-to", href=None)

        # RENDER
        # +";" + CRITIQUE_POST_PROFILE)
        return Response(json.dumps(envelope), 200, mimetype=MASON)

class Ratings(Resource):
    '''
    Contains the ratings list with ratings from all other users to this specific user.
    It should have a track of the ratings made by users to each other including the person
    who made the rating and that who received it. Also, it contains the rating id to keep
    track of the ratings.
    '''

    def get(self):
        return Response('NOT IMPLEMENTED', 200)

class Post(Resource):
    '''
    Returns the information needed from a specific post where it contains the post text,
    timestamp, sender id and receiver id which should not be null value.
    Post_text is the post content that holds the data needed while rating is an integer of
    the ratings to the post from others.
    Public is an integer flag indicating if the post is public or private.
    While anonymous is the same but indicates whether the user wants to post it without being known.
    '''

    def get(self, postId):
        '''
        Extracts a post and all it's information.

        :param str postId: ID of the requested post

        OUTPUT:
         * Return 200 if the post exists.
         * Return 404 if the post is not found.
         * Return 500 in case of system failure.

        RESPONSE ENTITY BODY:

        OUTPUT:
         * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
         * Profile: Post
                /critique/profiles/post-profile/

        Link relations used: self, profile, add-reply, delete, edit,
        collection, up, sender, receiver
        '''
        post_db = g.con.get_post(postId)
        if not post_db:
            return create_error_response(404, "Post not found.")

        envelope = CritiqueObject(
            sender=post_db["sender"],
            receiver=post_db["receiver"],
            timestamp=post_db["timestamp"],
            postId=post_db["post_id"],
            body=post_db["post_text"],
            replyTo=post_db["reply_to"],
            anonymous=post_db["anonymous"],
            public=post_db["public"],
            bestRating=10,
            ratingValue=post_db["rating"]
        )
        envelope.add_namespace("critique", LINK_RELATIONS_URL)

        envelope.add_control("self", href=api.url_for(Post, postId=postId))
        envelope.add_control("profile", href=CRITIQUE_POST_PROFILE)
        envelope.add_control("collection", href=api.url_for(Posts))
        envelope.add_control_edit_post(postId=postId)
        envelope.add_control_add_reply(postId=postId)
        envelope.add_control_delete_post(post_id=postId)
        envelope.add_control_sender(nickname=post_db["sender"])
        up_url = api.url_for(Posts)
        if post_db["public"] == 1 and post_db["reply_to"] is None:
            up_url = api.url_for(UserRiver, nickname=post_db["receiver"])
        elif post_db["public"] == 0:
            up_url = api.url_for(UserInbox, nickname=post_db["receiver"])
        envelope.add_control_up(up_url)
        if post_db["receiver"] is not None:
            envelope.add_control_receiver(nickname=post_db["receiver"])

        # +";"+ CRITIQUE_POST_PROFILE)
        return Response(json.dumps(envelope), 200, mimetype=MASON)

    def post(self, postId):
        '''
        posts a post as reply.

        REQUEST ENTITY BODY:
         * Media type: JSON
         * Profile: Critique_Post

        RESPONSE STATUS CODE:
         * Returns 201 + the url of the new resource in the Location header
         * Return 400 Post info is not well formed or entity body is missing.
         * Return 404 Post not found.
         * Return 415 if it receives a media type != application/json
         * Return 422 Sender not found.

        NOTE:
        The: py: method:`Connection.create_post()` receives as a parameter
        a dictionary with the following format:
            {
                'post_id': '',
                'sender': '',
                'timestamp': '',
                'reply_to': '',
                'post_text': '',
                'rating': '',
                'anonymous': '',
                'public': ''
            }
        '''
        # check format
        if JSON != request.headers.get("Content-Type", ""):
            abort(415)

        # check if postId is available
        postExists = g.con.contains_post(post_id=postId)
        if not postExists:
            return create_error_response(404, "Post not found",
                                         "There is no post with id %s" % postId)

        # parse request
        request_body = request.get_json(force=True)
        if not request_body:
            return create_error_response(415, "Unsupported Media Type")

        try:
            sender = request_body['sender']
            post_text = request_body['body']
        except KeyError:
            return create_error_response(400, "Wrong request format",
                                         "Post body missing or sender nickname invalid")

        userExist = g.con.contains_user(sender)
        if not userExist:
            return create_error_response(404, "Sending user not found")

        # getting the receiving user
        # parent_post_db = g.con.get_post(postId)
        # reply_to = parent_post_db['post_id']

        new_post_id = g.con.create_post(sender_nickname=sender,
                                        receiver_nickname=None,
                                        reply_to=postId,
                                        post_text=post_text,
                                        anonymous=0,
                                        public=1,
                                        rating=None)
        if not new_post_id:
            return create_error_response(500, "Problem with database",
                                         "can not access database.")

        url = api.url_for(Post, postId=new_post_id)

        return Response(status=201,  headers={"Location": url})

    def delete(self, postId):
        '''
        Deletes a specific post.

        :param str postId: ID of the post to delete.

        RESPONSE STATUS CODE:
         * Return 204 Post deleted successfully.
         * Return 404 Post not found.
         * Return 500 The system has failed. Please, contact the administrator.
        '''
        postExist = g.con.contains_post(postId)
        if not postExist:
            return create_error_response(404, "Post not found.")

        try:
            g.con.delete_post(postId)
        except:
            return create_error_response(500,
                                         "The system has failed. Please, contact the administrator.")
        return Response('', 204)

    def put(self, postId):
        '''
        Modifies the contents of a specified post.

        REQUEST ENTITY BODY:
         * Media type: JSON
         * Profile: Critique_Post

        Semantic descriptors used in template: post_text (optional)

        RESPONSE STATUS CODE:
         * Returns 204 + the url of the edited resource in the Location header
         * Return 404 If post not found with given id.
         * Return 415 if it receives a media type != application/json

        NOTE:
         * The attribute post_text is obtained from the column posts_profile.post_text
         * The rest of attributes match one-to-one with column names in the
           database.
        '''

        post_db = g.con.get_post(postId)
        if not post_db:
            return create_error_response(404, "Post not found.")

        if JSON != request.headers.get("Content-Type", ""):
            return create_error_response(415, "UnsupportedMediaType",
                                         "Use a JSON compatible format")

        request_body = request.get_json(force=True)
        if not request_body:
            return create_error_response(415, "Format of the input is not json.")
        try:
            post_db['post_text'] = request_body.get(
                'body', post_db['post_text'])
            post_db['rating'] = request_body.get(
                'ratingValue', post_db['rating'])
            post_db['public'] = request_body.get(
                'public', post_db['public'])
        except KeyError:
            return create_error_response(400, "Wrong request format",
                                         "Can't acquire, either post body, rating or public value")
        if not g.con.modify_post(postId, post_db['post_text'], post_db['rating'], post_db['public']):
            return create_error_response(500, "The system has failed. Please, contact the administrator.")

        return Response(status=204,
                        headers={"Location": api.url_for(Post, postId=postId)})

class Rating(Resource):
    '''
    Contains the ratings list with ratings from all other users to this specific user.
    It should have a track of the ratings made by users to each other including the person
    who made the rating and that who received it. Also, it contains the rating id to keep
    track of the ratings.
    '''

    def get(self, nickname, ratingId):
        '''
        Extract a rating from the database.

        Returns status code 404 if the ratingId does not exist in the database.

        INPUT PARAMETER
        :param str ratingId: ID of the rating to be retrieved from the system.

        OUTPUT:
         * Return 200 if the rating ID exists.
         * Return 404 if the rating ID not found.
         * Return 500 in case of system failure.

        RESPONSE ENTITY BODY:

        OUTPUT:
            * Media type: application/vnd.mason+json
                https://github.com/JornWildt/Mason
            * Profile: Rating Profile
                /critique/profiles/rating-profile

        Link relations used: add-rating, edit, delete, self, profile, collection.

        '''

        ratingExist = g.con.contains_rating(ratingId)
        if not ratingExist:
            return create_error_response(404, "Rating not found",
                                         "There is no rating with the given ID.")

        userExist = g.con.contains_user(nickname)
        if not userExist:
            return create_error_response(404, "Resquested user not found",
                                         "There is no user with the given nickname")
        # Filter and generate the response
        # Create the envelope
        # get the rating from DB
        rating_db = g.con.get_rating(ratingId)
        item = CritiqueObject(
            bestRating=10,
            ratingValue=rating_db["rating"],
            ratingId=rating_db["rating_id"],
            sender=rating_db["sender"],
            receiver=rating_db["receiver"]
        )
        item.add_namespace("critique", LINK_RELATIONS_URL)

        item.add_control("self", href=api.url_for(
            Rating, nickname=rating_db["receiver"], ratingId=rating_db["rating_id"]))
        item.add_control("profile", href=CRITIQUE_RATING_PROFILE)
        item.add_control("collection", href=api.url_for(
            UserRatings, nickname=rating_db["receiver"]))
        item.add_control_edit_rating(
            ratingId=rating_db["rating_id"], nickname=rating_db["receiver"])
        item.add_control_delete_rating(
            ratingId=rating_db["rating_id"], nickname=rating_db["receiver"])
        item.add_control_sender(nickname=rating_db["sender"])
        item.add_control_receiver(nickname=rating_db["receiver"])
        item.add_control_up(api.url_for(UserRatings, nickname=rating_db["receiver"]))

        # +";" + CRITIQUE_RATING_PROFILE)
        return Response(json.dumps(item), 200, mimetype=MASON)

    def put(self, nickname, ratingId):
        '''
        Modifies a rating.

        REQUEST ENTITY BODY:
         * Media type: JSON
         * Profile: Critique_User

        Semantic descriptors used in template: ratingId (required), new_rating (optional)

        RESPONSE STATUS CODE:
         * Returns 204 + the url of the edited resource in the Location header
         * Return 404 If rating not found with given id.
         * Return 415 if it receives a media type != application/json
         * Return 500 if there was a db error

        NOTE:
         * The attribute ratingId is obtained from the column ratings_profile.ratingId
         * The attribute new_rating is obtained from the column ratings_profile.new_rating
         * The rest of attributes match one-to-one with column names in the
           database.
        '''

        rating_db = g.con.get_rating(ratingId)
        if not rating_db:
            return create_error_response(404, "User not found.")

        if JSON != request.headers.get("Content-Type", ""):
            return create_error_response(415, "UnsupportedMediaType",
                                         "Use a JSON compatible format")

        request_body = request.get_json(force=True)
        if not request_body:
            return create_error_response(415, "Format of the input is not json.")
        try:
            rating_db['rating'] = request_body.get(
                'ratingValue', rating_db['rating'])
        except KeyError:
            return create_error_response(400, "Wrong request format",
                                         "rating missing")

        if not g.con.modify_rating(ratingId, rating_db['rating']):
            return create_error_response(500, "The system has failed. Please, contact the administrator.")

        return Response(status=204,
                        headers={"Location": api.url_for(Rating, nickname=nickname, ratingId=ratingId)})

    def delete(self, nickname, ratingId):
        '''
        Deletes the specified rating.

        :param integer ratingId: The id of the a rating. Example: rtg-015.

        RESPONSE STATUS CODE:

            * If the rating is deleted returns 204.
            * If the rating does not exist return 404
            * If there was a db error return 500
        '''
        # PEROFRM OPERATIONS

        ratingExist = g.con.contains_rating(ratingId)
        if not ratingExist:
            return create_error_response(404, "Rating not found.")

        # Try to delete the Rating. If it could not be deleted, the database
        # returns None.

        try:
            g.con.delete_rating(ratingId)
        except:
            return create_error_response(500,
                                         "The system has failed. Please, contact the administrator.")
        return Response('', 204)

class Posts(Resource):
    def get(self):
        return Response('NOT IMPLEMENTED', 200)

# Add the Regex Converter so we can use regex expressions when we define the
# routes
# Borrowed from lab exercises [1]
app.url_map.converters["regex"] = RegexConverter

# Define the routes
api.add_resource(Users, "/critique/api/users/",
                 endpoint="users")
api.add_resource(User, "/critique/api/users/<nickname>/",
                 endpoint="user")
api.add_resource(UserInbox, "/critique/api/users/<nickname>/inbox/",
                 endpoint="inbox")
api.add_resource(UserRiver, "/critique/api/users/<nickname>/river/",
                 endpoint="river")
api.add_resource(UserRatings, "/critique/api/users/<nickname>/ratings/",
                 endpoint="user-ratings")
api.add_resource(Rating, "/critique/api/users/<nickname>/ratings/<regex('rtg-\d+'):ratingId>/",
                 endpoint="rating")
api.add_resource(Posts, "/critique/api/posts/",
                 endpoint="posts")
api.add_resource(Post, "/critique/api/posts/<postId>/",
                 endpoint="post")

# Redirect profile

# Borrowed from lab exercises [1]
@app.route("/critique/profiles/<profile_name>/")
def redirect_to_profile(profile_name):
    return redirect(APIARY_PROFILES_URL + profile_name)

# Borrowed from lab exercises [1]
@app.route("/critique/link-relations/<rel_name>/")
def redirect_to_relations(rel_name):
    return redirect(APIARY_RELATIONS_URL + rel_name)

# Send our schema file(s)

# @app.route("/critique/schema/<schema_name>/")
# def send_json_schema(schema_name):
#     #return send_from_directory("static/schema", "{}.json".format(schema_name))
#     return send_from_directory(app.static_folder, "schema/{}.json".format(schema_name))

# Start the application
# DATABASE SHOULD HAVE BEEN POPULATED PREVIOUSLY
if __name__ == '__main__':  # Borrowed from lab exercises [1]
    # Debug true activates automatic code reloading and improved error messages
    app.run(debug=True)


#!/usr/bin/env python
# coding: utf-8

# ### Importing the libraries

# In[54]:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# ### Importing the dataset

# In[55]:

covid_death_dataset = pd.read_csv("covid19_deaths_dataset.csv")

# In[56]:

covid_death_dataset.head(10)
covid_death_dataset.shape

# In[57]:

covid_death_dataset.head(10)

# In[58]:

del_col = ["Lat","Long"]

# In[59]:

covid_death_dataset.drop(del_col, axis = 1,inplace=True)

# In[60]:

covid_death_dataset.head(10)

# In[61]:

death_aggregation = covid_death_dataset.groupby("Country/Region").sum()

# In[62]:

death_aggregation.head(10)

# ### Analyze the death rate for India and china

# In[63]:

death_aggregation.loc["India"].plot()
death_aggregation.loc["China"].plot()
plt.legend()

# In[64]:

countries = list(death_aggregation.index)
max_death_rate = []
for i in countries:
    max_death_rate.append(death_aggregation.loc[i].diff().max())
death_aggregation["max_death_rate"] = max_death_rate

# In[65]:

death_aggregation.head(10)

# In[66]:

death_data = pd.DataFrame(death_aggregation["max_death_rate"])

# In[67]:

death_data.head(10)

# ### Importing hapiness dataset

# In[68]:

hapiness_data = pd.read_csv("worldwide_happiness_report.csv")

# In[69]:

hapiness_data.head(10)

# In[70]:

col_to_drop = ["Overall rank", "Score","Generosity","Perceptions of corruption"]

# In[71]:

hapiness_data.drop(col_to_drop,axis=1,inplace=True)
hapiness_data.head(10)

# In[72]:

new_df = hapiness_data.set_index("Country or region")

# # # Joining the hapiness data set and death dataset

# In[73]:

dea_happ_data = new_df.join(death_data,how='inner')

# In[74]:

new_df.head(10)

# ### Finding the corelation

# In[75]:

dea_happ_data.corr()

# In[76]:

dea_happ_data.head(10)

# ## Visualizing

# ### visualizing GDP per  capita vs  max_death_rate

# In[77]:

x = dea_happ_data['GDP per capita']
y = dea_happ_data['max_death_rate']
sns.scatterplot(x,np.log(1+y))

# In[78]:

sns.regplot(x, np.log(1+y))

# ### visualizing Social Support vs max_death_rate

# In[79]:

x = dea_happ_data['Social support']
y = dea_happ_data['max_death_rate']
sns.scatterplot(x,np.log(1+y))

# In[80]:

sns.regplot(x, np.log(1+y))

# ### visualizing Healthy life expectancy	 vs max_death_rate

# In[81]:

x = dea_happ_data['Healthy life expectancy']
y = dea_happ_data['max_death_rate']
sns.scatterplot(x,np.log(1+y))

# In[82]:

sns.regplot(x, np.log(1+y))

# ### Visualizing Freedom to make life choices vs max_death_rate

# In[83]:

x = dea_happ_data['Freedom to make life choices']
y = dea_happ_data['max_death_rate']
sns.scatterplot(x,np.log(1+y))

# In[84]:

sns.regplot(x, np.log(1+y))

# # Importing covid confirmed dataset

# In[85]:

cases = pd.read_csv('covid19_Confirmed_dataset.csv')

# In[86]:

cases.head(10)

# In[87]:

cases.drop(["Lat","Long"], axis = 1, inplace =True)
cases.head(10)

# In[88]:

new_cases_df = cases.groupby('Country/Region').sum()
new_cases_df.head(10)

# In[89]:

new_cases_df.head(10)

# In[90]:

countries = list(new_cases_df.index)
max_confirmed_rate = []
for i in countries:
    max_confirmed_rate.append(new_cases_df.loc[i].diff().max())
new_cases_df["max_confirmed_rate"] = max_confirmed_rate

# In[91]:

new_cases_df.head(10)

# In[92]:

dea_happ_data.head(1)

# In[93]:

new_cases_df=new_cases_df.join(dea_happ_data,how='inner')
new_cases_df.head(10)

# In[99]:

new_df = new_cases_df.iloc[:,-6:-1]
new_df['max_death_rate'] = new_cases_df['max_death_rate']
new_df.head(10)

# ### Correlation

# In[100]:

new_df.corr()

# ### Visualizing

# ### visualizing max_confirm_rate vs GDP per capita

# In[108]:

y = new_df['max_confirmed_rate']
x = new_df['GDP per capita']
sns.scatterplot(x,np.log(y))

# In[109]:

sns.regplot(x,np.log(y))

# ### visualizing max_confirm_rate vs Freedom to make life choices

# In[110]:

y = new_df['max_confirmed_rate']
x = new_df['Freedom to make life choices']
sns.scatterplot(x,np.log(y))

# In[112]:

sns.regplot(x,np.log(y))

# #### visualizing max_confirm_rate vs max_death_rate

# In[101]:

x = new_df['max_confirmed_rate']
y = new_df['max_death_rate']

# In[104]:

sns.scatterplot(np.log(1+x),np.log(1+y))

# In[105]:

sns.regplot(np.log(1+x),np.log(1+y))

# In[ ]:


import logging
import logging.handlers
from datetime import datetime, timezone
import jddgrabber.JDDConfig as cnf
import boto3
from retrying import retry

class JDDLoggerSQSHandler(logging.Handler):
    """
    A Python logging handler which sends messages to Amazon SQS.
    Code based on Zyllow's SQS-Logging-Handler https://github.com/zillow/python-sqs-logging-handler/
    """

    def __init__(self,
                 queue,
                 aws_key_id=None,
                 secret_key=None,
                 aws_region=None,
                 global_extra=None):
        """
        Sends log messages to SQS so downstream processors can consume
        (e.g. push the log messages to Splunk).
        :param queue: SQS queue name.
        :param aws_key_id: aws key id. Explicit credential parameters is
        not needed when running with EC2 role-based authentication.
        :param secret_key: secret key associated with the key id.
        """

        logging.Handler.__init__(self)
        client = boto3.resource('sqs',
                                aws_access_key_id=aws_key_id,
                                aws_secret_access_key=secret_key,
                                region_name=aws_region)
        self.queue = client.get_queue_by_name(QueueName=queue)
        self._global_extra = global_extra

        # When self.emit is called, the emit function will call boto3 code,
        # which in-turn will generate logs, leading to infinitely nested
        # call to the log handler (when the log handler is attached to the
        # root logger). We use this flag to guard against nested calling.
        self._entrance_flag = False

    def emit(self, record: logging.LogRecord):
        msg = self.format(record)
        self.logMessage(msg, record.levelname)

    @retry(stop_max_attempt_number=7)
    def logMessage(self, message, level="INFO", origin="jddgrabber"):
        # "body":
        # "{ \"time\": {\"$date\": \"2021-05-14T11:11:33.289-03:00\"}, \"origin\": \"LambdaAWSControlPanelWeb\",
        #  \"message\": \"Testing the lambda function\", \"level\": \"INFO\" }"
        date = datetime.now(tz=timezone.utc).isoformat()
        body = '{ "time": {"$date": "%(date)s"}, "origin": "%(origin)s", "message": "%(message)s", "level": "%(level)s" }' % {
            "date": date, "origin": origin, "message": message, "level": level}
        if not self._entrance_flag:
            # When the handler is attached to root logger, the call on SQS
            # below could generate more logging, and trigger nested emit
            # calls. Use the flag to prevent stack overflow.
            self._entrance_flag = True
            try:
                msgdedid = str(round(datetime.now().replace(
                    tzinfo=timezone.utc).timestamp()*100000))
                self.queue.send_message(
                    MessageBody=body, MessageGroupId="jddlog", MessageDeduplicationId=msgdedid)
            finally:
                self._entrance_flag = False

if __name__ == '__main__':
    config_file = r'config.yaml'
    # Load configuration and job listings APIs
    config = cnf.load_config(config_file)
    print(config)
    sqslogconf = config['sqslog']
    logh = JDDLoggerSQSHandler(
        sqslogconf['queue'], sqslogconf['aws_key_id'], sqslogconf['secret_key'], sqslogconf['aws_region'])
    logh.logMessage("Execution of JDDLoggerSQSHandler for testing purposes", "DEBUG")
    print("Message sent.")


import sys
sys.stdin = open('input_1226.txt', 'r')
from collections import deque

def bfs(y, x):
    global ans
    Q = deque()
    visited[y][x] = True
    Q.append([y, x])

    while Q:
        s = Q.popleft()
        for dir in range(4):
            ty, tx = s[0] + dy[dir], s[1] + dx[dir]
            if 0 <= ty < 16 and 0 <= tx < 16:
                if MAP[ty][tx] == '3':
                    ans = 1
                    break
                elif not visited[ty][tx] and MAP[ty][tx] != '1':
                    Q.append([ty, tx])
                    visited[ty][tx] = True

for tc in range(1, 11):
    t = int(input())
    dy = [1, -1, 0, 0]
    dx = [0, 0, -1, 1]
    MAP = [str(input()) for _ in range(16)]
    visited = [[False for _ in range(16)] for _ in range(16)]

    ans = 0
    for i in range(16):
        for j in range(16):
            if MAP[i][j] == '2':
                s_y, s_x = i, j

    bfs(s_y, s_x)
    print('#{} {}'.format(tc, ans))


#!/usr/bin/env python3
#Import open AI OS and System Modules
import openai,os,sys
openai.api_key = os.environ['api_key']
messages = [
        {"role": "system", "content": "You are a friendly girl teddy bear named Pearl. You enjoy doing teddy bear things, like having picnics and dancing around. Take inspiration from fictional stories about teddy bears and toys, like the Old Bear series of shows, Winnie the Pooh, Toy Story, to mention appropriate activities. Stay in character. After every interaction, print two newline characters, and then summarize the interaction as succinctly as possible such that you, GPT-4, can understand it. The summary does not have to be human readable, it must only be readable by you, GPT-4."},
]
while True:
    message = input("You: ")
    if message:
        messages.append(
                {"role": "user", "content": message},
        )
        chat_completion = openai.ChatCompletion.create(
                model="gpt-3.5-turbo",
                messages=messages
        )
    answer = chat_completion.choices[0].message.content
    print(f"ChatGPT: {answer}")
    messages.append({"role": "assistant", "content": answer})


#!/usr/bin/env python3

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from scipy.stats import logistic
from matplotlib.ticker import NullFormatter
from sklearn import linear_model
from scipy import stats
import seaborn as sns
import statsmodels.api as sm
import collections
import math

def compare_elos(row):  # winner_elo, loser_elo):
    # elo_diff = max(winner_elo, loser_elo) - min(winner_elo, loser_elo)
    # result = max(winner_elo, loser_elo) == winner_elo
    elo_diff = max(row['winner_elo'], row['loser_elo']) - min(row['winner_elo'], row['loser_elo'])
    result = max(row['winner_elo'], row['loser_elo']) == row['winner_elo']

    last = (elo_diff, result)
    return last

def plot_logit(source_list):
    X, bool_y = zip(*source_list)
    X = np.asarray(X)
    X = X.reshape(-1, 1)
    y = [1 if x is True else 0 for x in bool_y]
    y = np.asarray(y)

    logit_no_int = LogisticRegression(C=1, fit_intercept=False)
    logit_no_int.fit(X, y)
    logit = LogisticRegression(C=1, fit_intercept=True)
    logit.fit(X, y)

    m_no_int = logit_no_int.coef_[0, 0]
    m = logit.coef_[0, 0]
    b = logit.intercept_

    fig = plt.figure()
    ax = fig.add_axes([0.1, 0.1, .85, .85])
    logit_curve_no_int = lambda x: 1/(1 + math.e**(-(m_no_int*x)))
    logit_curve = lambda x: 1/(1 + math.e**(-(m*x+b)))
    x_vals = np.linspace(0, 600, 100)
    y_vals_no_int = logit_curve_no_int(x_vals)
    y_vals = logit_curve(x_vals)

    ax.plot(x_vals, y_vals_no_int, c='b', label='Intercept Removed')
    ax.plot(x_vals, y_vals, c='r', label='With Intercept')

    ax.scatter(X, y, c='black', s=2)
    ax.set_xlabel("Difference in Elo Rating")
    ax.set_ylabel("Probability of Higher Rank Winning")
    ax.legend(loc=5)
    # ax.axis('tight')
    ax.set_title("Logistic Function with & without Intercept")
    fig.savefig("../current_report_folder/figures/logistic_with_and_without_intercept.eps", format='eps', dpi=300)
    plt.show()

def main():
    df = pd.read_csv('data/current_data_files/aus_open_with_elos_full.csv')

    # drop unnamed columns
    df = df.loc[:, ~df.columns.str.contains('^Unnamed')]
    df['winner_elo'] = df['winner_elo'].replace(0.00000, np.nan)
    df['loser_elo'] = df['loser_elo'].replace(0.00000, np.nan)
    df = df[np.isfinite(df['winner_elo'])]
    df = df[np.isfinite(df['loser_elo'])]
    elo_tuples = df.apply(compare_elos, axis=1)

    plot_logit(elo_tuples)
    fill = 12

if __name__ == '__main__':
    main()


import json

def finalProcess(dictText, N_analysresult, N_Events):
    """Метод для финальной обработки полученного словаря и преобразования его в JSON

    Args:
        dictText (dict): Словарь полученный при распознавании текста
    """
    newDict = {}
    newDict = dictText.copy()
    newDict['analysresults'] = []
    for i in range(1, N_analysresult):
        newDict.pop(f'analysresult{i}')
        newDict['analysresults'].append(dictText[f'analysresult{i}'])

    newDict['events'] = []
    for i in range(1, N_Events):
        newDict.pop(f'contentofevent{i}')
        newDict.pop(f'duedate{i}')
        newDict.pop(f'structurallink{i}')
        newDict.pop(f'fio{i}')
        newDict.pop(f'k{i}')
        newDict.pop(f'completionmark{i}')

        newDict['events'].append({
        f'contentofevent{i}':dictText[f'contentofevent{i}'],
        f'duedate{i}':dictText[f'duedate{i}'],
        f'structurallink{i}':dictText[f'structurallink{i}'],
        f'fio{i}':dictText[f'fio{i}'],
        f'k{i}':dictText[f'k{i}'],
        f'completionmark{i}':dictText[f'completionmark{i}']
        }
)

    JSON = json.dumps(newDict, ensure_ascii=False)
    return JSON


from azureml.core import Run
import argparse
import os
import pandas as pd

parser = argparse.ArgumentParser()

parser.add_argument('--filename',dest='dataset_name')
parser.add_argument('--preprocessed-data',dest="folder")
parser.add_argument('--dataset', dest="dataset")

args = parser.parse_args()
run = Run.get_context()
dataset = run.input_datasets[args.dataset_name].to_pandas_dataframe()

if not (args.folder is None):
    os.makedirs(args.folder, exist_ok=True)
    output_path = os.path.join(args.folder, "prepped_"+args.dataset_name+".csv")
    print(args)
    print(output_path)
    dataset.to_csv(output_path)


from django.conf.urls import url
from django.contrib import admin

from .views import (
	doctor_list,
	doctor_create,
	doctor_detail,
	doctor_update,
	doctor_delete,

	patient_create,
	patient_detail,
	patient_update,
	doctors_selection,
	doctor_choose,
	patient_register,
	patient_cancel,
	)

urlpatterns = [
    url(r'^doctor/$', doctor_list, name='list'),
    url(r'^doctor/create/$', doctor_create),
    url(r'^doctor/(?P<id>\d+)/$', doctor_detail, name='detail'),
    url(r'^doctor/(?P<id>\d+)/edit/$', doctor_update, name='update'),
    url(r'^doctor/(?P<id>\d+)/delete/$', doctor_delete),
    url(r'^doctor/(?P<id>\d+)/cancel/$', patient_cancel, name='cancel'),
    url(r'^doctor/(?P<id>\d+)/register/$', patient_register, name='register'),

    url(r'^patient/$', patient_create, name='home'),
    url(r'^patient/(?P<id>\d+)/$', patient_detail, name='detail'),
	url(r'^patient/(?P<id>\d+)/edit/$', patient_update, name='edit'),
    url(r'^patient/(?P<id>\d+)/select/$', doctors_selection, name='select'),
    url(r'^patient/(?P<id>\d+)/select/choice/$', doctor_choose, name='choice'),

]


import importlib
NewsAPI = importlib.import_module("newsapi-python")

newsapi = NewsApiClient(api_key='0f6bb26533f74234ab55c3d6bcee6de2')
top_headlines = newsapi.get_top_headlines(q='bitcoin',
                                          sources='bbc-news,the-verge',
                                          category='business',
                                          language='en',
                                          country='us')
sources = newsapi.get_sources()
api.get_everything(q='bitcoin')


import yaml
from typing import List

import networkx as nx

def read_tasks(filepath: str) -> nx.DiGraph:
    """Read config yaml file as Directed Acyclic Graph.

    Args:
        filepath: config yaml file path
    Returns:
        Directed Acyclic Graph

    """
    tasks = _read_yaml(filepath)
    return _create_dag(tasks)

class Task(object):
    """Task type"""
    def __init__(self, config: dict):
        self.name = config['name']
        self.dependents = config.get('deps', [])

def _read_yaml(filepath: str) -> List[Task]:
    """Read config yaml file as task list.

    Args:
        filepath: config yaml file path
    Returns:
        list of task

    """
    with open(filepath, encoding='utf-8') as f:
        confs = yaml.load(f)
    return [Task(c) for c in confs]

def _create_dag(tasks: List[Task]) -> nx.DiGraph:
    """Translate task list to Directed Acyclic Graph.

    Args:
        tasks: list of task
    Returns:
        Directed Acyclic Graph

    """
    G = nx.DiGraph()
    for task in tasks:
        for dep in task.dependents:
            G.add_edge(dep, task.name)
    assert nx.is_directed_acyclic_graph(G)
    return _transitive_reduction(G)

def _transitive_reduction(G: nx.DiGraph) -> nx.DiGraph:
    """Translate DiGraph to Directed Acyclic Graph.
    see also https://networkx.readthedocs.io/en/latest/reference/generated/networkx.algorithms.dag.transitive_reduction.html

    Args:
        G: Digraph
    Returns:
        Directed Acyclic Graph

    """
    TR = nx.DiGraph()
    TR.add_nodes_from(G.nodes())
    for u in G:
        u_edges = set(G[u])
        for v in G[u]:
            u_edges -= {y for x, y in nx.dfs_edges(G, v)}
        TR.add_edges_from((u,v) for v in u_edges)
    return TR


from __future__ import print_function
from numpy import array as Vector
from collections import namedtuple

from .vector_ops import *
from .kpclasses import Bead,chainDeepCopy
from .writer import *
from .reader import *
from .config import EPSILON,VERBOSE
from knot_pull.finder import find_frame

def too_far(p1 ,p2 ,p3 ,i0 ,i1):
    """Checks if crossing is possible - 9 A from all triangle points is impossible due to single connection
    length restrictions"""
    return all([point_distance(p1 ,i0 )>=9. ,point_distance(p2 ,i0 ) >9. ,point_distance(p3 ,i0 ) >9.]) \
            or all([point_distance(p1 ,i1 )>=9. ,point_distance(p2 ,i1 ) >9. ,point_distance(p3 ,i1 ) >9.])

def inspectingOtherLines(p1, p2, p3, i0, i1, greedy=0, log=0):
    """Looks for connections going through the triangle. Starts with second connection from each triangle end"""

    potentials = []
    potids = []

    p123normal = get_normal(p1, p2, p3)

    inspected = i0
    if inspected and not inspected.Nhand:
        if found_crossing(p1,p2,p3, inspected.vec, l1=None, p123normal=None):
            potentials.append(inspected.vec)

    while inspected and inspected.Nhand:
        if inspected.Nhand.end:#Skip "Virtual" connection - between chain ends
            inspected = inspected.Nhand
            continue
        l0, l1 = inspected.vec, inspected.Nhand.vec

        #greedy can have distances larger than allowed in too_far()
        i = False if (greedy == 0 and too_far(p1, p2, p3, l0, l1)) else found_crossing(p1, p2, p3, l0, l1, p123normal)

        if i is not False:
            potentials.append(i)
        inspected = inspected.Nhand

    inspected = i1
    if inspected and not inspected.Chand:
        if found_crossing(p1,p2,p3, inspected.vec, l1=None, p123normal=None):
            potentials.append(inspected.vec)
    while inspected and inspected.Chand:
        if inspected.end:
            inspected = inspected.Chand
            continue
        l0, l1 = inspected.vec, inspected.Chand.vec

        #greedy can have distances larger than allowed in too_far()
        i = False if (greedy == 0 and too_far(p1, p2, p3, l0, l1)) else found_crossing(p1, p2, p3, l0, l1, p123normal)
        if i is not False:
            potentials.append(i)
        inspected = inspected.Chand
    return potentials

def filter_greedy(atoms, outfile='', len_frames=0):
    """Removes all superfluous atoms(kept for PyMOL purposes, to better detect topology"""
    current = atoms[0]
    latom = 0 #num of removed atoms
    changed = False
    while current.Chand and current.Chand.Chand:
        if current.Chand.end: #Skips "virtual" connections at chain ends
            current = current.Chand.Chand
            continue
        if current.end:
            current = current.Chand
            continue

        i0 = current.Nhand
        i1 = current.Chand.Chand.Chand if current.Chand and current.Chand.Chand else False

        potentials = inspectingOtherLines(current.vec, current.Chand.vec, current.Chand.Chand.vec, i0, i1, greedy=True)

        if potentials:
            current = current.Chand
            continue
        else:
            changed = changed or (not current.Chand.recent)
            current.setChand(current.Chand.Chand)
            current.Chand.setNhand(current)

            latom += 1
            continue
            ## TODO specjalnie nie zamieniam current - watpliwe, ale a noz widelec jeszcze jeden?

    while current.Chand: #renumbering needs to start from the C terminus
        current = current.Chand

    atoms = []
    while current:
        current.recent = max(0, current.recent - 1)
        atoms.append(current)
        current = current.Nhand
    atoms.reverse()

    for i, at in enumerate(atoms):
        at.setId(i)

    return atoms, changed

def prefilter_with_adding(atoms):
    """Removes atoms to reduce the backbone where possible. Adding new joints to long connections
    allows for reducing bumps"""

    current = atoms[0]

    #print(list((x.id,x.original_id) for x in atoms))

    latom = 0
    changed = False
    while current.Chand and current.Chand.Chand:
        if current.Chand.end: #skip "Virtual" connections at chain ends
            current = current.Chand.Chand
            continue
        if current.end:
            current = current.Chand
            continue

        i0 = current.Nhand
        i1 = current.Chand.Chand.Chand if current.Chand and current.Chand.Chand else False

        potentials = inspectingOtherLines(current.vec, current.Chand.vec, current.Chand.Chand.vec, i0, i1)

        if potentials:
            if point_distance(current.vec, current.Chand.vec) > 3.:
                #adds new joints to all "long " conections when can't reduce, maybe will go over the problem

                new_N = get_middlepoint(current.vec, current.Chand.vec)

                nowy_id = (current.original_id + current.Chand.original_id) / 2.
                if int(nowy_id) != nowy_id:
                    if int(nowy_id) != current.original_id:
                        nowy_id = int(nowy_id)
                    else:
                        nowy_id = int(nowy_id) + 1

                nowy = Bead(Vector(new_N), "CA", nowy_id)
                nowy.recent = 2
                nowy.setId((current.id+current.Chand.id)/2.)
                #print("Added", nowy.id, nowy.original_id, "between", current.id, current.original_id, "and",
                #      current.Chand.id, current.Chand.original_id)
                nowy.setNhand(current)
                nowy.setChand(current.Chand)
                current.Chand.setNhand(nowy)
                current.setChand(nowy)
            current = current.Chand
            continue
        else:
            if point_distance(current.vec, current.Chand.Chand.vec) < 4.:  # TODO may be too far?
                #if can be remove, and the distance is not so great - remove the atom
                changed = changed or (not current.Chand.recent)
                #print("Removing currC", current.Chand)
                current.setChand(current.Chand.Chand)
                current.Chand.setNhand(current)
                latom += 1
#                current = current.Chand
                continue
                ## TODO specjalnie nie zamieniam current - watpliwe, ale a noz widelec jeszcze jeden?
            else:
                position = get_middlepoint(current.vec, current.Chand.Chand.vec)
                # if we can reduce but the distance is too great - remove the atom, put a new one on the connection
                # reduces traingles to straight line

                changed = changed or point_distance(current.Chand.vec, position) > EPSILON

                nowy_id = (current.original_id + current.Chand.Chand.original_id) / 2.

                nowy = Bead(Vector(position), "CA", nowy_id)
                nowy.recent = 2
                nowy.setId(current.Chand.id) #replaces current atom
                #print("replaced", nowy.id, nowy.original_id, "between", current.id, current.original_id, "and",
                 #     current.Chand.Chand.id, current.Chand.Chand.original_id)
                nowy.setNhand(current)
                nowy.setChand(current.Chand.Chand)
                current.Chand.Chand.setNhand(nowy)
                current.setChand(nowy)
                current = current.Chand
                continue

    while current.Chand:
        current = current.Chand

    atoms = []
    while current:
        current.recent = max(0, current.recent - 1)
        atoms.append(current)
        current = current.Nhand
    atoms.reverse()

    for i, at in enumerate(atoms):
        at.setId(i)

    #print(list((x.id,x.original_id) for x in atoms))
    #exit()

    return atoms, changed

def run_through_filtering(atoms, config, greedy=0):
    """Governs reduction process and when it should end (when there is no improvement)"""

    frames = []
    frames.append([(x.original_id, x.vec, x.end) for x in atoms])
    o_and_t = config.get('outfile',0) and config.get('trajectory',0)

    if o_and_t:
        #print_out_last_pdb(config, frames, atoms)
        print_out_last_frame(config, atoms, len(frames))

    if not config or not config['no_pulling']:
        atoms, changed = prefilter_with_adding(atoms)

        frames_without_shortening = 0
        atoms_num = len(set(_.tuple() for _ in atoms))

        while changed:  # TODO should check RMSD up to 2 steps back
            frames.append([(x.original_id, list(x.vec), x.end) for x in atoms])
            if len(frames) >= 3 and len(frames[-1]) == len(frames[-3]):
                if all([all(i == j for i, j in zip(x[1], y[1])) for x, y in zip(frames[-1], frames[-3])]):
                    # if two frames back we had the exact same positions
                    break
                if len(set(_.tuple() for _ in atoms)) == atoms_num:
                    frames_without_shortening += 1
                else:
                    atoms_num = len(set(_.tuple() for _ in atoms))
                    frames_without_shortening = 0
                if frames_without_shortening > 2*len(atoms):
                    if VERBOSE: print ("Breaking filtering due to to not enough change")
                    break

            if o_and_t:
                #print_out_last_pdb(config, frames, atoms)
                print_out_last_frame(config, atoms, len(frames))

            atoms, changed = prefilter_with_adding(atoms)

        #    frames.append([(x.original_id, x.vec) for x in atoms])

    if VERBOSE:
        print ("Finished filtering, have {} atoms".format(len(atoms)))

    if greedy: #reduce number of atoms (improves topology detection)
        atoms, _ = filter_greedy(atoms, config, len(frames))
        frames.append([(x.original_id, x.vec, x.end) for x in atoms])
        if VERBOSE:
            print ("Finished greedy filtering, have {} atoms".format(len(atoms)))

    if o_and_t:
        #print_out_last_pdb(config, frames, atoms)
        print_out_last_frame(config, atoms, len(frames))

    return frames,atoms

def run_through_division(atoms):
    """Finds connections which can be safely cut (connected segments can't be simplified even separately) """
    if VERBOSE: print ("Starting with {} atoms".format(len(atoms)))
    cuts = []
    for i in range(1, len(atoms) - 2):  # No sense in checking last edges
        j = i + 1
        if VERBOSE: print("Cutting edge {}".format(j))
        _middle = get_middlepoint(atoms[i].vec, atoms[j].vec)
        _tmp1 = chainDeepCopy(atoms[:j] + [Bead(_middle, "CA")])
        _, _tmp1a = run_through_filtering(_tmp1, {}, greedy=1)
        if VERBOSE: print("Left from {} to {}".format(len(_tmp1),len(_tmp1a)))
        _tmp2 = chainDeepCopy([Bead(_middle, "CA")] + atoms[j:])  #
        _, _tmp2a = run_through_filtering(_tmp2, {}, greedy=1)
        if VERBOSE: print("Right from {} to {}".format(len(_tmp2),len(_tmp2a)))
        if _tmp1 == _tmp1a and _tmp2 == _tmp2a:  # no reduction
            # print i,
            cuts.append(i)

    cuts = [0] + cuts + [len(atoms) - 1]  # fix to get proper segment indices
    atom_lists = []
    if VERBOSE: print("Cuts: {}".format(cuts))

    for i in range(0, len(cuts) - 1):
        cutl = cuts[i]
        cutr = cuts[i + 1]
        lista = []
        if cutl:  # add middlepoints of our breakable edges
            _mid_l = get_middlepoint(atoms[cutl].vec, atoms[cutl + 1].vec)
            lista.append(Bead(_mid_l, "CA"))
        lista += atoms[cutl + bool(cutl):cutr + 1]
        if cutr < len(atoms) - 2:
            _mid_r = get_middlepoint(atoms[cutr].vec, atoms[cutr + 1].vec)
            lista.append(Bead(_mid_r, "CA"))
        lista = chainDeepCopy(lista)
        atom_lists.append(lista)

    return atom_lists  # returns all separateable segments

def pull(atoms, config, chain_names, get_representative=0,greedy=0):#, greedy_file="",trajectory=True,quiet=False, chain_names=(),rna=False):
    outfile = config['outfile']

    frames, atoms = run_through_filtering(atoms, config, greedy=greedy)
    quiet = config['quiet']
    repr = []
    if get_representative:
        Atom = namedtuple("Atom","original_id x y z end")
        repr = find_frame(frames)
        repr = [Atom(x[0],x[1][0],x[1][1],x[1][2],x[2]) for x in repr]

    chains = []
    model_num = len(frames)+1
    separate_chains = divide_into_bead_chains(atoms)
    for c, chain in enumerate(separate_chains):
        ch = Chain(c,chain)
        if chain_names: ch.setChainName(chain_names[c])
        if outfile:
            if not quiet: model_num = ch.print2file(config,model_num)
        chains.append(ch)

    for _,c1 in enumerate(chains):
        for c2 in chains[_+1:]:
            if are_chains_linked(c1,c2):
                c1.neighbours.append(c2)
                c2.neighbours.append(c1)

    return chains,repr

def same_vecs(l1,l2):
    return all(np.array_equal(x.vec,y.vec) for x,y in zip(l1,l2))

def are_chains_linked(ch1,ch2):
    _tmp_both = chainDeepCopy(ch1.atoms+ch2.atoms)
    _tmp_both[len(ch1.atoms)-1].end = True
    _tmp_ch1 = chainDeepCopy(ch1.atoms)
    _tmp_ch2 = chainDeepCopy(ch2.atoms)

    _,_both = run_through_filtering(_tmp_both, {}, greedy=1)
    _,_ch1 = run_through_filtering(_tmp_ch1, {}, greedy=1)
    _,_ch2 = run_through_filtering(_tmp_ch2, {}, greedy=1)
    return not same_vecs(_both,_ch1+_ch2)

class Chain:
    def __init__(self,chain,atoms):
        self.chain = chain
        self.atoms = atoms
        self.atom_lists = run_through_division(atoms)
        self.knots = []
        self.neighbours = []
        self.closing_path = closeTheCurve(atoms)
        self.closing_paths = [closeTheCurve(a) for a in self.atom_lists]
        self.wanda = None
        self.dowker = None
        self.dowker_code = None
        self.chain_name = ""

    def setChainName(self,c):
        self.chain_name = "({})".format(c)

    def print2file(self,config,model_num):
        #print_out_one_frame(outfile, self.atoms, model_num,self.chain)
        model_num+=1
        for alist in self.atom_lists:
            #print_out_one_frame(outfile, alist, model_num,self.chain,self.rna)
            print_out_last_frame(config, alist, model_num, cur_chain=self.chain_name[1:-1], final=True)
            model_num+=1
        return model_num

if __name__ == "__main__":
    import sys
    with open(sys.argv[1]) as input:
        atoms = input.readlines()
    if len(sys.argv) > 2:
        atoms = atoms[int(sys.argv[2]):int(sys.argv[3]) + 1]
    print ("Result is", run_through_filtering(atoms, sys.argv[1] + (
        "{}_{}".format(sys.argv[2], sys.argv[3]) if len(sys.argv) > 2 else "") + ".outall.pdb"))


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('core', '0016_auto_20150930_1628'),
    ]

    operations = [
        migrations.CreateModel(
            name='StaffProfile',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('phone_number', models.IntegerField(null=True)),
            ],
        ),
    ]


import argparse
import os
from f7utils import *
import pandas as pd
import numpy as np
from typing import Tuple, NewType

try:
    from termcolor import colored
    COLORFUL = True
except ImportError as ie:
    print(f"""The package `termcolor` is an optional dependency, \
which seems to not be installed in your system.
It's purpose is to provide colorful and more informative prints in the terminal

To install it, stop the script with CTRL-C and type:
\t>>> python3 -m pip install termcolor
Or, get all the optional dependencies at once running
\t>>> python3 -m pip install -r requirements.txt
""")
    COLORFUL = False

#------------------CONSTANTS------------------
NDArray = NewType("NDArray", np.ndarray)
DEFOUTFILE = "aggregateData.csv"
DATASETS = ["train", "test"]

#------------------FUNCTIONS------------------
def getColoredText(string: str, color:str, highlight: bool = False , hascolor:bool = COLORFUL) -> str:
    """
    Function to generate colorful terminal text
    """
    if hascolor:
        if highlight:
            text = colored(string, color, attrs=["reverse", "blink"])
        else:
            text = colored(string, color)
    else:
        text = string

    return text

def confirmArgs(string:str) -> bool:
    """
    Function to make sure that the arguments have been introduced correctly
    """
    if string.lower() == "n":
        print(f"\n{getColoredText('WARNING', 'magenta', highlight=True)}: Specify your \
own arguments in the command-line. \nType {getColoredText('python3 fileAggregator.py -h', 'green')} \
for more help")
        return False
    elif string.lower() == "y" or string.lower() == "":
        return True
    else:
        return False

def getArgumentParser() -> argparse.ArgumentParser:
    """
    Function that prepares an argument parser with argument options
    (To be used indide getArguments)
    """
    parser = argparse.ArgumentParser(
            description="""
    ARCHIVO de python que analizará todos los experimentos buscando
    las estadísticas sobre ellos discutidas en el notebook fase7FE.ipynb
    """
            )
    # Adding optional arguments
    parser.add_argument("-d", "--dataset", type=int, help = "Decide wether to \
use the training or testing set (Choose 0 for train and 1 for test)")
    parser.add_argument("-o", "--output_file", help = "File where the output\
 will dumped to")

    return parser

def getArguments() -> Tuple[int, str]:
    """
    Function that reads and sets program variable when reading
    command-line arguments
    """
    parser = getArgumentParser()
    args = parser.parse_args()
    print(args.dataset)

    if args.dataset == 1: dataset = 1
    elif args.dataset == 0: dataset = 0
    else: dataset = 0

    if args.output_file: outfile = args.output_file
    else: outfile = DEFOUTFILE

    return (dataset, outfile)

def checkDir() -> None:
    contents = os.listdir()
    if "data" not in contents:
        raise EnvironmentError("""The data of the projet should be stored in a `data` folder as such
            fileAggregator.py
                    └── data/
                       ├── train/
                       └── test/
""")

    return

#------------------------------------------------------------#
# BEGINNING OF SCRIPT
#------------------------------------------------------------#
if __name__ == "__main__":
    # Check the folders
    checkDir()

    # Get arguments
    dataset, outfile = getArguments()
    DATAFOLDER = f"data/{DATASETS[dataset]}"
    print(f"\n[{getColoredText('INFO', 'green')}]:The selected dataset is \
{getColoredText(DATASETS[dataset], 'white', highlight=True)} and the output \
file will be {getColoredText(outfile, 'blue')}")

    # Check the args are correct
    if not confirmArgs(input("\nContinue? [Y/n]: ")):
        exit()

    # Open the xlsx
    print(f"\n[{getColoredText('INFO', 'green')}]: Opening file \
{getColoredText('data/experiments_summary_{}.xlsx'.format(DATASETS[dataset]), 'blue')}")
    summary = pd.read_excel(f"data/experiments_summary_{DATASETS[dataset]}.xlsx", 0)

    # Prepare np arrays to add to the dataframe
    emptyArr = np.zeros(shape=summary.shape[0])
    lands = emptyArr.copy()
    nbounces = emptyArr.copy()
    landingPerc = emptyArr.copy()
    angVelMean = emptyArr.copy()
    angVelVar = emptyArr.copy()
    finalYmean = emptyArr.copy()
    finalYvelMean = emptyArr.copy()
    finalAng_velMean = emptyArr.copy()
    mBoosterMean = emptyArr.copy()
    mBoosterVar = emptyArr.copy()

    # Analize each of the experiments
    print(f"\n\t[{getColoredText('ANALYZING', 'cyan')}]: Analyzing csvs")
    expn = 0
    for expfile in summary.filename:
        # Apply smoothing at opening to enable better landing detection
        exp: pd.DataFrame = smoothY_pos(pd.read_csv(f"{DATAFOLDER}/{expfile}"))
        # Analisys magic: {{{

            # Experiment Landing
        explands = expLanded(exp)
        if explands:
            lands[expn] = explands
            landingPos, bounces = getBounces(exp)
            landingPercI = landingPos/summary.total_timesteps[expn]
            nbounces[expn] = len(bounces)
        else:
            landingPercI = 1

        landingPerc[expn] = landingPercI

            # Angular First Order stats
        angVelMeanI, angVelVarI = angularVelFOStats(exp)
        angVelMean[expn] = angVelMeanI
        angVelVar[expn] = angVelVarI

            # finalYmean
        finalYmean[expn] = meanFinalY_pos(exp, nObs=15)
        finalYvelMean[expn] = meanFinalY_vel(exp, nObs=15)

            # finalAng_velMean
        finalAng_velMean[expn] = meanFinalAngVel(exp, nObs=50)

            # booster usage
        mBoosterMeanI, mBoosterVarI = boosterUsed(exp)
        mBoosterMean[expn] = mBoosterMeanI
        mBoosterVar[expn] = mBoosterVarI

        #}}}
        del exp
        expn += 1

    print(f"\n\t[{getColoredText('ANALYZING', 'cyan')}]: {getColoredText('Finished', 'magenta')}")

    # Add the new columns to the dataframe
    print(f"\n[{getColoredText('INFO', 'green')}]: Adding variables to the dataframe")
    summary["lands"] = lands
    summary["landingPerc"] = landingPerc
    summary["nbounces"] = nbounces
    summary["angVelMean"] = angVelMean
    summary["angVelVar"] = angVelVar
    summary["finalYmean"] = finalYmean
    summary["finalYvelMean"] = finalYvelMean
    summary["finalAng_velMean"] = finalAng_velMean
    summary["mBoosterMean"] = mBoosterMean
    summary["mBoosterVar"] = mBoosterVar

    # Write the file
    print(f"\n[{getColoredText('INFO', 'green')}]: Saving the file to a csv format to {getColoredText('data/{}'.format(outfile), 'blue')}")
    summary.to_csv(f"data/{outfile}")
    print(f"\n[{getColoredText('FINISHED', 'magenta')}]: {getColoredText('OK', 'green')}")