
import time
import colorsys

import zipfile
import bleach
import arrow
import pytz
from pytz import UnknownTimeZoneError
import operator
import warnings
import urllib
import yaml
from PIL import Image
from numbers import Number
from django.views.generic.base import TemplateView
from django.contrib.auth import views as auth_views
from django.db.models import Q
from django import template
from django.db import IntegrityError, transaction
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import get_object_or_404
from matplotlib.backends.backend_agg import FigureCanvas
import gc
from pyparsing import ParseException
from uuid import uuid4
import codecs
import isodate
import re
import cgi
from icalendar import Calendar, Event

from functools import reduce

from rules.contrib.views import PermissionRequiredMixin

import rowers.braintreestuff as braintreestuff
import rowers.payments as payments
from rowers.opaque import encoder

from rowers.rower_rules import (
    ispromember,is_coach_user,is_team_member,is_rower_team_member,
    is_workout_user,isplanmember,can_delete_session,
    can_view_target,can_change_target,can_delete_target,
    can_view_plan,can_change_plan,can_delete_plan,
    can_view_cycle,can_change_cycle,can_delete_cycle,
    can_add_workout_member,can_plan_user,is_paid_coach,
    can_start_trial, can_start_plantrial,can_plan,is_workout_team,
    is_promember,
    )

from django.shortcuts import render
from django.template.loader import render_to_string

from django.views.generic.edit import UpdateView,DeleteView

from django.http import (
    HttpResponse, HttpResponseRedirect,
    JsonResponse,
    HttpResponseForbidden, HttpResponseNotAllowed,
    HttpResponseNotFound,Http404
    )
from django.contrib.auth import authenticate, login, logout
from rowers.forms import (
    ForceCurveOptionsForm,HistoForm,TeamMessageForm,
    LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm,
    TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
    WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
    LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
    PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
    VirtualRaceSelectForm,WorkoutRaceSelectForm,CourseSelectForm,
    RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm,
    FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
    MetricsForm,DisqualificationForm,disqualificationreasons,
    disqualifiers,SearchForm,BillingForm,PlanSelectForm,
    VideoAnalysisCreateForm,WorkoutSingleSelectForm,
    VideoAnalysisMetricsForm,SurveyForm,HistorySelectForm,
    )

from django.urls import reverse, reverse_lazy

from django.core.exceptions import PermissionDenied
from django.template import RequestContext
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.conf import settings
from django.urls import resolve
from django.utils.datastructures import MultiValueDictKeyError
from django.utils import timezone,translation
from django.core.mail import send_mail, BadHeaderError
from rowers.forms import (
    SummaryStringForm,IntervalUpdateForm,StrokeDataForm,
    StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm,
    FitnessMetricForm,PredictedPieceFormNoDistance,
    EmailForm, RegistrationForm, RegistrationFormTermsOfService,
    RegistrationFormUniqueEmail,RegistrationFormSex,
    CNsummaryForm,UpdateWindForm,
    StandardsForm,
    UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm,
    FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm,
    TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm,
    AnalysisOptionsForm, AnalysisChoiceForm,
    PlannedSessionMultipleCloneForm,SessionDateShiftForm,RowerTeamForm,
    )
from rowers.models import (
    Workout, User, Rower, WorkoutForm,FavoriteChart,
    PlannedSession, DeactivateUserForm,DeleteUserForm,
    TrainingPlan,TrainingPlanForm,TrainingTarget,TrainingTargetForm,
    TrainingMacroCycle,TrainingMesoCycle,TrainingMicroCycle,
    TrainingTarget,TrainingTargetForm,
    TrainingMacroCycleForm,createmacrofillers,
    createmicrofillers, createmesofillers,
    microcyclecheckdates,mesocyclecheckdates,macrocyclecheckdates,
    TrainingMesoCycleForm, TrainingMicroCycleForm,
    RaceLogo,RowerBillingAddressForm,PaidPlan,
    AlertEditForm, ConditionEditForm,
    PlannedSessionComment,CoachRequest,CoachOffer,
    VideoAnalysis,ShareKey,
    StandardCollection,CourseStandard,
    VirtualRaceFollower,
    )
from rowers.models import (
    RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
    RowerPowerZonesForm,AccountRowerForm,UserForm,
    Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
    WorkoutComment,WorkoutCommentForm,RowerExportForm,
    CalcAgePerformance,
    PowerTimeFitnessMetric,BlogPost,
    PlannedSessionForm,PlannedSessionTemplateForm,
    PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
    VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm,
    IndoorVirtualRaceResultForm,IndoorVirtualRaceResult,
    IndoorVirtualRaceForm,PlannedSessionCommentForm,
    Alert, Condition, StaticChartRowerForm,
    FollowerForm,
    )
