# coding=utf-8
import sys
from rdflib import Graph, Namespace, RDF, Literal, BNode, URIRef, XSD
import json
import random

class WSQueries:

	def __init__(self, ttl_file):
		self.g = Graph()
		self.g.parse(ttl_file, format="n3")

	def getDistinctGenres(self):
		query = "SELECT DISTINCT ?genre WHERE { ?a :hasGenre ?genre }"

		qres = self.g.query(query)

		l = []

		for row in qres:
			l.append(str(row[0]).split("#")[1])

		return l

	def getFilmsByGenre(self, genre):
		namespace="<http://www.semanticmovies.pt/#"+genre+">"

		l=[]
		query = "SELECT  ?film ?filmID ?poster  WHERE { "+namespace+" :isGenreOf ?a. ?a :hasTitle ?film. ?a :hasFilmID ?filmID. ?a :hasPoster ?poster}"

		qres=self.g.query(query)

		for row in qres:
			l.append({"hasTitle":str(row[0]),"hasFilmID":str(row[1]),"hasPoster":str(row[2])})

		return l

	def getRelatedFilms(self, id):

		listFilmGenre = []
		listDirectors = []
		listRelated = []

		filmGenre = self.getFilmGenre(id)
		for i in filmGenre:
			listFilmGenre.append( i["hasGenre"])

		directors = self.getDirectorsByFilm(id)

		listDirectors.append( directors[0]["hasPersonID"])

		for i in listFilmGenre:
			listRelated += random.sample(self.getFilmsByGenre(i), 5)

		for i in listDirectors:
			listRelated += self.getFilmsByDirector(i)

		aux = 0
		index = -1
		for i in listRelated:
			if i["hasFilmID"] == id:
				index = aux
				break
			aux+=1

		if index >= 0:
			#del listRelated[index]
			listRelated.pop(index)

		return random.sample(listRelated,6)

	def getFilmGenre(self, id):

		l=[]
		query = "SELECT ?filmID ?title ?genre WHERE {?a :hasGenre ?genre. ?a :hasFilmID ?filmID. ?a :hasTitle ?title. FILTER (regex(?filmID,'"+id+"')).}"
		qres=self.g.query(query)

		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasTitle":str(row[1]),"hasGenre":str(row[2]).split("#")[1]})

		return l

	def getFilmsByActor(self, personID):
		l=[]
		query = "SELECT ?actorID ?actor ?film ?filmID ?poster WHERE {?a :isActorOf ?b. ?a :hasPersonID ?actorID. ?a :hasName ?actor. ?b :hasTitle ?film. ?b :hasFilmID ?filmID. ?b :hasPoster ?poster. FILTER (regex(?actorID,'"+personID+"')).}"
		qres=self.g.query(query)

		for row in qres:
			l.append({"hasPersonID":str(row[0]),"hasName":str(row[1]),"hasTitle":str(row[2]), "hasFilmID":str(row[3]), "hasPoster":str(row[4])})

		return l

	def getFilmsByDirector(self, personID):
		l=[]
		query = "SELECT ?directorID ?director ?film ?filmID ?poster WHERE {?a :isDirectorOf ?b. ?a :hasPersonID ?directorID. ?a :hasName ?director. ?b :hasTitle ?film. ?b :hasFilmID ?filmID. ?b :hasPoster ?poster. FILTER (regex(?directorID,'"+personID+"')).}"
		qres=self.g.query(query)

		for row in qres:
			l.append({"hasPersonID":str(row[0]),"hasName":str(row[1]),"hasTitle":str(row[2]), "hasFilmID":str(row[3]), "hasPoster":str(row[4])})

		return l

	def getFilmsByWriter(self, personID):
		l=[]
		query = "SELECT ?writerID ?writer ?film ?filmID ?poster WHERE {?a :isWriterOf ?b. ?a :hasPersonID ?writerID. ?a :hasName ?writer. ?b :hasTitle ?film. ?b :hasFilmID ?filmID. ?b :hasPoster ?poster. FILTER (regex(?writerID,'"+personID+"')).}"
		qres=self.g.query(query)

		for row in qres:
			l.append({"hasPersonID":str(row[0]),"hasName":str(row[1]),"hasTitle":str(row[2]), "hasFilmID":str(row[3]), "hasPoster":str(row[4])})

		return l

	def getAllPersons(self):
		l=[]
		query = "SELECT ?personID ?person  WHERE { ?a :hasName ?person. ?a :hasPersonID ?personID.}"
		qres=self.g.query(query)

		for row in qres:
			l.append({"hasPersonID":str(row[0]),"hasName":str(row[1])})

		return l

	def getAllFilms(self):
		l=[]
		query = "SELECT ?filmID ?film ?poster WHERE {?a :hasTitle ?film. ?a :hasFilmID ?filmID. ?a :hasPoster ?poster }"
		qres=self.g.query(query)

		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasTitle":str(row[1]),"hasPoster":str(row[2])})

		return l

	def getPersonInfo(self, personID):
		l=[]
		query = "SELECT ?name ?id ?job WHERE { ?a :hasName ?name. ?a :hasPersonID ?id. ?a :hasJob ?job FILTER (regex(?id,'"+personID+"')).}"
		qres=self.g.query(query)

		for row in qres:
			l.append({"hasName":str(row[0]),"hasPersonID":personID,"hasJob":str(row[2])})

		return l[0]

	def getActorsByFilm(self, id):
		l=[]
		query = "SELECT ?filmID ?actor ?actorID WHERE {?a :hasActor ?b. ?a :hasFilmID ?filmID. ?b :hasName ?actor. ?b :hasPersonID ?actorID FILTER (regex(?filmID,'"+id+"')).}"
		qres=self.g.query(query)
		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasName":str(row[1]),"hasPersonID":str(row[2])})

		return l

	def getWritersByFilm(self, id):
		l=[]
		query = "SELECT ?filmID ?writer ?writerID WHERE {?a :hasWriter ?b. ?a :hasFilmID ?filmID. ?b :hasName ?writer. ?b :hasPersonID ?writerID. FILTER (regex(?filmID,'"+id+"')).}"
		qres=self.g.query(query)
		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasName":str(row[1]),"hasPersonID":str(row[2])})

		return l

	def getDirectorsByFilm(self, id):
		l=[]
		query = "SELECT ?filmID ?director ?directorID WHERE {?a :hasDirector ?b. ?a :hasFilmID ?filmID. ?b :hasName ?director. ?b:hasPersonID ?directorID. FILTER (regex(?filmID,'"+id+"')).}"
		qres=self.g.query(query)
		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasName":str(row[1]),"hasPersonID":str(row[2])})

		return l

	def getProducersByFilm(self, id):
		l=[]
		query = "SELECT ?filmID ?producer ?producerID WHERE {?a :hasProducer ?b. ?a :hasFilmID ?filmID. ?b :hasName ?producer. ?b :hasPersonID ?producerID. FILTER (regex(?filmID,'"+id+"')).}"
		qres=self.g.query(query)
		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasName":str(row[1]),"hasPersonID":str(row[2])})

		return l

	def getFilmInfo(self, id):
		l=[]
		query = "SELECT ?filmID ?title ?runtime ?release_date ?rating ?votes ?tagline ?plot ?poster ?actor WHERE {?a a :Film. ?a :hasFilmID ?filmID.  ?a :hasTitle ?title. ?a :hasRuntime ?runtime. ?a :hasReleaseDate ?release_date. ?a :hasRating ?rating. ?a :hasVotes ?votes. ?a :hasTagline ?tagline. ?a :hasPlot ?plot. ?a :hasPoster ?poster.  FILTER (regex(?filmID,'"+id+"')).}"

		qres=self.g.query(query)
		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasTitle":str(row[1]),"hasRuntime":int(row[2]),"hasReleaseDate":str(row[3]),"hasRating":str(row[4]),"hasVotes":str(row[5]),"hasTagline":str(row[6]),"hasPlot":str(row[7]),"hasPoster":str(row[8]),"hasActor":str(row[9])})

		return l[0]

	def searchFilm(self, keyword,dates,genre):
		l=[]

		namespace="<http://www.semanticmovies.pt/#"+genre+">"
		if dates[0] != "" and dates[1] != "":
			dates[0] = dates[0]+"-01-01"
			dates[1] = dates[1]+"-12-31"
			query = "SELECT ?id ?title ?runtime ?release_date ?genre ?rating ?votes ?tagline ?plot ?poster WHERE {?a a :Film. ?a :hasFilmID ?id.  ?a :hasTitle ?title. ?a :hasRuntime ?runtime. ?a :hasReleaseDate ?release_date. ?a :hasRating ?rating. ?a :hasVotes ?votes. ?a :hasTagline ?tagline. ?a :hasPlot ?plot. ?a :hasPoster ?poster. FILTER (regex(?title,'.*"+keyword+".*','i')). FIlTER (?release_date >= '"+dates[0]+"').FIlTER (?release_date <= '"+dates[1]+"')."

		elif dates[0] != "":
			dates[0] = dates[0]+"-01-01"
			query = "SELECT ?id ?title ?runtime ?release_date ?rating ?votes ?tagline ?plot ?poster WHERE {?a a :Film. ?a :hasFilmID ?id.  ?a :hasTitle ?title. ?a :hasRuntime ?runtime. ?a :hasReleaseDate ?release_date. ?a :hasRating ?rating. ?a :hasVotes ?votes. ?a :hasTagline ?tagline. ?a :hasPlot ?plot. ?a :hasPoster ?poster. FILTER (regex(?title,'.*"+keyword+".*','i')). FIlTER (?release_date >= '"+dates[0]+"')."

		elif dates[1] != "":
			dates[1] = dates[1]+"-12-31"
			query = "SELECT ?id ?title ?runtime ?release_date ?rating ?votes ?tagline ?plot ?poster WHERE {?a a :Film. ?a :hasFilmID ?id.  ?a :hasTitle ?title. ?a :hasRuntime ?runtime. ?a :hasReleaseDate ?release_date. ?a :hasRating ?rating. ?a :hasVotes ?votes. ?a :hasTagline ?tagline. ?a :hasPlot ?plot. ?a :hasPoster ?poster. FILTER (regex(?title,'.*"+keyword+".*','i')). FIlTER (?release_date <= '"+dates[1]+"')."

		else:
			query = "SELECT ?id ?title ?runtime ?release_date ?rating ?votes ?tagline ?plot ?poster WHERE {?a a :Film. ?a :hasFilmID ?id.  ?a :hasTitle ?title. ?a :hasRuntime ?runtime. ?a :hasReleaseDate ?release_date. ?a :hasRating ?rating. ?a :hasVotes ?votes. ?a :hasTagline ?tagline. ?a :hasPlot ?plot. ?a :hasPoster ?poster. FILTER (regex(?title,'.*"+keyword+".*','i')). "

		if genre != "":
			query += "?a :hasGenre "+namespace+"."

		query +="}"

		qres=self.g.query(query)
		for row in qres:
			l.append({"hasFilmID":str(row[0]),"hasTitle":str(row[1]),"hasRuntime":str(row[2]),"hasReleaseDate":str(row[3]),"hasRating":str(row[4]),"hasVotes":str(row[5]),"hasTagline":str(row[6]),"hasPlot":str(row[7]),"hasPoster":str(row[8])})

		return l

	def searchPerson(self, keyword,job):
		l = []
		namespace="<http://www.semanticmovies.pt/#"+job+">"

		if job != "":
			 query = "SELECT ?id ?person ?job  WHERE {?a a :Person. ?a :hasName ?person. ?a :hasPersonID ?id. ?a :hasJob "+namespace+". FILTER (regex(?person,'.*"+keyword+".*','i')).}"
		else:
			query = "SELECT ?id ?person ?job  WHERE {?a a :Person. ?a :hasName ?person. ?a :hasPersonID ?id. ?a :hasJob ?job. FILTER (regex(?person,'.*"+keyword+".*','i')).}"

		qres=self.g.query(query)

		for row in qres:
			l.append({"hasPersonID":str(row[0]),"hasName":str(row[1]),"hasJob":str(row[2])})

		for i in range(len(l)-1):

			for j in range(i+1,len(l)):
				if type(l[i])!=int and type(l[j])!=int:
					if (l[i]["hasName"]==l[j]["hasName"]):
						l[i]["hasJob"]=[l[i]["hasJob"],l[j]["hasJob"]]
						l[j] = -1

		if -1 in l:
			l.remove(-1)

	def getInfo(self, lista):
		l=[]

		if type(lista) == dict:
			lista=[lista]

		for i in lista:
			v = list(i.values())[0]
			if "Film" in v:
				query = "SELECT ?filmID ?title ?runtime ?release_date ?rating ?votes ?tagline ?plot ?poster ?actor WHERE {<"+v+"> :hasFilmID ?filmID. <"+v+"> :hasTitle ?title. <"+v+"> :hasRuntime ?runtime. <"+v+"> :hasReleaseDate ?release_date. <"+v+"> :hasRating ?rating. <"+v+"> :hasVotes ?votes. <"+v+"> :hasTagline ?tagline. <"+v+"> :hasPlot ?plot. <"+v+"> :hasPoster ?poster. }"

				qres=self.g.query(query)
				for row in qres:
					l.append({"hasFilmID":str(row[0]),"hasTitle":str(row[1]),"hasRuntime":str(row[2]),"hasReleaseDate":str(row[3]),"hasRating":str(row[4]),"hasVotes":str(row[5]),"hasTagline":str(row[6]),"hasPlot":str(row[7]),"hasPoster":str(row[8]),"hasActor":str(row[9])})
			else:
				query = "SELECT ?name ?id ?job WHERE { <"+v+"> :hasName ?name. <"+v+"> :hasPersonID ?id. <"+v+"> :hasJob ?job.}"
				qres=self.g.query(query)

				for row in qres:
					l.append({"hasName":str(row[0]),"hasPersonID":str(row[1]),"hasJob":str(row[2])})

		return l

	def generateQuery(self, dict,length):
		l = []

		if length==1:
			query="SELECT "+dict["selectWord"]+" WHERE {"+dict["query1"]+" "+dict["query2"]+"}"

		elif (":hasGenre" in dict["query1"] or ":hasGenre" in dict["query2"] ):
			namespace="<http://www.semanticmovies.pt/#"+dict["filterWord"].capitalize()+">"

			if ":hasGenre" in dict["query1"]:
				dict["query1"]=dict["query1"].replace(dict["filter"],namespace)
			else:
				dict["query2"]=dict["query2"].replace(dict["filter"],namespace)
			query="SELECT "+dict["selectWord"]+" WHERE {"+dict["query1"]+" "+dict["query2"]+"}"
		elif (":hasReleaseDate" in dict["query1"] or ":hasReleaseDate" in dict["query2"] ):
			 query="SELECT "+dict["selectWord"]+" WHERE {"+dict["query1"]+" "+dict["query2"]+" FILTER ("+dict["filter"]+" >= '"+dict["filterWord"]+"-01-01').  FILTER ("+dict["filter"]+" <= '"+dict["filterWord"]+"-12-31').}"
		elif (":hasRating" in dict["query1"] or ":hasRating" in dict["query2"] ):
			query="SELECT "+dict["selectWord"]+" WHERE {"+dict["query1"]+" "+dict["query2"]+" FILTER ("+dict["filter"]+" >= "+dict["filterWord"]+"). FILTER ("+dict["filter"]+" <= "+dict["filterWord"].split(".")[0]+".9).}"
		else:
			query="SELECT "+dict["selectWord"]+" WHERE {"+dict["query1"]+" "+dict["query2"]+" FILTER (regex("+dict["filter"]+",'.*"+dict["filterWord"]+".*','i')).}"

		qres=self.g.query(query)
		for row in qres:
			l.append({"hasFilmID":str(row[0])})

		if len(l) > 1 or len(l) == 0:
			return l
		else:
			return l[0]

	def searchSemantic(self, frase):
		reservedWords=["todos","tem","cujo","de","da","do","com","em","que","os","o","as","a","onde","são","com","foi","é","no","participam","participa","participou","por","pelo"]

		ontology = {"filmes":"","filme":"","classificação":":hasRating","classificacao":":hasRating","genero":":hasGenre","ano":":hasReleaseDate","titulo":":hasTitle","nome":":hasName","atrizes":":hasActor","atriz":":hasActor","ator":":hasActor","atores":":hasActor","realizado":":hasDirector","realizados":":hasDirector","realizador":":hasDirector","dirigido":":hasDirector","dirigidos":":hasDirector","realizadores":":hasDirector","diretor":":hasDirector","diretores":":hasDirector","produzidos":":hasProducer","produzido":":hasProducer","produtor":":hasProducer","produtores":":hasProducer","escrito":":hasWriter","escritor":":hasWriter","escritores":":hasWriter"}
		string = frase.split()
		query1=""
		query2=""
		filterWord=""
		listaQuery=[]
		dict={}

		for i in string:
			if i.lower() in reservedWords:
				continue;
			elif i.lower() in ontology.keys():
				listaQuery.append(i)
			else:
				filterWord+=i+" "

		filterWord=filterWord.rstrip()

		if len(listaQuery)==1:
			dict["filterWord"] = ""
			dict["filter"] = "?a"
			dict["selectWord"] ="?a"
			dict["query2"] = ""

			if listaQuery[0] == "filme" or listaQuery[0] == "filmes":
				dict["query1"] = "?a a :Film."
			else:
				job = ontology[listaQuery[0]]
				job = job.replace(":has","")
				namespace = "<http://www.semanticmovies.pt/#"+job+">"
				dict["query1"] = "?a :hasJob "+namespace+"."

		elif len(listaQuery)==2:
			if ontology[listaQuery[1]]!="":
				query1="?a "+ontology[listaQuery[1]]+" ?b"
			else:
				query1="?a "+ontology[listaQuery[0]]+" ?b"

			if listaQuery[1] == "filme" or listaQuery[1] == "filmes":
				dict["filter"]="?a"
				dict["selectWord"]="?b"
			else:
				dict["filter"]="?b"
				dict["selectWord"]="?a"
			dict["filterWord"]=filterWord
			dict["query1"]=query1+"."
			if(":hasName" not in dict["query1"] and ":hasTitle" not in dict["query1"] and ":hasGenre" not in dict["query1"] and ":hasReleaseDate" not in dict["query1"] and ":hasRating" not in dict["query1"]):
				if listaQuery[1] == "filme" or listaQuery[1] == "filmes":
					 dict["query1"]+=dict["filter"]+" :hasTitle ?c."
				else:
					dict["query1"]+=dict["filter"]+" :hasName ?c."
				dict["filter"]="?c"
			dict["query2"]=""

		elif len(listaQuery)==3:
			query1+="?a "+ontology[listaQuery[0]]+ontology[listaQuery[1]]+" ?b"

			if listaQuery[1] == "filme" or listaQuery[1] == "filmes":
				compose="?a"
				dict["selectWord"]="?b"
			else:
				compose="?b"
				dict["selectWord"]="?a"

			query2+=compose+" "+ontology[listaQuery[2]]+" ?c"
			dict["filter"]="?c"
			dict["filterWord"]=filterWord
			dict["query1"]=query1+"."
			dict["query2"]=query2+". "
			if(":hasName" not in dict["query2"] and ":hasTitle" not in dict["query2"] and ":hasGenre" not in dict["query2"] and ":hasReleaseDate" not in dict["query2"] and ":hasRating" not in dict["query2"]):
				if listaQuery[1] == "filme" or listaQuery[1] == "filmes":
					 dict["query2"]+=dict["filter"]+" :hasTitle ?d."
				else:
					dict["query2"]+=dict["filter"]+" :hasName ?d."
				dict["filter"]="?d"

		l = self.generateQuery(dict,len(listaQuery))
		l = self.getInfo(l)
		return l


