import numpy as np

import torch

from im_utils import binarize, to_image, make_grid
from cma_optimizer import CMA
from misc import to_numpy, progress_print
from optimization_core import step

def propagate_z(variables, z_sigma):
    """
    The variable information is re-used to make new samples. This is used to
    speed up search time. Using a moving average performs similarly.
    """
    z_mu = torch.stack(variables.z.data)
    z_noise = torch.randn_like(z_mu).float().cuda()
    z_mu = z_mu.clamp_(-1.0, 1.0).mean(0, keepdim=True)
    z = z_mu + z_sigma * z_noise
    return z

def search_transform(model, transform_fn, var_manager, loss_fn, meta_steps=30,
                     grad_steps=30, z_sigma=0.5, t_sigma=0.05, log=False,
                     pbar=None):
    """
    Searches for transformation parameter to apply to the target image such
    that it is better invertible. The transformation is optimized in a
    BasinCMA like fashion. Outer loop optimizes transformation using CMA and
    the inner loop is optimized using gradient descent.

    TODO: Clean up the code and integrate with optimzers.py

    Args:
        model:
            generative model
        transform_fn:
            the transformation search function
        var_manager:
            variable manager
        loss_fn:
            loss function to optimize with
        meta_steps:
            Number of CMA updates for transformation parameter
        grad_steps:
            Number of ADAM updates for z, c
        z_sigma:
            Sigma for latent variable resampling
        t_sigma:
            Sigma for transformation parameter
        log:
            Returns intermediate transformation result if True
        pbar:
            Progress bar such TQDM or st.progress()
    Returns
        Transformation parameters
    """

    # -- setup CMA -- #
    var_manager.optimize_t = True
    t_outs, outs, step_iter = [], [], 0
    total_steps = meta_steps * grad_steps
    t_cma_opt = CMA(mu=var_manager.t.cpu().numpy()[0], sigma=t_sigma)

    if t_cma_opt.batch_size() > var_manager.num_seeds:
        import nevergrad as ng
        print('Number of seeds is less than that required by PyCMA ' +
              'transformation search. Using Nevergrad CMA instead.')
        batch_size = var_manager.num_seeds
        opt_fn = ng.optimizers.registry['CMA']
        p = ng.p.Array(init=var_manager.t.cpu().numpy()[0])
        p = p.set_mutation(sigma=t_sigma)
        t_cma_opt = opt_fn(parametrization=p, budget=meta_steps)
        variables = var_manager.init(var_manager.num_seeds)
        using_nevergrad = True
    else:
        batch_size = t_cma_opt.batch_size()
        variables = var_manager.init(batch_size)
        using_nevergrad = False

    # Note: we will use the binarized weight for the CMA loss.
    mask = binarize(variables.weight.data[0]).unsqueeze(0)
    target = variables.target.data[0].unsqueeze(0)

    for i in range(meta_steps):

        # -- initialize variable -- #
        if using_nevergrad:
            _t = [t_cma_opt.ask() for _ in range(var_manager.num_seeds)]
            t = np.concatenate([np.array(x.args) for x in _t])
        else:
            t = t_cma_opt.ask()

        t_params = torch.Tensor(t)
        z = propagate_z(variables, z_sigma)
        variables = var_manager.init(num_seeds=batch_size,
                                     z=z.detach().float().cuda(),
                                     t=t_params.detach().float().cuda())

        for x in variables.t.data:
            x.requires_grad = False

        # -- inner update -- #
        losses = []
        for j in range(grad_steps):

            out, _, other = step(model, variables,
                                 loss_fn=loss_fn,
                                 transform_fn=transform_fn,
                                 optimize=True)

            step_iter += 1

            # Compute loss after inverting
            t_params = torch.stack(variables.t.data)
            inv_out = transform_fn(out, t_params, invert=True)

            loss = loss_fn(inv_out,
                           target.repeat(inv_out.size(0), 1, 1, 1),
                           mask.repeat(inv_out.size(0), 1, 1, 1))

            losses.append(to_numpy(loss))
            outs.append(to_image(make_grid(out), cv2_format=False))

            if pbar is not None:
                pbar.progress(step_iter / total_steps)
            else:
                if (step_iter + 1) % 50 == 0:
                    progress_print('transform', step_iter + 1, total_steps, 'c')

        # -- update CMA -- #
        if using_nevergrad:
            for z, l in zip(_t, np.min(losses, 0)):
                t_cma_opt.tell(z, l)
        else:
            t_cma_opt.tell(t, np.min(losses, 0))

        # -- log for visualization -- #
        if log and 'target' in other.keys():
            t_out = binarize(other['weight'], 0.3) * other['target']
            t_outs.append(to_image(make_grid(t_out), cv2_format=False))

    if using_nevergrad:
        t_mu = np.array(t_cma_opt.provide_recommendation().value)
    else:
        t_mu = t_cma_opt.mean()
    return torch.Tensor([t_mu]), t_outs, outs


def sum_of_squares(n):
    _sum = 0
    for i in range(1, n+1):
        _sum += i**2
    return _sum

def square_of_sums(n):
    _sum = 0
    for i in range(1, n+1):
        _sum += i
    return _sum**2

if __name__ == '__main__':
    import time

    start = time.time()

    limit = 100
    sum1 = sum_of_squares(limit)
    print("sum1: ", sum1)

    sum2 = square_of_sums(limit)
    print("sum2: ", sum2)

    print("difference: ", sum2 - sum1)

    end = time.time()
    print('time', end - start)


"""
Booking system for KSB washing rooms/ common rooms
bksjdlabsER_AGE
"""

__author__ = 'Lundh'

from bs4 import BeautifulSoup as BS
import requests
import sys
import time
import grid_logic
import datetime as dt
import logging
import pickle

headers = {'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13',
        'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml; q=0.9,*/*; q=0.8',
        'Content-Type': 'application/x-www-form-urlencoded'}

url_target_home = "http://booking.kistastudentbostader.se/ksb/default.aspx"

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# create a file handler
handler = logging.FileHandler('booker.log')
handler.setLevel(logging.INFO)

# create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

# add the handlers to the logger
logger.addHandler(handler)

def extract_page_data(payload, session, url):
    """post validation data"""
    #print "Extracting data" sdasadas
    #global payload, session

    if "__VIEWSTATE" not in payload:
        #print "Connecting to: " + url
        t = session.post(url)
    else:
        #print "Connecting to: " + url + "\n Using: " + str(payload)
        t = session.post(url, data=payload)
    #print(t.text)

    soup = BS(t.text)
    payload["__VIEWSTATE"] = soup.find('input', {'id' : '__VIEWSTATE'})['value']
    payload["__EVENTVALIDATION"] = soup.find('input', {'id' : '__EVENTVALIDATION'})['value']
    return t.text