from rowers.models import (
    FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet,
    get_course_timezone,BaseConditionFormSet,
    )
from rowers.metrics import rowingmetrics,defaultfavoritecharts,nometrics,metricsgroups
from rowers import metrics as metrics
from rowers import courses as courses
import rowers.uploads as uploads
from django.forms.formsets import formset_factory
from django.forms import modelformset_factory

from django.contrib.auth.decorators import login_required #,user_passes_test
from rowers.decorators import user_passes_test
from time import strftime,strptime,mktime,time,daylight
import os,sys
import datetime
import iso8601
import rowers.c2stuff as c2stuff
from rowers.c2stuff import c2_open
from rowers.runkeeperstuff import runkeeper_open
from rowers.sporttracksstuff import sporttracks_open
from rowers.tpstuff import tp_open
from iso8601 import ParseError
import rowers.stravastuff as stravastuff
from rowers.stravastuff import strava_open
import rowers.polarstuff as polarstuff
import rowers.sporttracksstuff as sporttracksstuff
import rowers.underarmourstuff  as underarmourstuff
from rowers.underarmourstuff import underarmour_open
import rowers.tpstuff as tpstuff
import rowers.runkeeperstuff as runkeeperstuff
import rowers.ownapistuff as ownapistuff
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
from rowsandall_app.settings import (
    C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
    STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
    POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
    SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
    SPORTTRACKS_CLIENT_SECRET,
    UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI,
    UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
    RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
    TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
    BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY,
    PAYMENT_PROCESSING_ON,
    RECAPTCHA_SITE_KEY, RECAPTCHA_SITE_SECRET
    )

#from rowers.tasks_standalone import addcomment2
from django.contrib import messages
from async_messages import messages as a_messages

from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime


import requests
import json
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rowers.rows import handle_uploaded_file,handle_uploaded_image
from rowers.plannedsessions import *
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv
from rowers.tasks import (
    handle_sendemail_unrecognized,handle_sendemailnewcomment,
    handle_sendemailsummary,
    handle_send_disqualification_email,
    handle_send_withdraw_email,
    handle_sendemailfile,
    handle_sendemailkml,
    handle_sendemailnewresponse, handle_updatedps,
    handle_updatecp,long_test_task,long_test_task2,
    handle_zip_file,handle_getagegrouprecords,
    handle_updatefitnessmetric,
    handle_update_empower,
    handle_sendemailics,
    handle_sendemail_userdeleted,
    handle_sendemail_raceregistration,
    handle_sendemail_racesubmission,
    handle_sendemail_ical,
    )

from scipy.signal import savgol_filter
#from django.shortcuts import render_to_response
try:
    from Cookie import SimpleCookie
except ModuleNotFoundError:
    from http.cookies import SimpleCookie
from shutil import copyfile,move
import rowers.mytypes as mytypes
from rowingdata import rower as rrower
from rowingdata import main as rmain
from rowingdata import rowingdata as rrdata
from rowingdata import make_cumvalues
from rowingdata import summarydata
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from rowers.emails import send_template_email,htmlstrip
from rowers.alerts import *

from pytz import timezone as tz,utc
from timezonefinder import TimezoneFinder
import dateutil
import mpld3
from mpld3 import plugins
import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from rowers.weather import get_wind_data,get_airport_code,get_metar_data

from oauth2_provider.models import Application,Grant,AccessToken

import django_rq
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low')

import redis
import threading
from redis import StrictRedis,Redis
from rq.exceptions import NoSuchJobError
from rq.registry import StartedJobRegistry
from rq import Queue,cancel_job

from django.utils.crypto import get_random_string

from django.core.cache import cache
from django_mailbox.models import Message,Mailbox,MessageAttachment

from rules.contrib.views import permission_required, objectgetter

def get_totals(workouts):
    totalseconds = 0
    totalmeters = 0

    for w in workouts:
        totalmeters += w.distance
        totalseconds += 60*(w.duration.hour*60+w.duration.minute)+w.duration.second

    hours, remainder = divmod(totalseconds,3600)
    minutes, seconds = divmod(remainder,60)

    return totalmeters,hours, minutes, seconds

# creating shareable views
def allow_shares(view_func):
    def sharify(request, *args, **kwargs):
        shared = kwargs.get('__shared', None)
        if shared is not None:
            del kwargs["__shared"]
            request.session['shared'] = True
            return view_func(request, *args, **kwargs)
        else: return login_required(view_func)(request, *args, **kwargs)
    return sharify

class SharifyError(Exception):
    pass