from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import HttpResponseForbidden, HttpResponseBadRequest
from django.shortcuts import render
from django.urls import reverse

import common.forms as cf
import common.models as m
import common.permissions as p
import feedback.forms as ff
import teacher.forms as f
from common.permissions import require_teacher
from common.views import redirect
from runner import runner as r
from test_match import matcher

@login_required()
@require_teacher
def index(request):
    """Show all of the courses available for the
    logged in teacher as a list"""
    enrolled_in = m.EnrolledUser.objects.filter(login=request.user)
    my_courses = [x.course for x in enrolled_in]
    detail = {
        "courses": my_courses
    }
    return render(request, 'teacher/index.html', detail)

@login_required()
@require_teacher
def create_course(request):
    """Handle the creation of a course, or create the form to do so"""
    if request.method == "POST":
        return create_course_update(request, request.POST)
    return create_course_render(request)

def create_course_render(request):
    """Generate the form used to create a course"""
    detail = {
        "course_name": "New Course",
        "courseworks": None,
        "uf": f.CourseForm({"student": str(request.user) + ','}),
        "crumbs": [("Homepage", reverse("teacher_index"))]
    }
    return render(request, 'teacher/edit_course.html', detail)

@transaction.atomic
def create_course_update(request, new_details):
    """Given the @new_details from a course, create a new course.
    Also handle enrolling all of the specified users. The
    owner of the course is not allowed to remove themselves"""
    owner = request.user
    updated_form = f.CourseForm(new_details)
    if not updated_form.is_valid():
        raise Exception("validity problem")
    new_code = updated_form.cleaned_data['code']
    new_name = updated_form.cleaned_data['name']
    new_students = updated_form.cleaned_data['student'].strip().strip(',').split(',')
    new_students_list = list(map(lambda w: w.strip(), new_students))

    course = m.Course(code=new_code, name=new_name)
    course.save()

    for item in new_students_list:
        current_user = m.User.objects.get(username=item)
        new_item = m.EnrolledUser(login=current_user, course=course)
        new_item.save()

    if str(owner) not in new_students_list:
        new_item = m.EnrolledUser(login=owner, course=course)
        new_item.save()

    return redirect(request, "Course created", reverse('edit_course', args=[new_code]))

@login_required()
@require_teacher
def edit_course(request, c):
    """Handle creation of form, or post request for editing a course"""
    cw = m.Course.objects.get(code=c)
    if not p.is_enrolled_on_course(request.user, cw):
        return HttpResponseForbidden("You are not enrolled on this course")
    if request.method == "POST":
        edit_course_update(request, c, request.POST)
    return edit_course_render(request, c)

@transaction.atomic
def edit_course_update(request, course_code, new_details):
    """Given the @new_details for @course_code, update
    these details in the database, and handle any updates
    needed with regards to enrolled users"""
    owner = request.user
    old_course = m.Course.objects.get(code=course_code)
    updated_form = f.CourseForm(new_details)
    if not updated_form.is_valid():
        raise Exception("validity problem")
    old_course.code = updated_form.cleaned_data['code']
    old_course.name = updated_form.cleaned_data['name']
    old_course.save()

    new_students = updated_form.cleaned_data['student'].strip().strip(',').split(',')
    new_students_list = list(map(lambda w: w.strip(), new_students))

    enrollments = m.EnrolledUser.objects.filter(course=old_course)
    for item in enrollments:
        if item.login == owner:
            continue
        if str(item.login) not in new_students_list:
            item.delete()
    for item in new_students_list:
        current_user = m.User.objects.get(username=item)
        exists = m.EnrolledUser.objects.filter(login=current_user).filter(course=old_course)
        if exists.count() != 1:
            new_item = m.EnrolledUser(login=current_user, course=old_course)
            new_item.save()

def edit_course_render(request, requested_course_code):
    """Get the details of @requested_course_code, coursework
    items and all the enrolled users and then
    populate the form before displaying it to user"""
    course = m.Course.objects.get(code=requested_course_code)
    enrollments = m.EnrolledUser.objects.filter(course=course)
    students = ""
    for eu in enrollments:
        students += str(eu.login) + ", "
    courseworks = m.Coursework.objects.filter(course=course)
    initial = {"code": course.code, "name": course.name, "student": students}
    update_form = f.CourseForm(initial)
    detail = {
        "course_name": course.name,
        "course_code": course.code,
        "courseworks": courseworks,
        "uf": update_form,
        "crumbs": [("Homepage", reverse("teacher_index"))]
    }
    return render(request, 'teacher/edit_course.html', detail)

@login_required()
@require_teacher
def create_coursework(request, c):
    """Handle form for creating a coursework in course @[c]"""
    cw = m.Course.objects.get(code=c)
    if not p.is_enrolled_on_course(request.user, cw):
        return HttpResponseForbidden("You are not enrolled on this course")
    if request.method == "POST":
        return create_coursework_update(request.user, request, c)
    return create_coursework_render(request, c)