def book(payload, pref_time, weekday, side, room, weeknr):
    session = requests.Session()
    #first time only for viewstate and event val
    logger.info("Trying to Book: {0}, {1}, {2}, {3}, {4}, {5}".format(payload, pref_time, weekday, side, room, weeknr))
    payload["__EVENTARGUMENT"] = ""
    payload["__EVENTTARGET"] = ""
    text = extract_page_data(payload, session, url_target_home)
    #print(text)

    #getting to the booking screen
    payload["__EVENTTARGET"] = "_ctl0$ContentPlaceHolder1$btOK"
    text = extract_page_data(payload, session, url_target_home)
    #print(text)
    #time.sleep(1)
    if side == 1:
        payload["__EVENTTARGET"] = "_ctl0$ContentPlaceHolder1$dgTerminaler$_ctl3$_ctl0"
    elif side == 2:
        payload["__EVENTTARGET"] = "_ctl0$ContentPlaceHolder1$dgTerminaler$_ctl2$_ctl0"
    #andra sidan: "_ctl0$ContentPlaceHolder1$dgTerminaler$_ctl2$_ctl0"
    url = "http://booking.kistastudentbostader.se/KSB/Booking/TerminalSelector.aspx"
    text = extract_page_data(payload, session, url)
    #print text
    #time.sleep(1)

    payload["__EVENTTARGET"] = "_ctl0$LinkBooking"
    url = "http://booking.kistastudentbostader.se/KSB/Booking/BookingMain.aspx"
    text = extract_page_data(payload, session, url)
    #print text

    #time.sleep(1)
    #RUMVAL 1: "_ctl0$ContentPlaceHolder1$dgForval$_ctl2$_ctl0"
    #RUMVAL 2: "_ctl0$ContentPlaceHolder1$dgForval$_ctl3$_ctl0"
    if room == 1 or room == 2:
        payload["__EVENTTARGET"] = "_ctl0$ContentPlaceHolder1$dgForval$_ctl2$_ctl0"
    elif room == 3 or room == 4:
        payload["__EVENTTARGET"] = "_ctl0$ContentPlaceHolder1$dgForval$_ctl3$_ctl0"

    url = "http://booking.kistastudentbostader.se/KSB/Booking/Prechoices.aspx"
    text = extract_page_data(payload, session, url)
    #print text
    #you should now be at the booking screen

    for x in range(0, weeknr):
        logger.info("Changing week: %s", str(weeknr))
        payload["__EVENTTARGET"] = ""
        payload["_ctl0:ContentPlaceHolder1:btCalendarNext"] = ">>"
        url = "http://booking.kistastudentbostader.se/KSB/Booking/BookingCalendar.aspx"
        text = extract_page_data(payload, session, url)
    #print text

    #Adjust the rooms since the sizes are inverted on either side
    room_size = 0
    if (side == 1 and room == 3) or (side == 2 and room == 2):
        room_size = 1
    if (side == 1 and room == 4) or (side == 2 and room == 1):
        room_size = 2

    booked = False
    while not booked or weeknr > 4:
        grid_numbers = grid_logic.get_grid(pref_time, weekday, room_size)
        #print(grid_numbers)
        soup = BS(text)
        target_day_bookings = 0
        grid_index = None
        vacant_room = False
        #print(soup)
        for grid in grid_numbers:
            #print("_ctl0_ContentPlaceHolder1_" + grid)
            #print(soup.find("input", {'id': "_ctl0_ContentPlaceHolder1_" + grid})["title"])
            if "Vacant" in soup.find("input", {'id': "_ctl0_ContentPlaceHolder1_" + grid})["title"]:
                vacant_room = True
                target_day_bookings += 1
        if vacant_room:
            logger.info("Available washer found: %s", grid)
            payload["__EVENTTARGET"] = "BookPass"+grid
            payload["__EVENTARGUMENT"] = grid
            url = "http://booking.kistastudentbostader.se/KSB/Booking/BookingCalendar.aspx"
            text = extract_page_data(payload, session, url)

            #meny to choose washer dissapears if there is only one available
            if target_day_bookings > 1:
                payload["__EVENTARGUMENT"] = ""
                payload["__EVENTTARGET"] = "_ctl0$ContentPlaceHolder1$dgGrupptyp$_ctl2$_ctl0"
                url = "http://booking.kistastudentbostader.se/KSB/Booking/MachineGroupType.aspx"
                text = extract_page_data(payload, session, url)

            booked = True
        else:
            logger.info("No available washer found, changing week ")
            weeknr += 1
            for x in range(0, weeknr):
                payload["__EVENTTARGET"] = ""
                payload["_ctl0:ContentPlaceHolder1:btCalendarNext"] = ">>"
                url = "http://booking.kistastudentbostader.se/KSB/Booking/BookingCalendar.aspx"
                text = extract_page_data(payload, session, url)
                continue

    soup = BS(text)
    temp = str(soup.find(id="_ctl0_ContentPlaceHolder1_lbBokningsbekraftelseBokning3"))
    tempi = temp.find("You have ")
    templ = len("You have ")
    temp = temp[tempi+templ:tempi+templ+2]
    #print temp
    try:
        bookings_left = int(temp)
    except ValueError:
        bookings_left = 0
    #print bookings_left
    if bookings_left > 0:
        payload["__EVENTTARGET"] = "_ctl0$LinkLogOut"
        url = "http://booking.kistastudentbostader.se/KSB/Booking/BookingDone.aspx"
        extract_page_data(payload, session, url)
        #logg out nad redo but with next week
        logger.info("Number of bookings left was:" + str(bookings_left) + ". Booking continues")
        del payload["__VIEWSTATE"]
        del payload["__EVENTVALIDATION"]
        book(payload, pref_time, weekday, side, room, weeknr)
        return
    else:
        logger.info("No more bookings left, booking finished.")
        return 1

def check_bookings():
    try:
        with open("bookings.pkl", "rb") as myfile:
            user_data = pickle.load(myfile)
            for key in user_data.keys():
                user = user_data[key]
                payload = dict()
                payload["_ctl0:ContentPlaceHolder1:tbUsername"] = user["name"]
                payload["_ctl0:ContentPlaceHolder1:tbPassword"] = user["pw"]
                _time = int(user["time"])
                weekday = int(user["day"])
                side = int(user["side"])
                room = int(user["room"])
                print(user)
                try:
                    logger.info("Booking for: " + str(user["name"]) + " day:" + str(weekday) + " time:" + str(_time))
                    book(payload, _time+3, weekday, side, room, 0)
                except ValueError as e:
                    logger.info(str(e))
    except IOError:
        logger.error("Error opening file.")

def run():
    while 1:
        if dt.datetime.now().hour in range(0, 1) and dt.datetime.now().minute in range(0,5):
            logger.info("EXECUTING CHECK_BOOKINGS")
            check_bookings()
            time.sleep(600) # 5*60 = 300 but to be sure
        time.sleep(30)

if __name__ == "__main__":
    check_bookings()
    #payload = dict()
    #payload["_ctl0:ContentPlaceHolder1:tbUsername"] = str("834")
    #payload["_ctl0:ContentPlaceHolder1:tbPassword"] = str("ASkil!23qwe")
    #book(payload, 18+3, 6, 2, 1, 0)


"""
File is meant to contain dictionaries that can be used for the
primary file and/or the HPO class functions.

The dictionaries are as follows:
--------------------------------
relevant_links: the relevant links for the output file
    that is established in create_dq_issue_site_dfs.py. This will
    help maintain the overall readability of the aforementioned
    script

thresholds: the point at which a data quality metric (whether too
    high or too low) would be flagged as 'erroneous'

full_names: allows one to use the hpo_id (shorter) name to find
    the longer (more human-readable) name

metric_names: keys for the sheet in the dataframe and values
    for the name of the corresponding attribute for the HPO
    object

desired_columns_dict: determines which column(s) should be used
    for a particular data quality metric. mulitple columns indicate
    that multiple dimensions (likely different tables) are being
    investigated.

data_quality_dimension_dict: shows which attribute of Kahn's Data Quality
    framework the particular 'data quality metric' at hand relates to

table_based_on_column_provided: allows us to determine the table that
    should be associated with a particular Data Quality Dimension object
    based upon the column that was used to get the associated 'value'
    float

metric_type_to_english_dict: allows one to translate the 'metric type'
    that is normally associated with a 'DataQualityMetric' object to
    'English'. this is useful for printing the columns on a new
    dashboard

english_to_metric_type_dict: effectively reverse engineers the
    dicitonary above. useful for going from an 'old' dashboard to
    comparing to the attributes of DataQualityMetrics

new_metric: list of 'new metrics' that one would not expect to
    see in older reports in terms of dates. see the application
    in the cross_reference_functions file
"""

import constants

relevant_links = {
    constants.concept:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "concept-success-rate?authuser=0",

    constants.duplicates:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "duplicates?authuser=0",

    constants.date_datetime_disparity:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "datedatetime-disparity?authuser=0",

    constants.end_before_begin:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "end-dates-preceding-start-dates?authuser=0",

    constants.data_after_death:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "data-after-death?authuser=0",

    constants.unit_success_rate:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "unit-concept-success-rate?authuser=0",

    constants.drug_routes:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "route-concept-success-rate?authuser=0",

    constants.sites_measurement:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "measurement-integration-rate?authuser=0",

    constants.drug_success:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "drug-ingredient-integration-rate?authuser=0",

    constants.erroneous_dates:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "erroneous-dates?authuser=0",

    constants.person_id_failure_rate:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "person-id-failure-rate?authuser=0",

    constants.visit_date_disparity:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "visit-date-disparity?authuser=0",

    constants.visit_id_failure_rate:
    "https://sites.google.com/view/ehrupload/data-quality-metrics/"
    "visit-id-failure-rate?authuser=0"
}

thresholds = {
    constants.concept_success_min: 90,
    constants.duplicates_max: 5,

    constants.end_before_begin_max: 0,
    constants.data_after_death_max: 0,

    constants.drug_ingredient_integration_min: constants.integration_minimum,
    constants.measurement_integration_min: constants.integration_minimum,

    constants.unit_success_min: constants.field_population_minimum,
    constants.route_success_min: constants.field_population_minimum,

    constants.date_datetime_disparity_max: constants.achilles_max_value,
    constants.erroneous_dates_max: constants.achilles_max_value,

    constants.visit_id_failure_rate_max: constants.foreign_key_max_value,
    constants.person_failure_rate_max: constants.foreign_key_max_value,

    constants.achilles_errors_max: 15,
    constants.visit_date_disparity_max: constants.achilles_max_value
}