def sharedPage(request, key):
    try:
        try:
            shareKey = ShareKey.objects.get(pk=key)
        except:
            raise SharifyError
        if shareKey.expired:
            raise SharifyError
        func, args, kwargs = resolve(shareKey.location)
        kwargs["__shared"] = True
        return func(request, *args, **kwargs)
    except SharifyError:
        raise Http404 # or add a more detailed error page. This either means that the key doesn’t exist or is expired.

def createShareURL(request):
    if request.method == 'POST':
        url = request.POST['url']
        ndays = int(request.POST['ndays'])
        key = ShareKey.objects.create(pk=get_random_string(40),
                                  expiration_seconds=60*60*24*ndays,
                                  location = url)
        key.save()
        return render(request, 'share.html', {"key":key})
    else:
        raise Http404

def createShareModel(request, model_id):
    task = MyModel.objects.get(pk=model_id)
    key = ShareKey.objects.create(pk=get_random_string(40),
                                  expiration_seconds=60*60*24, # 1 day
                                  location = task.get_absolute_url(),
                                  )
    key.save()
    return render(request, 'share.html', {"key":key})

# Utility to get stroke data in a JSON response
class JSONResponse(HttpResponse):
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

def getfavorites(r,row):
    workouttype = 'ote'
    if row.workouttype in mytypes.otwtypes:
        workouttype = 'otw'

    matchworkouttypes = [workouttype,'all']

    workoutsource = row.workoutsource
    if 'speedcoach2' in row.workoutsource:
        workoutsource = 'speedcoach2'

    favorites = FavoriteChart.objects.filter(user=r,
                                             workouttype__in=matchworkouttypes).order_by("id")
    favorites2 = FavoriteChart.objects.filter(user=r,
                                              workouttype__in=[workoutsource]).order_by("id")



    favorites = favorites | favorites2

    maxfav = len(favorites)-1


    return favorites,maxfav

def get_logo_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(RaceLogo,pk=id)

def get_virtualevent_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(VirtualRace,pk=id)

def get_promember(request,*args,**kwargs):
    return request.user

def get_course_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(GeoCourse,pk=id)

def get_workout_by_opaqueid(request,id,**kwargs):
    pk = encoder.decode_hex(id)
    return get_object_or_404(Workout,pk=pk)

def get_session_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(PlannedSession,pk=id)

def get_target_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(TrainingTarget,pk=id)

def get_plan_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(TrainingPlan,pk=id)

def get_macro_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(TrainingMacroCycle,pk=id)

def get_meso_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(TrainingMesoCycle,pk=id)

def get_micro_by_pk(request,*args,**kwargs):
    id = kwargs['id']
    return get_object_or_404(TrainingMicroCycle,pk=id)

def get_workout_default_page(request,id):
    if request.user.is_anonymous:
        return reverse('workout_view',kwargs={'id':id})
    else:
        r = Rower.objects.get(user=request.user)
        if r.defaultlandingpage == 'workout_edit_view':
            return reverse('workout_edit_view',kwargs={'id':id})
        else:
            return reverse('workout_workflow_view',kwargs={'id':id})

def get_user_by_userid(*args,**kwargs):
    request = args[0]
    try:
        id = kwargs['userid']
    except KeyError:
        id = request.user.id

    if id is not None and int(id) == 0:
        id = request.user.id

    u = get_object_or_404(User,pk=id)
    return u

def get_user_by_id(*args,**kwargs):
    request = args[0]
    try:
        id = args[1]
    except IndexError:
        try:
            id = kwargs['id']
        except KeyError:
            id = request.user.id

    return get_object_or_404(User,pk=id)

def get_rower_by_id(request,id):
    u = User.objects.get(id=id)
    return u.rower

def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
    userid = int(userid)
    rowerid = int(rowerid)

    #if userid == 0:
    #    userid = request.user.id

    if notpermanent == False:
        if rowerid == 0 and 'rowerid' in request.session:
            rowerid = request.session['rowerid']

        if userid != 0:
            rowerid = 0

    try:

        if rowerid != 0:
            r = Rower.objects.get(id=rowerid)
            u = r.user
        elif userid != 0:
            u = User.objects.get(id=userid)
            r = getrower(u)
        elif request.user.is_anonymous:
            return None
        else:
            r = getrower(request.user)
            u = r.user


    except Rower.DoesNotExist:
        raise Http404("Rower doesn't exist")

    if r.user == request.user:
        request.session['rowerid'] = r.id
        return r

    if userid != 0 and not is_rower_team_member(request.user,u.rower):
        request.session['rowerid'] = request.user.rower.id
        raise PermissionDenied("You have no access to this user")

    if notpermanent == False:
        request.session['rowerid'] = r.id

    request.session['rowerid'] = r.id
    return r