def create_coursework_render(request, code):
    """Generate an empty form for creating a coursework for course @code"""
    detail = {
        "courseworks": {"name": "New Coursework"},
        "cw_form": f.CourseworkForm(),
        "creating": True,
        "crumbs": [("Homepage", reverse("teacher_index")),
                   ("Course", reverse("edit_course", args=[code]))]
    }
    return render(request, 'teacher/edit_cw.html', detail)

@transaction.atomic
def create_coursework_update(user, request, course_code):
    """@user - Given the @request for a coursework, and
    the @course_code we want to add it to, update the db"""
    cw_form = f.CourseworkForm(request.POST)
    if not cw_form.is_valid():
        return HttpResponseBadRequest("Form contained invalid data. Please try again")
    course = m.Course.objects.get(code=course_code)
    coursework = m.Coursework(id=m.new_random_slug(m.Coursework),
                              course=course,
                              name=cw_form.cleaned_data['name'],
                              state=cw_form.cleaned_data['state'],
                              runtime=cw_form.cleaned_data['runtime'],
                              test_class=cw_form.cleaned_data['test_class'])
    coursework.save()

    descriptor = m.Submission(id=m.new_random_slug(m.Submission), coursework=coursework,
                              creator=user, type=m.SubmissionType.CW_DESCRIPTOR,
                              display_name="Coursework Descriptor")
    descriptor.save()
    for each in request.FILES.getlist('descriptor'):
        descriptor.save_uploaded_file(each)

    oracle_exec = m.Submission(id=m.new_random_slug(m.Submission), coursework=coursework,
                               creator=user, type=m.SubmissionType.ORACLE_EXECUTABLE,
                               display_name="Oracle Solution")
    oracle_exec.save()
    for each in request.FILES.getlist('oracle_exec'):
        oracle_exec.save_uploaded_file(each)

    sig = m.Submission(id=m.new_random_slug(m.Submission), coursework=coursework,
                       creator=user, type=m.SubmissionType.SIGNATURE_TEST,
                       display_name="Signature Test")
    sig.save()
    for each in request.FILES.getlist('signature'):
        sig.save_uploaded_file(each)

    return redirect(request, "Coursework created", reverse('edit_cw', args=[coursework.id]))

@login_required()
@require_teacher
def edit_coursework(request, c):
    """Prepare a page to view and edit coursework @[c]"""
    coursework = m.Coursework.objects.get(id=c)
    if not p.is_enrolled_on_course(request.user, coursework.course):
        return HttpResponseForbidden("You are not enrolled on this course")
    if request.method == "POST":
        edit_coursework_update(request.POST, coursework)
    return edit_coursework_render(request, coursework)

@transaction.atomic()
def edit_coursework_update(new_details, old_coursework):
    """Given the @new_details, update the database for
    the details o @old_coursework"""
    updated_form = f.CourseworkForm(new_details)
    if not updated_form.is_valid():
        raise Exception("validity problem")
    old_coursework.name = updated_form.cleaned_data['name']
    old_coursework.state = updated_form.cleaned_data['state']
    old_coursework.runtime = updated_form.cleaned_data['runtime']
    old_coursework.test_class = updated_form.cleaned_data['test_class']
    old_coursework.save()

def edit_coursework_render(request, coursework):
    """Get all of the necessary details to display a
    page for a given @coursework, including the
    files uploaded for it, test data instances and
    of course the metadata about the coursework itself"""
    desc_types = [m.SubmissionType.CW_DESCRIPTOR, m.SubmissionType.ORACLE_EXECUTABLE,
                  m.SubmissionType.SIGNATURE_TEST]
    submissions = [(s, s.get_files()) for s in m.Submission.objects.filter(
        coursework=coursework, type__in=desc_types)]
    initial = {"name": coursework.name,
               "state": coursework.state,
               "runtime": coursework.runtime,
               "test_class": coursework.test_class}
    cw_form = f.CourseworkForm(initial)
    detail = {
        "coursework": coursework,
        "cw_form": cw_form,
        "creating": False,
        "submissions": submissions,
        "crumbs": [("Homepage", reverse("teacher_index")),
                   ("Course", reverse("edit_course", args=[coursework.course.code]))]
    }
    return render(request, 'teacher/edit_cw.html', detail)

@login_required()
@require_teacher
def view_coursework(request, c):
    """A teacher has made a @request to view the view
    page for coursework with @[c]. display files, tests"""
    coursework = m.Coursework.objects.get(id=c)
    if not p.is_enrolled_on_course(request.user, coursework.course):
        return HttpResponseForbidden("You are not enrolled on this course")
    submissions = [(s, s.get_files()) for s in
                   m.Submission.objects.filter(coursework=coursework)
                   .exclude(type=m.SubmissionType.TEST_RESULT)]
    tm_initial = {"coursework": coursework.id}
    tm_form = generate_teacher_easy_match_form(coursework)
    atm_form = f.AutoTestMatchForm(tm_initial)
    results = m.TestMatch.objects.filter(coursework=coursework)
    base_ff_form = ff.FeedbackGroupForm(tm_initial)
    extra_ff_forms = [ff.FeedbackGroupForm(initial) for
                      initial in ff.get_feedback_groups_as_iterable_forms(coursework)]
    detail = {
        "coursework": coursework,
        "submissions": submissions,
        "tm_form": tm_form,
        "atm_form": atm_form,
        "results": results,
        "feedback_form": base_ff_form,
        "feedback_forms": extra_ff_forms,
        "crumbs": [("Homepage", reverse("teacher_index")),
                   ("Course", reverse("edit_course", args=[coursework.course.code]))]

    }
    return render(request, 'teacher/view_cw.html', detail)

def generate_teacher_easy_match_form(coursework, post=None):
    """Given an instance of @coursework, generate a
    relevant easymatchform"""
    tests = [(t.id, t.display_name) for t in m.Submission.objects.filter(
        coursework=coursework,
        type__in=[m.SubmissionType.TEST_CASE, m.SubmissionType.SIGNATURE_TEST])]
    solutions = [(s.id, s.display_name) for s in m.Submission.objects.filter(
        coursework=coursework,
        type__in=[m.SubmissionType.SOLUTION, m.SubmissionType.ORACLE_EXECUTABLE])]
    if post is None:
        return cf.EasyMatchForm(tests, solutions)
    return cf.EasyMatchForm(tests, solutions, post)

@login_required()
@require_teacher
def create_test_match(request, c):
    """Create a new test match, according to what is specified in the POST request"""
    coursework = m.Coursework.objects.get(id=c)
    if not request.POST:
        return HttpResponseForbidden("You're supposed to POST a form here")
    if not p.is_enrolled_on_course(request.user, coursework.course):
        return HttpResponseForbidden("You're not enrolled on this course")
    return manual_test_match_update(request, coursework)

def manual_test_match_update(request, coursework):
    """Create a new test match with data specified in @request for @coursework"""
    tm_form = generate_teacher_easy_match_form(coursework, request.POST)
    if not tm_form.is_valid():
        return HttpResponseBadRequest("Invalid form data")
    try:
        new_tm = matcher.create_teacher_test(
            tm_form.cleaned_data['solution'],
            tm_form.cleaned_data['test'],
            coursework
        )
        r.run_test_on_thread(new_tm)
    except Exception as e:
        return HttpResponseBadRequest(str(e))
    return redirect(request, "Test created",
                    reverse('view_cw', args=[coursework.id]))

@login_required()
@require_teacher
def run_all_test_in_cw(request, c):
    """Run all of the queued tests for coursework with id @c"""
    cw = m.Coursework.objects.get(id=c)
    if not p.is_enrolled_on_course(request.user, cw.course):
        return HttpResponseForbidden("you're not enrolled on this course")
    r.run_queued_tests_on_thread(cw)
    return redirect(request, "Starting to run queued tests", reverse('view_cw', args=[cw.id]))

@login_required()
@require_teacher
@transaction.atomic()
def update_content(request):
    if request.method != "POST":
        return HttpResponseForbidden("You are only allowed to POST here")
    old_sub = m.Submission.objects.get(id=request.POST['old_id'])
    if not p.is_enrolled_on_course(request.user, old_sub.coursework.course):
        return HttpResponseForbidden("you're not enrolled on this course")

    old_sub.increment_version()
    for each in request.FILES.getlist('new_content'):
        old_sub.save_uploaded_file(each)

    return redirect(request, "Content Updated", reverse('edit_cw', args=[old_sub.coursework.id]))


#!/usr/bin/python

import os
import sys
import subprocess

def main():
    # number of nodes (first line)
    number_of_nodes = int(sys.stdin.readline())
    # number_of_nodes = int(input())

    # coordinates of nodes
    nodes = [ { 'x':0.0, 'y':0.0 } for _ in range(number_of_nodes) ]
    for i in range(number_of_nodes):
        line = sys.stdin.readline()
        x_str, y_str = line.split(' ')
        nodes[i]['x'] = float(x_str)
        nodes[i]['y'] = float(y_str)

    # route
    route = []
    while True:
        line = sys.stdin.readline()
        if not line:
            break
        else:
            route.append( int(line[:-1]) )
    route.append( route[0] )

    # generate plot data file
    file_vertex = 'data/points.dat'
    file_edge   = 'data/lines.dat'

    with open( file_vertex, 'w' ) as f:
        for node in nodes:
            f.write( '%d %d\n' % ( int(node['x']), int(node['y']) ) )

    with open( file_edge, 'w' ) as f:
        for i in route:
            f.write( '%d %d\n' % ( int(nodes[i]['x']), int(nodes[i]['y']) ) )

    # execute gnuplot
    subprocess.call( './plot.gp', shell=True)

if __name__ ==  '__main__':
    main()


import requests
import unittest
from HTMLTestRunner import HTMLTestRunner

if __name__ == '__main__':
    testunit = unittest.TestSuite()
    # testunit.addTest(testloginapi("testloginapi"))
    fp = open('result11.html', 'wb')
    runner = HTMLTestRunner(stream=fp,
                            title='运营商实时爬虫接口',
                            description='用例执行情况：')
    discover = unittest.defaultTestLoader.discover('E:\\test2\\exclelianxi\\lianxi\\port', pattern='logintest.py')
    runner.run(discover)
    fp.close()


class Recipe:
    def __init__(self, name, cooking_lvl, cooking_time, ingredients, recipe_type, description=''):
        self.name = name
        self.cooking_lvl = cooking_lvl
        self.cooking_time = cooking_time
        self.ingredients = ingredients
        self.description = description
        self.recipe_type = recipe_type

    def __str__(self):
        """Return the string to print with the recipe info"""
        txt = f"Name: {self.name}\nCooking Level: {self.cooking_lvl}\nCooking Time: {self.cooking_time}\nIngredients: {self.ingredients}\nRecipe Type: {self.recipe_type}\nDescription: {self.description}"
        return txt


# -*- coding: utf-8 -*-

try:
    from datetime import timedelta
    from functools import partial

    from django.conf import settings
    from django.contrib.contenttypes.models import ContentType
    from django.db import models
    from django.db.models.fields import FieldDoesNotExist
    from django.test.utils import override_settings
    from django.utils.timezone import now
    from django.utils.translation import ugettext as _

    from ..base import CremeTestCase

    from creme.creme_core.backends import _BackendRegistry
    from creme.creme_core.backends.csv_import import CSVImportBackend
    from creme.creme_core.backends.xls_import import XLSImportBackend
    from creme.creme_core.core.batch_process import batch_operator_manager, BatchAction
    from creme.creme_core.core.entity_cell import (EntityCellRegularField,
        EntityCellCustomField, EntityCellFunctionField, EntityCellRelation)
    from creme.creme_core.core.enumerable import _EnumerableRegistry, Enumerator
    from creme.creme_core.core.function_field import (FunctionField, FunctionFieldResult,
        FunctionFieldsManager, _FunctionFieldRegistry, function_field_registry)
    from creme.creme_core.core.imprint import _ImprintManager
    from creme.creme_core.core.job import JobManager
    from creme.creme_core.core.reminder import Reminder, ReminderRegistry, reminder_registry
    from creme.creme_core.core.sandbox import SandboxType, _SandboxTypeRegistry, sandbox_type_registry
    from creme.creme_core.core.setting_key import SettingKey, _SettingKeyRegistry
    from creme.creme_core.creme_jobs import reminder_type
    from creme.creme_core.models import (RelationType, Job, Imprint, Sandbox,
        CustomField, CustomFieldEnumValue, FakeContact, FakeOrganisation, FakeCivility,
        FakeDocument, FakeImageCategory, FakeImage, Language, FakeAddress, FakeReport)
    from creme.creme_core.models.fields import CTypeForeignKey, EntityCTypeForeignKey
    from creme.creme_core.utils.date_period import HoursPeriod
    from creme.creme_core.utils.dates import round_hour
