def logout():
    try:
        sessionId = request.form["sessionId"]
        AuthenticationService.logout(sessionId)
        return Response("Successfully logged out.", status=200)
    except:
        return Response("Unable to process request. Please reload and try again later.", status=400)
    def post(self):
        try:
            form = request.form  #the form submitted
            SID = form["sessionId"]  #gets SID from cookie
            session = AuthenticationService.getSession(
                SID)  #uses SID to get session from db
            user = session["user"]  #gets user from session

            # found user, remove their datasets
            try:
                Dataset.objects(author=user).delete()
            except:
                return Response("Error deleting datasets.", status=403)
            # once datasets have been removed, remove user from users
            try:
                # log out before deletion
                sessionId = request.form["sessionId"]
                AuthenticationService.logout(sessionId)
                # remove user with query by email
                user.delete()
            except:
                return Response("Error deleting user.", status=403)
            return Response("Account deleted.", status=200)
        except:
            return Response("Error getting user from session.", status=403)
def signup():
    user = {
        "firstName": request.form["firstName"],
        "lastName": request.form["lastName"],
        "email": request.form["email"],
        "password": request.form["password"],
        "organization": request.form["organization"],
        "location": request.form["location"],
        "userType": request.form["userType"]
    }

    try:
        User.objects.get(email=user["email"])
        return Response("There's already an account with the provided email.",
                        status=400)
    except:
        try:
            AuthenticationService.signup(user)
            userConfirmationId = uuid4()
            user = User.objects.get(email=user["email"])
            AuthenticationService.setUserConfirmationId(
                user, userConfirmationId)
            sub = "Confirm Account"
            msg = f"<p>Congratulations, you've registered for Agriworks. Please click the link below to confirm your account.</p><p><a href=\"{app.rootUrl}/confirm-user/{userConfirmationId}\"> Confirm account </a></p>"
            MailService.sendMessage(user, sub, msg)
            return Response("Signup successful", status=200)
        except:
            return Response("Signup unsuccessful. Please try again.",
                            status=403)
def confirmUser(userConfirmationId):
    try:
        user = User.objects.get(confirmationId=userConfirmationId)
        AuthenticationService.setUserAsConfirmed(user)
        return Response("Account confirmed successfully. You may now login.")
    except:
        return Response(
            "No account was found using the provided confirmation code.",
            status=404)
def login():
    session = AuthenticationService.authenticate(request.form["email"],
                                                 request.form["password"])
    if not session:
        return Response(
            "Incorrect username or password. Please check your credentials and try again.",
            status=403)

    user = User.objects.get(email=request.form["email"])
    if not AuthenticationService.isUserConfirmed(user):
        return Response("You must confirm your account to log in.", status=403)

    ret = make_response("Success")
    ret.set_cookie("SID", str(session.sessionId), expires=session.dateExpires)
    return ret
 def post(self, email):
     try:
         user = User.objects.get(email=email)
         if user.isConfirmed:
             return Response("User already confirmed.", status=403)
         newUserConfirmationId = uuid4()
         AuthenticationService.setUserConfirmationId(
             user, newUserConfirmationId)
         sub = "Confirm Account"
         msg = f"<p>Congratulations, you've registered for Agriworks. Please click the link below to confirm your account.</p><p><a href=\"{app.rootUrl}/confirm-user/{newUserConfirmationId}\"> Confirm account </a></p>"
         MailService.sendMessage(user, sub, msg)
         return Response("New confirmation email sent.", status=200)
     except:
         return Response("Resend confirmation email unsuccessful.",
                         status=403)