def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False):

    userid = int(userid)
    rowerid = int(rowerid)

    if notpermanent == False:
        if rowerid == 0 and 'rowerid' in request.session:
            rowerid = request.session['rowerid']

        if userid != 0:
            rowerid = 0

    try:

        if rowerid != 0:
            r = Rower.objects.get(id=rowerid)
        elif userid != 0:
            u = User.objects.get(id=userid)
            r = getrower(u)
        else:
            r = getrower(request.user)

    except Rower.DoesNotExist:
        raise Http404("Rower doesn't exist")

    if 'shared' in request.session and request.session['shared']:
        return r

    if r.user != request.user and not can_plan_user(request.user,r ):
        request.session['rowerid'] = r.id
        raise PermissionDenied("You have no access to this user")

    if notpermanent == False:
        request.session['rowerid'] = r.id

    return r


def getrower(user):
    try:
        if user is None or user.is_anonymous:
            return None
    except AttributeError:
        if User.objects.get(id=user).is_anonymous:
            return None
    try:
        r = Rower.objects.get(user=user)
    except Rower.DoesNotExist:
        r = Rower(user=user)
        r.save()

    return r


def get_workout(id):
    try:
        id = encoder.decode_hex(id)
        w = Workout.objects.get(id=id)
    except Workout.DoesNotExist:
        raise Http404("Workout doesn't exist")

    return w

def get_workoutuser(id,request):
    try:
        id = encoder.decode_hex(id)
        w = Workout.objects.get(id=id)
    except Workout.DoesNotExist:
        raise Http404("Workout doesn't exist")

    if not is_workout_user(request.user,w):
        raise PermissionDenied

    return w

def getvalue(data):
    perc = 0
    total = 1
    done = 0
    id = 0
    session_key = 'noot'
    for i in data.iteritems():
        if i[0] == 'total':
            total = float(i[1])
        if i[0] == 'done':
            done = float(i[1])
        if i[0] == 'id':
            id = i[1]
        if i[0] == 'session_key':
            session_key = i[1]

    return total,done,id,session_key

class SessionTaskListener(threading.Thread):
    def __init__(self, r, channels):
        threading.Thread.__init__(self)
        self.redis = r
        self.pubsub = self.redis.pubsub()
        self.pubsub.subscribe(channels)

    def work(self, item):

        try:
            data =  json.loads(item['data'])
            total,done,id,session_key = getvalue(data)
            perc = int(100.*done/total)
            cache.set(id,perc,3600)

        except TypeError:
            pass

    def run(self):
        for item in self.pubsub.listen():
            if item['data'] == "KILL":
                self.pubsub.unsubscribe()
                break
            else:
                self.work(item)


queuefailed = Queue("failed",connection=Redis())
redis_connection = StrictRedis()
r = Redis()

# this doesn't yet work on production
#if settings.DEBUG:
#    client = SessionTaskListener(r,['tasks'])
#    client.start()


rq_registry = StartedJobRegistry(queue.name,connection=redis_connection)
rq_registryhigh = StartedJobRegistry(queuehigh.name,connection=redis_connection)
rq_registrylow = StartedJobRegistry(queuelow.name,connection=redis_connection)

from rq.job import Job

try:
    from rest_framework_swagger.views import get_swagger_view
except ImportError:
    pass

from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rowers.serializers import RowerSerializer,WorkoutSerializer
try:
    from rest_framework import status,permissions,generics
except ImportError:
    pass

from rest_framework.decorators import api_view, renderer_classes


from rowers.permissions import IsOwnerOrNot

import rowers.plots as plots
import rowers.mailprocessing as mailprocessing

from io import BytesIO
from scipy.special import lambertw

from rowers.dataprep import timedeltaconv
from rowers.dataprep import getsmallrowdata_db

from scipy.interpolate import griddata

#LOCALTIMEZONE = tz('Etc/UTC')
USER_LANGUAGE = 'en-US'

from rowers.interactiveplots import *
from rowers.celery import result as celery_result

# Define the API documentation
try:
    schema_view = get_swagger_view(title='Rowsandall API')
except NameError:
    pass

def remove_asynctask(request,id):
    try:
        oldtasks = request.session['async_tasks']
    except KeyError:
        oldtasks = []

    newtasks = []
    for task in oldtasks:
        if id not in task[0]:
            newtasks += [(task[0],task[1])]

    request.session['async_tasks'] = newtasks