except Exception as e:
    print('Error in <{}>: {}'.format(__name__, e))

class BackendsTestCase(CremeTestCase):
    def test_registry(self):
        registry = _BackendRegistry([
            'creme.creme_core.backends.csv_import.CSVImportBackend',
            'creme.creme_core.backends.xls_import.XLSImportBackend',
        ])

        self.assertEqual(CSVImportBackend, registry.get_backend(CSVImportBackend.id))
        self.assertEqual(XLSImportBackend, registry.get_backend(XLSImportBackend.id))
        self.assertIsNone(registry.get_backend('unknown'))

        self.assertEqual({CSVImportBackend, XLSImportBackend},
                         set(registry.iterbackends())
                        )
        self.assertEqual({CSVImportBackend, XLSImportBackend},
                         set(registry.backends)
                        )

        self.assertEqual({CSVImportBackend.id, XLSImportBackend.id},
                         set(registry.iterkeys())
                        )
        self.assertEqual({CSVImportBackend.id, XLSImportBackend.id},
                         set(registry.extensions)
                        )

    def test_registration_errors(self):
        registry = _BackendRegistry([
            'creme.creme_core.backends.csv_import.CSVImportBackend',
            'creme.creme_core.backends.csv_import.CSVImportBackend',  # Twice
        ])

        with self.assertRaises(registry.DuplicatedId):
            registry.get_backend(CSVImportBackend.id)

    # TODO: test with invalid path

class FunctionFieldsTestCase(CremeTestCase):
    def test_manager01(self):
        "Constructor with no args, add() & get() methods"
        ffm = FunctionFieldsManager()
        self.assertFalse(list(ffm))

        fname01 = 'name01'
        fname02 = 'name02'

        class TestFunctionField01(FunctionField):
            name         = fname01
            verbose_name = 'Verbose name 01'

        class TestFunctionField02(FunctionField):
            name         = fname02
            verbose_name = 'Verbose name 02'

        ff01 = TestFunctionField01()
        ff02 = TestFunctionField02()

        self.assertIsNone(ffm.get(fname01))

        ffm.add(ff01, ff02)
        self.assertIs(ff01, ffm.get(fname01))
        self.assertIs(ff02, ffm.get(fname02))
        self.assertEqual([ff01, ff02], sorted(ffm, key=lambda ff: ff.name))

    def test_manager02(self):
        "Constructor with args"
        fname01 = 'name01'
        fname02 = 'name02'

        class TestFunctionField01(FunctionField):
            name         = fname01
            verbose_name = 'Verbose name 01'

        class TestFunctionField02(FunctionField):
            name         = fname02
            verbose_name = "Verbose name 02"

        ff01 = TestFunctionField01()
        ff02 = TestFunctionField02()

        ffm = FunctionFieldsManager(ff01, ff02)
        self.assertIs(ff01, ffm.get(fname01))
        self.assertIs(ff02, ffm.get(fname02))
        self.assertEqual([ff01, ff02], sorted(ffm, key=lambda ff: ff.name))

    def test_manager03(self):
        "new() method"
        fname01 = 'name01'
        fname02 = 'name02'

        class TestFunctionField01(FunctionField):
            name         = fname01
            verbose_name = 'Verbose name 01'

        class TestFunctionField02(FunctionField):
            name         = fname02
            verbose_name = 'Verbose name 02'

        ff01 = TestFunctionField01()
        ff02 = TestFunctionField02()

        ffm01 = FunctionFieldsManager(ff01)
        ffm02 = ffm01.new(ff02)

        self.assertIs(ff01, ffm01.get(fname01))
        self.assertIsNone(ffm01.get(fname02))
        self.assertEqual([ff01], list(ffm01))

        self.assertIs(ff01, ffm02.get(fname01))
        self.assertIs(ff02, ffm02.get(fname02))
        self.assertEqual([ff01, ff02], sorted(ffm02, key=lambda ff: ff.name))

    def test_manager04(self):
        "new() method + add() on 'base instance'"
        fname01 = 'name01'
        fname02 = 'name02'

        class TestFunctionField01(FunctionField):
            name         = fname01
            verbose_name = 'Verbose name 01'

        class TestFunctionField02(FunctionField):
            name         = fname02
            verbose_name = 'Verbose name 02'

        ff01 = TestFunctionField01()
        ff02 = TestFunctionField02()

        ffm01 = FunctionFieldsManager()
        ffm02 = ffm01.new(ff02)

        ffm01.add(ff01)  # <== added after new()

        self.assertIs(ff01, ffm01.get(fname01))
        self.assertIsNone(ffm01.get(fname02))
        self.assertEqual([ff01], list(ffm01))

        self.assertIs(ff02, ffm02.get(fname02))
        self.assertIs(ff01, ffm02.get(fname01)) # ok ?
        self.assertEqual([ff01, ff02], sorted(ffm02, key=lambda ff: ff.name))

    def test_registry01(self):
        class Klass1: pass
        class Klass2(Klass1): pass

        registry = _FunctionFieldRegistry()

        fname11 = 'name11'
        fname12 = 'name12'
        fname13 = 'name13'
        fname2  = 'name2'

        class TestFunctionField11(FunctionField):
            name         = fname11
            verbose_name = 'Verbose name 11'

        class TestFunctionField12(FunctionField):
            name         = fname12
            verbose_name = 'Verbose name 12'

        class TestFunctionField13(FunctionField):
            name         = fname13
            verbose_name = 'Verbose name 13'

        class TestFunctionField2(FunctionField):
            name         = fname2
            verbose_name = 'Verbose name 2'

        registry.register(Klass1, TestFunctionField11, TestFunctionField12, TestFunctionField13)
        registry.register(Klass2, TestFunctionField2)
        self.assertIsInstance(registry.get(Klass1, fname11), TestFunctionField11)
        self.assertIsInstance(registry.get(Klass1, fname12), TestFunctionField12)
        self.assertIsInstance(registry.get(Klass1, fname13), TestFunctionField13)
        self.assertIsNone(registry.get(Klass1, 'unknown'))
        self.assertIsNone(registry.get(Klass1, fname2))

        self.assertIsInstance(registry.get(Klass2, fname11), TestFunctionField11)
        self.assertIsInstance(registry.get(Klass2, fname12), TestFunctionField12)
        self.assertIsInstance(registry.get(Klass2, fname2),  TestFunctionField2)

        self.assertIsNone(registry.get(Klass1, fname2))

        # Function fields
        self.assertEqual({TestFunctionField11, TestFunctionField12, TestFunctionField13},
                         {ff.__class__ for ff in registry.fields(Klass1)}
                        )
        self.assertEqual({TestFunctionField11, TestFunctionField12, TestFunctionField13, TestFunctionField2},
                         {ff.__class__ for ff in registry.fields(Klass2)}
                        )

        # Unregister -----
        registry.unregister(Klass1, TestFunctionField11, TestFunctionField12)
        self.assertIsNone(registry.get(Klass1, fname11))
        self.assertIsNone(registry.get(Klass1, fname12))
        self.assertIsInstance(registry.get(Klass1, fname13), TestFunctionField13)

        self.assertIsNone(registry.get(Klass2, fname11))

    def test_registry02(self):
        "Duplicates error"
        class Klass: pass

        registry = _FunctionFieldRegistry()

        class TestFunctionField1(FunctionField):
            name         = 'name1'
            verbose_name = 'Verbose name 1'

        class TestFunctionField2(FunctionField):
            name         = TestFunctionField1.name # <==
            verbose_name = 'Verbose name 2'

        registry.register(Klass, TestFunctionField1)

        with self.assertRaises(_FunctionFieldRegistry.RegistrationError):
            registry.register(Klass, TestFunctionField2)

    def test_registry03(self):
        "Overridden field"
        class Klass1: pass
        class Klass2(Klass1): pass

        registry = _FunctionFieldRegistry()

        fname1 = 'name1'
        fname2 = 'name2'

        class TestFunctionField1(FunctionField):
            name         = fname1
            verbose_name = 'Verbose name 1'

        class TestFunctionField2(FunctionField):
            name         = fname2
            verbose_name = 'Verbose name 2'

        class TestFunctionField22(FunctionField):
            name         = TestFunctionField2.name  # <== Override
            verbose_name = 'Verbose name 2'

        registry.register(Klass1, TestFunctionField1, TestFunctionField2)
        registry.register(Klass2, TestFunctionField22)
        self.assertIsInstance(registry.get(Klass2, fname1), TestFunctionField1)
        self.assertIsInstance(registry.get(Klass2, fname2), TestFunctionField22)  # Not TestFunctionField2

        # Function fields
        self.assertEqual({TestFunctionField1, TestFunctionField2},
                         {ff.__class__ for ff in registry.fields(Klass1)}
                        )
        self.assertEqual({TestFunctionField1, TestFunctionField22},
                         {ff.__class__ for ff in registry.fields(Klass2)}
                        )

    def test_registry04(self):
        "Unregister() error"
        class Klass: pass

        registry = _FunctionFieldRegistry()

        class TestFunctionField(FunctionField):
            name         = 'name'
            verbose_name = 'Verbose name'

        with self.assertRaises(_FunctionFieldRegistry.RegistrationError):
            registry.unregister(Klass, TestFunctionField)

    # TODO: test other classes

class BatchOperatorTestCase(CremeTestCase):
    def test_upper(self):
        op = batch_operator_manager.get(models.CharField, 'upper')
        self.assertFalse(op.need_arg)
        self.assertEqual('GALLY', op('gally'))

    def test_lower(self):
        op = batch_operator_manager.get(models.CharField, 'lower')
        self.assertFalse(op.need_arg)
        self.assertEqual('gally', op('GALLY'))

    def test_title(self):
        op = batch_operator_manager.get(models.CharField, 'title')
        self.assertFalse(op.need_arg)
        self.assertEqual('Gally', op('gally'))

    def test_prefix(self):
        op = batch_operator_manager.get(models.CharField, 'prefix')
        self.assertTrue(op.need_arg)
        self.assertEqual('My Gally', op('Gally', 'My '))

    def test_suffix(self):
        op = batch_operator_manager.get(models.CharField, 'suffix')
        self.assertTrue(op.need_arg)
        self.assertEqual('Gally my love', op('Gally', ' my love'))

    def test_remove_substring(self):
        op = batch_operator_manager.get(models.CharField, 'rm_substr')
        self.assertTrue(op.need_arg)
        fieldval = 'Gally the battle angel'
        self.assertEqual('Gally the angel', op(fieldval, 'battle '))
        self.assertEqual(fieldval, op(fieldval, 'evil '))

    def test_remove_start(self):
        op = batch_operator_manager.get(models.CharField, 'rm_start')
        self.assertTrue(op.need_arg)
        self.assertEqual('Gally', op('GGGally', 2))
        self.assertEqual('',      op('Gally',   op.cast('10')))

    def test_remove_end(self):
        op = batch_operator_manager.get(models.CharField, 'rm_end')
        self.assertTrue(op.need_arg)
        self.assertEqual('Gally', op('Gallyyy', 2))
        self.assertEqual('',      op('Gally',   op.cast('10')))

    def test_add_int(self):
        op = batch_operator_manager.get(models.IntegerField, 'add_int')
        self.assertEqual(3, op(1, 2))
        self.assertEqual(5, op(4, op.cast('1')))

    def test_substract_int(self):
        op = batch_operator_manager.get(models.IntegerField, 'sub_int')
        self.assertEqual(1, op(3, 2))
        self.assertEqual(3, op(4, op.cast('1')))

    def test_multiply_int(self):
        op = batch_operator_manager.get(models.IntegerField, 'mul_int')
        self.assertEqual(6, op(3, 2))
        self.assertEqual(8, op(2, op.cast('4')))

    def test_divide_int(self):
        op = batch_operator_manager.get(models.IntegerField, 'div_int')
        self.assertEqual(3, op(6, 2))
        self.assertEqual(2, op(9, op.cast('4')))

    def test_operators01(self):
        ops = {(op_name, str(op)) for op_name, op in batch_operator_manager.operators(models.CharField)}
        self.assertIn(('upper', _('To upper case')), ops)
        self.assertIn(('lower', _('To lower case')), ops)
        self.assertNotIn('add_int', (e[0] for e in ops))

    def test_operators02(self):
        ops = {(op_name, str(op)) for op_name, op in batch_operator_manager.operators(models.IntegerField)}
        self.assertIn(('add_int', _('Add')), ops)
        self.assertNotIn('prefix', (e[0] for e in ops))

    def test_operators03(self):
        ops = {(op_name, str(op)) for op_name, op in batch_operator_manager.operators()}
        self.assertIn(('mul_int', _('Multiply')), ops)
        self.assertIn(('suffix',  _('Suffix')), ops)

