from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
from rowers.views.statements import *

import collections
from jinja2 import Template,Environment,FileSystemLoader

def floatformat(x,prec=2):
    return '{x}'.format(x=round(x,prec))

import time
env = Environment(loader = FileSystemLoader(["rowers/templates"]))
env.filters['floatformat'] = floatformat


from django.contrib.staticfiles import finders
from rowers.forms import analysischoices

# generic Analysis view -

defaultoptions = {
    'includereststrokes': False,
    'workouttypes':['rower','dynamic','slides'],
    'waterboattype': mytypes.waterboattype,
    'rankingonly': False,
    'function':'boxplot'
}


@user_passes_test(ispromember, login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def analysis_new(request,userid=0,function='boxplot',teamid=0):
    r = getrequestrower(request, userid=userid)
    user = r.user
    userid = user.id

    try:
        theteam = Team.objects.get(id=teamid)
    except Team.DoesNotExist:
        theteam = None


    if 'options' in request.session:
        options = request.session['options']
    else:
        options=defaultoptions

    options['userid'] = userid
    try:
        workouttypes = options['workouttypes']
    except KeyError:
        workouttypes = ['rower','dynamic','slides']

    try:
        modalities = options['modalities']
        modality = modalities[0]
    except KeyError:
        modalities = [m[0] for m in mytypes.workouttypes_ordered.items()]
        modality = 'all'



    try:
        rankingonly = options['rankingonly']
    except KeyError:
        rankingonly = False

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False

    if 'startdate' in request.session:
        startdate = iso8601.parse_date(request.session['startdate'])
    else:
        startdate=timezone.now()-datetime.timedelta(days=42)

    if function not in [c[0] for c in analysischoices]:
        function = 'boxplot'

    if 'enddate' in request.session:
        enddate = iso8601.parse_date(request.session['enddate'])
    else:
        enddate = timezone.now()

    workstrokesonly = not includereststrokes

    waterboattype = mytypes.waterboattype

    if request.method == 'POST':
        thediv = get_call()
        dateform = DateRangeForm(request.POST)
        if dateform.is_valid():
            startdate = dateform.cleaned_data['startdate']
            enddate = dateform.cleaned_data['enddate']
            startdatestring = startdate.strftime('%Y-%m-%d')
            enddatestring = enddate.strftime('%Y-%m-%d')
            request.session['startdate'] = startdatestring
            request.session['enddate'] = enddatestring
        optionsform = AnalysisOptionsForm(request.POST)
        if optionsform.is_valid():
            for key, value in optionsform.cleaned_data.items():
                options[key] = value

            modality = optionsform.cleaned_data['modality']
            waterboattype = optionsform.cleaned_data['waterboattype']
            if modality == 'all':
                modalities = [m[0] for m in mytypes.workouttypes]
            else:
                modalities = [modality]
            if modality != 'water':
                waterboattype = [b[0] for b in mytypes.boattypes]


            if 'rankingonly' in optionsform.cleaned_data:
                rankingonly = optionsform.cleaned_data['rankingonly']
            else:
                rankingonly = False

            options['modalities'] = modalities
            options['waterboattype'] = waterboattype

        chartform = AnalysisChoiceForm(request.POST)
        if chartform.is_valid():
            for key, value in chartform.cleaned_data.items():
                options[key] = value


        form = WorkoutMultipleCompareForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            selectedworkouts = cd['workouts']
            ids = [int(w.id) for w in selectedworkouts]
            options['ids'] = ids
        else:
            ids = []
            options['ids'] = ids
    else:
        thediv = ''
        dateform = DateRangeForm(initial={
            'startdate':startdate,
            'enddate':enddate,
            })



    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])


    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])



    if theteam is not None and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
        workouts = Workout.objects.filter(team=theteam,
                                          startdatetime__gte=startdate,
                                          startdatetime__lte=enddate,
                                          workouttype__in=modalities,
        )
    elif theteam is not None and theteam.viewing == 'coachonly':
        workouts = Workout.objects.filter(team=theteam,user=r,
                                          startdatetime__gte=startdate,
                                          startdatetime__lte=enddate,
                                          workouttype__in=modalities,
        )
    else:
        workouts = Workout.objects.filter(user=r,
                                          startdatetime__gte=startdate,
                                          startdatetime__lte=enddate,
                                          workouttype__in=modalities,
        )

    workouts = workouts.order_by(
            "-date", "-starttime"
        ).exclude(boattype__in=negtypes)


    if rankingonly:
        workouts = workouts.exclude(rankingpiece=False)

    query = request.GET.get('q')
    if query:
        query_list = query.split()
        workouts = workouts.filter(
            reduce(operator.and_,
                   (Q(name__icontains=q) for q in query_list)) |
            reduce(operator.and_,
                   (Q(notes__icontains=q) for q in query_list))
            )
        searchform = SearchForm(initial={'q':query})
    else:
        searchform = SearchForm()

    if request.method != 'POST':
        form = WorkoutMultipleCompareForm()
        chartform = AnalysisChoiceForm(initial={'function':function})
        selectedworkouts = Workout.objects.none()
    else:
        selectedworkouts = Workout.objects.filter(id__in=ids)

    form.fields["workouts"].queryset = workouts | selectedworkouts

    optionsform = AnalysisOptionsForm(initial={
        'modality':modality,
        'waterboattype':waterboattype,
        'rankingonly':rankingonly,
        })



    startdatestring = startdate.strftime('%Y-%m-%d')
    enddatestring = enddate.strftime('%Y-%m-%d')
    request.session['startdate'] = startdatestring
    request.session['enddate'] = enddatestring
    request.session['options'] = options


    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name':'Analysis'
        },
        {
            'url':reverse('analysis_new',kwargs={'userid':userid}),
            'name': 'Analysis Select'
        },
    ]
    return render(request, 'user_analysis_select.html',
                  {'workouts': workouts,
                   'dateform':dateform,
                   'startdate':startdate,
                   'enddate':enddate,
                   'rower':r,
                   'breadcrumbs':breadcrumbs,
                   'theuser':user,
                   'the_div':thediv,
                   'form':form,
                   'active':'nav-analysis',
                   'chartform':chartform,
                   'searchform':searchform,
                   'optionsform':optionsform,
                   'teams':get_my_teams(request.user),
                   })

def trendflexdata(workouts, options,userid=0):

    includereststrokes = options['includereststrokes']
    palette = options['palette']
    groupby = options['groupby']
    binsize = options['binsize']
    xparam  = options['xparam']
    yparam = options['yparam']
    spmmin = options['spmmin']
    spmmax = options['spmmax']
    workmin = options['workmin']
    workmax = options['workmax']
    ploterrorbars = options['ploterrorbars']
    ids = options['ids']
    workstrokesonly = not includereststrokes

    labeldict = {
        int(w.id): w.__str__() for w in workouts
    }

    fieldlist,fielddict = dataprep.getstatsfields()
    fieldlist = [xparam,yparam,groupby,
                 'workoutid','spm','driveenergy',
                 'workoutstate']

    # prepare data frame
    datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)

    if xparam == groupby:
        datadf['groupby'] = datadf[xparam]
        groupy = 'groupby'

    datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)


    datadf = dataprep.filter_df(datadf,'spm',spmmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'spm',spmmax,
                                largerthan=False)

    datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
                                largerthan=False)


    datadf.dropna(axis=0,how='any',inplace=True)


    datemapping = {
        w.id:w.date for w in workouts
    }

    datadf['date'] = datadf['workoutid']
    datadf['date'].replace(datemapping,inplace=True)

    today = datetime.date.today()

    try:
        datadf['days ago'] = list(map(lambda x : x.days, datadf.date - today))
    except TypeError:
        datadf['days ago'] = 0


    if groupby != 'date':
        try:
            bins = np.arange(datadf[groupby].min()-binsize,
                             datadf[groupby].max()+binsize,
                             binsize)
            groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False))
        except ValueError:
            return ('','Error: not enough data')
    else:
        bins = np.arange(datadf['days ago'].min()-binsize,
                         datadf['days ago'].max()+binsize,
                         binsize,
        )
        groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
                                           labels=False))


    xvalues = groups.mean()[xparam]
    yvalues = groups.mean()[yparam]
    xerror = groups.std()[xparam]
    yerror = groups.std()[yparam]
    groupsize = groups.count()[xparam]

    mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()])
    xvalues.loc[mask] = np.nan

    yvalues.loc[mask] = np.nan
    xerror.loc[mask] = np.nan
    yerror.loc[mask] = np.nan
    groupsize.loc[mask] = np.nan

    xvalues.dropna(inplace=True)
    yvalues.dropna(inplace=True)
    xerror.dropna(inplace=True)
    yerror.dropna(inplace=True)
    groupsize.dropna(inplace=True)

    if len(groupsize) == 0:
        messages.error(request,'No data in selection')
        url = reverse(user_multiflex_select)
        return HttpResponseRedirect(url)
    else:
        groupsize = 30.*np.sqrt(groupsize/float(groupsize.max()))

    df = pd.DataFrame({
        xparam:xvalues,
        yparam:yvalues,
        'x':xvalues,
        'y':yvalues,
        'xerror':xerror,
        'yerror':yerror,
        'groupsize':groupsize,
    })


    if yparam == 'pace':
        df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)

    aantal = len(df)

    if groupby != 'date':
        try:
            df['groupval'] = groups.mean()[groupby]
            df['groupval'].loc[mask] = np.nan

            groupcols = df['groupval']
        except ValueError:
            df['groupval'] =  groups.mean()[groupby].fillna(value=0)
            df['groupval'].loc[mask] = np.nan
            groupcols = df['groupval']
        except KeyError:
            messages.error(request,'Data selection error')
            url = reverse(user_multiflex_select)
            return HttpResponseRedirect(url)
    else:
        try:
            dates = groups.min()[groupby]
            dates.loc[mask] = np.nan
            dates.dropna(inplace=True)
            df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates]
            df['groupval'].loc[mask] = np.nan
            groupcols = 100.*np.arange(aantal)/float(aantal)
        except AttributeError:
            df['groupval'] = groups.mean()['days ago'].fillna(value=0)
            groupcols = 100.*np.arange(aantal)/float(aantal)


    groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min())

    if aantal == 1:
        groupcols = np.array([1.])


    colors = range_to_color_hex(groupcols,palette=palette)

    df['color'] = colors

    clegendx = np.arange(0,1.2,.2)
    legcolors = range_to_color_hex(clegendx,palette=palette)
    if groupby != 'date':
        clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())
    else:
        clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min())



    colorlegend =  zip(range(6),clegendy,legcolors)


    if userid == 0:
        extratitle = ''
    else:
        u = User.objects.get(id=userid)
        extratitle = ' '+u.first_name+' '+u.last_name



    script,div = interactive_multiflex(df,xparam,yparam,
                                       groupby,
                                       extratitle=extratitle,
                                       ploterrorbars=ploterrorbars,
                                       binsize=binsize,
                                       colorlegend=colorlegend,
                                       spmmin=spmmin,spmmax=spmmax,
                                       workmin=workmin,workmax=workmax)

    scripta= script.split('\n')[2:-1]
    script = ''.join(scripta)

    return(script,div)

def flexalldata(workouts, options):
    includereststrokes = options['includereststrokes']
    xparam = options['xaxis']
    yparam1 = options['yaxis1']
    yparam2 = options['yaxis2']
    promember=True

    workstrokesonly = not includereststrokes

    res = interactive_cum_flex_chart2(workouts, xparam=xparam,
                                      yparam1=yparam1,
                                      yparam2=yparam2,
                                      promember=promember,
                                      workstrokesonly=workstrokesonly,
                                      )
    script = res[0]
    div = res[1]

    scripta = script.split('\n')[2:-1]
    script = ''.join(scripta)

    return(script,div)

def histodata(workouts, options):
    includereststrokes = options['includereststrokes']
    plotfield = options['plotfield']
    function = options['function']
    spmmin = options['spmmin']
    spmmax = options['spmmax']
    workmin = options['workmin']
    workmax = options['workmax']


    workstrokesonly = not includereststrokes

    script, div = interactive_histoall(workouts,plotfield,includereststrokes,
                                       spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)


    scripta = script.split('\n')[2:-1]
    script = ''.join(scripta)

    return(script,div)

def statsdata(workouts, options):
    includereststrokes = options['includereststrokes']
    spmmin = options['spmmin']
    spmmax = options['spmmax']
    workmin = options['workmin']
    workmax = options['workmax']
    ids = options['ids']
    userid = options['userid']
    plotfield = options['plotfield']
    function = options['function']

    workstrokesonly = not includereststrokes

    ids = [w.id for w in workouts]

    datamapping = {
        w.id:w.date for w in workouts
        }

    fieldlist,fielddict = dataprep.getstatsfields()
    # prepare data frame
    datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)


    datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)

    # Create stats
    stats = {}
    #    fielddict.pop('workoutstate')
    #    fielddict.pop('workoutid')


    for field,verbosename in fielddict.items():
        try:
            thedict = {
                'mean':datadf[field].mean(),
                'min': datadf[field].min(),
                'std': datadf[field].std(),
                'max': datadf[field].max(),
                'median': datadf[field].median(),
                'firstq':datadf[field].quantile(q=0.25),
                'thirdq':datadf[field].quantile(q=0.75),
                'verbosename':verbosename,
                }
            stats[field] = thedict
        except KeyError:
            pass

    # Create a dict with correlation values
    cor = datadf.corr(method='spearman')
    cor.fillna(value=0,inplace=True)
    cordict = {}
    for field1,verbosename1 in fielddict.items():
        thedict = {}
        for field2,verbosename2 in fielddict.items():
            try:
                thedict[verbosename2] = cor.loc[field1,field2]
            except KeyError:
                thedict[verbosename2] = 0

        cordict[verbosename1] = thedict

    context = {
        'stats':stats,
        'cordict':cordict,
        }

    htmly = env.get_template('statsdiv.html')
    html_content = htmly.render(context)

    return('',html_content)

def comparisondata(workouts,options):
    includereststrokes = options['includereststrokes']
    xparam = options['xaxis']
    yparam1 = options['yaxis1']
    plottype = options['plottype']
    promember=True

    workstrokesonly = not includereststrokes

    ids = [w.id for w in workouts]

    labeldict = {
        int(w.id): w.__str__() for w in workouts
        }

    res = interactive_multiple_compare_chart(ids,xparam,yparam1,
                                                    promember=promember,
                                                    plottype=plottype,
                                                    labeldict=labeldict)

    script = res[0]
    div = res[1]

    scripta = script.split('\n')[2:-1]
    script = ''.join(scripta)

    return(script,div)