def get_job_result(jobid):
    if settings.TESTING:
        return None
    elif settings.CELERY:
        result = celery_result.AsyncResult(jobid).result
    else:
        running_job_ids = rq_registry.get_job_ids()
        if len(running_job_ids) and jobid in running_job_ids:
            # job is running
            return None
        else:
            # job is ready
            try:
                job = Job.fetch(jobid,connection=redis_connection)
                result = job.result
            except NoSuchJobError:
                return None

    return result

verbose_job_status = {
    'updatecp': 'Critical Power Calculation for Ergometer Workouts',
    'updatecpwater': 'Critical Power Calculation for OTW Workouts',
    'otwsetpower': 'Rowing Physics OTW Power Calculation',
    'agegrouprecords': 'Calculate age group records',
    'make_plot': 'Create static chart',
    'long_test_task': 'Long Test Task',
    'long_test_task2': 'Long Test Task 2',
    'update_empower': 'Correct Empower Inflated Power Bug',
    'submit_race': 'Checking Race Course Result',
    }

def get_job_status(jobid):
    if settings.TESTING:
        summary = {
            'status': 'failed',
            'result': 0,
            'finished': True,
            'failed': True,
            'started_at':None,
            }
        return summary
    elif settings.CELERY:
        job = celery_result.AsyncResult(jobid)
        jobresult = job.result


        if 'fail' in job.status.lower():
            jobresult = '0'
        summary = {
            'status': job.status,
            'result': jobresult,
            'started_at': None
        }
    else:
        try:
            job = Job.fetch(jobid,connection=redis_connection)
            try:
                status = job.status
            except AttributeError:
                status = job.get_status()

            summary = {
                'status':status,
                'result':job.result,
                'started_at':job.started_at
            }
        except NoSuchJobError:
            summary = {
                'status': 'success',
                'result': 1,
                'started_at':None,
            }

    try:
        if 'fail' in summary['status'].lower():
            summary['failed'] = True
        else:
            summary['failed'] = False

        if 'success' in summary['status'].lower():
            summary['finished'] = True
        elif 'finished' in summary['status'].lower():
            summary['finished'] = True
        else:
            summary['finished'] = False
    except AttributeError:
        summary = {
            'status': 'failed',
            'result': 0,
            'finished': True,
            'failed': True,
            'started_at':None,
            }

    return summary

def kill_async_job(request,id='aap'):
    if settings.CELERY:
        job = celery_result.AsyncResult(id)
        job.revoke()
    else:
        try:
            cancel_job(id,connection=redis_connection)
        except NoSuchJobError:
            pass

    remove_asynctask(request,id)
    cache.delete(id)
    url = reverse(session_jobs_status)

    return HttpResponseRedirect(url)

@login_required()
def raise_500(request):
    if request.user.is_superuser:
        raise ValueError
    else:
        return HttpResponse("invalid")

#@login_required()
#def test_job_view(request,aantal=100):
#
#    session_key = request.session._session_key
#
#    job = myqueue(queuehigh,long_test_task,int(aantal),
#                  session_key=session_key)
#
#
#    try:
#        request.session['async_tasks'] += [(job.id,'long_test_task')]
#    except KeyError:
#        request.session['async_tasks'] = [(job.id,'long_test_task')]
#
#    url = reverse(session_jobs_status)
#
#    return HttpResponseRedirect(url)

#@login_required()
#def test_job_view2(request,aantal=100):
#
#
#    job = myqueue(queuehigh,long_test_task2,int(aantal),
#                  secret=settings.PROGRESS_CACHE_SECRET)#
#
#
#    try:
#        request.session['async_tasks'] += [(job.id,'long_test_task2')]
#    except KeyError:
#        request.session['async_tasks'] = [(job.id,'long_test_task2')]
#
#    url = reverse(session_jobs_status)
#
#    return HttpResponseRedirect(url)

@csrf_exempt
def post_progress(request,id=None,value=0):
    if request.method == 'POST':
        try:
            secret = request.POST['secret']
        except KeyError:
            return HttpResponse('Access Denied',status=401)
        if secret == settings.PROGRESS_CACHE_SECRET:
            if not id:
                try:
                    id = request.POST['id']
                except KeyError:
                    return HttpResponse('Invalid request',400)
            try:
                value = request.POST['value']
            except KeyError:
                pass

            cache.set(id,value,3600)
            # test
            result = cache.get(id)

            return HttpResponse('progress cached '+str(result),
                                status=201)
        else: # secret not given
            return HttpResponse('access denied',status=401)

    else: # request method is not POST
        return HttpResponse('GET method not allowed',status=405)