class BatchActionTestCase(CremeTestCase):
    def test_changed01(self):
        baction = BatchAction(FakeContact, 'first_name', 'upper', value='')
        haruhi = FakeContact(first_name='Haruhi', last_name='Suzumiya')
        self.assertTrue(baction(haruhi))
        self.assertEqual('HARUHI', haruhi.first_name)

    def test_changed02(self):
        baction = BatchAction(FakeContact, 'last_name', 'rm_substr', value='Foobar')
        first_name = 'Haruhi'
        last_name = 'Suzumiya'
        haruhi = FakeContact(first_name=first_name, last_name=last_name)
        self.assertFalse(baction(haruhi))
        self.assertEqual(last_name,  haruhi.last_name)
        self.assertEqual(first_name, haruhi.first_name)

    def test_cast(self):
        baction = BatchAction(FakeContact, 'last_name', 'rm_start', value='3')
        haruhi = FakeContact(first_name='Haruhi', last_name='Suzumiya')
        baction(haruhi)
        self.assertEqual('umiya', haruhi.last_name)

    def test_null_field(self):
        baction = BatchAction(FakeContact, 'first_name', 'upper', value='')
        haruhi = FakeContact(first_name=None, last_name='Suzumiya')
        self.assertFalse(baction(haruhi))
        self.assertIsNone(haruhi.first_name)

    def test_operand_error(self):
        with self.assertRaises(BatchAction.ValueError) as cm:
            BatchAction(FakeContact, 'last_name', 'rm_start', value='three')  # Not int

        self.assertEqual(_('{operator} : {message}.').format(
                                operator=_('Remove the start (N characters)'),
                                message=_('enter a whole number'),
                            ),
                         str(cm.exception)
                        )

        with self.assertRaises(BatchAction.ValueError) as cm:
            BatchAction(FakeContact, 'last_name', 'rm_end', value='-3')  # Not positive

        self.assertEqual(_('{operator} : {message}.').format(
                                operator=_('Remove the end (N characters)'),
                                message=_('enter a positive number'),
                            ),
                         str(cm.exception)
                        )

    def test_unicode01(self):
        baction = BatchAction(FakeContact, 'first_name', 'upper', value='')
        self.assertEqual(_('{field} ➔ {operator}').format(
                                field=_('First name'),
                                operator=_('To upper case'),
                            ),
                         str(baction)
                        )

    def test_unicode02(self):
        "With argument"
        value = 'Foobarbaz'
        baction = BatchAction(FakeContact, 'last_name', 'rm_substr', value=value)
        self.assertEqual(_('{field} ➔ {operator}: «{value}»').format(
                                field=_('Last name'),
                                operator=_('Remove a sub-string'),
                                value=value,
                            ),
                         str(baction)
                        )

class EntityCellTestCase(CremeTestCase):
    @classmethod
    def setUpClass(cls):
        # super(EntityCellTestCase, cls).setUpClass()
        super().setUpClass()

        cls.contact_ct = ContentType.objects.get_for_model(FakeContact)

    def test_build_4_field01(self):
        field_name = 'first_name'
        cell = EntityCellRegularField.build(model=FakeContact, name=field_name)
        self.assertIsInstance(cell, EntityCellRegularField)
        self.assertEqual(field_name,      cell.value)
        self.assertEqual(_('First name'), cell.title)
        self.assertEqual('regular_field-first_name', cell.key)
        self.assertIs(cell.has_a_filter, True)
        self.assertIs(cell.editable, True)
        self.assertIs(cell.sortable, True)
        self.assertIs(cell.is_multiline, False)
        self.assertEqual('first_name__icontains', cell.filter_string)

    def test_build_4_field02(self):
        "Date field"
        cell = EntityCellRegularField.build(model=FakeContact, name='birthday')
        self.assertEqual('birthday__range', cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW,     cell.listview_css_class)
        self.assertEqual(settings.CSS_DATE_HEADER_LISTVIEW, cell.header_listview_css_class)

    def test_build_4_field03(self):
        "Boolean field"
        cell = EntityCellRegularField.build(model=FakeContact, name='is_a_nerd')
        self.assertEqual('is_a_nerd__creme-boolean', cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW, cell.listview_css_class)

    def test_build_4_field04(self):
        "ForeignKey"
        cell = EntityCellRegularField.build(model=FakeContact, name='position')
        self.assertEqual('regular_field-position', cell.key)
        self.assertEqual('position', cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW, cell.listview_css_class)

        cell = EntityCellRegularField.build(model=FakeContact, name='image')
        self.assertEqual('image__header_filter_search_field__icontains',
                         cell.filter_string
                        )
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW, cell.listview_css_class)

    def test_build_4_field05(self):
        "Basic ForeignKey subfield"
        cell = EntityCellRegularField.build(model=FakeContact, name='position__title')
        self.assertEqual('regular_field-position__title', cell.key)
        self.assertEqual('position__title__icontains', cell.filter_string)

        cell = EntityCellRegularField.build(model=FakeContact, name='image__name')
        self.assertEqual('image__name__icontains', cell.filter_string)

    def test_build_4_field06(self):
        "Date ForeignKey subfield"
        cell = EntityCellRegularField.build(model=FakeContact, name='image__created')
        self.assertEqual('{} - {}'.format(_('Photograph'), _('Creation date')), cell.title)
        self.assertEqual('image__created__range', cell.filter_string)

    def test_build_4_field07(self):
        "ForeignKey subfield is a FK"
        cell = EntityCellRegularField.build(model=FakeDocument, name='linked_folder__category')

        self.assertTrue(cell.has_a_filter)
        self.assertEqual('linked_folder__category', cell.filter_string)

    def test_build_4_field08(self):
        "ManyToMany"
        cell = EntityCellRegularField.build(model=FakeContact, name='languages')
        self.assertTrue(cell.has_a_filter)
        self.assertFalse(cell.sortable)
        self.assertTrue(cell.is_multiline)
        self.assertEqual('languages', cell.filter_string)

        cell = EntityCellRegularField.build(model=FakeContact, name='languages__name')
        self.assertTrue(cell.has_a_filter)
        self.assertFalse(cell.sortable)
        self.assertTrue(cell.is_multiline)
        self.assertEqual('languages__name__icontains', cell.filter_string)

    def test_build_4_field_errors(self):
        build = partial(EntityCellRegularField.build, model=FakeContact)
        self.assertIsNone(build(name='unknown_field'))
        self.assertIsNone(build(name='user__unknownfield'))

    def test_build_4_customfield01(self):
        "INT CustomField"
        name = 'Size (cm)'
        customfield = CustomField.objects.create(name=name, field_type=CustomField.INT,
                                                 content_type=self.contact_ct
                                                )

        cell = EntityCellCustomField(customfield)
        self.assertIsInstance(cell, EntityCellCustomField)
        self.assertEqual(str(customfield.id), cell.value)
        self.assertEqual(name,                cell.title)
        self.assertEqual('custom_field-{}'.format(customfield.id), cell.key)
        self.assertIs(cell.has_a_filter, True)
        self.assertIs(cell.editable,     False)
        self.assertIs(cell.sortable,     False)
        self.assertIs(cell.is_multiline, False)
        self.assertEqual('customfieldinteger__value__icontains', cell.filter_string)
        self.assertEqual(settings.CSS_NUMBER_LISTVIEW,           cell.listview_css_class)
        self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW,   cell.header_listview_css_class)

        cell = EntityCellCustomField.build(FakeContact, customfield.id)
        self.assertIsInstance(cell, EntityCellCustomField)
        self.assertEqual(str(customfield.id), cell.value)

        self.assertIsNone(EntityCellCustomField.build(FakeContact, 1000))

    def test_build_4_customfield02(self):
        "FLOAT CustomField"
        customfield = CustomField.objects.create(name='Weight', field_type=CustomField.FLOAT,
                                                 content_type=self.contact_ct
                                                )

        cell = EntityCellCustomField(customfield)
        self.assertEqual('customfieldfloat__value__icontains', cell.filter_string)
        self.assertEqual(settings.CSS_NUMBER_LISTVIEW,         cell.listview_css_class)
        self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class)

    def test_build_4_customfield03(self):
        "DATE CustomField"
        customfield = CustomField.objects.create(name='Day', field_type=CustomField.DATETIME,
                                                 content_type=self.contact_ct
                                                )

        cell = EntityCellCustomField(customfield)
        self.assertEqual('customfielddatetime__value__range', cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW,     cell.listview_css_class)
        self.assertEqual(settings.CSS_DATE_HEADER_LISTVIEW, cell.header_listview_css_class)

    def test_build_4_customfield04(self):
        "BOOL CustomField"
        customfield = CustomField.objects.create(name='Is fun ?', field_type=CustomField.BOOL,
                                                 content_type=self.contact_ct
                                                )

        cell = EntityCellCustomField(customfield)
        self.assertEqual('customfieldboolean__value__creme-boolean', cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW,        cell.listview_css_class)
        self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class)

    def test_build_4_customfield05(self):
        "ENUM CustomField"
        customfield = CustomField.objects.create(name='Eva', field_type=CustomField.ENUM,
                                                 content_type=self.contact_ct
                                                )

        create_enumvalue = partial(CustomFieldEnumValue.objects.create, custom_field=customfield)
        create_enumvalue(value='Eva-00')
        create_enumvalue(value='Eva-01')

        cell = EntityCellCustomField(customfield)
        self.assertEqual('customfieldenum__value__exact',      cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW,        cell.listview_css_class)
        self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class)

    def test_build_4_customfield06(self):
        "MULTI_ENUM CustomField"
        customfield = CustomField.objects.create(name='Eva', field_type=CustomField.MULTI_ENUM,
                                                 content_type=self.contact_ct
                                                )

        create_enumvalue = partial(CustomFieldEnumValue.objects.create, custom_field=customfield)
        create_enumvalue(value='Eva-00')
        create_enumvalue(value='Eva-01')

        cell = EntityCellCustomField(customfield)
        self.assertEqual('customfieldmultienum__value__exact', cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW,        cell.listview_css_class)
        self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class)

    def test_build_4_relation(self):
        loves = RelationType.create(('test-subject_love', 'Is loving'),
                                    ('test-object_love',  'Is loved by')
                                   )[0]
        cell = EntityCellRelation(model=FakeContact, rtype=loves)
        self.assertIsInstance(cell, EntityCellRelation)
        self.assertEqual(FakeContact,     cell.model)
        self.assertEqual(str(loves.id),   cell.value)
        self.assertEqual(loves.predicate, cell.title)
        self.assertEqual('relation-{}'.format(loves.id), cell.key)
        self.assertIs(cell.has_a_filter, True)
        self.assertIs(cell.editable,     False)
        self.assertIs(cell.sortable,     False)
        self.assertIs(cell.is_multiline, True)
        self.assertEqual('',    cell.filter_string)
        self.assertEqual(loves, cell.relation_type)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW,        cell.listview_css_class)
        self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class)

    def test_build_4_functionfield01(self):
        name = 'get_pretty_properties'
        # funfield = FakeContact.function_fields.get(name)
        funfield = function_field_registry.get(FakeContact, name)
        self.assertIsNotNone(funfield)

        cell = EntityCellFunctionField(model=FakeContact, func_field=funfield)
        self.assertIsInstance(cell, EntityCellFunctionField)
        self.assertEqual(name,            cell.value)
        self.assertEqual(str(funfield.verbose_name), cell.title)
        self.assertEqual('function_field-{}'.format(funfield.name), cell.key)
        self.assertIs(cell.has_a_filter, True)  # TODO: test with a non-filterable FunctionField
        self.assertIs(cell.editable,     False)
        self.assertIs(cell.sortable,     False)
        self.assertIs(cell.is_hidden,    False)
        self.assertIs(cell.is_multiline, True)
        self.assertEqual('', cell.filter_string)
        self.assertEqual(settings.CSS_DEFAULT_LISTVIEW,        cell.listview_css_class)
        self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class)

        cell = EntityCellFunctionField.build(FakeContact, func_field_name=name)
        self.assertIsInstance(cell, EntityCellFunctionField)
        self.assertEqual(name, cell.value)

        self.assertIsNone(EntityCellFunctionField.build(FakeContact, func_field_name='invalid'))

    def test_build_4_functionfield02(self):
        class PhoneFunctionField(FunctionField):
            name         = 'phone_or_mobile'
            verbose_name = 'Phone or mobile'

            def __call__(self, entity, user):
                return FunctionFieldResult(entity.phone or entity.mobile)

        funfield = PhoneFunctionField()
        cell = EntityCellFunctionField(model=FakeContact, func_field=funfield)
        self.assertEqual(funfield.name,         cell.value)
        self.assertEqual(funfield.verbose_name, cell.title)
        self.assertFalse(cell.is_multiline)