def boxplotdata(workouts,options):

    includereststrokes = options['includereststrokes']
    spmmin = options['spmmin']
    spmmax = options['spmmax']
    workmin = options['workmin']
    workmax = options['workmax']
    ids = options['ids']
    userid = options['userid']
    plotfield = options['plotfield']
    function = options['function']

    workstrokesonly = not includereststrokes
    labeldict = {
        int(w.id): w.__str__() for w in workouts
    }


    datemapping = {
        w.id:w.date for w in workouts
    }



    fieldlist,fielddict = dataprep.getstatsfields()
    fieldlist = [plotfield,'workoutid','spm','driveenergy',
                 'workoutstate']

    ids = [w.id for w in workouts]

    # prepare data frame
    datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)



    datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)

    datadf = dataprep.filter_df(datadf,'spm',spmmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'spm',spmmax,
                                largerthan=False)
    datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
                                largerthan=False)

    datadf.dropna(axis=0,how='any',inplace=True)


    datadf['workoutid'].replace(datemapping,inplace=True)
    datadf.rename(columns={"workoutid":"date"},inplace=True)
    datadf['date'] = pd.to_datetime(datadf['date'],errors='coerce')
    datadf = datadf.dropna(subset=['date'])
    datadf = datadf.sort_values(['date'])

    if userid == 0:
        extratitle = ''
    else:
        u = User.objects.get(id=userid)
        extratitle = ' '+u.first_name+' '+u.last_name



    script,div = interactive_boxchart(datadf,plotfield,
                                      extratitle=extratitle,
                                      spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)

    scripta = script.split('\n')[2:-1]
    script = ''.join(scripta)

    return(script,div)

@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def analysis_view_data(request,userid=0):
    if not request.is_ajax():
        url = reverse('analysis_new')
        return HttpResponseRedirect(url)

    if 'options' in request.session:
        options = request.session['options']
    else:
        options = defaultoptions


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

    workouts = []

    ids = options['ids']
    function = options['function']

    if not ids:
        return JSONResponse({
            "script":'',
            "div":'No data found'
        })

    for id in ids:
        try:
            workouts.append(Workout.objects.get(id=id))
        except Workout.DoesNotExist:
            pass

    if function == 'boxplot':
        script, div = boxplotdata(workouts,options)
    elif function == 'trendflex':
        script, div = trendflexdata(workouts, options,userid=userid)
    elif function == 'histo':
        script, div = histodata(workouts, options)
    elif function == 'flexall':
        script,div = flexalldata(workouts,options)
    elif function == 'stats':
        script,div = statsdata(workouts,options)
    elif function == 'compare':
        script,div = comparisondata(workouts,options)
    else:
        script = ''
        div = 'Unknown analysis functions'


    return JSONResponse({
        "script":script,
        "div":div,
        })


# Histogram for a date/time range
@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def histo(request,userid=0,
          startdate=timezone.now()-datetime.timedelta(days=365),
          enddate=timezone.now(),
          deltadays=-1,
          enddatestring=timezone.now().strftime("%Y-%m-%d"),
          startdatestring=(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
          options={
              'includereststrokes':False,
              'workouttypes':[i[0] for i in mytypes.workouttypes],
              'waterboattype':mytypes.waterboattype,
              'rankingonly': False,
              'histoparam':'power'
          }):

    r = getrequestrower(request,userid=userid)
    theuser = r.user

    if 'histoparam' in request.session:
        histoparam = request.session['histoparam']
    else:
        histoparam = 'power'

    if 'waterboattype' in request.session:
        waterboattype = request.session['waterboattype']
    else:
        waterboattype = mytypes.waterboattype


    if 'rankingonly' in request.session:
        rankingonly = request.session['rankingonly']
    else:
        rankingonly = False

    if 'modalities' in request.session:
        modalities = request.session['modalities']
        if len(modalities) > 1:
            modality = 'all'
        else:
            modality = modalities[0]
    else:
        modalities = [m[0] for m in mytypes.workouttypes]
        modality = 'all'


    try:
        rankingonly = options['rankingonly']
    except KeyError:
        rankingonly = False

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False


    workstrokesonly = not includereststrokes

    waterboattype = mytypes.waterboattype


    if startdatestring != "":
        startdate = iso8601.parse_date(startdatestring)

    if enddatestring != "":
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s


    # get all indoor rows of in date range

    # process form
    if request.method == 'POST':
        form = DateRangeForm(request.POST)
        modalityform = TrendFlexModalForm(request.POST)
        histoform = HistoForm(request.POST)
        if form.is_valid():
            startdate = form.cleaned_data['startdate']
            enddate = form.cleaned_data['enddate']
            if startdate > enddate:
                s = enddate
                enddate = startdate
                startdate = s
            startdatestring = startdate.strftime('%Y-%m-%d')
            enddatestring = enddate.strftime('%Y-%m-%d')
        if modalityform.is_valid():
            modality = modalityform.cleaned_data['modality']
            waterboattype = modalityform.cleaned_data['waterboattype']
            rankingonly = modalityform.cleaned_data['rankingonly']
            if modality == 'all':
                modalities = [m[0] for m in mytypes.workouttypes]
            else:
                modalities = [modality]

            if modality != 'water':
                waterboattype = [b[0] for b in mytypes.boattypes]


            request.session['modalities'] = modalities
            request.session['waterboattype'] = waterboattype
            request.session['rankingonly'] = rankingonly
            form = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        if histoform.is_valid():
            includereststrokes = histoform.cleaned_data['includereststrokes']
            histoparam = histoform.cleaned_data['histoparam']
            request.session['histoparam'] = histoparam
            request.session['includereststrokes'] = includereststrokes
    else:
        form = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        includereststrokes = False

        workstrokesonly = not includereststrokes
        modalityform = TrendFlexModalForm(
            initial={
                'modality':modality,
                'waterboattype':waterboattype,
                'rankingonly':rankingonly,
            }
        )
        histoform = HistoForm(initial={
            'includereststrokes':False,
            'histoparam':histoparam
        })

    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])



    script = ''
    div = get_call()
    js_resources = ''
    css_resources = ''




    options = {
        'modality': modality,
        'theuser': theuser.id,
        'waterboattype':waterboattype,
        'startdatestring':startdatestring,
        'enddatestring':enddatestring,
        'rankingonly':rankingonly,
        'includereststrokes':includereststrokes,
        'histoparam':histoparam,
        }

    request.session['options'] = options

    promember=0
    mayedit=0
    if not request.user.is_anonymous:
        result = request.user.is_authenticated and ispromember(request.user)
        if result:
            promember = 1


    request.session['options'] = options

    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name':'Analysis'
        },
        {
            'url':reverse('histo'),
            'name': 'Histogram'
        }
    ]

    return render(request, 'histo.html',
                  {'interactiveplot':script,
                   'the_div':div,
                   'breadcrumbs':breadcrumbs,
                   'id':theuser,
                   'active':'nav-analysis',
                   'theuser':theuser,
                   'rower':r,
                   'startdate':startdate,
                   'enddate':enddate,
                   'form':form,
                   'optionsform':modalityform,
                   'histoform':histoform,
                   'teams':get_my_teams(request.user),
                   })

# The Flex plot for a large selection of workouts
@login_required()
def cum_flex_data(
        request,
        options={
            'includereststrokes':False,
            'rankingonly':False,
            'modality':'all',
            'waterboattype':mytypes.waterboattype,
            'theuser':0,
            'xparam':'spm',
            'yparam1':'power',
            'yparam2':'None',
            'enddatestring':timezone.now().strftime("%Y-%m-%d"),
            'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
            'deltadays':-1,
        }):

    def_options = options

    if 'options' in request.session:
        options = request.session['options']


    modality = keyvalue_get_default('modality',options,def_options)
    rankingonly = keyvalue_get_default('rankingonly',options,def_options)
    includereststrokes = keyvalue_get_default('includereststrokes',options,def_options)
    waterboattype = keyvalue_get_default('waterboattype',options,def_options)
    workstrokesonly = not includereststrokes
    theuser = keyvalue_get_default('theuser',options,def_options)
    xparam = keyvalue_get_default('xparam',options,def_options)
    yparam1 = keyvalue_get_default('yparam1',options,def_options)
    yparam2 = keyvalue_get_default('yparam2',options,def_options)
    startdatestring = keyvalue_get_default('startdatestring',options,def_options)
    enddatestring = keyvalue_get_default('enddatestring',options,def_options)


    if modality == 'all':
        modalities = [m[0] for m in mytypes.workouttypes]
    else:
        modalities = [modality]

    try:
        startdate = iso8601.parse_date(startdatestring)
    except ParseError:
        startdate = timezone.now()-datetime.timedelta(days=7)

    try:
        enddate = iso8601.parse_date(enddatestring)
    except ParseError:
        enddate = timezone.now()


    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    promember=0
    if theuser == 0:
        theuser = request.user.id

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

    r2 = getrower(theuser)

    if rankingonly:
        rankingpiece = [True,]
    else:
        rankingpiece = [True,False]

    allworkouts = Workout.objects.filter(user=r2,
                                         workouttype__in=modalities,
                                         boattype__in=waterboattype,
                                         startdatetime__gte=startdate,
                                         startdatetime__lte=enddate,
                                         rankingpiece__in=rankingpiece)

    if allworkouts:
        res = interactive_cum_flex_chart2(allworkouts,xparam=xparam,
                                          yparam1=yparam1,
                                          yparam2=yparam2,
                                          promember=promember,
                                          workstrokesonly=workstrokesonly,
        )
        script = res[0]
        div = res[1]
    else:
        script = ''
        div = '<p>No pieces uploaded for this date range.</p>'

    scripta = script.split('\n')[2:-1]
    script = ''.join(scripta)

    data = {
        "script":script,
        "div":div,
        }

    return JSONResponse(data)

# The Flex plot for a large selection of workouts
@login_required()
def histo_data(
        request,
        options={
            'includereststrokes':False,
            'rankingonly':False,
            'modality':'all',
            'waterboattype':mytypes.waterboattype,
            'theuser':0,
            'enddatestring':timezone.now().strftime("%Y-%m-%d"),
            'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
            'deltadays':-1,
            'histoparam':'power',
        }):

    def_options = options


    if 'options' in request.session:
        options = request.session['options']

    modality = keyvalue_get_default('modality',options,def_options)
    rankingonly = keyvalue_get_default('rankingonly',options,def_options)
    includereststrokes = keyvalue_get_default('includereststrokes',options,def_options)
    waterboattype = keyvalue_get_default('waterboattype',options,def_options)
    workstrokesonly = not includereststrokes
    theuser = keyvalue_get_default('theuser',options,def_options)
    startdatestring = keyvalue_get_default('startdatestring',options,def_options)
    enddatestring = keyvalue_get_default('enddatestring',options,def_options)
    histoparam = keyvalue_get_default('histoparam',options,def_options)

    if modality == 'all':
        modalities = [m[0] for m in mytypes.workouttypes]
    else:
        modalities = [modality]

    try:
        startdate = iso8601.parse_date(startdatestring)
    except ParseError:
        startdate = timezone.now()-datetime.timedelta(days=7)

    try:
        enddate = iso8601.parse_date(enddatestring)
    except ParseError:
        enddate = timezone.now()


    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    promember=0
    if theuser == 0:
        theuser = request.user.id

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

    r2 = getrower(theuser)

    if rankingonly:
        rankingpiece = [True,]
    else:
        rankingpiece = [True,False]

    allworkouts = Workout.objects.filter(user=r2,
                                         workouttype__in=modalities,
                                         boattype__in=waterboattype,
                                         startdatetime__gte=startdate,
                                         startdatetime__lte=enddate,
                                         rankingpiece__in=rankingpiece)

    if allworkouts:
        res = interactive_histoall(allworkouts,histoparam,includereststrokes)
        script = res[0]
        div = res[1]
    else:
        script = ''
        div = '<p>No pieces uploaded for this date range.</p>'

    scripta = script.split('\n')[2:-1]
    script = ''.join(scripta)

    data = {
        "script":script,
        "div":div,
        }

    return JSONResponse(data)




@login_required()
def cum_flex(request,theuser=0,
             xparam='spm',
             yparam1='power',
             yparam2='None',
             startdate=timezone.now()-datetime.timedelta(days=10),
             enddate=timezone.now(),
             deltadays=-1,
             enddatestring=timezone.now().strftime("%Y-%m-%d"),
             startdatestring=(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
             options={
                 'includereststrokes':False,
                 'workouttypes':[i[0] for i in mytypes.workouttypes],
                 'waterboattype':mytypes.waterboattype,
                 'rankingonly':False,
             }):


    r = getrequestrower(request,userid=theuser)
    theuser = r.user

    if 'waterboattype' in request.session:
        waterboattype = request.session['waterboattype']
    else:
        waterboattype = mytypes.waterboattype


    if 'rankingonly' in request.session:
        rankingonly = request.session['rankingonly']
    else:
        rankingonly = False

    if 'modalities' in request.session:
        modalities = request.session['modalities']
        if len(modalities) > 1:
            modality = 'all'
        else:
            modality = modalities[0]
    else:
        modalities = [m[0] for m in mytypes.workouttypes]
        modality = 'all'


    try:
        rankingonly = options['rankingonly']
    except KeyError:
        rankingonly = False

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False


    workstrokesonly = not includereststrokes

    waterboattype = mytypes.waterboattype


    if startdatestring != "":
        startdate = iso8601.parse_date(startdatestring)

    if enddatestring != "":
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s


    # get all indoor rows of in date range

    # process form
    if request.method == 'POST':
        form = DateRangeForm(request.POST)
        modalityform = TrendFlexModalForm(request.POST)
        flexaxesform = FlexAxesForm(request,request.POST)
        if form.is_valid():
            startdate = form.cleaned_data['startdate']
            enddate = form.cleaned_data['enddate']
            if startdate > enddate:
                s = enddate
                enddate = startdate
                startdate = s
            startdatestring = startdate.strftime('%Y-%m-%d')
            enddatestring = enddate.strftime('%Y-%m-%d')
        if modalityform.is_valid():
            modality = modalityform.cleaned_data['modality']
            waterboattype = modalityform.cleaned_data['waterboattype']
            rankingonly = modalityform.cleaned_data['rankingonly']
            if modality == 'all':
                modalities = [m[0] for m in mytypes.workouttypes]
            else:
                modalities = [modality]

            if modality != 'water':
                waterboattype = [b[0] for b in mytypes.boattypes]


            request.session['modalities'] = modalities
            request.session['waterboattype'] = waterboattype
            request.session['rankingonly'] = rankingonly
            form = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        if flexaxesform.is_valid():
            xparam = flexaxesform.cleaned_data['xaxis']
            yparam1 = flexaxesform.cleaned_data['yaxis1']
            yparam2 = flexaxesform.cleaned_data['yaxis2']
    else:
        form = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        includereststrokes = False

        workstrokesonly = not includereststrokes
        modalityform = TrendFlexModalForm(
            initial={
                'modality':modality,
                'waterboattype':waterboattype,
                'rankingonly':rankingonly,
            }
        )
        initial = {
            'xaxis':xparam,
            'yaxis1':yparam1,
            'yaxis2':yparam2
            }
        flexaxesform = FlexAxesForm(request,initial=initial)

    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])



    script = ''
    div = get_call()
    js_resources = ''
    css_resources = ''




    options = {
        'xparam': xparam,
        'yparam1': yparam1,
        'yparam2': yparam2,
        'modality': modality,
        'theuser': theuser.id,
        'waterboattype':waterboattype,
        'startdatestring':startdatestring,
        'enddatestring':enddatestring,
        'rankingonly':rankingonly,
        'includereststrokes':includereststrokes,
        }

    request.session['options'] = options

    promember=0
    mayedit=0
    if not request.user.is_anonymous:
        result = request.user.is_authenticated and ispromember(request.user)
        if result:
            promember = 1


    request.session['options'] = options


    return render(request, 'cum_flex.html',
                  {'interactiveplot':script,
                   'the_div':div,
                   'js_res': js_resources,
                   'css_res':css_resources,
                   'id':theuser,
                   'rower':r,
                   'active':'nav-analysis',
                   'theuser':theuser,
                   'startdate':startdate,
                   'enddate':enddate,
                   'form':form,
                   'optionsform':modalityform,
                   'xparam':xparam,
                   'yparam1':yparam1,
                   'yparam2':yparam2,
                   'promember':promember,
                   'teams':get_my_teams(request.user),
                   'flexaxesform':flexaxesform,
                   })