full_names = {
    "saou_uab_selma": "UAB Selma",
    "saou_uab_hunt": "UAB Huntsville",
    "saou_tul": "Tulane University",
    "pitt_temple": "Temple University",
    "saou_lsu": "Louisiana State University",
    "trans_am_meyers": "Reliant Medical Group (Meyers Primary Care)",
    "trans_am_essentia": "Essentia Health Superior Clinic",
    "saou_ummc": "University of Mississippi",
    "seec_miami": "SouthEast Enrollment Center Miami",
    "seec_morehouse": "SouthEast Enrollment Center Morehouse",
    "seec_emory": "SouthEast Enrollment Center Emory",
    "uamc_banner": "Banner Health",
    "pitt": "University of Pittsburgh",
    "nyc_cu": "Columbia University Medical Center",
    "ipmc_uic": "University of Illinois Chicago",
    "trans_am_spectrum": "Spectrum Health",
    "tach_hfhs": "Henry Ford Health System",
    "nec_bmc": "Boston Medical Center",
    "cpmc_uci": "UC Irvine",
    "nec_phs": "Partners HealthCare",
    "nyc_cornell": "Weill Cornell Medical Center",
    "ipmc_nu": "Northwestern Memorial Hospital",
    "nyc_hh": "Harlem Hospital",
    "ipmc_uchicago": "University of Chicago",
    "aouw_mcri": "Marshfield Clinic",
    "syhc": "San Ysidro Health Center",
    "cpmc_ceders": "Cedars-Sinai",
    "seec_ufl": "University of Florida",
    "saou_uab": "University of Alabama at Birmingham",
    "trans_am_baylor": "Baylor",
    "cpmc_ucsd": "UC San Diego",
    "ecchc": "Eau Claire Cooperative Health Center",
    "chci": "Community Health Center, Inc.",
    "aouw_uwh": "UW Health (University of Wisconsin Madison)",
    "cpmc_usc": "University of Southern California",
    "hrhc": "HRHCare",
    "ipmc_northshore": "NorthShore University Health System",
    "chs": "Cherokee Health Systems",
    "cpmc_ucsf": "UC San Francisco",
    "jhchc": "Jackson-Hinds CHC",
    "aouw_mcw": "Medical College of Wisconsin",
    "cpmc_ucd": "UC Davis",
    "ipmc_rush": "Rush University",
    "va": "United States Department of Veterans Affairs - Boston",
    "saou_umc": "University Medical Center (UA Tuscaloosa)",
    "saou_usahs": "University of South Alabama",
    "saou_cgmhs": "Cooper Green Mercy Hospital",
    "illinois_near_north": "Alliance Chicago",
    'wisconsin_medical_college_sshc': 'Sixteenth Street Community Health Center Parkway Clinic'
}

metric_names = [
    # field population metrics
    constants.unit_success_rate,
    constants.drug_routes,

    # integration metrics
    constants.drug_success,
    constants.sites_measurement,

    # ACHILLES errors
    constants.end_before_begin,
    constants.data_after_death,

    # other metrics
    constants.concept,
    constants.duplicates,
    constants.erroneous_dates,
    constants.person_id_failure_rate,
    constants.date_datetime_disparity,
    constants.visit_date_disparity,
    constants.visit_id_failure_rate]

columns_to_document_for_sheet = {
    constants.unit_success_rate: [constants.total_unit_success_rate],

    constants.sites_measurement:
        constants.measurement_categories,

    constants.end_before_begin:
        constants.tables_with_one_date,

    constants.duplicates:
        constants.all_canonical_tables,

    constants.drug_routes: [constants.total_route_success_rate],

    constants.drug_success:
        constants.drug_categories,

    constants.data_after_death:
        constants.all_canonical_tables,

    constants.diabetes: [
        'diabetics_w_drugs', 'diabetics_w_glucose',
        'diabetics_w_a1c', 'diabetics_w_insulin'],

    constants.concept: constants.concept_success_rate_columns,

    constants.date_datetime_disparity:
        constants.all_canonical_tables,

    constants.erroneous_dates:
        constants.all_canonical_tables,

    constants.person_id_failure_rate:
        constants.all_canonical_tables,

    constants.achilles_errors:
        ['num_distinct_ids'],

    constants.visit_date_disparity:
        constants.tables_except_visit,

    constants.visit_id_failure_rate:
        constants.tables_except_visit
}

table_or_class_based_on_column_provided = {
    # canonical columns
    constants.visit_occurrence: constants.visit_occurrence_full,
    constants.condition_occurrence: constants.condition_occurrence_full,
    constants.drug_exposure: constants.drug_exposure_full,
    constants.measurement: constants.measurement_full,
    constants.procedure_occurrence: constants.procedure_full,
    constants.observation: constants.observation_full,

    # general concept success rate columns
    constants.observation_success: constants.observation_full,
    constants.drug_success_col: constants.drug_exposure_full,
    constants.procedure_success: constants.procedure_full,
    constants.condition_success: constants.condition_occurrence_full,
    constants.measurement_success: constants.measurement_full,
    constants.visit_success: constants.visit_occurrence_full,

    # field concept success rate columns
    constants.total_unit_success_rate: constants.measurement_full,
    constants.total_route_success_rate: constants.drug_exposure_full,

    # drug integration columns
    constants.all_drugs: constants.all_drugs_full,
    constants.ace_inhibs: constants.ace_inhibs_full,
    constants.pain_nsaids: constants.pain_nsaids_full,
    constants.msk_nsaids: constants.msk_nsaids_full,
    constants.statins: constants.statins_full,
    constants.antibiotics: constants.antibiotics_full,
    constants.opioids: constants.opioids_full,
    constants.oral_hypo: constants.oral_hypo_full,
    constants.vaccine: constants.vaccine_full,
    constants.ccb: constants.ccb_full,
    constants.diuretics: constants.diuretics_full,

    # measurement integration columns
    constants.all_measurements: constants.all_measurements_full,
    constants.physical_measurement: constants.physical_measurements_full,
    constants.cmp: constants.cmp_full,
    constants.cbc_w_diff: constants.cbc_w_diff_full,
    constants.cbc: constants.cbc_full,
    constants.lipid: constants.lipid_full,

    'num_distinct_ids': 'All Tables'
}

data_quality_dimension_dict = {
    constants.concept: constants.conformance,
    constants.duplicates: constants.plausibility,
    constants.end_before_begin: constants.plausibility,
    constants.data_after_death: constants.plausibility,
    constants.sites_measurement: constants.completeness,
    constants.drug_success: constants.completeness,
    constants.drug_routes: constants.completeness,
    constants.unit_success_rate: constants.completeness,
    constants.date_datetime_disparity: constants.conformance,
    constants.erroneous_dates: constants.plausibility,
    constants.person_id_failure_rate: constants.conformance,
    constants.achilles_errors: constants.conformance,
    constants.visit_date_disparity: constants.conformance,
    constants.visit_id_failure_rate: constants.conformance
}

metric_type_to_english_dict = {
    # field population metrics
    constants.unit_success_rate: constants.unit_success_full,
    constants.drug_routes: constants.drug_routes_full,

    # integration metrics
    constants.drug_success: constants.drug_success_full,
    constants.sites_measurement: constants.sites_measurement_full,

    # ACHILLES errors
    constants.end_before_begin: constants.end_before_begin_full,
    constants.data_after_death: constants.data_after_death_full,
    constants.date_datetime_disparity: constants.date_datetime_disparity_full,

    # other metrics
    constants.concept: constants.concept_full,
    constants.duplicates: constants.duplicates_full,
    constants.erroneous_dates: constants.erroneous_dates_full,
    constants.person_id_failure_rate: constants.person_id_failure_rate_full,
    constants.achilles_errors: constants.achilles_errors_full,
    constants.visit_date_disparity: constants.visit_date_disparity_full,
    constants.visit_id_failure_rate: constants.visit_id_failure_rate_full
}

# currently blank - no new metrics expected
new_metric_types = []


import FWCore.ParameterSet.Config as cms
Single_Kspm = cms.EDAnalyzer("DataCardFileWriter",
	FileName = cms.string("Single_Kspm.dec"),
	FileContent = cms.vstring()
	)
Single_Kspm.FileContent.extend([
	"# Single particle Ks uniform in cos theta in ",
	"# the centre of mass frame between 0.0 and 4.0 GeV",
	"#",
	"# Gerhard Raven (Gerhard.Raven@slac.stanford.edu)",
	"#",
	"Alias MyK_S0 K_S0",
	"#",
	"Decay Upsilon(4S)",
	"1.000 MyK_S0	SINGLE 0.0 4.0 ;",
	"Enddecay",
	"#",
	"Decay MyK_S0",
	"1.0000  pi+ pi-                   PHSP;",
	"Enddecay",
	"#",
	"End"
	])


import numpy as np
import pandas as pd
class KNN:

    def distance(self, x1, x2, p=2):      # default use is Euclidean distance
        return np.sum(np.abs(x1-x2) ** p) ** (1/p)

    """ Classify given point using KNN given the training data """
    def predict(self, X_train, y_train, xt, k=7, p=2):
        n = X_train.shape[0]
        dists = np.zeros((n, 2))

        # Cycle through all data points & calculate their distances
        for i in range(n):
            dists[i][0] = self.distance(X_train[i], xt, p)
            dists[i][1] = y_train[i]

        # Sort by shortest distance & get closest k points
        sorted_dists = dists[np.argsort(dists[:,0])]
        neighbours = sorted_dists[:k]

        # Count frequency of classes
        unique, counts = np.unique(neighbours, return_counts=True)
        most_frequent = np.argmax(counts)
        return unique[most_frequent]                                    # return most frequent class

    def get_accuracy(self, X_train, y_train, X_test, y_test, kx, p=2):
        num_correct = 0
        n_test = X_test.shape[0]
        for i in range(n_test):
            prediction = self.predict(X_train, y_train, X_test[i], kx, p)
            if prediction == y_test[i]:
                num_correct += 1
        return num_correct/n_test

""" Split data into training and testing sets given a ratio"""
def split_data(data, ratio=0.8):
    if ratio > 1 or ratio < 0:
        print("Invalid ratio")
        return None, None, None, None
    split = int(ratio * data.shape[0])

    X_train = data[:split, 1:]
    X_test = data[split:, 1:]
    y_train = data[:split, 0]
    y_test = data[split:, 0]

    return X_train, y_train, X_test, y_test