def getDatasetObjectsPrimary(dataset_id):

    user = AuthenticationService.verifySessionAndReturnUser(
        request.cookies["SID"])

    if (Dataset.objects.get(
            Q(id=dataset_id) & (Q(public=True) | Q(author=user))) != None):
        filename = dataset_id + ".csv"
        fileFromS3 = s3.get_object(Bucket="agriworks-user-datasets",
                                   Key=filename)
        dataset = pd.read_csv(fileFromS3["Body"], dtype=str)
    else:
        return Response("You do not have access to that dataset.", status=403)

    if (len(dataset) <= 1000):
        return Response({
            "datasetObjects":
            DatasetService.buildDatasetObjectsList(dataset)
        })
    else:
        cacheId = str(uuid4())
        DatasetCache[cacheId] = dataset[1000:]
        return Response({
            "datasetObjects":
            DatasetService.buildDatasetObjectsList(dataset[:1000]),
            "cacheId":
            cacheId
        })
    def post(self):
        flow = app.flow
        flow.redirect_uri = request.form["redirect_uri"]
        authCode = request.form["code"]
        flow.fetch_token(code=authCode)

        credentials = flow.credentials
        req_url = "https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + credentials.token
        user_info = requests.get(req_url).json()

        user = AuthenticationService.getUser(email=user_info['email'])

        if user:
            if not user.password:
                sessionId = uuid4()
                session = Session(user=user, sessionId=sessionId)
                session.save()
                ret = make_response(user_info)
                ret.set_cookie("SID",
                               str(session.sessionId),
                               expires=session.dateExpires)
                return ret
            return Response("Email already registered with our service",
                            status=403)
        else:
            ret = {}
            ret['message'] = "Redirect to complete sign up"
            ret['user'] = user_info
            return Response(ret, status=200)
    def createDatasetInfoObject(self, dataset, withHeaders=False):
        datasetAuthor = AuthenticationService.getUser(id=dataset.author.id)
        datasetInfoObject = {
            "name": dataset.name,
            "legend": dataset.legend,
            "type": dataset.datasetType,
            "author": datasetAuthor.getFullname(),
            "tags": dataset.tags,
            "id": str(dataset.id),
            "views": dataset.views
        }

        if (withHeaders):
            headers = []

            #v-table requires headers to be in this format
            #TODO: Update v-table so that we can just pass the headers in as normal without performing any extra work

            for header in dataset["keys"]:
                headerObj = {"text": header, "value": header}
                headers.append(headerObj)

            datasetInfoObject["headers"] = headers

        return datasetInfoObject
def get(pageNumber):
    retList = []
    datasets = []

    user = AuthenticationService.verifySessionAndReturnUser(
        request.cookies["SID"])

    allDatasets = Dataset.objects.filter(Q(public=True) | Q(
        author=user)).order_by('-dateCreated')

    if pageNumber == "all":
        datasets = allDatasets
    elif pageNumber == "0":
        datasets = allDatasets[:16]
    else:
        datasetIndex = 16 + 12 * (int(pageNumber) - 1)
        datasets = allDatasets[datasetIndex:datasetIndex + 12]

    if len(datasets) == 0:
        return Response("No datasets matching the query were found",
                        status=400)

    for dataset in datasets:
        retList.append(DatasetService.createDatasetInfoObject(dataset))

    return Response(retList)
    def createView(self, request):
        print("Creating view")
        try:
            # Get the user to establish view ownership
            user = AuthenticationService.verifySessionAndReturnUser(
                request.cookies["SID"])
            if (not user):
                return {"message": "Invalid session", "status": 400}

            # Get the dataset to link view to dataset
            #problem if the dataset name is not unique
            datasetId = request.form.get("dataset")
            dataset = Dataset.objects.get(id=datasetId)
            if (not dataset):
                return {"message": "Invalid dataset ID", "status": 400}

            viewAuthor = user
            viewDataset = dataset
            viewVisualtype = request.form.get("visualType")
            viewXData = request.form.get("xData")
            viewYData = request.form.get("yData")

            # Create and save view object
            view = AgriWatchView(author=viewAuthor,
                                 dataset=viewDataset,
                                 visualType=viewVisualtype,
                                 xData=viewXData,
                                 yData=viewYData)
            view.save()

            return view

        except ValidationError as e:
            print(e)
            return None