def planrequired_view(request):
    messages.info(request,"This functionality requires Coach or Self-Coach membership")

    return HttpResponseRedirect(reverse('paidplans_view'))

@user_passes_test(isplanmember,login_url="/rowers/paidplans",
                  message="This functionality requires a Coach or Self-Coach plan",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def fitnessmetric_view(request,userid=0,mode='rower',
                       startdate=timezone.now()-timezone.timedelta(days=365),
                       enddate=timezone.now()):


    therower = getrequestrower(request,userid=userid)
    theuser = therower.user


    if request.method == 'POST':
        form = FitnessMetricForm(request.POST)
        if form.is_valid():
            startdate = form.cleaned_data['startdate']
            enddate = form.cleaned_data['enddate']
            mode = form.cleaned_data['mode']
    else:
        form = FitnessMetricForm()

    fitnessmetrics = PowerTimeFitnessMetric.objects.filter(
        user=theuser,
        date__gte=startdate,
        date__lte=enddate)

    script,thediv = fitnessmetric_chart(
        fitnessmetrics,theuser,
        workoutmode=mode,startdate=startdate,
        enddate=enddate,
    )

    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name':'Analysis'
        },
        {
            'url':reverse('fitnessmetric_view'),
            'name': 'Power Progress'
        }
    ]


    return render(request,'fitnessmetric.html',
                  {
                      'rower':therower,
                      'active':'nav-analysis',
                      'chartscript':script,
                      'breadcrumbs':breadcrumbs,
                      'the_div':thediv,
                      'mode':mode,
                      'form':form,
                      })


# Show ranking distances including predicted paces
@login_required()
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def rankings_view(request,userid=0,
          startdate=timezone.now()-datetime.timedelta(days=365),
          enddate=timezone.now(),
          deltadays=-1,
          startdatestring="",
          enddatestring=""):

    if deltadays>0:
        startdate = enddate-datetime.timedelta(days=int(deltadays))

    if startdatestring != "":
        startdate = iso8601.parse_date(startdatestring)

    if enddatestring != "":
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s



    promember=0
    r = getrequestrower(request,userid=userid)
    theuser = r.user.id

    if r.birthdate:
        age = calculate_age(r.birthdate)
        worldclasspower = int(c2stuff.getagegrouprecord(
            age,
            sex=r.sex,
            weightcategory=r.weightcategory,
        ))
    else:
        worldclasspower = None

    result = request.user.is_authenticated and ispromember(request.user)
    if result:
        promember=1

    # get all indoor rows in date range

    # process form
    if request.method == 'POST' and "daterange" in request.POST:
        dateform = DateRangeForm(request.POST)
        deltaform = DeltaDaysForm(request.POST)
        if dateform.is_valid():
            startdate = dateform.cleaned_data['startdate']
            enddate = dateform.cleaned_data['enddate']
            if startdate > enddate:
                s = enddate
                enddate = startdate
                startdate = s
    elif request.method == 'POST' and "datedelta" in request.POST:
        deltaform = DeltaDaysForm(request.POST)
        if deltaform.is_valid():
            deltadays = deltaform.cleaned_data['deltadays']
            if deltadays:
                enddate = timezone.now()
                startdate = enddate-datetime.timedelta(days=deltadays)
                if startdate > enddate:
                    s = enddate
                    enddate = startdate
                    startdate = s
                dateform = DateRangeForm(initial={
                    'startdate': startdate,
                    'enddate': enddate,
                    })
            else:
                dateform = DateRangeForm()
                deltaform = DeltaDaysForm()

    else:
        dateform = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        deltaform = DeltaDaysForm()

    # get all 2k (if any) - this rower, in date range
    try:
        r = getrower(theuser)
    except Rower.DoesNotExist:
        allergworkouts = []
        r=0


    try:
        uu = User.objects.get(id=theuser)
    except User.DoesNotExist:
        uu = ''


    # test to fix bug
    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
    #enddate = enddate+datetime.timedelta(days=1)

    thedistances = []
    theworkouts = []
    thesecs = []

    rankingdistances.sort()
    rankingdurations.sort()

    for rankingdistance in rankingdistances:

        workouts = Workout.objects.filter(
            user=r,distance=rankingdistance,
            workouttype__in=['rower','dynamic','slides'],
            rankingpiece=True,
            startdatetime__gte=startdate,
            startdatetime__lte=enddate
        ).order_by('duration')
        if workouts:
            thedistances.append(rankingdistance)
            theworkouts.append(workouts[0])

            timesecs = 3600*workouts[0].duration.hour
            timesecs += 60*workouts[0].duration.minute
            timesecs += workouts[0].duration.second
            timesecs += 1.e-6*workouts[0].duration.microsecond

            thesecs.append(timesecs)

    for rankingduration in rankingdurations:

        workouts = Workout.objects.filter(
            user=r,duration=rankingduration,
            workouttype='rower',
            rankingpiece=True,
            startdatetime__gte=startdate,
            startdatetime__lte=enddate
        ).order_by('-distance')
        if workouts:
            thedistances.append(workouts[0].distance)
            theworkouts.append(workouts[0])

            timesecs = 3600*workouts[0].duration.hour
            timesecs += 60*workouts[0].duration.minute
            timesecs += workouts[0].duration.second
            timesecs += 1.e-5*workouts[0].duration.microsecond

            thesecs.append(timesecs)

    thedistances = np.array(thedistances)
    thesecs = np.array(thesecs)

    thevelos = thedistances/thesecs
    theavpower = 2.8*(thevelos**3)


    # create interactive plot
    if len(thedistances) !=0 :
        res = interactive_cpchart(
            r,thedistances,thesecs,theavpower,
            theworkouts,promember=promember
        )
        script = res[0]
        div = res[1]
        paulslope = res[2]
        paulintercept = res[3]
        p1 = res[4]
        message = res[5]
    else:
        script = ''
        div = '<p>No ranking pieces found.</p>'
        paulslope = 1
        paulintercept = 1
        p1 = [1,1,1,1]
        message = ""


    if request.method == 'POST' and "piece" in request.POST:
        form = PredictedPieceForm(request.POST)
        if form.is_valid():
            value = form.cleaned_data['value']
            hourvalue,value = divmod(value,60)
            if hourvalue >= 24:
                hourvalue = 23
            pieceunit = form.cleaned_data['pieceunit']
            if pieceunit == 'd':
                rankingdistances.append(value)
            else:
                rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
    else:
        form = PredictedPieceForm()

    rankingdistances.sort()
    rankingdurations.sort()


    predictions = []
    cpredictions = []


    for rankingdistance in rankingdistances:
        # Paul's model
        p = paulslope*np.log10(rankingdistance)+paulintercept
        velo = 500./p
        t = rankingdistance/velo
        pwr = 2.8*(velo**3)
        a = {'distance':rankingdistance,
             'duration':timedeltaconv(t),
             'pace':timedeltaconv(p),
             'power':int(pwr)}
        predictions.append(a)

        # CP model -
        pwr2 = p1[0]/(1+t/p1[2])
        pwr2 += p1[1]/(1+t/p1[3])

        if pwr2 <= 0:
            pwr2 = 50.

        velo2 = (pwr2/2.8)**(1./3.)

        if np.isnan(velo2) or velo2 <= 0:
            velo2 = 1.0

        t2 = rankingdistance/velo2

        pwr3 = p1[0]/(1+t2/p1[2])
        pwr3 += p1[1]/(1+t2/p1[3])

        if pwr3 <= 0:
            pwr3 = 50.

        velo3 = (pwr3/2.8)**(1./3.)
        if np.isnan(velo3) or velo3 <= 0:
            velo3 = 1.0

        t3 = rankingdistance/velo3
        p3 = 500./velo3

        a = {'distance':rankingdistance,
             'duration':timedeltaconv(t3),
             'pace':timedeltaconv(p3),
             'power':int(pwr3)}
        cpredictions.append(a)




    for rankingduration in rankingdurations:
        t = 3600.*rankingduration.hour
        t += 60.*rankingduration.minute
        t += rankingduration.second
        t += rankingduration.microsecond/1.e6

        # Paul's model
        ratio = paulintercept/paulslope

        u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope

        d = 500*t*np.log(10.)
        d = d/(paulslope*lambertw(u))
        d = d.real

        velo = d/t
        p = 500./velo
        pwr = 2.8*(velo**3)
        a = {'distance':int(d),
             'duration':timedeltaconv(t),
             'pace':timedeltaconv(p),
             'power':int(pwr)}
        predictions.append(a)

        # CP model
        pwr = p1[0]/(1+t/p1[2])
        pwr += p1[1]/(1+t/p1[3])

        if pwr <= 0:
            pwr = 50.

        velo = (pwr/2.8)**(1./3.)

        if np.isnan(velo) or velo <=0:
            velo = 1.0

        d = t*velo
        p = 500./velo
        a = {'distance':int(d),
             'duration':timedeltaconv(t),
             'pace':timedeltaconv(p),
             'power':int(pwr)}
        cpredictions.append(a)


    messages.error(request,message)
    return render(request, 'rankings.html',
                  {'rankingworkouts':theworkouts,
                   'interactiveplot':script,
                   'the_div':div,
                   'predictions':predictions,
                   'cpredictions':cpredictions,
                   'nrdata':len(thedistances),
                   'form':form,
                   'rower':r,
                   'active':'nav-analysis',
                   'dateform':dateform,
                   'deltaform':deltaform,
                   'worldclasspower':worldclasspower,
                   'id': theuser,
                   'theuser':uu,
                   'startdate':startdate,
                   'enddate':enddate,
                   'teams':get_my_teams(request.user),
                   })

@login_required()
def ajax_agegrouprecords(request,
                         age=25,
                         sex='female',
                         weightcategory='hwt',
                         userid=0):

    wcdurations = []
    wcpower = []
    durations = [1,4,30,60]
    distances = [100,500,1000,2000,5000,6000,10000,21097,42195]

    df = pd.DataFrame(
        list(
            C2WorldClassAgePerformance.objects.filter(
                sex=sex,
                weightcategory=weightcategory
            ).values()
        )
    )

    jsondf = df.to_json()

    job = myqueue(queue,
                  handle_getagegrouprecords,
                  jsondf,distances,durations,age,sex,weightcategory,
    )



    return JSONResponse(
        {
            'job':job.id
            }
        )