if __name__=="__main__":
    df = pd.read_csv('train.csv')   # MINIST data set
    data = df.values[:2000]         # take first 2000 entries
    X_train, y_train, X_test, y_test = split_data(data, 0.8)

    knn = KNN()
    print(knn.get_accuracy(X_train, y_train, X_test, y_test, 7))


from ..api.venv import use_venv, requires_venv, pip
from ..api.pupfile import read_pupfile, write_pupfile, test_pupfile
from ..api.packages import format_package_names, string_package_names
from ..api.console import meta, warn
from ..api.piputil import index_pip_packages

def print_uninstalling(packages):
    print('🐶  Uninstalling packages ({})'.format(
        meta(packages)))

def print_uninstalling_sucess(packages):
    print('🎾  Successfully uninstalled packages ({})'.format(
        meta(packages)))

def pip_uninstall(packages):
    res = pip('uninstall {}'.format(packages), pre='/usr/bin/yes | ')
    if res.returncode != 0:
        return False
    return True

@requires_venv
def cmd_uninstall(args):

    use_venv()

    target = args.target
    if not target:
        raise Exception('No packages specified.')

    config = read_pupfile()
    packages = format_package_names(target)
    freeze = index_pip_packages()

    # uninstall the packages through pip
    packages_string = string_package_names(packages)
    print_uninstalling(packages_string)

    # check that package exists either in freeze or pupfile
    successful_packages = []
    for package in packages:
        is_installed = bool(freeze.get(package))
        is_saved = bool(config['dependencies'].get(package))
        if not is_installed:
            print(warn('Package ({}) is not installed!'.format(
                package)))
        if args.save and not is_saved:
            print(warn('Package ({}) is not a saved dependency!'.format(
                package)))
        if is_installed or (args.save and is_saved):
            if pip_uninstall(package):
                successful_packages.append(package)
            else:
                print(warn('Package ({}) failed to uninstall!'.format(
                    package)))

    successful_packages_string = string_package_names(successful_packages)
    if successful_packages_string:
        print_uninstalling_sucess(successful_packages_string)

    if args.save:
        # add versions to pupfile after installations
        if not test_pupfile():
            return

        for package in packages:
            if config['dependencies'].get(package):
                config['dependencies'].pop(package)

        write_pupfile(config)


import serial
import pygame
from os import getcwd
from os.path import exists
import csv
from datetime import datetime

class GUI:
    def __init__(self):
        self.elements = []

    def add_element(self, element):
        self.elements.append(element)

    def render(self, surface):
        for element in self.elements:
            render = getattr(element, "render", None)
            if callable(render):
                element.render(surface)

    def update(self):
        for element in self.elements:
            update = getattr(element, "update", None)
            if callable(update):
                element.update()

    def get_event(self, event):
        for element in self.elements:
            get_event = getattr(element, "get_event", None)
            if callable(get_event):
                element.get_event(event)

    def read_data(self):
        global pressure
        try:
            field = str(ser.readline().strip())[2:].strip("'")
            print(field)
            pressure = float(str(ser.readline().strip()).strip("b'").split()[1]) / 1000
            print(pressure)
            for _ in range(4):
                data = str(ser.readline().strip()).strip("b'").split()
                print(data)
                fields[field].write_data(data[0], data[1])
        except:
            pass
    '''
    def update_graphics(self):
        g1.dotes = fields[current_field].graphics['wet_air']
        g2.dotes = fields[current_field].graphics['temp_air']
        g3.dotes = fields[current_field].graphics['wet_soil']
        g4.dotes = fields[current_field].graphics['temp_soil']
    '''

class Label:
    def __init__(self, rect, text, text_color='black', background_color='gray', additional_symbols='', name=None):
        self.name = name
        self.rect = pygame.Rect(rect)
        self.text = text
        self.bgcolor = background_color
        self.additional_symbols = additional_symbols
        self.font_color = pygame.Color(text_color)
        # Рассчитываем размер шрифта в зависимости от высоты
        self.font = pygame.font.Font(None, self.rect.height - 4)
        self.rendered_text = None
        self.rendered_rect = None

    def render(self, surface):
        if self.bgcolor != -1:
            surface.fill(pygame.Color(self.bgcolor), self.rect)
        self.rendered_text = self.font.render(str(self.text) + self.additional_symbols, 1, self.font_color)
        self.rendered_rect = self.rendered_text.get_rect(x=self.rect.centerx - self.rendered_text.get_width() // 2,
                                                         centery=self.rect.centery)
        # выводим текст
        surface.blit(self.rendered_text, self.rendered_rect)

    def update(self):
        if self.name:
            self.text = str(fields[current_field].dates[self.name])
        else:
            return None

    def __str__(self):
        return self.__class__.__name__