def getDataset(datasetId):
    user = AuthenticationService.verifySessionAndReturnUser(
        request.cookies["SID"])
    dataset = Dataset.objects.get(id=datasetId)

    if dataset == None:
        return Response(
            "Unable to retrieve dataset information. Please try again later.",
            status=400)
    if (dataset.public == False and dataset.author != user):
        return Response("You do not have permission to access that dataset.",
                        status=403)

    Dataset.objects(id=datasetId).update_one(inc__views=1)
    AuthenticationService.updateRecentDatasets(request.cookies["SID"],
                                               datasetId)
    return Response(
        DatasetService.createDatasetInfoObject(dataset, withHeaders=True))
def getUsersDatasets():
    retList = []
    user = AuthenticationService.verifySessionAndReturnUser(
        request.cookies["SID"])
    datasets = Dataset.objects.filter(author=user).order_by('-dateCreated')
    for dataset in datasets:
        if dataset == None:
            return Response("No datasets found", status=400)
        retList.append(DatasetService.createDatasetInfoObject(dataset))
    return Response(retList)
    def post(self):
        user = {
            "firstName": request.form["firstName"],
            "lastName": request.form["lastName"],
            "email": request.form["email"],
            "password": request.form["password"],
            "organization": request.form["organization"],
            "location": request.form["location"],
            "userType": request.form["userType"]
        }

        try:
            User.objects.get(email=user["email"])
            return Response(
                "There's already an account with the provided email.",
                status=400)
        except:
            try:
                AuthenticationService.signup(user)
                userConfirmationId = uuid4()
                user = User.objects.get(email=user["email"])
                if AuthenticationService.isUserConfirmed(user):
                    sessionId = uuid4()
                    session = Session(user=user, sessionId=sessionId)
                    session.save()
                    data = {
                        "message": "Google authorized successful!",
                        "user": user.email
                    }
                    ret = make_response(data)
                    ret.set_cookie("SID",
                                   str(session.sessionId),
                                   expires=session.dateExpires)
                    return ret
                AuthenticationService.setUserConfirmationId(
                    user, userConfirmationId)
                sub = "Confirm Account"
                msg = f"<p>Congratulations, you've registered for Agriworks. Please click the link below to confirm your account.</p><p><a href=\"{app.rootUrl}/confirm-user/{userConfirmationId}\"> Confirm account </a></p>"
                MailService.sendMessage(user, sub, msg)
                return Response("Signup successful", status=200)
            except:
                return Response("Signup unsuccessful. Please try again.",
                                status=403)
 def post(self):
     try:
         user = AuthenticationService.getUser(email=request.form["email"])
         passwordResetId = uuid4()
         AuthenticationService.setUserResetID(user, passwordResetId)
         try:
             subject = "Reset Password"
             html = f"<p>We heard you lost your password. No worries, just click the link below to reset your password.</p><p>You can safely ignore this email if you did not request a password reset</p><br/><a href=\"{app.rootUrl}/reset-password/{passwordResetId}\"> Reset password </a><br/>"
             MailService.sendMessage(user, subject, html)
             return Response(
                 "An email with instructions to reset your password has been sent to the provided email.",
                 status=200)
         except:
             return Response(
                 "Unable to send password reset email. Please try again later.",
                 status=400)
     except:
         return Response(
             "No account with given email found. Please try creating a new account.",
             status=403)
def signup():
    user = {"firstName": request.form["firstName"],
            "lastName": request.form["lastName"],
            "email": request.form["email"],
            "password": request.form["password"]
            }

    if (not AuthenticationService.signup(user)):
        return Response("There's already an account with the provided email.", status=400)

    return Response("Signup successful", status=200)
    def decorator(*args, **kwargs):
        if "SID" not in request.cookies:
            return Response(status=403)

        cookie = request.cookies["SID"]

        try:
            user = AuthenticationService.verifySessionAndReturnUser(cookie)
            return f(*args, **kwargs)
        except Exception as e:
            print(e)
            return Response("Invalid session ID", status=403)
    def post(self, passwordResetId):
        try:
            user = AuthenticationService.checkUserResetID(passwordResetId)
            if ("password" not in request.form):
                return Response("Please provide a new password.", status=400)

            newPassword = request.form["password"]
            confirmPassword = request.form["confirmPassword"]

            if (newPassword != confirmPassword):
                return Response(
                    "Password and Confirm Password fields must be the same",
                    status=403)

            if (AuthenticationService.resetPasswordSame(user, newPassword)):
                return Response(
                    "Please choose a password that you haven't used before",
                    status=403)

            AuthenticationService.setUserResetID(user, "")
            AuthenticationService.changePassword(user.email, newPassword)
            return Response("Password sucessfully updated", status=200)
        except:
            return Response(
                "Your password reset link is either invalid or expired. Please request a new one.",
                status=403)