def get_all_queued_jobs(userid=0):
    r = StrictRedis()

    jobs = []

    celerykeys = r.keys('celery*')
    for key in celerykeys:
        id= key[17:]
        job = celery_result.AsyncResult(id)
        jobresult = job.result
        if 'fail' in job.status.lower():
            jobresult = '0'
        jobs.append(
            (id,{
                'status':job.status,
                'result':jobresult,
                'function':'',
                'meta':job.info,
                }))

    ids = [j.id for j in queue.jobs]
    ids += [j.id for j in queuehigh.jobs]
    ids += [j.id for j in queuelow.jobs]
    ids += [j.id for j in queuefailed.jobs]


    for id in ids:
        job = Job.fetch(id,connection=redis_connection)
        jobs.append(
            (id,{
                'status':job.get_status(),
                'result':job.result,
                'function':job.func_name,
                'meta':job.meta,
                }))

    return jobs

def get_stored_tasks_status(request):
    try:
        taskids = request.session['async_tasks']
    except KeyError:
        taskids = []

    taskstatus = []
    for id,func_name in reversed(taskids):
        progress = 0
        try:
            cached_progress = cache.get(id)
            if cached_progress is not None:
                cached_progress = int(cached_progress)
            else:
                cached_progress = 0
        except ValueError:
            cached_progress = 0
        finished = get_job_status(id)['finished']
        if finished:
            cache.set(id,100)
            progress = 100
        elif cached_progress is not None and cached_progress>0:
            progress = cached_progress
        else:
            progress = 0

        this_task_status = {
            'id':id,
            'status':get_job_status(id)['status'],
            'failed':get_job_status(id)['failed'],
            'finished':get_job_status(id)['finished'],
            'func_name':func_name,
            'verbose': verbose_job_status[func_name],
            'progress': progress,
        }

        taskstatus.append(this_task_status)


    return taskstatus

@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def get_thumbnails(request,id):
    row = get_workout_by_opaqueid(request,id)


    r = getrower(request.user)
    result = request.user.is_authenticated and ispromember(request.user)
    if result:
        promember=1
    if request.user == row.user.user:
        mayedit=1

    comments = WorkoutComment.objects.filter(workout=row)

    aantalcomments = len(comments)

    favorites,maxfav = getfavorites(r,row)

    charts = []

    charts = thumbnails_set(r,encoder.decode_hex(id),favorites)
    try:
        if charts[0]['script'] == '':
            charts = []
    except IndexError:
        charts = []


    return JSONResponse(charts)

def get_blog_posts(request):
    blogposts = BlogPost.objects.all().order_by("-date")

    jsondata = []

    if blogposts:
        for blogpost in blogposts[0:3]:
            thedict = {
                'title':blogpost.title,
                'link':blogpost.link,
            }

            jsondata.append(thedict)

    return JSONResponse(jsondata)

def get_blog_posts_old(request):
    try:
        response = requests.get(
            'https://analytics.rowsandall.com/wp-json/wp/v2/posts?per_page=3')
        if response.status_code == 200:
            blogs_json = response.json()
            #        with open('blogs.txt','w') as o:
            #            o.write(json.dumps(blogs_json,indent=2,sort_keys=True))
        else:
            blogs_json = []
    except ConnectionError:
        pass

    blogposts = []


    for postdata in blogs_json[0:3]:

        try:
            title = postdata['title']['rendered'].encode(
                'ascii','xmlcharrefreplace')

        except TypeError:
            title = postdata['title']['rendered'].encode(
                'ascii','xmlcharrefreplace').decode('utf-8')


        thedict = {
            'title': title,
#            'author': '',
#            'image': image_url,
#            'excerpt': excerpt_first,
            'link': postdata['link'],
            }

        blogposts.append(thedict)

    return JSONResponse(blogposts)



@login_required()
def session_jobs_view(request):
    taskstatus = get_stored_tasks_status(request)

    return HttpResponse(json.dumps(taskstatus))

@login_required()
def session_jobs_status(request):
    taskstatus = get_stored_tasks_status(request)

    return render(request,
                  'async_tasks.html',
                  {'taskstatus':taskstatus})

# Test if row data include candidates
def rowhascoordinates(row):
    # create interactive plot
    f1 = row.csvfilename
    u = row.user.user
    r = getrower(u)
    rowdata = rdata(f1)
    hascoordinates = 1
    if rowdata != 0:
        try:
            latitude = rowdata.df[' latitude']

            if not latitude.std():
                hascoordinates = 0
        except (KeyError,AttributeError):
            hascoordinates = 0

    else:
        hascoordinates = 0

    return hascoordinates