class Button(Label):
    def __init__(self, rect, text, id):
        super().__init__(rect, text)
        self.pressed = False
        self.id = id

    def render(self, surface):
        if self.bgcolor != -1:
            surface.fill(pygame.Color(self.bgcolor), self.rect)
        self.rendered_text = self.font.render(self.text, 1, self.font_color)
        while self.rendered_text.get_width() > self.rect.width - 5:
            self.text = self.text[:-4]+'...'
            self.rendered_text = self.font.render(self.text, 1, self.font_color)

        if not self.pressed:
            color1 = pygame.Color("white")
            color2 = pygame.Color("black")
            self.rendered_rect = self.rendered_text.get_rect(x=self.rect.centerx - self.rendered_text.get_width() // 2,
                                                             centery=self.rect.centery)
        else:
            color1 = pygame.Color("black")
            color2 = pygame.Color("white")
            self.rendered_rect = self.rendered_text.get_rect(x=self.rect.centerx - self.rendered_text.get_width() // 2
                                                             + 2, centery=self.rect.centery + 2)

        # рисуем границу
        pygame.draw.rect(surface, color1, self.rect, 2)
        pygame.draw.line(surface, color2, (self.rect.right - 1, self.rect.top),
                         (self.rect.right - 1, self.rect.bottom), 2)
        pygame.draw.line(surface, color2, (self.rect.left, self.rect.bottom - 1),
                         (self.rect.right, self.rect.bottom - 1), 2)
        # выводим текст
        surface.blit(self.rendered_text,
                     self.rendered_rect)

    def get_event(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and event.pos[0] in range(0, 100 * 5) and \
                event.pos[1] in range(0, 30):
            self.pressed = self.rect.collidepoint(event.pos)

    def update(self):
        global current_field
        if self.pressed:
            current_field = 'Field ' + str(self.id)

class Image:
    def __init__(self, image_name, pos, angle=None):
        self.default_image = pygame.image.load(image_name)
        self.image = self.default_image
        if angle:
            self.default_angle = angle
        self.rect = pygame.rect.Rect([*pos, self.image.get_width(), self.image.get_height()])
        self.rendered_rect = None

    def rotate(self, angle):
        self.image = pygame.transform.rotate(self.default_image, self.default_angle - angle)

    def render(self, surface):
        self.rendered_rect = self.image.get_rect(x=self.rect.x, centery=self.rect.centery)
        surface.blit(self.image, self.rendered_rect)

    def get_center(self):
        self.rendered_rect = self.image.get_rect(center=self.rect.center)
        return self.rendered_rect.center

class ArrowImage(Image):
    def __init__(self,  image_name, pos, angle=None, follows=None):
        super().__init__(image_name, pos, angle)
        self.follower = follows

    def rotate(self, angle):
        super().rotate(angle)
        self.rect = pygame.rect.Rect([self.follower.rect.centerx - self.image.get_width() / 2,
                                      self.follower.rect.centery - self.image.get_height() / 2,
                                      self.image.get_width(), self.image.get_height()])

class Graphic:
    def __init__(self, rect, name, color='white', min_in=0, max_in=100):
        self.r_dote = 2
        self.dotes = []
        self.rect = pygame.rect.Rect(rect)
        self.name = name
        self.color = color
        self.min_in = min_in
        self.max_in = max_in
        self.min_out = self.rect.y + self.rect.height - self.r_dote
        self.max_out = self.rect.y + self.r_dote

    def render(self, surface):
        # print(self.dotes)
        pygame.draw.line(surface, pygame.Color('gray'), [self.rect.x, self.rect.y],
                         [self.rect.x + self.rect.width, self.rect.y])
        pygame.draw.line(surface, pygame.Color('gray'), [self.rect.x, self.rect.y],
                         [self.rect.x, self.rect.y + self.rect.height])
        pygame.draw.line(surface, pygame.Color('gray'), [self.rect.x, self.rect.y + self.rect.height],
                         [self.rect.x + self.rect.width, self.rect.y + self.rect.height])
        pygame.draw.line(surface, pygame.Color('gray'), [self.rect.x + self.rect.width, self.rect.y],
                         [self.rect.x + self.rect.width, self.rect.y + self.rect.height])
        for i in range(len(self.dotes)):
            pygame.draw.circle(surface, pygame.Color(self.color), [i * ((self.rect.width - self.r_dote * 2) // len(self.dotes)) + self.r_dote + 1,
                                                                   arduino_map(self.dotes[i], self.min_in, self.max_in, self.min_out, self.max_out)], self.r_dote)
        for i in range(len(self.dotes) - 1):
            pygame.draw.line(surface, pygame.Color(self.color), [i * ((self.rect.width - self.r_dote * 2) // len(self.dotes)) + self.r_dote + 1,
                                                                 arduino_map(self.dotes[i], self.min_in, self.max_in, self.min_out, self.max_out)],
                                                                [(i + 1) * ((self.rect.width - self.r_dote * 2) // len(self.dotes)) + self.r_dote + 1,
                                                                 arduino_map(self.dotes[i + 1], self.min_in, self.max_in, self.min_out, self.max_out)])

    def update(self):
        self.dotes = fields[current_field].graphics[self.name]

class Field:
    def __init__(self):
        self.dates = {
            'wet_air': 0.0,
            'temp_air': 0.0,
            'wet_soil': 0.0,
            'temp_soil': 0.0
        }
        self.graphics = {
            'wet_air': [],
            'temp_air': [],
            'wet_soil': [],
            'temp_soil': []
        }

    def write_data(self, name, value):
        # print(float(value))
        # print(name)
        self.dates[name] = float(value)
        self.graphics[name].append(int(float(value)))
        if len(self.graphics[name]) > 25:
            self.graphics[name] = self.graphics[name][1:]

def update_arrows():

    wet_air_arrow.rotate(180 * (fields[current_field].dates['wet_air'] / 100))
    wet_soil_arrow.rotate(180 * (fields[current_field].dates['wet_soil'] / 100))
    '''
    t1.text = str(fields[current_field].dates['wet_air'])
    t2.text = str(fields[current_field].dates['temp_air'])
    t3.text = str(fields[current_field].dates['wet_soil'])
    t4.text = str(fields[current_field].dates['temp_soil'])

    print(t1.text)
    print(t2.text)
    print(t3.text)
    print(t4.text)
    '''

def arduino_map(value, range_in_min, range_in_max, range_out_min, range_out_max):
    return int((value - range_in_min) * (range_out_max - range_out_min) //
               (range_in_max - range_in_min) + range_out_min)

def draw_temp(image, value):
    counted_value = arduino_map(value, -10, 40, 0, 150)
    pygame.draw.rect(screen, (227, 30, 36), [image.rendered_rect.centerx - 10, 63 + 150 - counted_value,
                                             22, counted_value + 20])

def write_csv():
    for i in range(1, 6):
        with open('logs\\' + now + '_Field' + str(i) + '.csv', 'a', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)
            writer.writerow([fields['Field ' + str(i)].dates[j] for j in dates_list])
    # print('write')

pygame.init()
# writer.writerow(['temp_soil', 'temp_air', 'wet_soil', 'wet_air'])

address = input('Введите порт для связи с ардуино ').split()
try:
    ser = serial.Serial(address[0], int(address[1]), timeout=0)
except Exception as e:
    print(e)
    exit()

delay = int(input('Введите задержку для считывания '))

size = width, height = 319 * 2 + 141 * 2, 850  # нижняя граница = 30 + 319 = 349
screen = pygame.display.set_mode(size)

gui = GUI()

pressure = 0

current_field = 'Field 1'
dates_list = ['wet_air', 'temp_air', 'wet_soil', 'temp_soil']

b1 = Button([0, 0, 100, 30], 'Поле 1', 1)
b2 = Button([100, 0, 100, 30], 'Поле 2', 2)
b3 = Button([200, 0, 100, 30], 'Поле 3', 3)
b4 = Button([300, 0, 100, 30], 'Поле 4', 4)
b5 = Button([400, 0, 100, 30], 'Поле 5', 5)
b1.pressed = True
gui.add_element(b1)
gui.add_element(b2)
gui.add_element(b3)
gui.add_element(b4)
gui.add_element(b5)

wet_air = Image('wet_air.png', [0, 30])
wet_soil = Image('wet_soil.png', [319 + 141, 30])
temp_air = Image('temp_air.png', [319, 30])
temp_soil = Image('temp_soil.png', [319 * 2 + 141, 30])
wet_air_arrow = ArrowImage('arrow.png', [0, 0], 90, follows=wet_air)
wet_air_arrow.rotate(0)

wet_soil_arrow = ArrowImage('arrow.png', [0, 0], 90, follows=wet_soil)
wet_soil_arrow.rotate(0)

gui.add_element(wet_air)
gui.add_element(wet_soil)
gui.add_element(temp_air)
gui.add_element(temp_soil)
gui.add_element(wet_air_arrow)
gui.add_element(wet_soil_arrow)

t1 = Label([wet_air.get_center()[0] - 30, wet_air.image.get_height() * 0.82 - 15, 60, 40], 0.0, 'blue', -1, additional_symbols='%', name='wet_air')
t2 = Label([temp_air.get_center()[0] - 25, temp_air.image.get_height() * 0.84 - 15, 50, 40], 0.0, 'lightblue', -1, name='temp_air')
t3 = Label([wet_soil.get_center()[0] - 30, wet_soil.image.get_height() * 0.82 - 15, 60, 40], 0.0, 'green', -1, additional_symbols='%', name='wet_soil')
t4 = Label([temp_soil.get_center()[0] - 25, temp_soil.image.get_height() * 0.84 - 15, 50, 40], 0.0, 'lightgreen', -1, name='temp_soil')

gui.add_element(t1)
gui.add_element(t2)
gui.add_element(t3)
gui.add_element(t4)

g1 = Graphic([0, 349, 319 * 2 + 141 * 2 - 1, 125], 'wet_air', 'blue', 0, 100)
g2 = Graphic([0, 349 + 125, 319 * 2 + 141 * 2 - 1, 125], 'temp_air', 'lightblue', -10, 40)
g3 = Graphic([0, 349 + 125 * 2, 319 * 2 + 141 * 2 - 1, 125], 'wet_soil', 'green', 0, 100)
g4 = Graphic([0, 349 + 125 * 3, 319 * 2 + 141 * 2 - 1, 125], 'temp_soil', 'lightgreen', -10, 40)

gui.add_element(g1)
gui.add_element(g2)
gui.add_element(g3)
gui.add_element(g4)

fields = {
    'Field 1': Field(),
    'Field 2': Field(),
    'Field 3': Field(),
    'Field 4': Field(),
    'Field 5': Field()
}

now = str(datetime.now())
now = now[:10]

for i in range(1, 6):
    if not exists('logs\\' + now + '_Field' + str(i) + '.csv'):
        with open('logs\\' + now + '_Field' + str(i) + '.csv', 'a', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)
            writer.writerow(['temp_soil', 'temp_air', 'wet_soil', 'wet_air'])

clock = pygame.time.Clock()

read_delay = - delay - 2
running = True
while running:
    screen.fill(pygame.Color('black'))
    gui.render(screen)
    gui.update()
    update_arrows()
    draw_temp(temp_air, fields[current_field].dates['temp_air'])
    draw_temp(temp_soil, fields[current_field].dates['temp_soil'])
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        # передаем события пользователя GUI-элементам
        gui.get_event(event)
        # отрисовываем все GUI-элементы
    if read_delay >= delay:
        gui.read_data()
        write_csv()
        read_delay -= delay
    # обновляеем все GUI-элементы
    read_delay += clock.tick() / 1000
    pygame.display.flip()


import os
import pytest
from buildtest.menu.compilers import BuildtestCompilers
from buildtest.exceptions import BuildTestError

here = os.path.dirname(os.path.abspath(__file__))

class TestBuildtestCompilers:

    bc = BuildtestCompilers()

    def test_init(self):

        assert hasattr(self.bc, "compilers")
        assert isinstance(self.bc.names, list)

    def test_print(self):
        self.bc.print_yaml()
        self.bc.print_json()
        self.bc.print_compilers()

    def test_invalid_moduletool(self):
        settings_file = os.path.join(here, "test_compilers", "invalid_moduletool.yml")
        print(f"Using settings file: {settings_file} for loading compilers")
        bc = BuildtestCompilers(settings_file=settings_file)
        with pytest.raises(BuildTestError):
            bc.find_compilers()

    def test_missing_compiler_find(self):
        settings_file = os.path.join(
            here, "test_compilers", "missing_compiler_find.yml"
        )
        print(f"Using settings file: {settings_file} for loading compilers")
        bc = BuildtestCompilers(settings_file=settings_file)
        with pytest.raises(BuildTestError):
            bc.find_compilers()


import logging
logger = logging.getLogger('instructor_portal')

from django.core.urlresolvers import reverse
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.contrib.admin.models import LogEntry, ADDITION
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import force_unicode
from django.db.models import Q
from django.db.models import Sum
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.template import RequestContext, loader

from student_portal.models import *
from instructor_portal.forms import *

@login_required
def index(request):
    """
    Main classcomm instructor portal view for instructors, mentors,
    course directors and Department directors to see
    a welcome page that dynamically links them to their allowed actions.

    """
    # Get all Courses where request.user is Director, Instructor, Mentor or Mentor_Assigned
    user = request.user
    courses = Course.objects.all().filter(Q(director=user) |
        Q(instructor__instructor=user) | Q(enrollment__mentor=user) |
        Q(enrollment__course__mentor__mentor=user)).distinct().select_related('department', 'director').order_by('name')
    instructors = Instructor.objects.all().filter(instructor=user).select_related('course', 'course__department')
    mentors = Mentor.objects.all().filter(mentor=user).select_related('course', 'course__department')
    mentor_assigns = Enrollment.objects.all().filter(mentor=user).select_related('course', 'course__department',
                                                                    'student').order_by('student__username')
    # Create Dictionary of Course Tuples for easy template cycle
    # Tuple:(Course, Department, Roles, Mentor Assigns)
    course_data = dict()
    for course in courses: # Add All Courses to Dictionary
        if course.director == user:
            course_data[course.id] = (course, course.department, ['Director'], [])
        else:
            course_data[course.id] = (course, course.department, [], [])
    # Fill out remaining User Roles
    for instructor in instructors: # Add Instructor Roles
        course = instructor.course
        if not course_data.has_key(course.id): # (add Course if it doesn't exist)
            course_data[course.id] = (course, course.department, ['Instructor',], [])
        if not 'Instructor' in course_data[course.id][2]:
            course_data[course.id][2].append('Instructor')
    for mentor in mentors: # Add Mentor Roles
        course = mentor.course
        if not course_data.has_key(course.id): # (add a Course if it doesn't exist)
            course_data[course.id] = (course, course.department, ['Mentor',], [])
        if not 'Mentor' in course_data[course.id][2]:
            course_data[course.id][2].append('Mentor')
    for mentor_assign in mentor_assigns: # Add Mentor Assign Roles
        course = mentor_assign.course
        if not course_data.has_key(course.id): # (add a Course if it doesn't exist)
            course_data[course.id] = (course, course.department, ['Mentor_ASN',], [])
        if not 'Mentor_ASN' in course_data[course.id][2]:
            course_data[course.id][2].append('Mentor_ASN')
    # Fill out Mentor Assigned Students
    for enrollment in mentor_assigns:
        course = enrollment.course
        if not course_data.has_key(course.id): # (add a Course if it doesn't exist)
            course_data[course.id] = (course, course.department, [], [])
        if not enrollment.student in course_data[course.id][3]:
            course_data[course.id][3].append(enrollment.student)

    # Get all global announcements ordered by date
    announcements = Announcement.objects.all().filter(make_global=1).select_related('author').order_by('-pub_date')

    # Specify template, generate context, and return response
    template = loader.get_template('instructor_portal/index.html')
    context = RequestContext(request, {"course_data": course_data, "announcements": announcements})
    return HttpResponse(template.render(context))
# End Def

@login_required
def course_index(request, course_id):
    """
    Course welcome view for instructors to see announcements,
    details about selected course as well as
    an expanded course menu for assignments, resources and information.

    """
    try: # Try to get Course and raise 404 if it doesn't exist.
        course = Course.objects.select_related('department', 'director').get(id__exact=course_id)
    except Course.DoesNotExist:
        raise Http404
    access_type = verify_course(request.user, course) # Verify access with default blocking behavior
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {"access_flag": access_type})
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR, COURSE_MENTOR_LIMIT]:

        # Find all Instructors, Mentors and Announcements for this Course
        instructors = Instructor.objects.all().filter(course=course).select_related('instructor')
        mentors = Mentor.objects.all().filter(course=course).select_related('mentor')
        student_assigns = Enrollment.objects.all().filter(mentor=request.user).select_related('student')
        announcements = Announcement.objects.all().filter(Q(course=course) |
                                                          Q(department=course.department)
                                                          ).select_related('author').order_by('-pub_date')
        # Set template; Create Context; Return page view
        template = loader.get_template('instructor_portal/course_index.html')
        context = RequestContext(request, {"access_type": access_type,"course": course,  "instructors": instructors,
            "mentors": mentors, "student_assigns":  student_assigns, "announcements": announcements})
    return HttpResponse(template.render(context))
# End Def

@login_required
def course_announcement_add(request, course_id):
    """
    Instructor announcements view for adding an Announcement to a particular Course.
    Authenticates for request.user, processes any POST data
    as an AnnouncementForm and effectively adds the Announcement to the database
    and redirects to course_index or returns to course_announcement_add
    with appropriate error messages.
    Protects submissions against CSRF attacks using {% csrf_token %}.

    """
    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {"access_flag": access_type})
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR]:

        form = AnnouncementForm()
        if request.POST:
            # Create and save Announcement when form passes validation
            instance = Announcement(make_global=False, course_id=course.id)
            form = AnnouncementForm(request.POST, instance=instance)
            if form.is_valid():
                form.save()
                messages.success(request, 'Announcement successfully added to Course!')
                LogEntry.objects.log_action(request.user.pk, ContentType.objects.get_for_model(instance).pk,
                                            instance.pk, force_unicode(instance), action_flag=ADDITION)
                return HttpResponseRedirect(reverse(
                    'instructor_portal.views.course_index', args=[course.id,]))

        # Finally Set template, create Context and return Response
        template = loader.get_template('instructor_portal/course_announcement_add.html')
        context = RequestContext(request, {"course": course,"form": form})
    return HttpResponse( template.render(context) )