def verifySession():
    try:
        sessionId = request.form["sessionId"]
        if not AuthenticationService.verifySessionAndReturnUser(sessionId):
            return Response("Your session has expired. Please login again.",
                            status=401)
        else:
            return Response(status=200)
    except DoesNotExist as e:
        return Response("Your session was not found. Please login again.",
                        status=401)
    except ValueError as e:
        return Response("Invalid session. Please login again.", status=400)
    def get(self):
        retList = []
        user = AuthenticationService.verifySessionAndReturnUser(
            request.cookies["SID"])

        views = AgriWatchView.objects.filter(
            author=user).order_by('-dateCreated')

        for view in views:
            if view == None:
                return Response("No views found", status=400)
            retList.append(AgriWatchViewService.makeViewObject(view))
        return Response(retList)
def new():
    try:
        retList = []
        user = AuthenticationService.verifySessionAndReturnUser(
            request.cookies["SID"])
        # get users datasets by date created and sort by descending order
        newDatasets = Dataset.objects(author=user).order_by("-dateCreated")[:5]
        for dataset in newDatasets:
            if dataset == None:
                return Response("No datasets found", status=404)
            retList.append(DatasetService.createDatasetInfoObject(dataset))
        return Response(retList)
    except Exception as e:
        print(e)
        return Response("Couldn't retrieve recent datasets", status=400)
def popular():
    try:
        retList = []
        user = AuthenticationService.verifySessionAndReturnUser(
            request.cookies["SID"])
        # sorts the datasets by ascending order
        datasets = Dataset.objects.filter(Q(author=user) | Q(
            public=True)).order_by("-views")[:5]
        for dataset in datasets:
            if dataset == None:
                return Response("No datasets found", status=400)
            retList.append(DatasetService.createDatasetInfoObject(dataset))
        return Response(retList)
    except:
        return Response("Couldn't retrieve popular datasets", status=400)
    def get(self, searchQuery):
        datasets = []
        browseURL = "browse"
        manageURL = "manage"
        referrerURL = request.headers["referer"].split('/')[-1]

        matchedDatasets = []
        typeUser = None

        user = AuthenticationService.verifySessionAndReturnUser(
            request.cookies["SID"])

        try:
            if searchQuery == "" or searchQuery == " ":
                raise
            else:
                #Perform search only on user datasets
                if referrerURL == manageURL:
                    userDatasets = Dataset.objects.filter(author=user)
                    matchedDatasets = userDatasets.search_text(
                        searchQuery).order_by('$text_score')
                    typeUser = True
                #Perform search on all datasets
                elif referrerURL == browseURL:
                    visibleDatasetsToUser = Dataset.objects.filter(
                        Q(author=user) | Q(public=True))
                    matchedDatasets = visibleDatasetsToUser.search_text(
                        searchQuery).order_by('$text_score')
                    typeUser = False
                else:
                    # invalid referrer url
                    return Response(
                        "Error processing search request. Please try again later.",
                        status=400)

            for dataset in matchedDatasets:
                datasets.append(
                    DatasetService.createDatasetInfoObject(dataset))

            if typeUser:
                return Response({"datasets": datasets, "type": "user"})
            return Response({"datasets": datasets, "type": "all"})

        except:
            return Response(
                "Unable to retrieve datasets with the given search parameter.",
                status=400)
def recent():
    try:
        retList = []
        # use cookies to retrieve user
        user = AuthenticationService.verifySessionAndReturnUser(
            request.cookies["SID"])
        recentDatasetIds = user.recentDatasets[:5]
        # retrieve the actual datasets from these ids
        for datasetId in recentDatasetIds:
            try:
                retList.append(
                    DatasetService.createDatasetInfoObject(
                        Dataset.objects.get(id=datasetId)))
            except:
                continue
        return Response(retList)

    except Exception as e:
        return Response("Couldn't retrieve recent datasets", status=400)