# Wrapper around the rowingdata call to catch some exceptions
# Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0
def rdata(file,rower=rrower()):
    try:
        res = rrdata(csvfile=file,rower=rower)
    except (IOError, IndexError, EOFError,FileNotFoundError):
        try:
            res = rrdata(csvfile=file+'.gz',rower=rower)
        except (IOError, IndexError, EOFError,FileNotFoundError):
            res = 0

    return res

# Query to get teams managed and member of
def get_my_teams(user):
    try:
        therower = Rower.objects.get(user=user)
        try:
            teams1 = therower.team.all()
        except AttributeError:
            teams1 = []

        teams2 = Team.objects.filter(manager=user)
        teams = list(set(teams1).union(set(teams2)))
    except TypeError:
        teams = []

    return teams

# Used for the interval editor - translates seconds to a time object
def get_time(second):
    if (second<=0) or (second>1e9):
        hours = 0
        minutes=0
        sec=0
        microsecond = 0
    elif math.isnan(second):
        hours = 0
        minutes=0
        sec=0
        microsecond = 0
    else:
        days = int(second/(24.*3600.)) % (24*3600)
        hours = int((second-24.*3600.*days)/3600.) % 24
        minutes = int((second-3600.*(hours+24.*days))/60.) % 60
        sec = int(second-3600.*(hours+24.*days)-60.*minutes) % 60
        microsecond = int(1.0e6*(second-3600.*(hours+24.*days)-60.*minutes-sec))
    return datetime.time(hours,minutes,sec,microsecond)


# get the workout ID from the SportTracks URI
def getidfromsturi(uri,length=8):
    return uri[len(uri)-length:]

import re

def getidfromuri(uri):
    m = re.search('/(\w.*)\/(\d+)',uri)
    return m.group(2)



from rowers.utils import (
    geo_distance,serialize_list,deserialize_list,uniqify,
    str2bool,range_to_color_hex,absolute,myqueue,get_call,
    calculate_age,rankingdistances,rankingdurations,
    is_ranking_piece,my_dict_from_instance,wavg,NoTokenError
    )

import rowers.datautils as datautils

# Check if a user is a Coach member
def iscoachmember(user):
    if not user.is_anonymous:
        try:
            r = Rower.objects.get(user=user)
        except Rower.DoesNotExist:
            r = Rower(user=user)
            r.save()

        result = user.is_authenticated and ('coach' in r.rowerplan)
    else:
        result = False

    return result


from rowers.utils import ProcessorCustomerError


# More User/Rower utils
def add_defaultfavorites(r):
    for c in defaultfavoritecharts:
        f = FavoriteChart(user=r,
                          yparam1=c['yparam1'],
                          yparam2=c['yparam2'],
                          xparam=c['xparam'],
                          plottype=c['plottype'],
                          workouttype=c['workouttype'],
                          reststrokes=c['reststrokes'],
                          notes=c['notes'])

        f.save()
    return 1


# Shows email form and sends it if submitted
def sendmail(request):
    if request.method == 'POST':
        # test recaptcha
        response_string = request.POST.get('g-recaptcha-response')
        # replace below with settings
        recaptcha_secret = RECAPTCHA_SITE_SECRET
        url = 'https://www.google.com/recaptcha/api/siteverify'
        data = {
            'secret':recaptcha_secret,
            'response': response_string,
        }
        response = requests.post(url,data=data,verify=True)
        success = False
        if response.status_code == 200:
            success = response.json().get('success')

        form = EmailForm(request.POST)
        if form.is_valid() and success:
            firstname = form.cleaned_data['firstname']
            lastname = form.cleaned_data['lastname']
            email = form.cleaned_data['email']
            subject = 'Rowsandall Contact Form:'+form.cleaned_data['subject']
            message = form.cleaned_data['message']
            fullemail = firstname + " " + lastname + " " + "<" + email + ">"
            try:
                send_mail(subject, message, fullemail, ['info@rowsandall.com'])
            except:
                messages.error(request,"Something went wrong trying to send the email")
            return HttpResponseRedirect('/rowers/email/thankyou/')
        else:
            if not success:
                messages.error(request,'Bots are not welcome')
            else:
                messages.error(request,'Something went wrong. Please try again')
            return HttpResponseRedirect('/rowers/email/')
    else:
        return HttpResponseRedirect('/rowers/email/')


# Create workout data from Strava or Concept2
# data and create the associated Workout object and save it
def add_workout_from_strokedata(user,importid,data,strokedata,
                                source='c2',splitdata=None,
                                workoutsource='concept2'):
    try:
        workouttype = data['type']
    except KeyError:
        workouttype = 'rower'

    if workouttype not in [x[0] for x in Workout.workouttypes]:
        workouttype = 'other'
    try:
        comments = data['comments']
    except:
        comments = ' '