# End Def

@login_required
def course_announcement_delete(request, course_id, announcement_id):
    """
    Delete an Announcement with @param Announcement so long
    as it is in Course with @param Course.

    """
    try: # Verify Course @param with 404 block page error
        course = Course.objects.select_related().get(id__exact=course_id)
    except Course.DoesNotExist:
        raise Http404
    access_type = verify_course(request.user, course)
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR]:
        if request.POST:
            try: # Grab Announcement from URL parameters
                announcement = Announcement.objects.select_related().get(id__exact=announcement_id)
            except ObjectDoesNotExist: # Add warning message and forward to the Course index
                messages.warning(request, 'Announcement Delete Failure: No matching Announcement found!')
                return HttpResponseRedirect(reverse(
                    'instructor_portal.views.course_index', args=[course_id,]))
            # Verify Announcement corresponds to the verified Course
            if (announcement.department == course.department) or (announcement.course == course):
                announcement.delete()
                messages.success(request, 'Announcement successfully deleted from Course!')
            else:
                messages.warning(request, 'Announcement Delete Failure: Invalid URL Parameters!')
    return HttpResponseRedirect(reverse('instructor_portal.views.course_index', args=[course_id,]))
# End Def

@login_required
def course_assignments(request, course_id):
    """
    Instructor Assignments view for a particular Course.
    Authenticates access for request.user, link to Assignment Add
    gathers and arranges assignment data as a list of tuples:
    (Assignment, Submission, Grade, DueDateOverride) for the template.

    """
    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {"access_flag": access_type})
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR, COURSE_MENTOR_LIMIT]:

        # Find all Assignments, Grades, Submissions and DueDateOverrides
        assignments = Assignment.objects.all().filter(course=course.id).order_by('name')

        # Tuck data into a Django Paginator (which will means extra total page views +Queries)
        paginator = Paginator(assignments, settings.PAGINATOR_NUM_ASSIGNMENTS)
        try: # Page request must be an int--If not, deliver first page.
            page = int(request.GET.get('page', '1'))
        except ValueError:
            page = 1
        try: # If page request is out of range, deliver last page of results.
            current_page = paginator.page(page)
        except (EmptyPage, InvalidPage):
            current_page = paginator.page(paginator.num_pages)

        # Set template, and create Context
        template = loader.get_template('instructor_portal/course_assignments.html')
        context = RequestContext(request, {"access_type": access_type, "course": course, "assignments": current_page})
    return HttpResponse( template.render(context) )
# End Def

@login_required
def assignment_add(request, course_id):
    """
    Instructor assignments view for adding an assignment to a particular Course.
    Authenticates for request.user, processes any POST data as an AssignmentForm,
    and effectively adds the assignment to the database redirecting to course_assignments,
    or returns to assignment_add with form errors.
    Protects submissions against CSFR attacks using {% csrf_token %}.

    """
    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {"access_flag": access_type})
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR]:

        # Process POST given POST data
        form = AssignmentForm()
        if request.POST:
            # Create and save Assignment when form passes validation
            instance = Assignment(course_id=course.id)
            form = AssignmentForm(request.POST, request.FILES, instance=instance)
            if form.is_valid():
                form.save()
                messages.success(request, "Assignment " + instance.name + " added to Course " + course.name)
                LogEntry.objects.log_action(request.user.pk, ContentType.objects.get_for_model(instance).pk,
                                            instance.pk, force_unicode(instance), action_flag=ADDITION)
                return HttpResponseRedirect(reverse('instructor_portal.views.course_assignments', args=(course.id,)))

        # Set template, create Context and Return page view
        template = loader.get_template('instructor_portal/course_assignment_add.html')
        context = RequestContext(request, {"course": course, "form": form})
    return HttpResponse( template.render(context) )
# End View