# Show ranking distances including predicted paces
@login_required()
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def rankings_view2(request,userid=0,
          startdate=timezone.now()-datetime.timedelta(days=365),
          enddate=timezone.now(),
          deltadays=-1,
          startdatestring="",
          enddatestring=""):

    if deltadays>0:
        startdate = enddate-datetime.timedelta(days=int(deltadays))

    if startdatestring != "":
        startdate = iso8601.parse_date(startdatestring)

    if enddatestring != "":
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    if userid == 0:
        userid = request.user.id
    else:
        lastupdated = "01-01-1900"


    promember=0
    r = getrequestrower(request,userid=userid)
    theuser = r.user

    wcdurations = []
    wcpower = []

    lastupdated = "01-01-1900"
    userid = 0
    if 'options' in request.session:
        options = request.session['options']
        try:
            wcdurations = options['wcdurations']
            wcpower = options['wcpower']
            lastupdated = options['lastupdated']
        except KeyError:
            pass
        try:
            userid = options['userid']
        except KeyError:
            userid = 0
    else:
        options = {}



    lastupdatedtime = arrow.get(lastupdated).timestamp
    current_time = arrow.utcnow().timestamp

    deltatime_seconds = current_time - lastupdatedtime
    recalc = False
    if str(userid) != str(theuser) or deltatime_seconds > 3600:
        recalc = True
        options['lastupdated'] = arrow.utcnow().isoformat()
    else:
        recalc = False

    options['userid'] = theuser.id

    if r.birthdate:
        age = calculate_age(r.birthdate)
    else:
        worldclasspower = None
        age = 0

    agerecords = CalcAgePerformance.objects.filter(
        age = age,
        sex = r.sex,
        weightcategory = r.weightcategory)

    if len(agerecords) == 0:
        recalc = True
        wcpower = []
        wcduration = []
    else:
        wcdurations = []
        wcpower = []
        for record in agerecords:
            wcdurations.append(record.duration)
            wcpower.append(record.power)

    options['wcpower'] = wcpower
    options['wcdurations'] = wcdurations
    if theuser:
        options['userid'] = theuser.id

    request.session['options'] = options


    result = request.user.is_authenticated and ispromember(request.user)
    if result:
        promember=1

    # get all indoor rows in date range

    # process form
    if request.method == 'POST' and "daterange" in request.POST:
        dateform = DateRangeForm(request.POST)
        deltaform = DeltaDaysForm(request.POST)
        if dateform.is_valid():
            startdate = dateform.cleaned_data['startdate']
            enddate = dateform.cleaned_data['enddate']
            if startdate > enddate:
                s = enddate
                enddate = startdate
                startdate = s
    elif request.method == 'POST' and "datedelta" in request.POST:
        deltaform = DeltaDaysForm(request.POST)
        if deltaform.is_valid():
            deltadays = deltaform.cleaned_data['deltadays']
            if deltadays:
                enddate = timezone.now()
                startdate = enddate-datetime.timedelta(days=deltadays)
                if startdate > enddate:
                    s = enddate
                    enddate = startdate
                    startdate = s
                dateform = DateRangeForm(initial={
                    'startdate': startdate,
                    'enddate': enddate,
                    })
            else:
                dateform = DateRangeForm()
                deltaform = DeltaDaysForm()

    else:
        dateform = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        deltaform = DeltaDaysForm()

    # get all 2k (if any) - this rower, in date range
    try:
        r = getrower(theuser)
    except Rower.DoesNotExist:
        allergworkouts = []
        r=0


    uu = theuser


    # test to fix bug
    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
    #enddate = enddate+datetime.timedelta(days=1)


    thedistances = []
    theworkouts = []
    thesecs = []



    rankingdistances.sort()
    rankingdurations.sort()

    for rankingdistance in rankingdistances:

        workouts = Workout.objects.filter(
            user=r,distance=rankingdistance,
            workouttype__in=['rower','dynamic','slides'],
            rankingpiece=True,
            startdatetime__gte=startdate,
            startdatetime__lte=enddate).order_by('duration')
        if workouts:
            thedistances.append(rankingdistance)
            theworkouts.append(workouts[0])

            timesecs = 3600*workouts[0].duration.hour
            timesecs += 60*workouts[0].duration.minute
            timesecs += workouts[0].duration.second
            timesecs += 1.e-6*workouts[0].duration.microsecond

            thesecs.append(timesecs)

    for rankingduration in rankingdurations:

        workouts = Workout.objects.filter(
            user=r,duration=rankingduration,
            workouttype='rower',
            rankingpiece=True,
            startdatetime__gte=startdate,
            startdatetime__lte=enddate).order_by('-distance')
        if workouts:
            thedistances.append(workouts[0].distance)
            theworkouts.append(workouts[0])

            timesecs = 3600*workouts[0].duration.hour
            timesecs += 60*workouts[0].duration.minute
            timesecs += workouts[0].duration.second
            timesecs += 1.e-5*workouts[0].duration.microsecond

            thesecs.append(timesecs)

    thedistances = np.array(thedistances)
    thesecs = np.array(thesecs)

    thevelos = thedistances/thesecs
    theavpower = 2.8*(thevelos**3)


    # create interactive plot
    if len(thedistances) !=0 :
        res = interactive_cpchart(
            r,thedistances,thesecs,theavpower,
            theworkouts,promember=promember,
            wcdurations=wcdurations,wcpower=wcpower
        )
        script = res[0]
        div = res[1]
        paulslope = res[2]
        paulintercept = res[3]
        p1 = res[4]
        message = res[5]
        try:
            testcalc = pd.Series(res[6])*3
        except TypeError:
            age = 0

    else:
        script = ''
        div = '<p>No ranking pieces found.</p>'
        paulslope = 1
        paulintercept = 1
        p1 = [1,1,1,1]
        message = ""


    if request.method == 'POST' and "piece" in request.POST:
        form = PredictedPieceForm(request.POST)
        if form.is_valid():
            value = form.cleaned_data['value']
            hourvalue,value = divmod(value,60)
            if hourvalue >= 24:
                hourvalue = 23
            pieceunit = form.cleaned_data['pieceunit']
            if pieceunit == 'd':
                rankingdistances.append(value)
            else:
                rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
    else:
        form = PredictedPieceForm()

    rankingdistances.sort()
    rankingdurations.sort()


    predictions = []
    cpredictions = []


    for rankingdistance in rankingdistances:
        # Paul's model
        p = paulslope*np.log10(rankingdistance)+paulintercept
        velo = 500./p
        t = rankingdistance/velo
        pwr = 2.8*(velo**3)
        a = {'distance':rankingdistance,
             'duration':timedeltaconv(t),
             'pace':timedeltaconv(p),
             'power':int(pwr)}
        predictions.append(a)

        # CP model -
        pwr2 = p1[0]/(1+t/p1[2])
        pwr2 += p1[1]/(1+t/p1[3])

        if pwr2 <= 0:
            pwr2 = 50.

        velo2 = (pwr2/2.8)**(1./3.)

        if np.isnan(velo2) or velo2 <= 0:
            velo2 = 1.0

        t2 = rankingdistance/velo2

        pwr3 = p1[0]/(1+t2/p1[2])
        pwr3 += p1[1]/(1+t2/p1[3])

        if pwr3 <= 0:
            pwr3 = 50.

        velo3 = (pwr3/2.8)**(1./3.)
        if np.isnan(velo3) or velo3 <= 0:
            velo3 = 1.0

        t3 = rankingdistance/velo3
        p3 = 500./velo3

        a = {'distance':rankingdistance,
             'duration':timedeltaconv(t3),
             'pace':timedeltaconv(p3),
             'power':int(pwr3)}
        cpredictions.append(a)




    for rankingduration in rankingdurations:
        t = 3600.*rankingduration.hour
        t += 60.*rankingduration.minute
        t += rankingduration.second
        t += rankingduration.microsecond/1.e6

        # Paul's model
        ratio = paulintercept/paulslope

        u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope

        d = 500*t*np.log(10.)
        d = d/(paulslope*lambertw(u))
        d = d.real

        velo = d/t
        p = 500./velo
        pwr = 2.8*(velo**3)
        try:
            a = {'distance':int(d),
                 'duration':timedeltaconv(t),
                 'pace':timedeltaconv(p),
                 'power':int(pwr)}
            predictions.append(a)
        except:
            pass

        # CP model
        pwr = p1[0]/(1+t/p1[2])
        pwr += p1[1]/(1+t/p1[3])

        if pwr <= 0:
            pwr = 50.

        velo = (pwr/2.8)**(1./3.)

        if np.isnan(velo) or velo <=0:
            velo = 1.0

        d = t*velo
        p = 500./velo
        a = {'distance':int(d),
             'duration':timedeltaconv(t),
             'pace':timedeltaconv(p),
             'power':int(pwr)}
        cpredictions.append(a)

    if recalc:
        wcdurations = []
        wcpower = []
        durations = [1,4,30,60]
        distances = [100,500,1000,2000,5000,6000,10000,21097,42195]

        df = pd.DataFrame(
            list(
                C2WorldClassAgePerformance.objects.filter(
                    sex=r.sex,
                    weightcategory=r.weightcategory
                ).values()
            )
        )

        jsondf = df.to_json()

        job = myqueue(queue,
                      handle_getagegrouprecords,
                      jsondf,distances,durations,age,r.sex,r.weightcategory)
        try:
            request.session['async_tasks'] += [(job.id,'agegrouprecords')]
        except KeyError:
            request.session['async_tasks'] = [(job.id,'agegrouprecords')]



    messages.error(request,message)
    return render(request, 'rankings.html',
                  {'rankingworkouts':theworkouts,
                   'interactiveplot':script,
                   'the_div':div,
                   'predictions':predictions,
                   'cpredictions':cpredictions,
                   'nrdata':len(thedistances),
                   'form':form,
                   'dateform':dateform,
                   'deltaform':deltaform,
                   'id': theuser,
                   'theuser':uu,
                   'rower':r,
                   'active':'nav-analysis',
                   'age':age,
                   'sex':r.sex,
                   'recalc':recalc,
                   'weightcategory':r.weightcategory,
                   'startdate':startdate,
                   'enddate':enddate,
                   'teams':get_my_teams(request.user),
                   })


# Show ranking distances including predicted paces
@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def otwrankings_view(request,userid=0,
                     startdate=timezone.now()-datetime.timedelta(days=365),
                     enddate=timezone.now(),
                     startdatestring="",
                     enddatestring=""):

    if startdatestring != "":
        try:
            startdate = iso8601.parse_date(startdatestring)
        except ParseError:
            pass

    if enddatestring != "":
        try:
            enddate = iso8601.parse_date(enddatestring)
        except ParseError:
            pass

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    r = getrequestrower(request,userid=userid)
    promember = True

    theuser = r.user

    # get all OTW rows in date range

    # process form
    if request.method == 'POST':
        dateform = DateRangeForm(request.POST)
        if dateform.is_valid():
            startdate = dateform.cleaned_data['startdate']
            enddate = dateform.cleaned_data['enddate']
            if startdate > enddate:
                s = enddate
                enddate = startdate
                startdate = s
        form = PredictedPieceFormNoDistance(request.POST)
        if form.is_valid():
            value = form.cleaned_data['value']
        else:
            value = None
        trankingdurations = form.cleaned_data['trankingdurations']
        trankingdurations = [
            datetime.datetime.strptime(d,"%H:%M:%S").time() for d in trankingdurations
        ]
        if value:
            hourvalue,tvalue = divmod(value,60)
            hourvalue = int(hourvalue)
            minutevalue = int(tvalue)
            tvalue = int(60*(tvalue-minutevalue))
            if hourvalue >= 24:
                hourvalue = 23
            trankingdurations.append(datetime.time(
                minute=minutevalue,
                hour=hourvalue,
                second=tvalue
            ))

    else:
        form = PredictedPieceFormNoDistance()
        dateform = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        workouttypes = ['rower','slides','dynamic']
        trankingdurations = rankingdurations

    # get all 2k (if any) - this rower, in date range
    try:
        r = Rower.objects.get(user=theuser)
        request.session['rowerid'] = r.id
    except Rower.DoesNotExist:
        raise Http404("Rower doesn't exist")



    uu = theuser


    # test to fix bug
    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
    #enddate = enddate+datetime.timedelta(days=1)



    thedistances = []
    theworkouts = []
    thesecs = []

    theworkouts = Workout.objects.filter(
        user=r,rankingpiece=True,
        workouttype='water',
        startdatetime__gte=startdate,
        startdatetime__lte=enddate
    ).order_by(
        "-startdatetime"
        )


    delta,cpvalue,avgpower = dataprep.fetchcp(r,theworkouts)

    runningjob = 0

    taskstatus = get_stored_tasks_status(request)
    for task in taskstatus:
        if task['func_name'] == 'updatecpwater':
            if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
                runningjob = 1
                messages.info(request,'CP chart data have been updated')
                remove_asynctask(request,task['id'])
            elif 'fail' in task['status'].lower():
                runningjob = 0
                try:
                    remove_asynctask(request,task[id])
                    messages.error(request,'Oh, your task failed')
                except KeyError:
                    pass
            elif 'started' in task['status'].lower():
                messages.info(request,'Busy updating CP chart data')
                runningjob = 1
            elif 'queued' in task['status'].lower() or 'pending' in task['status'].lower():
                messages.info(request,'Getting ready to update CP chart data')
                runningjob = 1



    if not runningjob:
        job = dataprep.runcpupdate(
            r,type='water',
            startdate=startdate,
            enddate=enddate
        )
        request.session['job_id'] = job.id
        try:
            request.session['async_tasks'] += [(job.id,'updatecpwater')]
        except KeyError:
            request.session['async_tasks'] = [(job.id,'updatecpwater')]
        messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')

    powerdf = pd.DataFrame({
        'Delta':delta,
        'CP':cpvalue,
        })

    if powerdf.empty:
        messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')

    powerdf = powerdf[powerdf['CP']>0]
    powerdf.dropna(axis=0,inplace=True)
    powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
    powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)


    rowername = r.user.first_name+" "+r.user.last_name
    # create interactive plot
    if len(powerdf) !=0 :
        res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
        script = res[0]
        div = res[1]
        p1 = res[2]
        ratio = res[3]
        r.p0 = p1[0]
        r.p1 = p1[1]
        r.p2 = p1[2]
        r.p3 = p1[3]
        r.cpratio = ratio
        r.save()
        paulslope = 1
        paulintercept = 1
        message = res[4]
    else:
        script = ''
        div = '<p>No ranking pieces found.</p>'
        paulslope = 1
        paulintercept = 1
        p1 = [1,1,1,1]
        message = ""




    cpredictions = []

    for rankingduration in trankingdurations:
        t = 3600.*rankingduration.hour
        t += 60.*rankingduration.minute
        t += rankingduration.second
        t += rankingduration.microsecond/1.e6


        # CP model
        pwr = p1[0]/(1+t/p1[2])
        pwr += p1[1]/(1+t/p1[3])


        if pwr <= 0:
            pwr = 50.


        if  not np.isnan(pwr):
            try:
                pwr2 = pwr*ratio
            except:
                pwr2 = pwr

            a = {
                'duration':timedeltaconv(t),
                'power':int(pwr),
                'upper':int(pwr2)}
            cpredictions.append(a)



    startdatestring = startdate.strftime('%Y-%m-%d')
    enddatestring = enddate.strftime('%Y-%m-%d')
    request.session['startdate'] = startdatestring
    request.session['enddate'] = enddatestring

    messages.error(request,message)
    return render(request, 'otwrankings.html',
                  {'rankingworkouts':theworkouts,
                   'interactiveplot':script,
                   'the_div':div,
                   'cpredictions':cpredictions,
                   'rower':r,
                   'active':'nav-analysis',
                   'avgpower':avgpower,
                   'form':form,
                   'dateform':dateform,
                   'id': theuser,
                   'theuser':uu,
                   'startdate':startdate,
                   'enddate':enddate,
                   'teams':get_my_teams(request.user),
                   'workouttype':'water',
                   })

@login_required()
def otecp_toadmin_view(request,theuser=0,
                       startdate=timezone.now()-datetime.timedelta(days=365),
                       enddate=timezone.now(),
                       startdatestring="",
                       enddatestring="",
                       ):

    if startdatestring != "":
        try:
            startdate = iso8601.parse_date(startdatestring)
        except ParseError:
            pass

    if enddatestring != "":
        try:
            enddate = iso8601.parse_date(enddatestring)
        except ParseError:
            pass

    if theuser == 0:
        theuser = request.user.id

    u = User.objects.get(id=theuser)
    r = Rower.objects.get(user=u)

    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))

    theworkouts = Workout.objects.filter(
        user=r,rankingpiece=True,
        workouttype__in=[
            'rower',
            'dynamic',
            'slides'
        ],
        startdatetime__gte=startdate,
        startdatetime__lte=enddate
    ).order_by("-startdatetime")


    delta,cpvalue,avgpower = dataprep.fetchcp(
        r,theworkouts,table='cpergdata'
    )

    powerdf = pd.DataFrame({
        'Delta':delta,
        'CP':cpvalue,
        })

    csvfilename = 'CP_data_user_{id}.csv'.format(
        id = theuser
        )

    powerdf = powerdf[powerdf['CP']>0]
    powerdf.dropna(axis=0,inplace=True)
    powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
    powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
    powerdf.to_csv(csvfilename)

    res = myqueue(queuehigh,
                  handle_sendemailfile,
                  'Sander',
                  'Roosendaal',
                  'roosendaalsander@gmail.com',
                  csvfilename,
                  delete=True)

    successmessage = "The CSV file was sent to the site admin per email"
    messages.info(request,successmessage)
    response = HttpResponseRedirect('/rowers/list-workouts/')

    return response

@login_required()
def otwcp_toadmin_view(request,theuser=0,
                       startdate=timezone.now()-datetime.timedelta(days=365),
                       enddate=timezone.now(),
                       startdatestring="",
                       enddatestring="",
                       ):

    if startdatestring != "":
        try:
            startdate = iso8601.parse_date(startdatestring)
        except ParseError:
            pass

    if enddatestring != "":
        try:
            enddate = iso8601.parse_date(enddatestring)
        except ParseError:
            pass

    if theuser == 0:
        theuser = request.user.id

    u = User.objects.get(id=theuser)
    r = Rower.objects.get(user=u)

    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))

    theworkouts = Workout.objects.filter(
        user=r,rankingpiece=True,
        workouttype='water',
        startdatetime__gte=startdate,
        startdatetime__lte=enddate
    ).order_by("-startdatetime")


    delta,cpvalue,avgpower = dataprep.fetchcp(
        r,theworkouts,table='cpdata'
    )

    powerdf = pd.DataFrame({
        'Delta':delta,
        'CP':cpvalue,
        })

    csvfilename = 'CP_data_user_{id}.csv'.format(
        id = theuser
        )

    powerdf = powerdf[powerdf['CP']>0]
    powerdf.dropna(axis=0,inplace=True)
    powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
    powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
    powerdf.to_csv(csvfilename)

    res = myqueue(queuehigh,
                  handle_sendemailfile,
                  'Sander',
                  'Roosendaal',
                  'roosendaalsander@gmail.com',
                  csvfilename,
                  delete=True)

    successmessage = "The CSV file was sent to the site admin per email"
    messages.info(request,successmessage)
    response = HttpResponseRedirect('/rowers/list-workouts/')

    return response