#    comments = "Imported data \n %s" % comments
#    comments = "Imported data \n"+comments  # str(comments)
    try:
        thetimezone = tz(data['timezone'])
    except:
        thetimezone = 'UTC'

    r = getrower(user)
    try:
        rowdatetime = iso8601.parse_date(data['date_utc'])
    except KeyError:
        rowdatetime = iso8601.parse_date(data['start_date'])
    except ParseError:
        rowdatetime = iso8601.parse_date(data['date'])


    try:
        c2intervaltype = data['workout_type']

    except KeyError:
        c2intervaltype = ''

    try:
        title = data['name']
    except KeyError:
        title = ""
        try:
            t = data['comments'].split('\n', 1)[0]
            title += t[:20]
        except:
            title = 'Imported'

    starttimeunix = arrow.get(rowdatetime).timestamp

    res = make_cumvalues(0.1*strokedata['t'])
    cum_time = res[0]
    lapidx = res[1]

    unixtime = cum_time+starttimeunix
    # unixtime[0] = starttimeunix
    seconds = 0.1*strokedata.loc[:,'t']

    nr_rows = len(unixtime)

    try:
        latcoord = strokedata.loc[:,'lat']
        loncoord = strokedata.loc[:,'lon']
    except:
        latcoord = np.zeros(nr_rows)
        loncoord = np.zeros(nr_rows)


    try:
        strokelength = strokedata.loc[:,'strokelength']
    except:
        strokelength = np.zeros(nr_rows)

    dist2 = 0.1*strokedata.loc[:,'d']

    try:
        spm = strokedata.loc[:,'spm']
    except KeyError:
        spm = 0*dist2

    try:
        hr = strokedata.loc[:,'hr']
    except KeyError:
        hr = 0*spm
    pace = strokedata.loc[:,'p']/10.
    pace = np.clip(pace,0,1e4)
    pace = pace.replace(0,300)

    velo = 500./pace

    power = 2.8*velo**3

    # save csv
    # Create data frame with all necessary data to write to csv
    df = pd.DataFrame({'TimeStamp (sec)':unixtime,
                       ' Horizontal (meters)': dist2,
                       ' Cadence (stokes/min)':spm,
                       ' HRCur (bpm)':hr,
                       ' longitude':loncoord,
                       ' latitude':latcoord,
                       ' Stroke500mPace (sec/500m)':pace,
                       ' Power (watts)':power,
                       ' DragFactor':np.zeros(nr_rows),
                       ' DriveLength (meters)':np.zeros(nr_rows),
                       ' StrokeDistance (meters)':strokelength,
                       ' DriveTime (ms)':np.zeros(nr_rows),
                       ' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
                       ' AverageDriveForce (lbs)':np.zeros(nr_rows),
                       ' PeakDriveForce (lbs)':np.zeros(nr_rows),
                       ' lapIdx':lapidx,
                       ' ElapsedTime (sec)':seconds
                       })


    df.sort_values(by='TimeStamp (sec)',ascending=True)

    timestr = strftime("%Y%m%d-%H%M%S")


    # Create CSV file name and save data to CSV file
    csvfilename ='media/{code}_{importid}.csv'.format(
        importid=importid,
        code = uuid4().hex[:16]
        )

    res =  df.to_csv(csvfilename+'.gz',index_label='index',
                     compression='gzip')


    # with Concept2
    if source=='c2':
        try:
            totaldist = data['distance']
            totaltime = data['time']/10.
        except KeyError:
            totaldist = 0
            totaltime = 0
    else:
        totaldist = 0
        totaltime = 0

    id,message = dataprep.save_workout_database(
        csvfilename,r,
        workouttype=workouttype,
        title=title,notes=comments,
#        totaldist=totaldist,
#        totaltime=totaltime,
        workoutsource=workoutsource,
        dosummary=True
    )



    return id,message




def keyvalue_get_default(key,options,def_options):

    try:
        return options[key]
    except KeyError:
        return def_options[key]




# Creates unix time stamp from a datetime object
def totimestamp(dt, epoch=datetime.datetime(1970,1,1,tzinfo=tz('UTC'))):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6
# Check if a column of a dataframe has the required (aantal)
# number of elements. Also checks if the column is a numerical type
# Replaces any faulty columns with zeros
def trydf(df,aantal,column):
    try:
        s = df[column]
        if len(s) != aantal:
            return np.zeros(aantal)
        if not np.issubdtype(s,np.number):
            return np.zeros(aantal)
    except KeyError:
        s = np.zeros(aantal)

    return s

import rowers.teams as teams
from rowers.models import C2WorldClassAgePerformance