class SettingKeyTestCase(CremeTestCase):
    def test_register(self):
        sk1 = SettingKey('creme_core-test_sk_string',
                         description='Page title',
                         app_label='creme_core',
                         type=SettingKey.STRING,
                         blank=True,
                        )
        sk2 = SettingKey('creme_core-test_sk_int',
                         description='Page size',
                         app_label='creme_core',
                         type=SettingKey.INT, hidden=False,
                        )
        sk3 = SettingKey('creme_core-test_sk_bool',
                         description='Page hidden',
                         app_label='creme_core',
                         type=SettingKey.BOOL,
                        )

        registry = _SettingKeyRegistry()
        registry.register(sk1, sk2, sk3)

        # ------
        with self.assertNoException():
            sk = registry[sk1.id]
        self.assertEqual(sk1, sk)

        self.assertEqual(sk2, registry[sk2.id])
        self.assertEqual(sk3, registry[sk3.id])

        # ------
        all_key_ids = {sk.id for sk in registry}
        self.assertIn(sk1.id, all_key_ids)
        self.assertIn(sk2.id, all_key_ids)
        self.assertIn(sk3.id, all_key_ids)

        # ------
        registry.unregister(sk1, sk3)

        self.assertEqual(sk2, registry[sk2.id])

        with self.assertRaises(KeyError):
            registry[sk1.id]

        with self.assertRaises(KeyError):
            registry[sk3.id]

        all_key_ids = {sk.id for sk in registry}
        self.assertIn(sk2.id, all_key_ids)
        self.assertNotIn(sk1.id, all_key_ids)
        self.assertNotIn(sk3.id, all_key_ids)

        # with self.assertNoException():
        with self.assertRaises(registry.RegistrationError):
            registry.unregister(sk3)

    def test_duplicate(self):
        sk1 = SettingKey('creme_core-test_sk_string',
                         description='Page title',
                         app_label='creme_core',
                         type=SettingKey.STRING,
                         blank=True,
                        )
        sk2 = SettingKey(sk1.id,  # <===
                         description='Page size',
                         app_label='creme_core',
                         type=SettingKey.INT, hidden=False,
                        )

        registry = _SettingKeyRegistry()
        registry.register(sk1)

        with self.assertRaises(registry.RegistrationError):
            registry.register(sk2)

class ReminderTestCase(CremeTestCase):
    def test_empty(self):
        registry = ReminderRegistry()

        self.assertFalse(list(registry))
        self.assertFalse(list(registry.itervalues()))

    # def test_register_legacy(self):
    #     registry = ReminderRegistry()
    #
    #     class TestReminder1(Reminder):
    #         id = Reminder.generate_id('creme_core', 'ReminderTestCase_test_register_legacy_1')
    #
    #     class TestReminder2(Reminder):
    #         id = Reminder.generate_id('creme_core', 'ReminderTestCase_test_register_legacy_2')
    #
    #     reminder1 = TestReminder1()
    #     reminder2 = TestReminder2()
    #     registry.register(reminder1)
    #     registry.register(reminder2)
    #     self.assertEqual({(reminder1.id, reminder1),
    #                       (reminder2.id, reminder2),
    #                      },
    #                      set(registry)
    #                     )
    #     self.assertEqual({reminder1, reminder2}, set(registry.itervalues()))
    #
    #     # --
    #     registry.unregister(reminder1)
    #     self.assertEqual([reminder2], list(registry.itervalues()))
    #
    #     with self.assertNoException():
    #         registry.unregister(reminder1)

    def test_register(self):
        registry = ReminderRegistry()

        class TestReminder1(Reminder):
            id = Reminder.generate_id('creme_core', 'ReminderTestCase_test_register_1')

        class TestReminder2(Reminder):
            id = Reminder.generate_id('creme_core', 'ReminderTestCase_test_register_2')

        registry.register(TestReminder1)
        registry.register(TestReminder2)

        # items = dict(registry)
        # self.assertEqual(2, len(items))
        # self.assertIsInstance(items[TestReminder1.id], TestReminder1)
        # self.assertIsInstance(items[TestReminder2.id], TestReminder2)
        self.assertEqual({TestReminder1, TestReminder2},
                         {r.__class__ for r in registry}
                        )
        self.assertEqual({TestReminder1, TestReminder2},
                         {r.__class__ for r in registry.itervalues()}
                        )

        # --
        registry.unregister(TestReminder1)
        self.assertEqual([TestReminder2],
                         # [r.__class__ for r in registry.itervalues()]
                         [r.__class__ for r in registry]
                        )

        # with self.assertNoException():
        with self.assertRaises(registry.RegistrationError):
            registry.unregister(TestReminder1)

    def test_register_error(self):
        registry = ReminderRegistry()

        class TestReminder1(Reminder):
            id = Reminder.generate_id('creme_core', 'ReminderTestCase_test_register_error')

        class TestReminder2(Reminder):
            id = TestReminder1.id  # < ===

        registry.register(TestReminder1)

        with self.assertRaises(registry.RegistrationError):
            registry.register(TestReminder2)

class JobManagerTestCase(CremeTestCase):
    def setUp(self):
        # super(JobManagerTestCase, self).setUp()
        super().setUp()
        self.reminders = []

    def tearDown(self):
        # super(JobManagerTestCase, self).tearDown()
        super().tearDown()

        for reminder in self.reminders:
            reminder_registry.unregister(reminder)

    def _register_reminder(self, reminder):
        reminder_registry.register(reminder)
        self.reminders.append(reminder)

    @override_settings(PSEUDO_PERIOD=1)
    def test_next_wake_up01(self):
        "PSEUDO_PERIODIC job"
        rounded_hour = round_hour(now())
        job = Job.objects.get(type_id=reminder_type.id)

        # self.assertEqual(rounded_hour, job.reference_run)
        if job.reference_run != rounded_hour:
            job.reference_run = rounded_hour
            job.save()

        self.assertEqual(HoursPeriod(value=1), job.real_periodicity)

        next_wakeup = JobManager()._next_wakeup

        next_hour = rounded_hour + timedelta(hours=1)
        self.assertEqual(next_hour, next_wakeup(job))

        job.reference_run = rounded_hour - timedelta(hours=1)  # should not be used because "rounded_hour" is given
        self.assertEqual(next_hour, next_wakeup(job, reference_run=rounded_hour))

    @override_settings(PSEUDO_PERIOD=1)
    def test_next_wake_up02(self):
        "PSEUDO_PERIODIC job + reminder return a wake up date before the new security period"
        rounded_hour = round_hour(now())
        job = Job.objects.get(type_id=reminder_type.id)

        wake_up = rounded_hour + timedelta(minutes=20)

        class TestReminder(Reminder):
            id = Reminder.generate_id('creme_core', 'test_jobmanager_1')

            def next_wakeup(self, now_value):
                return wake_up

        # self._register_reminder(TestReminder())
        self._register_reminder(TestReminder)
        self.assertEqual(wake_up, JobManager()._next_wakeup(job))

    @override_settings(PSEUDO_PERIOD=1)
    def test_next_wake_up03(self):
        "PSEUDO_PERIODIC job + reminder return a wake up date after the new security period"
        rounded_hour = round_hour(now())
        job = Job.objects.get(type_id=reminder_type.id)

        class TestReminder(Reminder):
            id = Reminder.generate_id('creme_core', 'test_jobmanager_2')

            def next_wakeup(self, now_value):
                return rounded_hour + timedelta(minutes=70)

        # self._register_reminder(TestReminder())
        self._register_reminder(TestReminder)

        self.assertEqual(rounded_hour + timedelta(hours=1),
                         JobManager()._next_wakeup(job)
                        )

class SandboxTestCase(CremeTestCase):
    def test_registry01(self):
        name = 'Test sandbox #1'

        class TestSandboxType1(SandboxType):
            id = SandboxType.generate_id('creme_core', 'test1')
            verbose_name = name

        sandbox_type_registry.register(TestSandboxType1)  # TODO: unregister in tearDown ?

        sandbox = Sandbox(type_id=TestSandboxType1.id)

        st_type = sandbox.type
        self.assertIsInstance(st_type, TestSandboxType1)
        self.assertEqual(name, st_type.verbose_name)

    def test_registry02(self):
        registry = _SandboxTypeRegistry()

        st_id = SandboxType.generate_id('creme_core', 'test2')

        class TestSandboxType2_2(SandboxType):
            id = st_id
            verbose_name = 'Test sandbox #2'

        class TestSandboxType2_3(SandboxType):
            id = st_id
            verbose_name = 'Test sandbox #3'

        registry.register(TestSandboxType2_2)

        with self.assertRaises(_SandboxTypeRegistry.Error):
            registry.register(TestSandboxType2_3)

        sandbox1 = Sandbox(type_id=TestSandboxType2_2.id)
        self.assertIsInstance(registry.get(sandbox1), TestSandboxType2_2)

        class TestSandboxType2_4(SandboxType):  # Not registered
            id = SandboxType.generate_id('creme_core', 'unknown')
            verbose_name = 'Test sandbox #4'

        sandbox2 = Sandbox(type_id=TestSandboxType2_4.id)

        with self.assertLogs(level='CRITICAL') as logs_manager:
            sb_type = registry.get(sandbox2)

        self.assertIsNone(sb_type)
        self.assertEqual(logs_manager.output,
                         ['CRITICAL:creme.creme_core.core.sandbox:Unknown SandboxType: {}'.format(
                                 TestSandboxType2_4.id
                            ),
                         ],
                        )

    def test_sandbox_data(self):
        user = self.login()
        fmt = 'Restricted to "{}"'

        class TestSandboxType3(SandboxType):
            id = SandboxType.generate_id('creme_core', 'test3')

            @property
            def verbose_name(self):
                return fmt.format(self.sandbox.user)

        sandbox_type_registry.register(TestSandboxType3)  # TODO: unregister in tearDown ?

        sandbox = Sandbox(type_id=TestSandboxType3.id, user=user)
        self.assertEqual(fmt.format(user), sandbox.type.verbose_name)