# Show ranking distances including predicted paces
@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def oterankings_view(request,userid=0,
                     startdate=timezone.now()-datetime.timedelta(days=365),
                     enddate=timezone.now(),
                     startdatestring="",
                     enddatestring=""):

    if startdatestring != "":
        try:
            startdate = iso8601.parse_date(startdatestring)
        except ParseError:
            pass

    if enddatestring != "":
        try:
            enddate = iso8601.parse_date(enddatestring)
        except ParseError:
            pass

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    r = getrequestrower(request,userid=userid)
    theuser = r.user


    promember=0

    # get all OTW rows in date range

    # process form
    if request.method == 'POST':
        dateform = DateRangeForm(request.POST)
        if dateform.is_valid():
            startdate = dateform.cleaned_data['startdate']
            enddate = dateform.cleaned_data['enddate']
            if startdate > enddate:
                s = enddate
                enddate = startdate
                startdate = s
        workouttypeform = OteWorkoutTypeForm(request.POST)
        if workouttypeform.is_valid():
            workouttypes = workouttypeform.cleaned_data['workouttypes']
        form = PredictedPieceForm(request.POST)
        if form.is_valid():
            value = form.cleaned_data['value']
            pieceunit = form.cleaned_data['pieceunit']
        else:
            value = None

        try:
            trankingdistances = form.cleaned_data['trankingdistances']
        except KeyError:
            trankingdistances = []

        trankingdistances = [int(d) for d in trankingdistances]

        try:
            trankingdurations = form.cleaned_data['trankingdurations']
        except KeyError:
            trankingdurations = []

        trankingdurations = [
            datetime.datetime.strptime(d,"%H:%M:%S").time() for d in trankingdurations
        ]
        if value:
            hourvalue,tvalue = divmod(value,60)
            hourvalue = int(hourvalue)
            minutevalue = int(tvalue)
            tvalue = int(60*(tvalue-minutevalue))
            if hourvalue >= 24:
                hourvalue = 23
            if pieceunit == 'd':
                trankingdistances.append(value)
            else:
                trankingdurations.append(datetime.time(
                    minute=minutevalue,
                    hour=hourvalue,
                    second=tvalue
                ))
    else:
        form = PredictedPieceForm()
        dateform = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        workouttypeform = OteWorkoutTypeForm()
        workouttypes = ['rower','slides','dynamic']
        trankingdistances = rankingdistances
        trankingdurations = rankingdurations

    # get all 2k (if any) - this rower, in date range
    try:
        r = Rower.objects.get(user=theuser)
        request.session['rowerid'] = r.id
    except Rower.DoesNotExist:
        allergworkouts = []
        raise Http404("Rower doesn't exist")


    uu = theuser



    # test to fix bug
    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))



    thedistances = []
    theworkouts = []
    thesecs = []

    theworkouts = Workout.objects.filter(
        user=r,rankingpiece=True,
        workouttype__in=workouttypes,
        startdatetime__gte=startdate,
        startdatetime__lte=enddate
    ).order_by("-startdatetime")


    delta,cpvalue,avgpower = dataprep.fetchcp(
        r,theworkouts,table='cpergdata'
    )

    runningjob = 0

    taskstatus = get_stored_tasks_status(request)
    for task in taskstatus:
        if task['func_name'] == 'updatecp':
            if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
                runningjob = 1
                messages.info(request,'CP chart data have been updated')
                remove_asynctask(request,task['id'])
            elif 'fail' in task['status'].lower():
                runningjob = 0
                try:
                    remove_asynctask(request,task[id])
                    messages.error(request,'Oh, your task failed')
                except KeyError:
                    pass
            elif 'started' in task['status'].lower():
                messages.info(request,'Busy updating CP chart data')
                runningjob = 1
            elif 'queued' in task['status'].lower():
                messages.info(request,'Getting ready to update CP chart data')
                runningjob = 1


    if not runningjob:
        job = dataprep.runcpupdate(
            r,type='rower',
            startdate=startdate,
            enddate=enddate
        )
        request.session['job_id'] = job.id
        try:
            request.session['async_tasks'] += [(job.id,'updatecp')]
        except KeyError:
            request.session['async_tasks'] = [(job.id,'updatecp')]
        messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')

    powerdf = pd.DataFrame({
        'Delta':delta,
        'CP':cpvalue,
        })

    if powerdf.empty:
        messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')

    powerdf = powerdf[powerdf['CP']>0]
    powerdf.dropna(axis=0,inplace=True)
    powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
    powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)

    rowername = r.user.first_name+" "+r.user.last_name
    # create interactive plot
    if len(powerdf) !=0 :
        res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
        script = res[0]
        div = res[1]
        p1 = res[2]
        ratio = res[3]
        r.ep0 = p1[0]
        r.ep1 = p1[1]
        r.ep2 = p1[2]
        r.ep3 = p1[3]
        r.ecpratio = ratio
        r.save()
        paulslope = 1
        paulintercept = 1
        message = res[4]
    else:
        ratio = 1
        script = ''
        div = '<p>No ranking pieces found.</p>'
        paulslope = 1
        paulintercept = 1
        p1 = [1,1,1,1]
        message = ""





    cpredictions = []



    for rankingduration in trankingdurations:
        t = 3600.*rankingduration.hour
        t += 60.*rankingduration.minute
        t += rankingduration.second
        t += rankingduration.microsecond/1.e6


        # CP model
        pwr = p1[0]/(1+t/p1[2])
        pwr += p1[1]/(1+t/p1[3])

        velo = (pwr/2.8)**(1./3.)
        p = 500./velo
        d = t*velo

        if pwr <= 0:
            pwr = 50.


        if  not np.isnan(pwr):
            try:
                pwr2 = pwr*ratio
            except:
                pwr2 = pwr

            a = collections.OrderedDict()
            a['duration'] = timedeltaconv(t)
            a['distance'] = int(d)
            a['pace'] = timedeltaconv(p)
            a['power'] = int(pwr)
            a['upper'] = int(pwr2)

            cpredictions.append(a)


    # initiation - get 10 min power, then use Paul's law

    t_10 = 600.
    power_10 = p1[0]/(1+t_10/p1[2])
    power_10 += p1[1]/(1+t_10/p1[3])

    velo_10 = (power_10/2.8)**(1./3.)
    pace_10 = 500./velo_10
    distance_10 = t_10*velo_10

    paulslope = 5.

    for rankingdistance in trankingdistances:

        delta = paulslope * np.log(rankingdistance/distance_10)/np.log(2)


        p = pace_10+delta
        velo = 500./p
        t = rankingdistance/velo

        pwr2 = p1[0]/(1+t/p1[2])
        pwr2 += p1[1]/(1+t/p1[3])
        try:
            pwr2 *= ratio
        except UnboundLocalError:
            pass

        if pwr2 <= 0:
            pwr2 = 50.

        velo2 = (pwr2/2.8)**(1./3.)

        if np.isnan(velo2) or velo2 <= 0:
            velo2 = 1.0

        t2 = rankingdistance/velo2

        pwr3 = p1[0]/(1+t2/p1[2])
        pwr3 += p1[1]/(1+t2/p1[3])
        pwr3 *= ratio


        if pwr3 <= 0:
            pwr3 = 50.

        velo3 = (pwr3/2.8)**(1./3.)
        if np.isnan(velo3) or velo3 <= 0:
            velo3 = 1.0

        t3 = rankingdistance/velo3
        p3 = 500./velo3

        a = {
            'distance':rankingdistance,
            'duration':timedeltaconv(t3),
            'power':'--',
            'upper':int(pwr3),
            'pace':timedeltaconv(p3)}

        cpredictions.append(a)

        #    del form.fields["pieceunit"]


    messages.error(request,message)
    return render(request, 'oterankings.html',
                  {'rankingworkouts':theworkouts,
                   'interactiveplot':script,
                   'the_div':div,
                   'rower':r,
                   'active':'nav-analysis',
                   'cpredictions':cpredictions,
                   'avgpower':avgpower,
                   'form':form,
                   'dateform':dateform,
                   'workouttypeform':workouttypeform,
                   'id': theuser,
                   'theuser':uu,
                   'startdate':startdate,
                   'enddate':enddate,
                   'teams':get_my_teams(request.user),
                   'workouttype':'rower',
                   })



# Multi Flex Chart with Grouping
@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def user_multiflex_select(request,
                          startdatestring="",
                          enddatestring="",
                          message='',
                          successmessage='',
                          startdate=timezone.now()-datetime.timedelta(days=30),
                          enddate=timezone.now(),
                          userid=0):

    r = getrequestrower(request,userid=userid)
    user = r.user

    if 'options' in request.session:
        options = request.session['options']
    else:
        options = {}

    try:
        palette = request.session['palette']
    except KeyError:
        palette = 'monochrome_blue'

    try:
        includereststrokes = request.session['includereststrokes']
    except KeyError:
        includereststrokes = False

    try:
        ploterrorbars = request.session['ploterrorbars']
    except:
        ploterrorbars = False

    if 'startdate' in request.session:
        startdate = iso8601.parse_date(request.session['startdate'])


    if 'enddate' in request.session:
        enddate = iso8601.parse_date(request.session['enddate'])

        try:
            waterboattype = request.session['waterboattype']
        except KeyError:
            waterboattype = mytypes.waterboattype
    else:
        waterboattype = mytypes.waterboattype

    if 'rankingonly' in request.session:
        rankingonly = request.session['rankingonly']
    else:
        rankingonly = False


    if 'modalities' in request.session:
        modalities = request.session['modalities']
        if len(modalities) > 1:
            modality = 'all'
        else:
            modality = modalities[0]
    else:
        modalities = [m[0] for m in mytypes.workouttypes]
        modality = 'all'

    if request.method == 'POST':
        dateform = DateRangeForm(request.POST)
        if dateform.is_valid():
            startdate = dateform.cleaned_data['startdate']
            enddate = dateform.cleaned_data['enddate']
            startdatestring = startdate.strftime('%Y-%m-%d')
            enddatestring = enddate.strftime('%Y-%m-%d')
            request.session['startdate'] = startdatestring
            request.session['enddate'] = enddatestring
        modalityform = TrendFlexModalForm(request.POST)
        if modalityform.is_valid():
            modality = modalityform.cleaned_data['modality']
            waterboattype = modalityform.cleaned_data['waterboattype']
            rankingonly = modalityform.cleaned_data['rankingonly']
            if modality == 'all':
                modalities = [m[0] for m in mytypes.workouttypes]
            else:
                modalities = [modality]

            if modality != 'water':
                waterboattype = [b[0] for b in mytypes.boattypes]


            request.session['modalities'] = modalities
            request.session['waterboattype'] = waterboattype
            request.session['rankingonly'] = rankingonly
    else:
        dateform = DateRangeForm(initial={
            'startdate':startdate,
            'enddate':enddate,
            })


    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
    #enddate = enddate+datetime.timedelta(days=1)

    if startdatestring:
        startdate = iso8601.parse_date(startdatestring)
    if enddatestring:
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s


    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])

    if rankingonly:
        rankingpiece = [True]
    else:
        rankingpiece = [True,False]

    workouts = Workout.objects.filter(
        user=r,
        startdatetime__gte=startdate,
        startdatetime__lte=enddate,
        workouttype__in=modalities,
        rankingpiece__in=rankingpiece
    ).order_by(
        "-date", "-starttime"
    ).exclude(
        boattype__in=negtypes
    )

    query = request.GET.get('q')
    if query:
        query_list = query.split()
        workouts = workouts.filter(
            reduce(operator.and_,
                   (Q(name__icontains=q) for q in query_list)) |
            reduce(operator.and_,
                   (Q(notes__icontains=q) for q in query_list))
            )
        searchform = SearchForm(initial={'q':query})
    else:
        searchform = SearchForm()

    form = WorkoutMultipleCompareForm()
    form.fields["workouts"].queryset = workouts

    chartform = MultiFlexChoiceForm(initial={
        'palette':palette,
        'ploterrorbars':ploterrorbars,
        'includereststrokes':includereststrokes,
    })

    modalityform = TrendFlexModalForm(initial={
        'modality':modality,
        'waterboattype':waterboattype,
        'rankingonly':rankingonly,
    })

    messages.info(request,successmessage)
    messages.error(request,message)

    startdatestring = startdate.strftime('%Y-%m-%d')
    enddatestring = enddate.strftime('%Y-%m-%d')
    request.session['startdate'] = startdatestring
    request.session['enddate'] = enddatestring

    request.session['waterboattype'] = waterboattype
    request.session['rankingonly'] = rankingonly
    request.session['modalities'] = modalities


    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name':'Analysis'
        },
        {
            'url':reverse(user_multiflex_select,kwargs={'userid':userid}),
            'name': 'Compare Select'
        },
                {
                    'url':reverse('multi_compare_view'),
                    'name': 'Comparison Chart'
                }
    ]

    return render(request, 'user_multiflex_select.html',
                  {'workouts': workouts,
                   'dateform':dateform,
                   'breadcrumbs':breadcrumbs,
                   'startdate':startdate,
                   'enddate':enddate,
                   'theuser':user,
                   'rower':r,
                   'form':form,
                   'chartform':chartform,
                   'searchform':searchform,
                   'modalityform':modalityform,
                   'teams':get_my_teams(request.user),
                   })