@login_required
def course_information(request, course_id):
    """
    Instructor Course View for listing Course and Department Resources
    with @param Course and corresponding Department.

    """
    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {"access_flag": access_type})
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR, COURSE_MENTOR_LIMIT]:

        # Get the requested Course/Department Information
        course_information = Information.objects.all().filter(course=course.id).order_by('name')
        department_information = Information.objects.all().filter(department=course.department.id).order_by('name')

        # Set template; Create context
        template = loader.get_template('instructor_portal/course_information.html')
        context = RequestContext(request, {"access_type": access_type, "course": course,
            "course_infos": course_information, "department_infos": department_information})
    # Return page view response
    return HttpResponse( template.render(context) )
# End View

@login_required
def course_resources(request, course_id):
    """
    Instructor Course View for listing Course and Department Resources
    with @param Course and corresponding Department.

    """
    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {"access_flag": access_type})
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR, COURSE_MENTOR_LIMIT]:

        # Get the requested Course/Department Resources
        course_resources = Resource.objects.all().filter(course=course).order_by('name')
        department_resources = Resource.objects.all().filter(department=course.department).order_by('name')

        # Set template; Create context
        template = loader.get_template('instructor_portal/course_resources.html')
        context = RequestContext(request, {"access_type": access_type, "course": course,
            "course_resources": course_resources, "department_resources": department_resources})
    # Return page view response
    return HttpResponse( template.render(context) )
# EndView

@login_required
def checkscript_all(request):
    """
    Script for finding which homework sets (Submissions) have not yet been graded
    based on the access permissions of the current user.
    Unfortunately this is one area that is tricky and requires several queries
    at our desired data.

    """
    if request.user.is_superuser: # Get all checkscript submissions for super users
        courses = Course.objects.all().select_related('department')
        submissions = Submission.objects.all().select_related().filter(add_checkscript=True).order_by('date')
        # Add message; Set template; Create Context; Return Page View.
        messages.success(request, 'Now Showing: Complete Checkscript Listing!')
        template = loader.get_template('instructor_portal/checkscript.html')
        context = RequestContext(request, {'courses': courses, 'submissions': submissions})
        return HttpResponse(template.render(context))
    else: # Otherwise redirect to the default checkscript View with warning message
        messages.warning(request, "Checkscript/All only works for super users!  " +
                         "Default checkscript shows all content related to your classcomm roles.")
        return HttpResponseRedirect(reverse('instructor_portal.views.checkscript', args=[]))
# EndView

@login_required
def checkscript(request):
    """
    Script for finding which homework sets (Submissions) have not yet been graded
    based on the access permissions of the current user.
    Unfortunately this is one area that is tricky and requires several queries
    at our desired data.

    """
    # This is one of the most complex and beautiful query refactors in classcomm thus far!
    courses = Course.objects.all().filter(Q(director=request.user) |
            Q(instructor__instructor=request.user) |
            Q(enrollment__mentor=request.user) |
            Q(enrollment__course__mentor__mentor=request.user)).distinct()
    submissions = Submission.objects.all().filter(add_checkscript=True).filter(
            Q(enrollment__course__director=request.user) |
            Q(enrollment__course__instructor__instructor=request.user) |
            Q(enrollment__mentor=request.user) |
            Q(enrollment__course__mentor__mentor=request.user)
        ).distinct().select_related('assignment', 'assignment__course', 'assignment__course__department',
                                    'enrollment', 'enrollment__student', 'enrollment__mentor' ).order_by('date')
    # Add Message; Set template; Create Context; Return page view
    messages.success(request, 'Now Showing: Checkscript Listings for Your User Roles!')
    template = loader.get_template('instructor_portal/checkscript.html')
    context = RequestContext(request, {'courses': courses, 'submissions': submissions})
    return HttpResponse(template.render(context))
# EndView

@login_required
def checkscript_course(request, course_id):
    """
    Script for finding which homework sets have not yet been graded
    based on the access permissions of the current user.

    """
    try: # Verify Course @param with 404 block page error
        course = Course.objects.select_related().get(id__exact=course_id)
    except Course.DoesNotExist:
        raise Http404
    courses = Course.objects.all().filter(Q(director=request.user) |
            Q(instructor__instructor=request.user) |
            Q(enrollment__mentor=request.user) |
            Q(enrollment__course__mentor__mentor=request.user)).distinct()
    submissions = Submission.objects.all().filter(add_checkscript=True).filter(enrollment__course=course).filter(
            Q(enrollment__course__director=request.user) |
            Q(enrollment__course__instructor__instructor=request.user) |
            Q(enrollment__mentor=request.user) |
            Q(enrollment__course__mentor__mentor=request.user)
        ).distinct().select_related('assignment', 'assignment__course', 'assignment__course__department',
                                    'enrollment', 'enrollment__student', 'enrollment__mentor' ).order_by('date')
    # Add Message; Set template; Create context; Return page view
    messages.success(request, "Now Showing: Checkscript Listing for your role in Course " + course.name + "!")
    template = loader.get_template('instructor_portal/checkscript.html')
    context = RequestContext(request, {'courses': courses, 'submissions': submissions})
    return HttpResponse(template.render(context))
# EndView

@login_required
def roster_tools(request, course_id):
    """ Instructor view for course roster with links to student grade reports/actions. """

    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {"access_flag": access_type})
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR,
        COURSE_MENTOR_LIMIT]:

        # Find all enrollments for this course
        enrollments = Enrollment.objects.all().filter(course=course.id).select_related().order_by()

        # Tuck data into a Paginator which will means extra total page views =>
        # more SQL queries, sorting, and thus processing overhead. However
        # it will keep page view organization more clean and scalable.
        paginator = Paginator(enrollments, settings.PAGINATOR_NUM_ENROLLMENTS)
        try: # Page request must be an int--If not, deliver first page.
            page = int(request.GET.get('page', '1'))
        except ValueError:
            page = 1
        try: # If page request is out of range, deliver last page of results.
            current_page = paginator.page(page)
        except (EmptyPage, InvalidPage):
            current_page = paginator.page(paginator.num_pages)

        # Set template; Create Context
        template = loader.get_template('instructor_portal/course_roster.html')
        context = RequestContext(request, {"course": course,
            "access_type": access_type,
            "enrollments": current_page})
    # Return page view
    return HttpResponse(template.render(context))
# EndView

@login_required
def grade_report(request, course_id, enrollment_id):
    """ Main view for an Enrollment Grade Report. """

    # Verify access with default blocking behavior
    enrollment = Enrollment.objects.select_related().get(id__exact=enrollment_id)
    course = Course.objects.select_related().get(id=course_id)
    template = loader.get_template('instructor_portal/course_blocked.html')
    context = RequestContext(request, {})
    if enrollment.course.id == course.id: # Verify URL Correctness
        access_type = verify_course(request.user, course)
        context = RequestContext(request, {"access_flag": access_type})
        if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR, COURSE_MENTOR_LIMIT]:

            # Find all Assignments and related data for this Course/Enrollment
            assignments = Assignment.objects.all().filter(course=course.id).order_by('name')
            grades = Grade.objects.all().filter(enrollment=enrollment.id)
            submissions = Submission.objects.all().filter(enrollment=enrollment.id)
            dueDateOverrides = DueDateOverride.objects.all().filter(enrollment=enrollment.id)
            extra_credit = ExtraCredit.objects.all().filter(enrollment=enrollment.id)

            # Create list of Tuples for easy template cycle
            # Tuple:(Assignment, Submission, Grade, DueDateOverride)
            assignment_data = list()
            for assignment in assignments:
                # Find the submission
                currentSubmission = None
                for submission in submissions:
                    if submission.assignment_id == assignment.id:
                        currentSubmission = submission
                        break
                # Find the grade
                currentGrade = None
                for grade in grades:
                    if grade.assignment_id == assignment.id:
                        currentGrade = grade
                        break
                # Find any associated DueDateOverride
                currentDDO = None
                for dueDateOverride in dueDateOverrides:
                    if dueDateOverride.assignment_id == assignment.id:
                        currentDDO = dueDateOverride
                        break
                # Add the tuple to a list
                assignment_data.append((assignment, currentSubmission, currentGrade, currentDDO))
            # EndFor

            # Compute Aggregate Values for the context now also
            points_earned = grades.aggregate(Sum('points_earned'))
            points_possible = assignments.aggregate(Sum('points_possible'))
            points_extra_credit = extra_credit.aggregate(Sum('points_earned'))
            point_summary = (points_earned, points_possible, points_extra_credit)

            # Tuck data into a Paginator which will means extra total page views =>
            # more SQL queries, sorting, and thus processing overhead. However
            # it will keep page view organization more clean and scalable.
            paginator = Paginator(assignment_data, settings.PAGINATOR_NUM_ASSIGNMENTS)
            try: # Page request must be an int--If not, deliver first page.
                page = int(request.GET.get('page', '1'))
            except ValueError:
                page = 1
            try: # If page request is out of range, deliver last page of results.
                current_page = paginator.page(page)
            except (EmptyPage, InvalidPage):
                current_page = paginator.page(paginator.num_pages)

            # Set template; Create Context
            template = loader.get_template('instructor_portal/grade_report.html')
            context = RequestContext(request, {"access_type": access_type, "course": course, "enrollment": enrollment,
                "point_summary": point_summary, "assignment_list": current_page, "extra_credit": extra_credit})
    else:
        messages.error(request, "Grade Report Error: Invalid URL parameters!")
    return HttpResponse(template.render(context))
# EndView