def deleteDataset(datasetId):
    user = AuthenticationService.verifySessionAndReturnUser(
        request.cookies["SID"])
    dataset = Dataset.objects.get(id=datasetId)

    if dataset == None:
        return Response(
            "Unable to retrieve dataset information. Please try again later.",
            status=400)
    if (dataset.author != user):
        return Response("You do not have permission to delete that dataset.",
                        status=403)

    try:
        s3.delete_object(Bucket="agriworks-user-datasets",
                         Key=datasetId + ".csv")
        dataset.delete()
        return Response("Succesfully deleted dataset.", status=200)
    except:
        return Response("Unable to delete dataset.", status=500)
from flask import Blueprint, request, make_response
from Response import Response
from Services.AuthenticationService import AuthenticationService
from Models.User import User
from flask import current_app as app
from mongoengine import DoesNotExist
import yaml
from yaml import Loader, Dumper

AuthenticationService = AuthenticationService()

auth = Blueprint("AuthenticationController", __name__, url_prefix="/api/auth")

@auth.route("/login", methods=["POST"])
def login():
    print("Logging in")
    print(request.form)
    email = request.form['email']
    password = request.form['password']
    print(email)
    print(password)
    stream = open('creds.yaml')
    data = yaml.load(stream, Loader=Loader)
    if (email == data['DB_USER'] and password == data['DB_PASSWORD']):
        return Response("Logged in", status = 200)
    else:
        return Response("Incorrect username or password", status = 403)
    
    # session = AuthenticationService.authenticate(
    #     request.form["email"], request.form["password"])
    # if not session:
    def createDataset(self, request, uploadTime):
        try:
            #keep track of when request was made
            user = AuthenticationService.verifySessionAndReturnUser(
                request.cookies["SID"])

            if (not user):
                return {"message": "Invalid session", "status": 400}

            #TODO: verify that these parameters exist
            uploadedFile = request.files['file']
            dataSetName = request.form.get("name")
            dataSetAuthor = user
            dataSetIsPublic = True if request.form.get(
                "permissions") == "Public" else False
            dataSetTags = request.form.get("tags").split(',')
            dataSetType = request.form.get("type")

            if (len(dataSetTags) == 1):
                if (dataSetTags[0] == ""):
                    dataSetTags.pop()

            data = pd.read_csv(uploadedFile)
            keys = list(data.columns)

            if (data.isnull().values.sum() > 0):
                raise ValueError

            #Add new tags to collection
            for tag in dataSetTags:
                newTag = Tag(name=tag, datasetType=dataSetType)
                newTag.validate()
                if not self.tagExist(newTag):
                    newTag.save()

            #Create and save dataset object
            dataset = Dataset(name=dataSetName,
                              author=dataSetAuthor,
                              keys=keys,
                              public=dataSetIsPublic,
                              tags=dataSetTags,
                              datasetType=dataSetType,
                              views=1)
            dataset.save()

            #Go back to the front of the file
            uploadedFile.seek(0)

            #Save to S3
            self.uploadToAWS(dataset.id, uploadedFile)

            uploadCompletedDate = str(datetime.datetime.now()).split(".")[0]

            headline = f"Your <b>{dataset.name}</b> dataset has finished processing. <br> <br> "
            uploadString = f"<b>Upload Received</b>: {uploadTime} <br> <br> <b>Upload Completed</b>: {uploadCompletedDate}<br> <br>"
            datasetLink = f"<b> Link below to view your dataset: </b> <br> <a href ='{app.rootUrl}/dataset/{dataset.id}'>{app.rootUrl}/dataset/{dataset.id}</a>."
            formattedMessage = headline + uploadString + datasetLink
            MailService.sendMessage(user, "Dataset successfully uploaded",
                                    formattedMessage)

            return dataset

        except ValidationError as e:
            print(e)
            return None