@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def multiflex_data(request,userid=0,
                 options={
                     'includereststrokes':False,
                     'ploterrorbars':False,
                     'userid':0,
                     'palette': 'monochrome_blue',
                     'groupby': 'spm',
                     'binsize': 1,
                     'xparam': 'hr',
                     'yparam': 'pace',
                     'spmmin': 15,
                     'spmmax': 55,
                     'workmin': 400,
                     'workmax': 1500,
                     'ids': [],
                     'ploterrorbars':False,
                     }):

    def_options = options

    if 'options' in request.session:
        options = request.session['options']

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False

    try:
        ploterrorbars = options['ploterrorbars']
    except KeyError:
        ploterrorbars = False

    try:
        palette = request.session['palette']
    except KeyError:
        palette = 'monochrome_blue'

    workstrokesonly = not includereststrokes

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


    palette = keyvalue_get_default('palette',options, def_options)
    groupby = keyvalue_get_default('groupby',options, def_options)
    binsize = keyvalue_get_default('binsize',options, def_options)
    xparam  = keyvalue_get_default('xparam',options, def_options)
    yparam = keyvalue_get_default('yparam',options, def_options)
    spmmin = keyvalue_get_default('spmmin',options, def_options)
    spmmax = keyvalue_get_default('spmmax',options, def_options)
    workmin = keyvalue_get_default('workmin',options, def_options)
    workmax = keyvalue_get_default('workmax',options, def_options)
    ids = keyvalue_get_default('ids',options, def_options)

    workouts = []

    for id in ids:
        try:
            workouts.append(Workout.objects.get(id=id))
        except Workout.DoesNotExist:
            pass

    labeldict = {
        int(w.id): w.__str__() for w in workouts
    }

    fieldlist,fielddict = dataprep.getstatsfields()
    fieldlist = [xparam,yparam,groupby,
                 'workoutid','spm','driveenergy',
                 'workoutstate']

    # prepare data frame
    datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)

    if xparam == groupby:
        datadf['groupby'] = datadf[xparam]
        groupy = 'groupby'

    datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)


    datadf = dataprep.filter_df(datadf,'spm',spmmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'spm',spmmax,
                                largerthan=False)

    datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
                                largerthan=False)


    datadf.dropna(axis=0,how='any',inplace=True)


    datemapping = {
        w.id:w.date for w in workouts
    }

    datadf['date'] = datadf['workoutid']
    datadf['date'].replace(datemapping,inplace=True)

    today = datetime.date.today()
    try:
        datadf['days ago'] = map(lambda x : x.days, datadf.date - today)
    except TypeError:
        datadf['days ago'] = 0

    if groupby != 'date':
        try:
            bins = np.arange(datadf[groupby].min()-binsize,
                             datadf[groupby].max()+binsize,
                             binsize)
            groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False))
        except ValueError:
            messages.error(
                request,
                "Unable to compete. Probably not enough data selected"
            )
            url = reverse(user_multiflex_select)
            return HttpResponseRedirect(url)
    else:
        bins = np.arange(datadf['days ago'].min()-binsize,
                         datadf['days ago'].max()+binsize,
                         binsize,
        )
        groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
                                           labels=False))


    xvalues = groups.mean()[xparam]
    yvalues = groups.mean()[yparam]
    xerror = groups.std()[xparam]
    yerror = groups.std()[yparam]
    groupsize = groups.count()[xparam]

    mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()])
    xvalues.loc[mask] = np.nan

    yvalues.loc[mask] = np.nan
    xerror.loc[mask] = np.nan
    yerror.loc[mask] = np.nan
    groupsize.loc[mask] = np.nan

    xvalues.dropna(inplace=True)
    yvalues.dropna(inplace=True)
    xerror.dropna(inplace=True)
    yerror.dropna(inplace=True)
    groupsize.dropna(inplace=True)

    if len(groupsize) == 0:
        messages.error(request,'No data in selection')
        url = reverse(user_multiflex_select)
        return HttpResponseRedirect(url)
    else:
        groupsize = 30.*np.sqrt(groupsize/float(groupsize.max()))

    df = pd.DataFrame({
        xparam:xvalues,
        yparam:yvalues,
        'x':xvalues,
        'y':yvalues,
        'xerror':xerror,
        'yerror':yerror,
        'groupsize':groupsize,
    })


    if yparam == 'pace':
        df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)

    aantal = len(df)

    if groupby != 'date':
        try:
            df['groupval'] = groups.mean()[groupby]
            df['groupval'].loc[mask] = np.nan

            groupcols = df['groupval']
        except ValueError:
            df['groupval'] =  groups.mean()[groupby].fillna(value=0)
            df['groupval'].loc[mask] = np.nan
            groupcols = df['groupval']
        except KeyError:
            messages.error(request,'Data selection error')
            url = reverse(user_multiflex_select)
            return HttpResponseRedirect(url)
    else:
        try:
            dates = groups.min()[groupby]
            dates.loc[mask] = np.nan
            dates.dropna(inplace=True)
            df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates]
            df['groupval'].loc[mask] = np.nan
            groupcols = 100.*np.arange(aantal)/float(aantal)
        except AttributeError:
            df['groupval'] = groups.mean()['days ago'].fillna(value=0)
            groupcols = 100.*np.arange(aantal)/float(aantal)


    groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min())

    if aantal == 1:
        groupcols = np.array([1.])


    colors = range_to_color_hex(groupcols,palette=palette)

    df['color'] = colors

    clegendx = np.arange(0,1.2,.2)
    legcolors = range_to_color_hex(clegendx,palette=palette)
    if groupby != 'date':
        clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())
    else:
        clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min())



    colorlegend =  zip(range(6),clegendy,legcolors)


    if userid == 0:
        extratitle = ''
    else:
        u = User.objects.get(id=userid)
        extratitle = ' '+u.first_name+' '+u.last_name



    script,div = interactive_multiflex(df,xparam,yparam,
                                       groupby,
                                       extratitle=extratitle,
                                       ploterrorbars=ploterrorbars,
                                       binsize=binsize,
                                       colorlegend=colorlegend,
                                       spmmin=spmmin,spmmax=spmmax,
                                       workmin=workmin,workmax=workmax)

    scripta= script.split('\n')[2:-1]
    script = ''.join(scripta)


    return JSONResponse({
        "script":script,
        "div":div,
        })


@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def multiflex_view(request,userid=0,
                 options={
                     'includereststrokes':False,
                     'ploterrorbars':False,
                     }):

    if 'options' in request.session:
        options = request.session['options']

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False

    try:
        ploterrorbars = options['ploterrorbars']
    except KeyError:
        ploterrorbars = False

    try:
        palette = request.session['palette']
    except KeyError:
        palette = 'monochrome_blue'

    if 'startdate' in request.session:
        startdate = iso8601.parse_date(request.session['startdate'])


    if 'enddate' in request.session:
        enddate = iso8601.parse_date(request.session['enddate'])

    workstrokesonly = not includereststrokes

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

    if request.method == 'POST' and 'workouts' in request.POST:
        form = WorkoutMultipleCompareForm(request.POST)
        chartform = MultiFlexChoiceForm(request.POST)
        if form.is_valid() and chartform.is_valid():
            cd = form.cleaned_data
            workouts = cd['workouts']
            xparam = chartform.cleaned_data['xparam']
            yparam = chartform.cleaned_data['yparam']
            includereststrokes = chartform.cleaned_data['includereststrokes']
            ploterrorbars = chartform.cleaned_data['ploterrorbars']

            workstrokesonly = not includereststrokes
            palette = chartform.cleaned_data['palette']

            groupby = chartform.cleaned_data['groupby']
            binsize = chartform.cleaned_data['binsize']
            if binsize <= 0:
                binsize = 1
            if groupby == 'pace':
                binsize *= 1000

            spmmin = chartform.cleaned_data['spmmin']
            spmmax = chartform.cleaned_data['spmmax']
            workmin = chartform.cleaned_data['workmin']
            workmax = chartform.cleaned_data['workmax']

            ids = [int(w.id) for w in workouts]
            request.session['ids'] = ids

        else:
            return HttpResponse("Form is not valid")
    elif request.method == 'POST' and 'ids' in request.session:
        chartform = MultiFlexChoiceForm(request.POST)
        if chartform.is_valid():
            xparam = chartform.cleaned_data['xparam']
            yparam = chartform.cleaned_data['yparam']
            includereststrokes = chartform.cleaned_data['includereststrokes']
            ploterrorbars = chartform.cleaned_data['ploterrorbars']
            request.session['ploterrorbars'] = ploterrorbars
            request.session['includereststrokes'] = includereststrokes
            workstrokesonly = not includereststrokes
            palette = chartform.cleaned_data['palette']

            groupby = chartform.cleaned_data['groupby']
            binsize = chartform.cleaned_data['binsize']
            if binsize <= 0:
                binsize = 1
            if groupby == 'pace':
                binsize *= 1000.

            spmmin = chartform.cleaned_data['spmmin']
            spmmax = chartform.cleaned_data['spmmax']
            workmin = chartform.cleaned_data['workmin']
            workmax = chartform.cleaned_data['workmax']

            ids = request.session['ids']
            request.session['ids'] = ids
            workouts = dataprep.get_workouts(ids,userid)
            if not workouts:
                message = 'Error: Workouts in session storage do not belong to this user.'
                messages.error(request,message)
                url = reverse(user_multiflex_select,
                              kwargs={
                                  'userid':userid,
                                  }
                              )
                return HttpResponseRedirect(url)

            # workouts = [Workout.objects.get(id=id) for id in ids]


        else:
            return HttpResponse("invalid form")
    else:
        url = reverse(user_multiflex_select)
        return HttpResponseRedirect(url)

    div = get_call()

    options['includereststrokes'] = includereststrokes
    options['ploterrorbars'] = ploterrorbars
    options['userid'] = userid
    options['palette'] = palette
    options['groupby'] = groupby
    options['binsize'] = binsize
    options['xparam'] = xparam
    options['yparam'] = yparam
    options['spmmin'] = spmmin
    options['spmmax'] = spmmax
    options['workmin'] = workmin
    options['workmax'] = workmax
    options['idso'] = ids


    request.session['options'] = options

    r = getrequestrower(request,userid=userid)

    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name':'Analysis'
        },
        {
            'url':reverse(user_multiflex_select,kwargs={'userid':userid}),
            'name': 'Trend Flex Select'
        },
        {
            'url':reverse(multiflex_view),
            'name': 'Trend Flex Chart'
        }
    ]


    return render(request,'multiflex.html',
                  {'interactiveplot':'',
                   'active':'nav-analysis',
                   'rower':r,
                   'breadcrumbs':breadcrumbs,
                   'the_div':div,
                   'active':'nav-analysis',
                   'chartform':chartform,
                   'userid':userid,
                   'teams':get_my_teams(request.user),
                  })


# Box plots
@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def user_boxplot_select(request,
                        startdatestring="",
                        enddatestring="",
                        message='',
                        successmessage='',
                        startdate=timezone.now()-datetime.timedelta(days=30),
                        enddate=timezone.now(),
                        options={
                            'includereststrokes':False,
                            'workouttypes':['rower','dynamic','slides'],
                            'waterboattype':mytypes.waterboattype,
                            'rankingonly':False,
                        },
                        userid=0):

    r = getrequestrower(request,userid=userid)
    user = r.user
    userid = user.id

    if 'options' in request.session:
        options = request.session['options']


    try:
        workouttypes = options['workouttypes']
    except KeyError:
        workouttypes = ['rower','dynamic','slides']

    try:
        rankingonly = options['rankingonly']
    except KeyError:
        rankingonly = False

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False

    if 'startdate' in request.session:
        startdate = iso8601.parse_date(request.session['startdate'])


    if 'enddate' in request.session:
        enddate = iso8601.parse_date(request.session['enddate'])

    workstrokesonly = not includereststrokes

    waterboattype = mytypes.waterboattype


    if startdatestring != "":
        startdate = iso8601.parse_date(startdatestring)

    if enddatestring != "":
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s


    if request.method == 'POST':
        dateform = DateRangeForm(request.POST)
        if dateform.is_valid():
            startdate = dateform.cleaned_data['startdate']
            enddate = dateform.cleaned_data['enddate']
            startdatestring = startdate.strftime('%Y-%m-%d')
            enddatestring = enddate.strftime('%Y-%m-%d')
            request.session['startdate'] = startdatestring
            request.session['enddate'] = enddatestring
        optionsform = TrendFlexModalForm(request.POST)
        if optionsform.is_valid():
            modality = optionsform.cleaned_data['modality']
            waterboattype = optionsform.cleaned_data['waterboattype']
            if modality == 'all':
                modalities = [m[0] for m in mytypes.workouttypes]
            else:
                modalities = [modality]
            if modality != 'water':
                waterboattype = [b[0] for b in mytypes.boattypes]


            if 'rankingonly' in optionsform.cleaned_data:
                rankingonly = optionsform.cleaned_data['rankingonly']
            else:
                rankingonly = False

            request.session['modalities'] = modalities
            request.session['waterboattype'] = waterboattype
    else:
        dateform = DateRangeForm(initial={
            'startdate':startdate,
            'enddate':enddate,
            })

    if 'modalities' in request.session:
        modalities = request.session['modalities']
        if len(modalities) > 1:
            modality = 'all'
        else:
            modality = modalities[0]
    else:
        modalities = [m[0] for m in mytypes.workouttypes]
        modality = 'all'




    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])


    startdate = datetime.datetime.combine(startdate,datetime.time())
    enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
    #enddate = enddate+datetime.timedelta(days=1)

    if startdatestring:
        startdate = iso8601.parse_date(startdatestring)
    if enddatestring:
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])


    workouts = Workout.objects.filter(user=r,
                                       startdatetime__gte=startdate,
                                       startdatetime__lte=enddate,
                                       workouttype__in=modalities,
        ).order_by(
            "-date", "-starttime"
        ).exclude(boattype__in=negtypes)
    #        workouttypes = [w for w in workouttypes if w not in mytypes.otwtypes]

    if rankingonly:
        workouts = workouts.exclude(rankingpiece=False)

    query = request.GET.get('q')
    if query:
        query_list = query.split()
        workouts = workouts.filter(
            reduce(operator.and_,
                   (Q(name__icontains=q) for q in query_list)) |
            reduce(operator.and_,
                   (Q(notes__icontains=q) for q in query_list))
            )
        searchform = SearchForm(initial={'q':query})
    else:
        searchform = SearchForm()

    form = WorkoutMultipleCompareForm()
    form.fields["workouts"].queryset = workouts

    chartform = BoxPlotChoiceForm()
    optionsform = TrendFlexModalForm(initial={
        'modality':modality,
        'waterboattype':waterboattype,
        'rankingonly':rankingonly,
        })

    messages.info(request,successmessage)
    messages.error(request,message)

    startdatestring = startdate.strftime('%Y-%m-%d')
    enddatestring = enddate.strftime('%Y-%m-%d')
    request.session['startdate'] = startdatestring
    request.session['enddate'] = enddatestring


    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name':'Analysis'
        },
        {
            'url':reverse(user_boxplot_select,kwargs={'userid':userid}),
            'name': 'BoxPlot Select'
        },
    ]
    return render(request, 'user_boxplot_select.html',
                  {'workouts': workouts,
                   'dateform':dateform,
                   'startdate':startdate,
                   'enddate':enddate,
                   'rower':r,
                   'breadcrumbs':breadcrumbs,
                   'theuser':user,
                   'form':form,
                   'active':'nav-analysis',
                   'chartform':chartform,
                   'searchform':searchform,
                   'optionsform':optionsform,
                   'teams':get_my_teams(request.user),
                   })