class ImprintManagerTestCase(CremeTestCase):
    def test_register_n_get(self):
        manager = _ImprintManager()
        self.assertIsNone(manager.get_granularity(FakeContact))

        manager.register(FakeContact, minutes=60)
        self.assertEqual(timedelta(minutes=60), manager.get_granularity(FakeContact))

        manager.register(FakeDocument, hours=2)
        self.assertEqual(timedelta(hours=2), manager.get_granularity(FakeDocument))

    def test_double_registering(self):
        manager = _ImprintManager()
        manager.register(FakeContact, minutes=60)

        with self.assertRaises(manager.RegistrationError):
            manager.register(FakeContact, minutes=90)

    def test_create01(self):
        manager = _ImprintManager()
        manager.register(FakeContact, minutes=60)

        user = self.login()
        willy = FakeContact.objects.create(user=user, first_name='Willy', last_name='Wonka')
        self.assertFalse(Imprint.objects.all())

        imprint = manager.create_imprint(entity=willy, user=user)
        self.assertIsInstance(imprint, Imprint)
        self.assertIsNotNone(imprint.id)
        self.assertDatetimesAlmostEqual(now(), imprint.date)
        self.assertEqual(imprint.entity.get_real_entity(), willy)
        self.assertEqual(imprint.user, user)

    def test_create02(self):
        "Delay is not passed"
        manager = _ImprintManager()
        manager.register(FakeContact, minutes=60)

        user = self.login()
        willy = FakeContact.objects.create(user=user, first_name='Willy', last_name='Wonka')

        imprint1 = Imprint.objects.create(entity=willy, user=user)
        self.assertIsNone(manager.create_imprint(entity=willy, user=user))

        # Other entity
        charlie = FakeContact.objects.create(user=user, first_name='Charlie', last_name='Bucket')
        self.assertIsNotNone(manager.create_imprint(entity=charlie, user=user))

        # Other user
        other_user = self.other_user
        imprint3 = manager.create_imprint(entity=willy, user=other_user)
        self.assertIsNotNone(imprint3)
        self.assertEqual(imprint3.user, other_user)

        # With older imprint
        Imprint.objects.filter(id=imprint1.id).update(date=now() - timedelta(minutes=59))
        self.assertIsNone(manager.create_imprint(entity=willy, user=user))

    def test_create03(self):
        "Delay_is passed"
        manager = _ImprintManager()
        manager.register(FakeContact, minutes=30)

        user = self.login()
        willy = FakeContact.objects.create(user=user, first_name='Willy', last_name='Wonka')

        imprint1 = Imprint.objects.create(entity=willy, user=user)
        Imprint.objects.filter(id=imprint1.id).update(date=now() - timedelta(minutes=31))

        self.assertIsNotNone(manager.create_imprint(entity=willy, user=user))

    def test_create04(self):
        "Model not registered"
        manager = _ImprintManager()
        manager.register(FakeDocument, minutes=60)

        user = self.login()
        willy = FakeContact.objects.create(user=user, first_name='Willy', last_name='Wonka')

        self.assertIsNone(manager.create_imprint(entity=willy, user=user))

class EnumerableTestCase(CremeTestCase):
    def test_basic_choices_fk(self):
        user = self.login()
        registry = _EnumerableRegistry()

        enum1 = registry.enumerator_by_fieldname(model=FakeContact, field_name='civility')
        expected = [{'value': id, 'label': title}
                        for id, title in FakeCivility.objects.values_list('id', 'title')
                   ]
        self.assertEqual(expected, enum1.choices(user))

        # --
        field = FakeContact._meta.get_field('civility')
        enum2 = registry.enumerator_by_field(field=field)
        self.assertEqual(expected, enum2.choices(user))

    def test_basic_choices_m2m(self):
        user = self.login()
        registry = _EnumerableRegistry()

        enum1 = registry.enumerator_by_fieldname(model=FakeImage, field_name='categories')
        expected = [{'value': id, 'label': name}
                        for id, name in FakeImageCategory.objects.values_list('id', 'name')
                   ]
        self.assertEqual(expected, enum1.choices(user))

        # --
        field = FakeImage._meta.get_field('categories')
        enum2 = registry.enumerator_by_field(field)
        self.assertEqual(expected, enum2.choices(user))

    def test_basic_choices_limited_choices_to(self):
        user = self.login()
        registry = _EnumerableRegistry()

        create_lang = Language.objects.create
        lang1 = create_lang(name='Klingon [deprecated]')
        lang2 = create_lang(name='Namek')

        enum1 = registry.enumerator_by_fieldname(model=FakeContact, field_name='languages')
        choices = enum1.choices(user)
        ids = {t['value'] for t in choices}
        self.assertIn(lang2.id, ids)
        self.assertNotIn(lang1.id, ids)

        # --
        field = FakeContact._meta.get_field('languages')
        enum2 = registry.enumerator_by_field(field)
        self.assertEqual(choices, enum2.choices(user))

    def test_choices_not_entity_model(self):
        registry = _EnumerableRegistry()

        with self.assertRaises(ValueError) as error_ctxt1:
            registry.enumerator_by_fieldname(model=FakeAddress, field_name='entity')

        self.assertEqual('This model is not a CremeEntity: creme.creme_core.tests.fake_models.FakeAddress',
                         str(error_ctxt1.exception)
                        )

        # --
        field = FakeAddress._meta.get_field('entity')

        with self.assertRaises(ValueError) as error_ctxt2:
            registry.enumerator_by_field(field)

        self.assertEqual('This model is not a CremeEntity: creme.creme_core.tests.fake_models.FakeAddress',
                         str(error_ctxt2.exception)
                        )

    def test_choices_field_does_not_exist(self):
        registry = _EnumerableRegistry()

        with self.assertRaises(FieldDoesNotExist):
            registry.enumerator_by_fieldname(model=FakeContact, field_name='unknown')

    def test_choices_field_not_enumerable(self):
        registry = _EnumerableRegistry()

        with self.assertRaises(ValueError) as error_ctxt1:
            registry.enumerator_by_fieldname(model=FakeContact, field_name='address')

        self.assertEqual('This field is not enumerable: creme_core.FakeContact.address',
                         str(error_ctxt1.exception)
                        )

        # --
        field = FakeContact._meta.get_field('address')
        with self.assertRaises(ValueError) as error_ctxt2:
            registry.enumerator_by_field(field)

        self.assertEqual('This field is not enumerable: creme_core.FakeContact.address',
                         str(error_ctxt2.exception)
                        )

    def test_register_related_model(self):
        class FakeCivilityEnumerator1(Enumerator):
            pass

        registry = _EnumerableRegistry()
        registry.register_related_model(FakeCivility, FakeCivilityEnumerator1)

        enumerator = partial(registry.enumerator_by_fieldname, model=FakeContact)
        self.assertIsInstance(enumerator(field_name='civility'),
                              FakeCivilityEnumerator1
                             )
        self.assertNotIsInstance(enumerator(field_name='sector'),
                                 FakeCivilityEnumerator1
                                )

        # Model already registered
        class FakeCivilityEnumerator2(Enumerator):
            pass

        with self.assertRaises(registry.RegistrationError):
            registry.register_related_model(FakeCivility, FakeCivilityEnumerator2)

    def test_register_specific_field(self):
        class FakeContactSectorEnumerator1(Enumerator):
            pass

        registry = _EnumerableRegistry()
        registry.register_field(FakeContact, field_name='sector',
                                enumerator_class=FakeContactSectorEnumerator1,
                               )

        enumerator1 = registry.enumerator_by_fieldname
        self.assertIsInstance(enumerator1(model=FakeContact, field_name='sector'),
                              FakeContactSectorEnumerator1
                             )
        self.assertNotIsInstance(enumerator1(model=FakeOrganisation, field_name='sector'),
                                 FakeContactSectorEnumerator1
                                )

        # --
        field = FakeContact._meta.get_field('sector')
        self.assertIsInstance(registry.enumerator_by_field(field),
                              FakeContactSectorEnumerator1
                             )

        # Field registered
        class FakeContactSectorEnumerator2(Enumerator):
            pass

        with self.assertRaises(registry.RegistrationError):
            registry.register_field(FakeContact, field_name='sector',
                                    enumerator_class=FakeContactSectorEnumerator2,
                                   )

    def test_register_field_type01(self):
        class EntityCTypeForeignKeyEnumerator(Enumerator):
            pass

        registry = _EnumerableRegistry()
        registry.register_field_type(EntityCTypeForeignKey,
                                     enumerator_class=EntityCTypeForeignKeyEnumerator,
                                    )

        self.assertIsInstance(registry.enumerator_by_fieldname(model=FakeReport, field_name='ctype'),
                              EntityCTypeForeignKeyEnumerator
                             )

    def test_register_field_type02(self):
        "Inheritance"
        class CTypeForeignKeyEnumerator(Enumerator):
            pass

        registry = _EnumerableRegistry()
        registry.register_field_type(CTypeForeignKey,
                                     enumerator_class=CTypeForeignKeyEnumerator,
                                    )

        self.assertIsInstance(registry.enumerator_by_fieldname(model=FakeReport, field_name='ctype'),
                              CTypeForeignKeyEnumerator
                             )

    def test_convert_choices(self):
        self.assertEqual(
            [{'value': 1, 'label': 'Bad'},
             {'value': 2, 'label': 'Not bad'},
             {'value': 3, 'label': 'Great'},
            ],
            list(Enumerator.convert_choices(
                [(1, 'Bad'), (2, 'Not bad'), (3, 'Great')]
            ))
        )

    def test_convert_choices_with_group(self):
        self.assertEqual(
            [{'value': 'vinyl',   'label': 'Vinyl',    'group': 'Audio'},
             {'value': 'cd',      'label': 'CD',       'group': 'Audio'},
             {'value': 'vhs',     'label': 'VHS Tape', 'group': 'Video'},
             {'value': 'dvd',     'label': 'DVD',      'group': 'Video'},
             {'value': 'unknown', 'label': 'Unknown'},

            ],
            list(Enumerator.convert_choices(
                [('Audio',
                    (('vinyl', 'Vinyl'),
                     ('cd',    'CD'),
                    )
                 ),
                 ('Video',
                    (('vhs', 'VHS Tape'),
                     ('dvd', 'DVD'),
                    )
                 ),
                 ('unknown', 'Unknown'),
                ]
            ))
        )


# https://www.acmicpc.net/problem/6497
# Online Judge 코드 작동 방식이 기존과 조금 다름(아래 코드 참고)
# 13:45-14:20
# My Solution
'''
간선의 비용 합이 최소가 되도록하는 MST를 구하는 문제
-> Kruskal
'''
string = '''7 11
0 1 7
0 3 5
1 2 8
1 3 9
1 4 7
2 4 5
3 4 15
3 5 6
4 5 8
4 6 9
5 6 11
0 0'''

# string = '''2 1
# 1 2'''

def testcase(string):
    string = string.split("\n")
    for i in string:
        yield i

G = testcase(string)

def input():
    return next(G)

import sys
input = sys.stdin.readline

def find_p(parent, x):
    if parent[x] != x:
        parent[x] = find_p(parent, parent[x])
    return parent[x]

def union_p(parent, a, b):
    a = find_p(parent, a)
    b = find_p(parent, b)
    if a < b:
        parent[b] = a
    else:
        parent[a] = b

while True:
    m, n = map(int, input().split())
    if (m, n) == (0, 0):
        break
    parent = list(range(m + 1))

    edges = []
    total_cost = 0
    for i in range(n):
        a, b, c = map(int, input().split())
        edges.append((c, a, b))
        total_cost += c

    edges.sort()

    min_cost = 0
    for edge in edges:
        cost, a, b = edge
        if find_p(parent, a) != find_p(parent, b):
            union_p(parent, a, b)
            min_cost += cost
    print(total_cost - min_cost)

# Solution (동빈북 문제 기준)
# 특정 원소가 속한 집합 찾기
def find_parent(parent, x):
    if parent[x] != x:
        parent[x] = find_parent(parent, parent[x])
    return parent[x]

def union_parent(parent, a, b):
    a = find_parent(parent, a)
    b = find_parent(parent, b)
    if a < b:
        parent[b] = a
    else:
        parent[a] = b

m, n = map(int, input().split())
parent = [0] * (n + 1)

# 모든 간선을 담을 리스트와 최종 비용 담을 변수
edges = []
result = 0

for i in range(1, n + 1):
    parent[i] = i

for _ in range(m):
    x, y, z = map(int, input().split())
    edges.append((z, x, y))

edges.sort()
total = 0 # 전체 가로등 비용

for edge in edges:
    cost, a, b = edge
    total += cost
    # 사이클이 발생하지 않는 경우만 집합에 포함
    if find_parent(parent, a) != find_parent(parent, b):
        union_parent(parent, a, b)
        result += cost
print(total - result)


import networkx as nx
import time
import matplotlib.pyplot as plt
import random
from IPython import display
import operator
from functools import reduce
import numpy as np
import copy

p = 0.3 #The probability p in Erdos-Renyi random graph model
N = 20  # number of nodes
M = 10000  # A large number represents the maximum message limit
INF = 999  # distance upper bound

#The first function used to generate the graph
def BuildGraph_Matrix_1(n):
    Graph = [[0] * n for row in range(n)]#Initialize the graph

    for i in range(0, n):
        for j in range(0, n):
            if i == j:
                Graph[i][j] = 0
            else:
                Graph[i][j] = INF #Set disconnected nodes to INF(999)
    # randomly set edges base on p
    for i in range(0, n):
        for j in range(i, n):
            if i != j and random.random() <= p:
                Graph[i][j] = 1
                Graph[j][i] = 1
    return Graph