@login_required
def return_grade(request, enrollment_id, assignment_id):
    """
    Instructor checkscript view for returning a Grade for a particular Assignment/Enrollment.
    Authenticates for request.user, processes any POST data using appropriate ModelForm,
     and effectively adds the Announcement to the database.
    Redirects to course_index or returns to course_announcement_add with appropriate error messages.
    Protects adding Grade against CSRF attacks using {% csrf_token %}.

    """
    # Verify access with default blocking behavior
    enrollment = Enrollment.objects.select_related().get(id__exact=enrollment_id)
    assignment = Assignment.objects.select_related().get(id__exact=assignment_id)
    if enrollment.course == assignment.course: # Parameter URL must be correctly formed
        access_type = verify_course(request.user, enrollment.course)
        if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR, COURSE_MENTOR_LIMIT]:

            form = GradeForm()
            if request.POST: # When POST: Create Grade and save obj when form passes validation
                instance = Grade(enrollment_id=enrollment.id, assignment_id=assignment.id)
                form = GradeForm(request.POST, request.FILES, instance=instance)
                if form.is_valid():
                    form.save()
                    messages.success(request, 'New Grade returned for Assignment: ' + assignment.name)
                    LogEntry.objects.log_action(request.user.pk, ContentType.objects.get_for_model(instance).pk,
                                                instance.pk, force_unicode(instance), action_flag=ADDITION)
                    return HttpResponseRedirect(reverse('instructor_portal.views.grade_report',
                                                        args=[assignment.course.id, enrollment.id,]))
        else:
            # Return Action Blocked if not authorized user
            template = loader.get_template('instructor_portal/course_blocked.html')
            context = RequestContext(request, {"access_flag": access_type})
            return HttpResponse( template.render(context) )
    else:
        messages.warning(request, "return_grade: Invalid URL parameters!")

    # Set template, and create Context and Return page view
    template = loader.get_template('instructor_portal/student_grade_return.html')
    context = RequestContext(request, {"form": form,
        "enrollment": enrollment,
        "course": enrollment.course,
        "assignment": assignment})
    return HttpResponse( template.render(context) )
# EndView

@login_required
def delete_grade(request, course_id, enrollment_id, grade_id):
    """ Delete Grade with @param Grade so long as it is a valid request.

    """
    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR]:

        if request.POST: # POST: Grab Grade from URL parameters
            try:
                grade = Grade.objects.select_related().get(id__exact=grade_id)
            except ObjectDoesNotExist: # Return to grade_report with warning
                messages.warning(request, 'Grade with grade_id ' + grade_id + 'not found!')
                return HttpResponseRedirect(reverse( 'instructor_portal.views.grade_report',
                                                     args=[course_id, enrollment_id,]))
            # Verify Grade corresponds to Verified Course
            if grade.assignment.course == course:
                messages.success(request, 'Deleted Grade for Assignment: ' + grade.assignment.name)
                grade.delete()
    # Redirect to the course Assignments view
    return HttpResponseRedirect(reverse('instructor_portal.views.grade_report', args=[course_id, enrollment_id,]))
# End Def

@login_required
def add_DDO(request, enrollment_id, assignment_id):
    """
    Instructor checkscript view for adding a DueDateOverride for a particular Assignment/Enrollment.
    Authenticates for request.user, processes any POST data as an Form,
     and effectively adds the Announcement to the database.
    Redirects to course_index or returns to course_announcement_add with appropriate error messages.
    Protects adding DueDateOverride against CSRF attacks using {% csrf_token %}.

    """
    # Verify access with default blocking behavior
    enrollment = Enrollment.objects.select_related().get(id__exact=enrollment_id)
    assignment = Assignment.objects.select_related().get(id__exact=assignment_id)
    if enrollment.course == assignment.course: # Parameter URL must also be formed correctly
        access_type = verify_course(request.user, enrollment.course)
        if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR, COURSE_MENTOR_LIMIT]:

            form = DueDateOverrideForm()
            if request.POST: # When POST: Create and save Grade if form passes validation
                instance = DueDateOverride(enrollment_id=enrollment.id, assignment_id=assignment.id)
                form = DueDateOverrideForm(request.POST, request.FILES, instance=instance)
                if form.is_valid():
                    form.save()
                    messages.success(request, 'New Due Date Override added for Assignment ' + assignment.name)
                    LogEntry.objects.log_action(request.user.pk, ContentType.objects.get_for_model(instance).pk,
                                                instance.pk, force_unicode(instance), ADDITION)
                    return HttpResponseRedirect(reverse('instructor_portal.views.grade_report',
                                                        args=[assignment.course.id, enrollment.id,]))
        else:
            # Return Action Blocked if not authorized user
            template = loader.get_template('instructor_portal/course_blocked.html')
            context = RequestContext(request, {"access_flag": access_type})
            return HttpResponse( template.render(context) )
    else:
        messages.warning(request, "Unable to add Due Date Override: Invalid URL parameters!")
    # Set template, and create Context and Return page view
    template = loader.get_template('instructor_portal/student_DDO_add.html')
    context = RequestContext(request, {"form": form,
        "enrollment": enrollment,
        "course": enrollment.course,
        "assignment": assignment})
    return HttpResponse( template.render(context) )
# EndView

@login_required
def delete_DDO(request, course_id, enrollment_id, DDO_id):
    """ Delete a DueDateOverride with @param DDO_id for valid requests.

    """
    # Verify access with default blocking behavior
    course = Course.objects.select_related().get(id__exact=course_id)
    access_type = verify_course(request.user, course)
    if access_type in [COURSE_DIRECTOR, COURSE_INSTRUCTOR, COURSE_MENTOR]:

        if request.POST:
            try: # Grab DueDateOverride from URL parameters
                DDO = DueDateOverride.objects.select_related().get(id__exact=DDO_id)
            except ObjectDoesNotExist:
                # Return to the Course index
                messages.warning(request, 'Unable to Delete Due Date Override: Invalid DDO_id in parameter URL!')
                return HttpResponseRedirect(reverse('instructor_portal.views.grade_report',
                                                    args=[course_id, enrollment_id,]))
            # Verify DueDateOverride corresponds to the verified Course
            if DDO.assignment.course == course:
                messages.success(request, 'Due Date Override removed from Assignment: ' + DDO.assignment.name)
                DDO.delete()
    # Return Redirect to the course assignments view page
    return HttpResponseRedirect(reverse('instructor_portal.views.grade_report', args=[course_id, enrollment_id,]))
# EndView

#################################################
# BEGIN PERMISSIONS HELPER FUNCTIONS
#################################################

#################################################
# Permissions Flags for the request.user:
# When these values change, they correspondingly need to
# be adjusted in the course_blocked.html template.
COURSE_BAD_USER = -1
COURSE_DIRECTOR = 2
COURSE_INSTRUCTOR = 3
COURSE_MENTOR = 4
COURSE_MENTOR_LIMIT = 5
DEPT_DIRECTOR = 6 # New (Unimplemented) User type.
#################################################

def verify_course(user, course):
    """
    Returns one of the above permission flags that determines if the @param user
      (which should be request.user) has access level in relation to the @param Course object.

    """
    # Check if user is COURSE_DIRECTOR
    if course.director == user:
        return COURSE_DIRECTOR

    # Check if user is COURSE_INSTRUCTOR
    instructors = Instructor.objects.all().select_related().filter(course=course).filter(instructor=user)
    for instructor in instructors:
        if instructor.course == course:
            return COURSE_INSTRUCTOR

    # Check if user is COURSE_MENTOR
    mentors = Mentor.objects.all().select_related().filter(course=course).filter(mentor=user)
    for mentor in mentors:
        if mentor.course == course:
            return COURSE_MENTOR

    # Check if user is COURSE_MENTOR_LIMIT
    enrollment_assigns = Enrollment.objects.all().filter(mentor=user).filter(course=course)
    for enrollment in enrollment_assigns:
        if enrollment.course == course:
            return COURSE_MENTOR_LIMIT

    # User does not verify course, return COURSE_BAD_USER
    return COURSE_BAD_USER
# End Def

def verify_enrollment(user, enrollment):
    """
    Returns one of the above permission flags that determines if the @param user
      (which should be request.user) has access level in relation to the @param Enrollment object.

    """
    # Check if user is COURSE_DIRECTOR
    if enrollment.course.director == user:
        return COURSE_DIRECTOR

    # Check if user is COURSE_INSTRUCTOR
    instructors = Instructor.objects.all().select_related().filter(course=enrollment.course).filter(instructor=user)
    for instructor in instructors:
        if instructor.course == course:
            return COURSE_INSTRUCTOR

    # Check if user is COURSE_MENTOR
    mentors = Mentor.objects.all().select_related().filter(course=course).filter(mentor=user)
    for mentor in mentors:
        if mentor.course == course:
            return COURSE_MENTOR

    # Check if user is COURSE_MENTOR_LIMIT
    enrollment_assigns = Enrollment.objects.all().filter(mentor=user).filter(course=course)
    for enrollment in enrollment_assigns:
        if enrollment.course == course:
            return COURSE_MENTOR_LIMIT

    # User does not verify course, return COURSE_BAD_USER
    return COURSE_BAD_USER
# End Def