@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def boxplot_view_data(request,userid=0,
                 options={
                     'includereststrokes':False,
                     'spmmin':15,
                     'spmmax':55,
                     'workmin':0,
                     'workmax':1500,
                     'ids':[],
                     'userid':0,
                     'plotfield':'spm',
                     }):

    if 'options' in request.session:
        options = request.session['options']

    try:
        includereststrokes = options['includereststrokes']
        spmmin = options['spmmin']
        spmmax = options['spmmax']
        workmin = options['workmin']
        workmax = options['workmax']
        ids = options['ids']
        userid = options['userid']
        plotfield = options['plotfield']
    except KeyError:
        includereststrokes = False
        spmmin = 15
        spmmax = 55
        workmin = 0
        workmax = 55
        ids = []
        userid = 0
        plotfield = 'spm'


    workstrokesonly = not includereststrokes

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

    workouts = []


    if not ids:
        return JSONResponse({
            "script":'',
            "div":'No data found'
        })

    for id in ids:
        try:
            workouts.append(Workout.objects.get(id=id))
        except Workout.DoesNotExist:
            pass

    labeldict = {
        int(w.id): w.__str__() for w in workouts
    }


    datemapping = {
        w.id:w.date for w in workouts
    }



    fieldlist,fielddict = dataprep.getstatsfields()
    fieldlist = [plotfield,'workoutid','spm','driveenergy',
                 'workoutstate']

    # prepare data frame
    datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)



    datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)

    datadf = dataprep.filter_df(datadf,'spm',spmmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'spm',spmmax,
                                largerthan=False)
    datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
                                largerthan=True)
    datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
                                largerthan=False)

    datadf.dropna(axis=0,how='any',inplace=True)


    datadf['workoutid'].replace(datemapping,inplace=True)
    datadf.rename(columns={"workoutid":"date"},inplace=True)
    datadf = datadf.sort_values(['date'])

    if userid == 0:
        extratitle = ''
    else:
        u = User.objects.get(id=userid)
        extratitle = ' '+u.first_name+' '+u.last_name



    script,div = interactive_boxchart(datadf,plotfield,
                                      extratitle=extratitle,
                                      spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)

    scripta = script.split('\n')[2:-1]
    script = ''.join(scripta)


    return JSONResponse({
        "script":script,
        "div":div,
        })

@user_passes_test(ispromember,login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def boxplot_view(request,userid=0,
                 options={
                     'includereststrokes':False,
                     'rankingonly':False,
                     }):

    if 'options' in request.session:
        options = request.session['options']
    else:
        options = {}

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False
        options['includereststrokes'] = False

    try:
        rankingonly = options['rankingonly']
    except KeyError:
        rankingonly = False
        options['rankingonly'] = False

    workstrokesonly = not includereststrokes

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


    if request.method == 'POST' and 'workouts' in request.POST:
        form = WorkoutMultipleCompareForm(request.POST)
        chartform = BoxPlotChoiceForm(request.POST)
        if form.is_valid() and chartform.is_valid():
            cd = form.cleaned_data
            workouts = cd['workouts']
            plotfield = chartform.cleaned_data['yparam']
            includereststrokes = chartform.cleaned_data['includereststrokes']
            request.session['includereststrokes'] = includereststrokes
            workstrokesonly = not includereststrokes

            spmmin = chartform.cleaned_data['spmmin']
            spmmax = chartform.cleaned_data['spmmax']
            workmin = chartform.cleaned_data['workmin']
            workmax = chartform.cleaned_data['workmax']

            ids = [int(w.id) for w in workouts]
            request.session['ids'] = ids

        else:
            url = reverse(user_boxplot_select,kwargs={'userid':userid})
            return HttpResponseRedirect(url)
    elif request.method == 'POST' and 'ids' in request.session:
        chartform = BoxPlotChoiceForm(request.POST)
        if chartform.is_valid():
            plotfield = chartform.cleaned_data['yparam']
            includereststrokes = chartform.cleaned_data['includereststrokes']
            spmmin = chartform.cleaned_data['spmmin']
            spmmax = chartform.cleaned_data['spmmax']
            workmin = chartform.cleaned_data['workmin']
            workmax = chartform.cleaned_data['workmax']
            request.session['includereststrokes'] = includereststrokes
            workstrokesonly = not includereststrokes
            ids = request.session['ids']
            request.session['ids'] = ids


        else:
            url = reverse(user_boxplot_select,kwargs={'userid':userid})
            return HttpResponseRedirect(url)
    else:
        url = reverse(user_boxplot_select,kwargs={'userid':userid})
        return HttpResponseRedirect(url)

    div = get_call()


    options['spmmin'] = spmmin
    options['spmmax'] = spmmax
    options['workmin'] = workmin
    options['workmax'] = workmax
    options['ids'] = ids
    options['userid'] = userid
    options['plotfield'] = plotfield
    options['rankingonly'] = rankingonly


    request.session['options'] = options

    r = getrequestrower(request,userid=userid)
    breadcrumbs = [
        {
            'url':'/rowers/Analysis',
            'name':'Analysis'
        },
        {
            'url':reverse(user_boxplot_select,kwargs={'userid':userid}),
            'name': 'BoxPlot Select'
        },
        {
            'url':reverse(boxplot_view,kwargs={'userid':userid}),
            'name': 'BoxPlot Select'
        },
    ]

    return render(request,'boxplot.html',
                  {'interactiveplot':'',
                   'the_div':div,
                   'rower':r,
                   'breadcrumbs':breadcrumbs,
                   'active':'nav-analysis',
                   'chartform':chartform,
                   'userid':userid,
                   'teams':get_my_teams(request.user),
                  })


# Cumulative stats page
@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def cumstats(request,userid=0,
             startdate=timezone.now()-datetime.timedelta(days=30),
             enddate=timezone.now(),
             deltadays=-1,
             startdatestring="",
             enddatestring="",
             options={
                 'includereststrokes':False,
                 'workouttypes':['rower','dynamic','slides'],
                 'waterboattype':mytypes.waterboattype,
                 'rankingonly':False,
             }):

    r = getrequestrower(request,userid=userid)
    theuser = r.user

    if 'waterboattype' in request.session:
        waterboattype = request.session['waterboattype']
    else:
        waterboattype = mytypes.waterboattype


    if 'rankingonly' in request.session:
        rankingonly = request.session['rankingonly']
    else:
        rankingonly = False

    if 'modalities' in request.session:
        modalities = request.session['modalities']
        if len(modalities) > 1:
            modality = 'all'
        else:
            modality = modalities[0]
    else:
        modalities = [m[0] for m in mytypes.workouttypes]
        modality = 'all'


    try:
        rankingonly = options['rankingonly']
    except KeyError:
        rankingonly = False

    try:
        includereststrokes = options['includereststrokes']
    except KeyError:
        includereststrokes = False


    workstrokesonly = not includereststrokes

    waterboattype = mytypes.waterboattype


    if startdatestring != "":
        startdate = iso8601.parse_date(startdatestring)

    if enddatestring != "":
        enddate = iso8601.parse_date(enddatestring)

    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s


    # get all indoor rows of in date range

    # process form
    if request.method == 'POST':
        form = DateRangeForm(request.POST)
        modalityform = TrendFlexModalForm(request.POST)
        if form.is_valid():
            startdate = form.cleaned_data['startdate']
            enddate = form.cleaned_data['enddate']
            if startdate > enddate:
                s = enddate
                enddate = startdate
                startdate = s
            startdatestring = startdate.strftime('%Y-%m-%d')
            enddatestring = enddate.strftime('%Y-%m-%d')
        if modalityform.is_valid():
            modality = modalityform.cleaned_data['modality']
            waterboattype = modalityform.cleaned_data['waterboattype']
            rankingonly = modalityform.cleaned_data['rankingonly']
            if modality == 'all':
                modalities = [m[0] for m in mytypes.workouttypes]
            else:
                modalities = [modality]

            if modality != 'water':
                waterboattype = [b[0] for b in mytypes.boattypes]


            request.session['modalities'] = modalities
            request.session['waterboattype'] = waterboattype
            request.session['rankingonly'] = rankingonly
            form = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
    else:
        form = DateRangeForm(initial={
            'startdate': startdate,
            'enddate': enddate,
            })
        includereststrokes = False

        workstrokesonly = not includereststrokes
        modalityform = TrendFlexModalForm(
            initial={
                'modality':modality,
                'waterboattype':waterboattype,
                'rankingonly':rankingonly,
            }
        )

    negtypes = []
    for b in mytypes.boattypes:
        if b[0] not in waterboattype:
            negtypes.append(b[0])



    script = ''
    div = get_call()
    js_resources = ''
    css_resources = ''

    options = {
        'modality': modality,
        'userid': theuser.id,
        'waterboattype':waterboattype,
        'startdatestring':startdatestring,
        'enddatestring':enddatestring,
        'rankingonly':rankingonly,
        'includereststrokes':includereststrokes,
        }


    request.session['options'] = options


    if modality == 'all':
        modalities = [m[0] for m in mytypes.workouttypes]
    else:
        modalities = [modality]

    try:
        startdate = iso8601.parse_date(startdatestring)
    except ParseError:
        startdate = timezone.now()-datetime.timedelta(days=7)

    try:
        enddate = iso8601.parse_date(enddatestring)
    except ParseError:
        enddate = timezone.now()


    if enddate < startdate:
        s = enddate
        enddate = startdate
        startdate = s

    promember=0
    if theuser == 0:
        theuser = request.user.id

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

    r2 = getrower(theuser)

    if rankingonly:
        rankingpiece = [True,]
    else:
        rankingpiece = [True,False]

    allworkouts = Workout.objects.filter(
        user=r2,
        workouttype__in=modalities,
        boattype__in=waterboattype,
        startdatetime__gte=startdate,
        startdatetime__lte=enddate,
        rankingpiece__in=rankingpiece
    ).order_by("-date", "-starttime")

    ids = [int(workout.id) for workout in allworkouts]

    datemapping = {
        w.id:w.date for w in allworkouts
        }



    fieldlist,fielddict = dataprep.getstatsfields()

    # prepare data frame
    datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)

    datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)

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

    if datadf.empty:
        stats = {}
        cordict = {}

        response = render(request,
                          'cumstats.html',
                          {
                              'stats':stats,
                              'teams':get_my_teams(request.user),
                              'options':options,
                              'active':'nav-analysis',
                              'rower':r,
                              'id':theuser,
                              'theuser':theuser,
                              'startdate':startdate,
                              'enddate':enddate,
                              'form':form,
                              'optionsform':modalityform,
                              'cordict':cordict,
                          })

        request.session['options'] = options

        return response



    # Create stats
    stats = {}
    try:
        fielddict.pop('pace')
    except KeyError:
        pass

    for field,verbosename in fielddict.items():
        thedict = {
            'mean':datadf[field].mean(),
            'min': datadf[field].min(),
            'std': datadf[field].std(),
            'max': datadf[field].max(),
            'median': datadf[field].median(),
            'firstq':datadf[field].quantile(q=0.25),
            'thirdq':datadf[field].quantile(q=0.75),
            'verbosename':verbosename,
        }
        stats[field] = thedict

    # Create a dict with correlation values
    cor = datadf.corr(method='spearman')
    cor.fillna(value=0,inplace=True)
    cordict = {}
    for field1,verbosename1 in fielddict.items():
        thedict = {}
        for field2,verbosename2 in fielddict.items():
            try:
                thedict[verbosename2] = cor.loc[field1,field2]
            except KeyError:
                thedict[verbosename2] = 0

        cordict[verbosename1] = thedict

    # set options form correctly
    initial = {}
    initial['includereststrokes'] = includereststrokes
    initial['waterboattype'] = waterboattype
    initial['rankingonly'] = rankingonly


    response = render(request,
                  'cumstats.html',
                  {
                      'stats':stats,
                      'teams':get_my_teams(request.user),
                      'active':'nav-analysis',
                      'rower':r,
                      'options':options,
                      'id':theuser,
                      'theuser':theuser,
                      'startdate':startdate,
                      'enddate':enddate,
                      'form':form,
                      'optionsform':modalityform,
                      'cordict':cordict,
                  })

    request.session['options'] = options

    return response


def agegroupcpview(request,age,normalize=0):
    script,div = interactive_agegroupcpchart(age,normalized=normalize)

    response = render(request,'agegroupcp.html',
                      {
                          'active': 'nav-analysis',
                          'interactiveplot':script,
                          'the_div':div,
                          }
                      )

    return response

def agegrouprecordview(request,sex='male',weightcategory='hwt',
                       distance=2000,duration=None):
    if not duration:
        df = pd.DataFrame(
            list(
                C2WorldClassAgePerformance.objects.filter(
                    distance=distance,
                    sex=sex,
                    weightcategory=weightcategory
                ).values()
            )
        )
    else:
        duration = int(duration)*60
        df = pd.DataFrame(
            list(
                C2WorldClassAgePerformance.objects.filter(
                    duration=duration,
                    sex=sex,
                    weightcategory=weightcategory
                ).values()
            )
        )


    script,div = interactive_agegroup_plot(df,sex=sex,distance=distance,
                                           duration=duration,
                                           weightcategory=weightcategory)

    return render(request, 'agegroupchart.html',
                  {
                      'interactiveplot':script,
                      'active':'nav-analysis',
                      'the_div':div,
                   })

# alert overview view
#@user_passes_test(ispromember, login_url="/rowers/paidplans",
#                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
#                  redirect_field_name=None)
@login_required
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alerts_view(request,userid=0):
    r = getrequestrower(request,userid=userid)

    alerts = Alert.objects.filter(rower=r).order_by('next_run')

    stats = []

    for alert in alerts:
        stats.append(alert_get_stats(alert))


    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name': 'Analysis'
        },
        {
            'url': reverse('alerts_view'),
            'name': 'Alerts',
        },
    ]

    return render(request,'alerts.html',
                  {
                      'breadcrumbs':breadcrumbs,
                      'alerts':alerts,
                      'rower':r,
                      'stats':stats,
                  })

# alert create view
@user_passes_test(ispromember, login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alert_create_view(request,userid=0):
    r = getrequestrower(request,userid=userid)
    FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=1)
    filter_formset = FilterFormSet()

    if request.method == 'POST':
        form = AlertEditForm(request.POST)
        measuredform = ConditionEditForm(request.POST)
        filter_formset = FilterFormSet(request.POST)
        if form.is_valid() and measuredform.is_valid() and filter_formset.is_valid():
            ad = form.cleaned_data
            measured = measuredform.cleaned_data

            period = ad['period']
            emailalert = ad['emailalert']
            reststrokes = ad['reststrokes']
            workouttype = ad['workouttype']
            boattype = ad['boattype']
            name = ad['name']

            filters = []

            for filter_form in filter_formset:
                metric = filter_form.cleaned_data.get('metric')
                condition = filter_form.cleaned_data.get('condition')
                value1 = filter_form.cleaned_data.get('value1')
                value2 = filter_form.cleaned_data.get('value2')

                filters.append(
                    {
                        'metric':metric,
                        'condition':condition,
                        'value1':value1,
                        'value2':value2,
                        }
                    )

            result,message = create_alert(request.user,r,measured,period=period,emailalert=emailalert,
                                          reststrokes=reststrokes,workouttype=workouttype,
                                          boattype=boattype,
                                          filter = filters,
                                          name=name)

            if result:
                messages.info(request,message)

                url = reverse('alert_edit_view',kwargs={'id':result})
                return HttpResponseRedirect(url)
    else:
        form = AlertEditForm()
        measuredform = ConditionEditForm()

    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name': 'Analysis'
        },
        {
            'url': reverse('alerts_view'),
            'name': 'Alerts',
        },
        {
            'url': reverse('alert_create_view'),
            'name': 'Create'
        }
    ]

    return render(request,'alert_create.html',
                  {
                      'breadcrumbs':breadcrumbs,
                      'formset': filter_formset,
                      'rower':r,
                      'form':form,
                      'measuredform':measuredform,
                      })