#Calculate the shortest distance between two nodes
def Floyd(vertex_total):
    distance = [[0] * vertex_total for row in range(vertex_total)]
    # initialize
    for i in range(0, vertex_total):
        for j in range(0, vertex_total):
            distance[i][j] = Graph_Matrix[i][j]
    # Use the Floyd algorithm to find the shortest distance between two vertices of all vertices
    for k in range(0, vertex_total):
        for i in range(0, vertex_total):
            for j in range(i, vertex_total):
                if distance[i][k] + distance[k][j] < distance[i][j]:
                    distance[i][j] = distance[i][k] + distance[k][j]
                    distance[j][i] = distance[i][k] + distance[k][j]
    return distance

Graph_Matrix = BuildGraph_Matrix_1(14)
distance = Floyd(14)

Graph_Matrix
distance

G = nx.MultiDiGraph()
G.add_node(1, pos=(2, 10))
G.add_node(2, pos=(4, 14))
G.add_node(3, pos=(4, 6))
G.add_node(4, pos=(6, 10))
G.add_node(5, pos=(8, 14))
G.add_node(6, pos=(8, 6))
G.add_node(7, pos=(10, 10))
G.add_node(8, pos=(12, 14))
G.add_node(9, pos=(12, 6))
G.add_node(10, pos=(14, 10))
G.add_node(11, pos=(6, 18))
G.add_node(12, pos=(10, 18))
G.add_node(13, pos=(6, 2))
G.add_node(14, pos=(10, 2))

# pos = nx.get_node_attributes(G,'pos')
pos = nx.circular_layout(G)
node_sizes = [500]*14
node_colors = ['green']*14

for i in range(0,14):
    print("Node %d's neighbor is :" % (i+1))
    for j in range(0,14):
        if Graph_Matrix[i][j] == 1:
            print("%d " % (j+1))
            G.add_edge(i + 1,j + 1)

plt.clf()
plt.title("Test")
plt.figure(1)
#nx.draw(G, pos, with_labels=True, node_size=node_sizes, edgecolors='black', node_color=node_colors, connectionstyle='arc3, rad = 0.1')
nx.draw(G, pos, with_labels=True, node_size=node_sizes, edgecolors='black', node_color=node_colors, arrows=False)
plt.show(block=False)
plt.pause(0)


import json
import re
import requests

def string_validation(s, rgx, exc):
    """

    :param str s:
    :param str rgx:
    :param ApzmException exc:
    :return:
    """
    try:
        type_check = type(s) == str
        value_check = re.search(rgx, s)

        if not type_check or value_check is None:
            raise exc("Invalid mark value.")
    except Exception as e:
        raise exc(e)

def numeric_validation(num_value, param_type, param_range, exc):
    """

    :param num_value:
    :param param_type:
    :param list param_range:
    :param ApzmException exc:
    :return:
    """
    try:
        check_param_type = type(num_value) == param_type
        check_param_value = min(param_range) <= num_value <= max(param_range)

        if not check_param_type or not check_param_value:
            raise exc("Invalid value.")

    except Exception as e:
        raise exc(e)

def request_prediction(endpoint, df):
    """

    :param str endpoint:
    :param pd.Dataframe df:
    """
    headers = {"Content-Type": "application/json"}
    payload = json.dumps({"data": df.to_json()})

    response = requests.request('post', endpoint, headers=headers, data=payload)
    if response.status_code == 200:
        return response.json()['prediction']
    else:
        raise Exception

def request_registry(endpoint):
    """

    :param str endpoint:
    """

    response = requests.request('get', endpoint)
    if response.status_code == 200:

        return response.json()['records']
    else:
        raise Exception

def request_new_record(endpoint, data):
    """

    :param endpoint:
    :param Client data:
    :return:
    """
    headers = {"Content-Type": "application/json"}
    payload = json.dumps({"data": data.build_predict_payload()})
    url = '/'.join((endpoint, data.__id__))
    response = requests.request('post', url, headers=headers, data=payload)
    if response.status_code == 200:
        pass
    else:
        raise Exception


#例如将123反转成321
class Solution:
    def reverse(self,number):
        a=int(number/100)
        b=int((number%100)/10)
        c=number%10
        return c*100+b*10+a

if __name__=='__main__':
    number=123
    sol=Solution()
    nummber_reverse=sol.reverse(number)
    print('---before reverse：{}'.format(number))
    print('---after reverse: {}'.format(nummber_reverse))

1_反转一个3位整数.py

1_反转一个3位整数.py


import pandas as pd
import re
from WM_Translator import Translator

# WM_DREAM extracts and transforms data from WM-EAM download.
class WM_DREAM(object):

    def __init__(self, path, trail):
        trans = Translator(path)
        self.trans = trans
        self.path = path
        self.trail = trail
        return

    # load_bodon creates a new DataFrame of corresponding A and BES numbers using Translator.
    def load_bodon(self):
        df_bodon = self.trans.bodon()
        self.df_bodon = df_bodon

    # dream_import creates a new DataFrame based on data from WM-EAM download.
    # dream_import also defines two unique lists of A numbers and project tasks respectively.
    def dream_import(self):

        # To mount data from WM-EAM download and store it in df_og for further cleansing.
        powerball = [1, 3, 4, 5, 6, 9, 10, 13, 14, 15]
        df_og = pd.read_excel(self.trail, usecols=powerball)
        row = df_og.shape[0] - 1
        df_og = df_og.iloc[3:row, :]
        df_og.columns = df_og.loc[3]
        df_og = df_og.drop(3, axis=0)
        cashmoney = ["A", "ReportNo", "Status", "Location", "WO_Date", "Foreman", "Task", "8in_Daily", "12in_Daily"]
        df_og.columns = cashmoney

        # To normalize time data, weed out cancelled projects, and sort by dates.
        df_og["WO_Date"] = pd.to_datetime(df_og["WO_Date"])
        df_og = df_og[df_og["Status"] != "Cancelled"]
        df_og = df_og.sort_values("WO_Date", ascending=False)

        # To generate two unique lists of A numbers and project tasks.
        anums = list(pd.Series(df_og["A"]).drop_duplicates())
        tasks = list(pd.Series(df_og["Task"]).drop_duplicates().dropna())

        self.df_og = df_og
        self.anums = anums
        self.tasks = tasks

    # output utilizes temp_frame to compile summaries for individual BES in bes_list.
    def output(self):

        # To create an empty dictionary to store the summaries of all projects.
        output_dict = {}

        # To loop through the A numbers and call temp_frame to summarize a particular A number.
        # To update output_dict with the results returned by temp_frame.
        for anum in self.anums:
            output_dict.update(self.temp_frame(self.df_og, anum, self.tasks))

        # To transfer data in output_dict to a new DataFrame, df_output.
        # To re-organized and re-name columns for export.
        df_output = pd.DataFrame.from_dict(output_dict, orient = "index")
        qtip = ["BES", "Update_Date", "ReportNo", "Status", "Foreman", "Total_Footage", "Mobilization", \
                   "Pressure_S", "Pressure_E", "Chlorination_S", "Chlorination_E", "ChlorinationApp_S", \
                   "ChlorinationApp_E", "Final_S", "Final_E", "Completion"]
        df_output.columns = qtip

        self.df_output = df_output

    # temp_frame takes three arguments (df, anum, task) from output to generate metrics for a particular project.
    # temp_frame utilizes vladivostok to compute task milestones.
    # temp_frame returns a dictionary to be added to output_dict in output later.
    def temp_frame(self, df_og, anum, tasks):

        # To create a temporary dictionary to hold the project metrics.
        temp_dict = {}

        # To verify the A number.
        bes = self.trans.bes_lookup(self.df_bodon, anum[2:8])

        # To extract all data pertaining to the project using its A number.
        df_temp = df_og[df_og["A"]==anum].sort_values("WO_Date", ascending=False)

        # To update project status to mode.
        # To reset the index of df_temp for further analysis.
        mode = str(pd.Series(df_temp["Status"]).mode())
        status = re.sub("[0-9 ]", "", mode).split("\n", 1)[0]
        df_temp = df_temp.reset_index(drop=True)

        # To record the date and report number of the most recent project update.
        update_date = df_temp.loc[0, "WO_Date"]
        report_no = df_temp.loc[0, "ReportNo"]

        # To determine the foreman responsible for the project.
        foreman = df_temp.loc[0, "Foreman"]

        # To remove duplicate data with criteria: report number, 8in daily footage, and 12in daily footage.
        df_footcalc = df_temp.drop_duplicates(subset=["ReportNo", "8in_Daily", "12in_Daily", "16in_Daily"])

        """

        FUTURE: footage calculation using machine learning

        """

        # To calculate the total footage of the project.
        footage = round((df_footcalc["8in_Daily"].sum() + df_footcalc["12in_Daily"].sum() + df_footcalc["16in_Daily"].sum()), 1)

        # To produce a new dictionary from the given sequence of task with a value of 0.
        template = dict.fromkeys(tasks, 0)

        # To acquire compute task milestones by call vladivostok.
        milestones = self.vladivostok(template, df_temp)

        # To assign specific milestones to corresponding variables.
        mob = milestones["MOBILIZATION"][0]
        p_start = milestones["PRESSURETEST"][0]
        p_end = milestones["PRESSURETEST"][1]
        c_start = milestones["CHLORINATION"][0]
        c_end = milestones["CHLORINATION"][1]
        ca_start = milestones["CHLORINATIONAPP"][0]
        ca_end = milestones["CHLORINATIONAPP"][1]
        f_start = milestones["FINALCONN"][0]
        f_end = milestones["FINALCONN"][1]
        comp = milestones["WMCOMP"][0]

        # To combine the A number with its milestones (in variables) into a temporary dictionary for return.
        temp_dict = {anum : [bes, update_date, report_no, status, foreman, footage, mob, p_start, p_end, c_start, c_end, \
                          ca_start, ca_end, f_start, f_end, comp]}

        return temp_dict

    # vladivostok is very remote yet highly critical to the delicate balance of the universe.
    # vladivostok takes two arguments from temp_frame (template, df_temp) to compute task milestones.
    # vladivostok stores milestones in template and returns it.
    def vladivostok(self, template, df_temp):

        # To loop through all assigned tasks in template.
        for item in template:

            # To create a temporary list to store miletones.
            list_vlad = []

            # To extract all data pertaining to a particular task.
            df_vlad = df_temp[df_temp["Task"]==item]
            df_vlad = df_vlad.reset_index(drop=True)

            # If no milestone is present, mark the temporary list empty.
            if df_vlad.empty:
                list_vlad = ["-", "-"]

            # If milestones are detected, retrieve the first and last date to represent the start and end date.
            # To normalize time data.
            else:
                start_date = df_vlad.loc[(df_vlad.shape[0] - 1), "WO_Date"].strftime("%m/%d/%Y")
                end_date = df_vlad.loc[0, "WO_Date"].strftime("%m/%d/%Y") + " (" + str(df_vlad.shape[0]) + ")"
                list_vlad = [start_date, end_date]

            # To store the milestone results in template under corresponding task.
            template[item] = list_vlad

        return template


def printTable(table):

    colWidth = [max([len(ele) for ele in lst]) for lst in table]
    '''
    # Alternative Method
    row , col =  len(table[0]) , len(table)

    # Transpose table
    table = list(zip(*table))

    for i in range(row) :
        for j in range(col):
            print(ttable[i][j].rjust(colWidth[j]),end = ' ')
        print()
    '''
    for col in range(len(table[0])):
        for row in range(len(table)):
            print(table[row][col].rjust(colWidth[row]), end = ' ')
        print()

tableData = [['apples', 'oranges', 'cherries', 'banana'],
             ['Alice', 'Bob', 'Carol', 'David'],
             ['dogs', 'cats', 'moose', 'goose']]

printTable(tableData)

Automate the boring stuff with python/6_Manipulating_Strings/Table Printer.py


from typing import List

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        n = len(nums)
        l, r = 0, n - 1

        while l < r:
            mid = l + (r - l) // 2

            if nums[mid] < nums[mid + 1]:   l = mid + 1
            else:                           r = mid

        return l

# 162. Find Peak Element
# https://leetcode.com/problems/find-peak-element/description/