# alert report view
@login_required()
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alert_report_view(request,id=0,userid=0,nperiod=0):
    r = getrequestrower(request,userid=userid)
    if userid == 0:
        userid = request.user.id

    alert = Alert.objects.get(id=id)
    nperiod = int(nperiod)

    try:
        alert = Alert.objects.get(id=id)
    except Alert.DoesNotExist:
        raise Http404("This alert doesn't exist")


    if not checkalertowner(alert,request.user):
        raise PermissionDenied('You are not allowed to edit this Alert')

    stats = alert_get_stats(alert,nperiod=nperiod)

    if request.is_ajax():
        return JSONResponse({
            "stats":stats,
            })

    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name': 'Analysis'
        },
        {
            'url':reverse('alerts_view'),
            'name':'Alerts',
        },
        {
            'url': reverse('alert_edit_view',
                           kwargs={'userid':userid,'id':alert.id}),
            'name': alert.name,
        },
        {
            'url': reverse('alert_report_view',
                           kwargs={'userid':userid,'id':alert.id}),
            'name': 'Report',
        },
    ]
    return render(request,'alert_stats.html',
                  {
                      'breadcrumbs':breadcrumbs,
                      'stats':stats,
                      'rower':r,
                      'alert':alert,
                      'nperiod':nperiod,
                      })

# alert edit view
@user_passes_test(ispromember, login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def alert_edit_view(request,id=0,userid=0):
    r = getrequestrower(request,userid=userid)

    try:
        alert = Alert.objects.get(id=id)
    except Alert.DoesNotExist:
        raise Http404("This alert doesn't exist")


    if alert.manager != request.user:
        raise PermissionDenied('You are not allowed to edit this Alert')

    FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet,extra=0)
    if len(alert.filter.all()) == 0:
        FilterFormSet = formset_factory(ConditionEditForm, formset=BaseConditionFormSet, extra=1)

    filter_data = [{'metric':m.metric,
                    'value1':m.value1,
                    'value2':m.value2,
                    'condition':m.condition}
                   for m in alert.filter.all()]

    if request.method == 'POST':
        form = AlertEditForm(request.POST)
        measuredform = ConditionEditForm(request.POST)
        filter_formset = FilterFormSet(request.POST)
        if form.is_valid() and measuredform.is_valid() and filter_formset.is_valid():
            ad = form.cleaned_data
            measured = measuredform.cleaned_data

            period = ad['period']
            emailalert = ad['emailalert']
            reststrokes = ad['reststrokes']
            workouttype = ad['workouttype']
            boattype = ad['boattype']
            name = ad['name']

            m = alert.measured
            m.metric = measured['metric']
            m.value1 = measured['value1']
            m.value2 = measured['value2']
            m.condition = measured['condition']
            m.save()

            alert.period = period
            alert.emailalert = emailalert
            alert.reststrokes = reststrokes
            alert.workouttype = workouttype
            alert.boattype = boattype
            alert.name = name
            alert.save()

            filters = []

            for filter_form in filter_formset:
                metric = filter_form.cleaned_data.get('metric')
                condition = filter_form.cleaned_data.get('condition')
                value1 = filter_form.cleaned_data.get('value1')
                value2 = filter_form.cleaned_data.get('value2')

                filters.append(
                    {
                        'metric':metric,
                        'condition':condition,
                        'value1':value1,
                        'value2':value2,
                        }
                    )

            res = alert_add_filters(alert, filters)
            messages.info(request,'Alert was changed')

    else:
        form = AlertEditForm(instance=alert)
        measuredform = ConditionEditForm(instance=alert.measured)
        filter_formset = FilterFormSet(initial=filter_data)



    breadcrumbs = [
        {
            'url':'/rowers/analysis',
            'name': 'Analysis'
        },
        {
            'url':reverse('alerts_view'),
            'name':'Alerts',
        },
        {
            'url': reverse('alert_edit_view',
                           kwargs={'userid':userid,'id':alert.id}),
            'name': alert.name,
        },
    ]


    return render(request,'alert_edit.html',
                  {
                      'breadcrumbs':breadcrumbs,
                      'rower':r,
                      'form':form,
                      'measuredform':measuredform,
                      'formset':filter_formset,
                      'alert':alert,
                      })

# alert delete view

class AlertDelete(DeleteView):
    login_requird = True
    model = Alert
    template_name = 'alert_delete_confirm.html'

    # extra parameters
    def get_context_data(self, **kwargs):
        context = super(AlertDelete, self).get_context_data(**kwargs)

        if 'userid' in kwargs:
            userid = kwargs['userid']
        else:
            userid = 0

        context['rower'] = getrequestrower(self.request,userid=userid)
        context['alert'] = self.object

        breadcrumbs = [
            {
                'url':'/rowers/analysis',
                'name': 'Analysis'
                },
            {
                'url':reverse('alerts_view'),
                'name':'Alerts',
                },
            {
                'url': reverse('alert_edit_view',
                               kwargs={'userid':userid,'id':self.object.pk}),
                'name': 'Alert',
                },
            {
                'url': reverse('alert_delete_view',kwargs={'pk':self.object.pk}),
                'name': 'Delete'
                }
            ]

        context['breadcrumbs'] = breadcrumbs

        return context

    def get_success_url(self):
        return reverse('alerts_view')

    def get_object(self, *args, **kwargs):
        obj = super(AlertDelete, self).get_object(*args, **kwargs)

        if obj.manager != self.request.user:
            raise PermissionDenied("You are not allowed to delete this Alert")

        # some checks

        return obj

@user_passes_test(ispromember, login_url="/rowers/paidplans",
                  message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",
                  redirect_field_name=None)
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def history_view(request,userid=0):
    r = getrequestrower(request,userid=userid)

    usertimezone = pytz.timezone(r.defaulttimezone)

    time_min = datetime.time(hour=0,minute=0,second=0)
    time_max = datetime.time(hour=23,minute=59,second=59)


    if request.GET.get('startdate'):
        startdate,enddate = get_dates_timeperiod(request)
        sstartdate = startdate
        senddate = enddate
        activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate,time_min))
        activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate,time_max))
    else:
        activity_enddate = timezone.now()
        activity_enddate = usertimezone.localize(timezone.datetime.combine(activity_enddate.date(),time_max))
        startdate = timezone.now()-datetime.timedelta(days=14)
        activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate.date(),time_min))
        sstartdate = activity_startdate.date
        senddate = activity_enddate.date
        startdate = sstartdate
        enddate = senddate



    typeselect = 'All'
    if request.GET.get('workouttype'):
        typeselect = request.GET.get('workouttype')

    if typeselect not in mytypes.checktypes:
        typeselect = 'All'

    form = HistorySelectForm(initial={'startdate':activity_startdate,
                                      'enddate':activity_enddate,
                                      'workouttype':typeselect})



    g_workouts = Workout.objects.filter(
        user=r,
        startdatetime__gte=activity_startdate,
        startdatetime__lte=activity_enddate,
        duplicate=False,
        privacy='visible'
    ).order_by("-startdatetime")


    ids = [w.id for w in g_workouts]

    columns = ['hr','power','time']

    tscript,tdiv = interactive_workouttype_piechart(g_workouts)

    totalmeters,totalhours, totalminutes, totalseconds = get_totals(g_workouts)


    # meters, duration per workout type
    wtypes = list(set([w.workouttype for w in g_workouts]))

    typechoices = [("All","All")]
    for wtype in wtypes:
        if wtype in mytypes.checktypes:
            typechoices.append((wtype,mytypes.workouttypes_ordered[wtype]))

    form.fields['workouttype'].choices = typechoices

    listofdicts = []

    for wtype in wtypes:
        a_workouts = g_workouts.filter(workouttype=wtype)
        wmeters, whours, wminutes,wseconds = get_totals(a_workouts)
        ddict = {}
        ddict['id'] = wtype
        try:
            ddict['wtype'] = mytypes.workouttypes_ordered[wtype]
        except KeyError:
            ddict['wtype'] = wtype
        ddict['distance'] = wmeters
        ddict['duration'] = "{whours}:{wminutes:02d}:{wseconds:02d}".format(
            whours=whours,
            wminutes=wminutes,
            wseconds=wseconds,
        )
        ddict['nrworkouts'] = a_workouts.count()
        listofdicts.append(ddict)


    # interactive hr pie chart
    totalscript = ''
    totaldiv = get_call()

    totalsdict = {}
    totalsdict['duration'] = "{totalhours:02d}:{totalminutes:02d}:{totalseconds:02d}".format(
        totalhours=totalhours,
        totalminutes=totalminutes,
        totalseconds=totalseconds,
    )

    totalsdict['distance'] = totalmeters


    totalsdict['nrworkouts'] =  g_workouts.count()

    breadcrumbs = [
        {
            'url':'rowers/analysis',
            'name':'Analysis',
        },
        {
            'url':reverse('history_view'),
            'name': 'History',
        },
        ]

    lastseven = timezone.now()-datetime.timedelta(days=7)
    lastfourteen = timezone.now()-datetime.timedelta(days=14)
    last28 = timezone.now()-datetime.timedelta(days=28)

    today = timezone.now()

    lastyear = datetime.datetime(year=today.year-1,month=today.month,day=today.day)

    firstmay = datetime.datetime(year=today.year,month=5,day=1).astimezone(usertimezone)
    if firstmay>today:
        firstmay = datetime.datetime(year=today.year-1,month=5,day=1)


    return render(request,'history.html',
                  {
                      'tscript':tscript,
                      'tdiv':tdiv,
                      'rower':r,
                      'breadcrumbs':breadcrumbs,
                      'active':'nav-analysis',
                      'totalsdict':totalsdict,
                      'typedicts':listofdicts,
                      'totalscript':totalscript,
                      'totaldiv':totaldiv,
                      'form':form,
                      'startdate':activity_startdate,
                      'enddate':activity_enddate,
                      'lastseven':lastseven,
                      'lastfourteen':lastfourteen,
                      'last28':last28,
                      'lastyear':lastyear,
                      'today':today,
                      'workouttype':typeselect,
                      'firstmay':firstmay,
                      'sstartdate':sstartdate,
                      'senddate':senddate,
                  })

@login_required()
def history_view_data(request,userid=0):
    r = getrequestrower(request,userid=userid)

    usertimezone = pytz.timezone(r.defaulttimezone)
    time_min = datetime.time(hour=0,minute=0,second=0)
    time_max = datetime.time(hour=23,minute=59,second=59)

    if request.GET.get('startdate'):
        startdate = datetime.datetime.strptime(request.GET.get('startdate'),"%Y-%m-%d")
        enddate = datetime.datetime.strptime(request.GET.get('enddate'),"%Y-%m-%d")
        activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate,time_min))
        activity_enddate = usertimezone.localize(timezone.datetime.combine(enddate,time_max))
    else:
        activity_enddate = timezone.now()
        activity_enddate = usertimezone.localize(timezone.datetime.combine(activity_enddate.date(),time_max))
        startdate = timezone.now()-datetime.timedelta(days=14)
        activity_startdate = usertimezone.localize(timezone.datetime.combine(startdate.date(),time_min))


    typeselect = 'All'
    if request.GET.get('workouttype'):
        typeselect = request.GET.get('workouttype')

    if typeselect not in mytypes.checktypes:
        typeselect = 'All'


    g_workouts = Workout.objects.filter(
        user=r,
        startdatetime__gte=activity_startdate,
        startdatetime__lte=activity_enddate,
        duplicate=False,
        privacy='visible'
    ).order_by("-startdatetime")

    ids = [w.id for w in g_workouts]

    columns = ['hr','power','time']

    df = getsmallrowdata_db(columns,ids=ids)
    try:
        df['deltat'] = df['time'].diff()
    except KeyError:
        pass
    df = dataprep.clean_df_stats(df,workstrokesonly=True,
                                     ignoreadvanced=True,ignorehr=False)

    totalmeters,totalhours, totalminutes,totalseconds = get_totals(g_workouts)

    # meters, duration per workout type
    wtypes = list(set([w.workouttype for w in g_workouts]))

    typechoices = [("All","All")]
    for wtype in wtypes:
        if wtype in mytypes.checktypes:
            typechoices.append((wtype,mytypes.workouttypes_ordered[wtype]))

    listofdicts = []

    for wtype in wtypes:
        a_workouts = g_workouts.filter(workouttype=wtype)
        wmeters, whours, wminutes,wseconds = get_totals(a_workouts)
        ddict = {}
        try:
            ddict['wtype'] = mytypes.workouttypes_ordered[wtype]
        except KeyError:
            ddict['wtype'] = wtype

        ddict['id'] = wtype
        ddict['distance'] = wmeters
        ddict['duration'] = "{whours}:{wminutes:02d}:{wseconds:02d}".format(
            whours=whours,
            wminutes=wminutes,wseconds=wseconds,
        )
        ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts])
        try:
            ddf['deltat'] = ddf['time'].diff()
        except KeyError:
            pass
        ddf = dataprep.clean_df_stats(ddf,workstrokesonly=True,
                                         ignoreadvanced=True)

        ddict['hrmean'] = int(wavg(ddf,'hr','deltat'))
        ddict['hrmax'] = ddf['hr'].max().astype(int)
        ddict['powermean'] = int(wavg(df,'power','deltat'))
        ddict['powermax'] = ddf['power'].max().astype(int)
        ddict['nrworkouts'] = a_workouts.count()
        listofdicts.append(ddict)

    totalsdict = {}
    totalsdict['duration'] = "{totalhours}:{totalminutes}".format(
        totalhours=totalhours,
        totalminutes=totalminutes
    )

    totalsdict['distance'] = totalmeters
    try:
        totalsdict['powermean'] = int(wavg(df,'power','deltat'))
        totalsdict['powermax'] = df['power'].max().astype(int)
    except KeyError:
        totalsdict['powermean'] = 0
        totalsdict['powermax'] = 0
    try:
        totalsdict['hrmean'] = int(wavg(df,'hr','deltat'))
        totalsdict['hrmax'] = df['hr'].max().astype(int)
    except KeyError:
        totalsdict['hrmean'] = 0
        totalsdict['hrmax'] = 0

    totalsdict['nrworkouts'] =  g_workouts.count()

    # activity chart
    activity_script, activity_div = interactive_activitychart2(g_workouts,startdate,enddate)

    # interactive hr pie chart
    if typeselect == 'All':
        totalseconds = 3600*totalhours+60*totalminutes+totalseconds
        totalscript,totaldiv = interactive_hr_piechart(df,r,'All Workouts',
                                                       totalseconds=totalseconds)
    else:
        a_workouts = g_workouts.filter(workouttype=typeselect)
        meters, hours,minutes,seconds = get_totals(a_workouts)
        totalseconds = 3600*hours+60*minutes+seconds
        ddf = getsmallrowdata_db(columns,ids=[w.id for w in a_workouts])
        try:
            ddf['deltat'] = ddf['time'].diff()
        except KeyError:
            pass
        ddf = dataprep.clean_df_stats(ddf,workstrokesonly=True,
                                         ignoreadvanced=True)
        totalscript, totaldiv = interactive_hr_piechart(
            ddf,r,mytypes.workouttypes_ordered[typeselect],
            totalseconds=totalseconds)

    return JSONResponse({
        'script':totalscript,
        'div':totaldiv,
        'totalsdict':totalsdict,
        'listofdicts':listofdicts,
        'activities_script':activity_script,
        'activities_chart':activity_div,
    })
