from django.http import HttpResponse, HttpResponseRedirect, Http404
from django import forms
from django.views.generic import ListView, CreateView, UpdateView
from django.template import loader
from django.shortcuts import render, redirect
from inv_check.models import Item, Coming, Sale, Order, FrontPics
from django.db.models import Max, Q
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from django.core.exceptions import ValidationError
import json

from .forms import NameForm,ItemSaleForm, addItemForm, orderForm, itemOrderForm,orderContactForm #, ItemSelectForm

## -------------------------- home-page functions --------------------------
def index(request):
   #return HttpResponse("
SC Cycling Inventory
")
   latestYear = Item.objects.aggregate(Max('year'))['year__max']

   # fetch the latest items that are for sale
   latestItems = Item.objects.filter(year=latestYear) & Item.objects.filter(forSale=True)
   latestItems = latestItems.order_by('-retail_price').order_by('-gender')
   # create dictionary of fields and values to print
   datDict = {}
   datDict['headers'] =  ['item','gender','available sizes','price']
   datDict['rows'] = []
   countFields = [ 'xxs','xs','s','m','l','xl','xxl','xxxl','count']

   for item in latestItems:
       itemName =  item.item
       sizeList = []
       for size in countFields:
            if getattr(item,size)>0:
                sizeList.append(size)
       datDict['rows'].append([itemName.title(),item.gender,",".join(sizeList).upper(),item.retail_price])

   # create context
   picList = [i for i in FrontPics.objects.all()][:3]
   context = {'fields':datDict, 'frontImages':picList}
   return render(request, 'inv_check/index.html', context)

## --------------------------  HELPER FUNCTIONS --------------------------
class itemsListView(ListView):
    model = Item
    context_object_name = 'item'

def fetchItemDetails(item_id, fields2exclude = ['coming', 'sale', 'id','order','imgurl_1']):
    countFields = [ 'xxs','xs','s','m','l','xl','xxl','xxxl','count']
    item = Item.objects.get(pk = item_id)

    # get item fields and values into a dictionary
    datDict = {}
    for field in [i.name for i in item._meta.get_fields()]:
        if field not in fields2exclude:
            val = getattr(item, field)
            # filter out 0 value count fields
            if (field in countFields):
                if (val>0):
                    datDict[field.upper()] = val
            else:
                datDict[field.title()] = val
    return(datDict)

def fetchOrderDetails(item_id, fields2exclude = ['coming', 'sale', 'id','order','imgurl_1']):
    countFields = [ 'xxs','xs','s','m','l','xl','xxl','xxxl','count']
    item = Order.objects.get(pk = item_id)

    # get item fields and values into a dictionary
    datDict = {}
    for field in [i.name for i in item._meta.get_fields()]:
        if field not in fields2exclude:
            val = getattr(item, field)
            datDict[field.title()] = val
    return(datDict)

### -------------------------- PUBLIC FUNCTIONS --------------------------
def productViewOrder(request):
    # only show items for sale
    itemChoices = Item.objects.filter(forSale=True).filter(numForsale__gt=0).order_by('item').order_by('-year')

    # get item types
    #itemTypes = [i[0] for i in Item._meta.get_field('itemtype').choices] # this gets all item types

    # get item types from for-sale items
    itemTypes = list(dict.fromkeys([i.itemcategory for i in itemChoices]))
    itemTypes.insert(0, "select")
    itemGenders = list(dict.fromkeys([i.gender for i in itemChoices]))
    itemGenders.insert(0,'select')

    # create initial context
    defaultImageUrl = 'https://drive.google.com/uc?id=1WZFyFdPikqZtkAI1KtvgmMJzJBzNHT8U'
    context = {'items':itemChoices,'itemtypes':itemTypes,'itemgenders':itemGenders, 'fields':[], 'img_url':defaultImageUrl}

    if request.method == 'GET':
        # process GET for filter
        itemType = request.GET.get('item-type')
        itemGender = request.GET.get('item-gender')

        # filter item menu by type or gender
        if itemType:
            itemChoices = Item.objects.filter(itemcategory__icontains=itemType).filter(forSale=True).order_by('item').order_by('-year')
            context['items'] = itemChoices

        if itemGender:
            itemChoices = Item.objects.filter(gender__icontains=itemGender).filter(forSale=True).order_by('item').order_by('-year')
            context['items'] = itemChoices

        # process GET for actual item info request
        itemName = request.GET.get('item-choice')

        # if an item was selected, load its info and picture.'
        # also, show order form
        if itemName:
            # fetch the item from the database
            itemsFound = Item.objects.filter(item__icontains=itemName)
            itemID = itemsFound[0].id

            # display team price if user is authenticated
            if request.user.is_authenticated:
                datDict = fetchItemDetails(itemID,['coming', 'sale','order','id','forSale','imgurl_1','itemcategory','itemtype'])

            # do not display team price if user is not authenticated
            else:
                datDict = fetchItemDetails(itemID,['coming', 'sale','order','id','forSale','team_price','imgurl_1','itemcategory','itemtype'])
            keys = datDict.keys()
            # unpack them into another dictionary for printing table
            #fields = {
            #    'headers': list(keys),
            #    'rows':[datDict[key] for key in keys]}
            fields = []
            for key in datDict:
                fields.append((key,datDict[key]))

            # update output dictionary values based on query results
            context['fields'] = fields
            context['img_url'] = itemsFound[0].imgurl_1

            # get available sizes
            countFields = [ 'xxs','xs','s','m','l','xl','xxl','xxxl','count']
            sizes = [key for key in datDict.keys() if key.lower() in countFields]
            sizeChoices = [(s.lower(),s.upper()) for s in sizes]

            # get max quant that can be ordered
            max_quant = 0
            for s in sizes:
                if datDict[s]>max_quant:
                    max_quant = datDict[s]

            # update form with sizes, item, and quantity max
            context['form'] = itemOrderForm(size_choices = sizeChoices,max_quant = max_quant, item=itemsFound[0].item) #initial = {'item':itemsFound[0],'recipient':'RETAIL'},, item=itemsFound[0]

            #save item to session
            request.session['orderItem'] = itemsFound[0].id

            return render(request, 'inv_check/itemchoicesshowANDorder.html', context)

    #process submitted form
    if request.method == 'POST':
        form = itemOrderForm(request.POST)

        if form.is_valid():
            datDict = {}
            # fetch data from session
            item = Item.objects.get(pk = request.session.get('orderItem'))
            print(item)
            # fetch data from database

            ## if user is authenticated, user team price
            if request.user.is_authenticated:
                datDict['price'] = int(item.team_price)
            ## else use team price
            else:
                datDict['price'] = int(item.retail_price)
            #datDict['item'] = item.item
            datDict['item'] = form.cleaned_data['item']
            #datDict['img_url'] = item.imgurl_1

            # fetch data from the form
            datDict['size'] = form.cleaned_data['size'].upper()
            datDict['quantity'] = int(form.cleaned_data['quantity'])

            fields = {}
            fields['rows'] = []
            fields['headers'] = ['item','size','quantity','price']
            fields['rows'].append([datDict[key] for key in fields['headers']])

            # add items to order
            if request.session.get('order'):
                #sessionOrderNum = len(json.loads(request.session.get('order')))
                print("Adding items to order")

                tmpList = json.loads(request.session['order'])
                tmpList.append(fields)
                request.session['order'] = json.dumps(tmpList)
                print(request.session['order'])

            else:
                print("First order addition")
                request.session['order'] = json.dumps([fields])

            print(json.loads(request.session.get('order')))
            #context = {'fields':json.loads(request.session.get('order'))} #:fields

            return redirect('order-cart')
        else:
            print("Form didn't validate")
            print(request.session.get('orderItem'))
            print(form)
            print(form.errors)

    context['form'] = itemOrderForm()#orderForm_part1()
    return render(request, 'inv_check/itemchoicesshowANDorder.html', context)

def orderCart(request):
    # grab order info and print to table
    fields = {}
    context = {'fields':{'headers':[],'rows':[]}}

    # populate order details with order info from session
    if request.session.get('order'):
        basket = json.loads(request.session.get('order'))
        fields['headers'] = basket[0]['headers']
        fields['rows'] = []
        for i in basket:
            fields['rows'].append(*i['rows'])
        print(fields)
        context = {'fields':fields}

    # clear cart on clear button click:
    if request.method=='GET':
        print(request.GET.get('clear-cart'))
        if request.GET.get('clear-cart'):
            request.session.pop('order')
            context['fields'] = {'headers':[],'rows':[]}
            print("basket has been cleared")
            return render(request, 'inv_check/ordercart.html', context)
        print("Get was passed but no action taken")

    if request.method=='POST':
        # if form is valid, create a complete Order form for each entry in the basket
        form = orderContactForm(request.POST)

        # process the form
        if form.is_valid():
            headers = basket[0]['headers']
            # loop over the basket
            for i in basket:
                orderDict = {}
                # process item details
                for col,val in zip(headers,*i['rows']):
                    if col == 'item':
                        itemInst = Item.objects.filter(item__icontains = val)[0]
                        orderDict['item'] = itemInst#[0]
                    if col == 'price':
                        pass
                    if col == 'size':
                        orderDict[col] = val.lower()
                    if col not in ['item', 'price', 'size']:
                        orderDict[col.lower()] = val
                # add contact from contact form
                contactMessage = 'Contact details\n'
                for col in ('name','email','phone','address1','address2','city','state','zip'):
                    orderDict[col] = form.cleaned_data[col]
                    contactMessage += "\t\n" + ": ".join([col,str(form.cleaned_data[col])])
                print(orderDict)
                # save the orders
                order = Order(**orderDict)
                try:
                    order.full_clean()
                    print(" -------- Order  saved! --------------")
                    order.save()
                except ValidationError as e:
                    print(e)
                    raise Http404(" \nIf you see this, email burinski@usc.edu with the above message. Thank you! ")
            # if order is good,
            # clear up the basket:
            request.session.pop('order')
            sendOrderEmail_cart(basket,contactMessage,orderDict['email'])

            # send to confirmation page
            return redirect('order-confirm')

    # get contact info
    context['contactform'] = orderContactForm()
    return render(request, 'inv_check/ordercart.html', context)

def readbasket(basket):
    headers = basket[0]['headers']
    lines = []
    for i in basket:
        linedict = {}
        for col,val in zip(headers,*i['rows']):
            #print(col,val)
            linedict[col] = str(val)
        lines.append(", ".join(["%s: %s" %(key,str(linedict[key])) for key in linedict.keys()]))
    messageContent = "\n".join(lines)
    return(messageContent)

def sendOrderEmail_cart(basket, contactDetails ,recipientemail):
    subjectline = 'New USC Merch Order'
    fromemail = 'info@usccycling.com'
    toemail = 'info@usccycling.com'
    preable = "Your order confirmation USC Cycling Club:\n\n"
    messageContent = readbasket(basket)
    postable = "\n\nThank you and fight on!"
    send_mail(subjectline,
        "\n\n".join([preable,messageContent,contactDetails,postable]),
        fromemail,
        [toemail,recipientemail],
    fail_silently=False)

def sendOrderEmail(datdict):
    subjectline = 'New USC Merch Order'
    fromemail = 'info@usccycling.com'
    toemail = 'info@usccycling.com'
    messageContent = ",\n".join(["%s: %s" %(key,str(datdict[key])) for key in datdict.keys()])
    send_mail(subjectline,
        messageContent,
        fromemail,
        [toemail,datdict['Email']],
    fail_silently=False)

def orderConfirm(request):
   #context = {}
   return render(request, 'inv_check/orderconfirmation.html')

def findBySelectionPublic(request):
    items = Item.objects.all()
    # only show items for sale
    itemChoices = Item.objects.filter(forSale=True).order_by('item').order_by('-year')

    # create initial context
    defaultImageUrl = 'https://drive.google.com/uc?id=1WZFyFdPikqZtkAI1KtvgmMJzJBzNHT8U'
    context = {'itemChoices':itemChoices, 'fields':{'headers':[], 'rows':[]}, 'img_url':defaultImageUrl}

    # show request on the website
    if request.method == 'GET':
        # create a form instance and populate it with data from the request:
        itemName = request.GET.get('item-choice')
        #print(itemName)
        if itemName:
            # fetch the item from the database
            itemsFound = Item.objects.filter(item__icontains=itemName)
            itemID = itemsFound[0].id
            datDict = fetchItemDetails(itemID,['coming', 'sale','order','id','forSale','team_price','imgurl_1'])
            keys = datDict.keys()
            # unpack them into another dictionary for printing table
            fields = {
                'headers': list(keys),
                'rows':[datDict[key] for key in keys]}

            # update output dictionary values based on query results
            context['fields'] = fields
            context['img_url'] = itemsFound[0].imgurl_1
            return render(request, 'inv_check/itemchoicesshow.html', context)

    return render(request, 'inv_check/itemchoicesshow.html', context)

#### ------------ sendOrderEmail is an obsolete function (Delete later) --------------
def sendOrderEmail(datdict):
    subjectline = 'New USC Merch Order'
    fromemail = 'info@usccycling.com'
    toemail = 'info@usccycling.com'
    messageContent = ",\n".join(["%s: %s" %(key,str(datdict[key])) for key in datdict.keys()])
    send_mail(subjectline,
        messageContent,
        fromemail,
        [toemail,datdict['Email']],
    fail_silently=False)
#### --------------  makeOrder is an obsolete function (Delete later) --------------
def makeOrder(request):
    context = {'form':orderForm(),'fields':{'headers':[], 'rows':[],'confirmation':[]}}
    if request.method == 'POST':
        form = orderForm(request.POST)
        if form.is_valid():
            item = form.save(commit=True)
            itemID = item.id
            datDict = fetchOrderDetails(itemID,fields2exclude = ['coming', 'sale', 'id','order','date','completed'])
            keys = datDict.keys()
            # unpack them into another dictionary for printing table
            fields = {
                'headers': list(keys),
                'rows':[datDict[key] for key in keys]}
            context['fields'] = fields
            context['confirmation'] = ['Thank you for your order, confirmation e-mail has been sent', 'Your order details']
            # send notification of new order
            sendOrderEmail(datDict)
            return render(request, 'inv_check/makeorder.html', context)

    return render(request, 'inv_check/makeorder.html', context)

### -------------------------- EBOARD FUNCTIONS: --------------------------
####  -------------- findbyname is an obsolete function (Delete later) --------------
@login_required
def findbyname(request):
    defaultImageUrl = 'https://drive.google.com/uc?id=1WZFyFdPikqZtkAI1KtvgmMJzJBzNHT8U'
    fields = {'headers':[], 'rows':[]}
    context = {'fields':fields, 'img_url':defaultImageUrl}

    # if this is a POST request we need to process the form data
    if request.method == 'GET':
        # create a form instance and populate it with data from the request:
        itemName = request.GET.get('item-name')
        print(itemName)
        if itemName:
            itemsFound = Item.objects.filter(item__icontains=itemName)
            try:
                itemID = itemsFound[0].id
                datDict = fetchItemDetails(itemID)
                keys = datDict.keys()
                # unpack them into another dictionary for printing table
                fields = {
                    'headers': list(keys),
                    'rows':[datDict[key] for key in keys]}
                context = {'fields':fields}
                context['img_url'] = itemsFound[0].imgurl_1
                return render(request, 'inv_check/detailshow.html', context)
            except IndexError:
                context['fields']['headers'] = ['item']
                context['fields']['rows'] = ["Can't find item. Being more specific may help"]

    return render(request, 'inv_check/detailshow.html', context)

## Select item functions
@login_required
def findBySelection(request):
    items = Item.objects.all()
    # show all items
    itemChoices = Item.objects.all().order_by('item').order_by('-year')

    # create initial context
    defaultImageUrl = 'https://drive.google.com/uc?id=1WZFyFdPikqZtkAI1KtvgmMJzJBzNHT8U'
    context = {'itemChoices':itemChoices, 'fields':{'headers':[], 'rows':[]}, 'img_url':defaultImageUrl}

    # show request on the website
    if request.method == 'GET':
        # create a form instance and populate it with data from the request:
        itemName = request.GET.get('item-choice')
        print(itemName)
        if itemName:
            itemsFound = Item.objects.filter(item__icontains=itemName)
            itemID = itemsFound[0].id
            datDict = fetchItemDetails(itemID, )
            keys = datDict.keys()
            # unpack them into another dictionary for printing table
            fields = {
                'headers': list(keys),
                'rows':[datDict[key] for key in keys]}
            context['fields'] = fields
            context['img_url'] = itemsFound[0].imgurl_1
            return render(request, 'inv_check/itemchoicesshow.html', context)

    return render(request, 'inv_check/itemchoicesshow.html', context)

@login_required
def recordSale(request):
   context = {'form':ItemSaleForm(),'fields':{'headers':[], 'rows':[]}}
   if request.method == 'POST':
        form = ItemSaleForm(request.POST)

        if form.is_valid():
            sale = form.save(commit=False)
            # get corresponding item
            item = Item.objects.get(item__iexact = sale.item.item)
            itemID = item.id
            if sale.size == 'count':
                n = item.count #old quant
                m = sale.quantity #new quant
                item.count = n - m
                item.save(update_fields = ['count'])
            if sale.size != 'count':
                n = getattr(item, sale.size) #old value
                m = n-sale.quantity #new value
                setattr(item,sale.size,m)
                item.save(update_fields=[sale.size])
        context['fields'] = {'headers':['size','old quantity', 'new quantity'],'rows':[sale.item.item,n,m]}
        return render(request, 'inv_check/sale.html', context)
   return render(request, 'inv_check/sale.html', context)

@login_required
def addNewItem(request):
   context = {'form':addItemForm(),'fields':{'headers':[], 'rows':[]}}
   if request.method == 'POST':
        form = addItemForm(request.POST)
        if form.is_valid():
            item = form.save(commit=False)
            item.save()
        itemID = item.id
        datDict = fetchItemDetails(itemID)
        keys = datDict.keys()
        # unpack them into another dictionary for printing table
        fields = {
            'headers': list(keys),
            'rows':[datDict[key] for key in keys]}
        context['fields'] = fields
        return render(request, 'inv_check/newItem.html', context)

   return render(request, 'inv_check/newitem.html', context)

@login_required
def showInventory(request, item_id):
    try:
        datDict = fetchItemDetails(item_id)
        keys = datDict.keys()
        # unpack them into another dictionary for printing table
        fields = {
            'headers': list(keys),
            'rows':[datDict[key] for key in keys]}
        context = {'fields':fields}

    except Item.DoesNotExist:
        raise Http404("Item does not exist")

    return render(request, 'inv_check/detail.html', context)

@login_required(login_url='/accounts/login/')
def showOrders_ver2(request):
    context = {'fields':{'headers':[], 'rows':[]}} #,'form':ItemSaleForm()}
    orders = Order.objects.filter(completed=False)
    context['orders'] = orders

    # print incompleted orders
    if orders:
        fields = [s.name for s in orders[0]._meta.get_fields()]
        datDict = {}
        datDict['headers'] =  fields
        datDict['rows'] = []
        for order in orders:
            row = []
            for field in fields:
                row.append(getattr(order,field))
            datDict['rows'].append(row)
        for key in context['fields'].keys():
            context['fields'][key] = datDict[key]

    else:
        context['fields']['headers'] = ['Orders']
        context['fields']['rows']=[["Seems there are no outstanding orders"]]

    # mark selected order as completed
    if request.method == 'GET':
        orderID = request.GET.get("item-order")
        if orderID:
            print(orderID)
            order2complete = Order.objects.get(id=orderID)
            prefill = {}
            for field in ('item','size','recipient', 'quantity'):
               prefill[field] = getattr(order2complete, field)

            if prefill['recipient'] == 'RETAIL':
                prefill['price'] = Item.objects.get(pk =  prefill['item'].id).retail_price
            else:
                prefill['price'] = Item.objects.get(pk =  prefill['item'].id).team_price

            prefill['ship'] = False

            context['form'] = ItemSaleForm(prefill)

            # mark order as completed
            order2complete.completed=True
            order2complete.save(update_fields = ['completed'])

    # fill fields in case order needs to be modified
    if request.method == 'POST':
        form = ItemSaleForm(request.POST)

        if form.is_valid():
            sale = form.save(commit=False)
            # get corresponding item
            item = Item.objects.get(item__iexact = sale.item.item)
            itemID = item.id
            if sale.size == 'count':
                n = item.count #old quant
                m = sale.quantity #new quant
                item.count = n - m
                item.save(update_fields = ['count'])
            if sale.size != 'count':
                n = getattr(item, sale.size) #old value
                m = n-sale.quantity #new value
                setattr(item,sale.size,m)
                item.save(update_fields=[sale.size])

        context['fields2'] = {'headers':['item','old quantity', 'new quantity'],'rows':[sale.item.item,n,m]}
        return render(request, 'inv_check/showOrders.html', context)

    return render(request, 'inv_check/showOrders.html', context)

## -------------------------- showOrders is now obsolete, delete later ------------------- ##
@login_required(login_url='/accounts/login/')
def showOrders(request):
    context = {'fields':{'headers':[], 'rows':[]}}
    orders = Order.objects.filter(completed=False)
    context['orders'] = orders
    # mark selected order as completed
    if request.method == 'GET':
        orderID = request.GET.get("item-order")
        if orderID:
            print(orderID)
            order2complete = Order.objects.get(id=orderID)
            order2complete.completed = True
            order2complete.save(update_fields=['completed'])

    # print incompleted orders
    if orders:
        fields = [s.name for s in orders[0]._meta.get_fields()]
        datDict = {}
        datDict['headers'] =  fields
        datDict['rows'] = []
        for order in orders:
            row = []
            for field in fields:
                row.append(getattr(order,field))
            datDict['rows'].append(row)
        for key in context['fields'].keys():
            context['fields'][key] = datDict[key]

    else:
        context['fields']['headers'] = ['Orders']
        context['fields']['rows']=[["Seems there are no outstanding orders"]]

    return render(request, 'inv_check/showOrders.html', context)


import requests
import re
from bs4 import BeautifulSoup
from lxml import etree
import time

head = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0',
        'Host': 'movie.douban.com',
        'Cookie': 'll="118208"; bid=7oCzYf0fk0w; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1'
                  '543191350%2C%22https%3A%2F%2Fwww.baidu.com%2Fs%3Fie%3Dutf-8%26f%3D8%26rsv_b'
                  'p%3D1%26rsv_idx%3D1%26tn%3Dmonline_6_dg%26wd%3D%25E8%25B1%2586%25E7%2593%25'
                  'A3%26oq%3D%2525E7%25258C%2525AB%2525E7%25259C%2525BC%2525E7%252594%2525B5%'
                  '2525E5%2525BD%2525B1%26rsv_pq%3D92f0bbb10001cda4%26rsv_t%3D8bc8J0WsOLTeWlY'
                  'lTmTtiC2GrQJJ%252Bjv4IBc%252BiO%252FoESrXmrO3%252FfpNx%252FJTDJnnzkObnLrS%2'
                  '6rqlang%3Dcn%26rsv_enter%3D1%26inputT%3D1016%26rsv_sug3%3D41%26rsv_sug1%3D'
                  '36%26rsv_sug7%3D100%26rsv_sug2%3D0%26rsv_sug4%3D1712%22%5D; _pk_id.100001.4'
                  'cf6=e5dddf1ba3ddddc7.1543191350.1.1543191673.1543191350.; _pk_ses.100001.4c'
                  'f6=*; ap_v=0,6.0; __yadk_uid=zaO3DBqlq1sYsYvkjrtRzfCR1VaIP7Vn; __utma=3014'
                  '9280.81002317.1543191351.1543191351.1543191351.1; __utmb=30149280.0.10.154'
                  '3191351; __utmc=30149280; __utmz=30149280.1543191351.1.1.utmcsr=(direct)|ut'
                  'mccn=(direct)|utmcmd=(none); __utma=223695111.1225746777.1543191351.1543191'
                  '351.1543191351.1; __utmb=223695111.0.10.1543191351; __utmc=223695111; __utmz=2'
                  '23695111.1543191351.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic|utmctr=%E'
                  '8%B1%86%E7%93%A3; _vwo_uuid_v2=DFAB26D354461D79AFD2E9930EE48773C|babee1b1bca2'
                  'dad586f81c66493a0f5d'
}
params = {
        'limit': "20",
        'sort': "new_score",
        'start': None,
        'status': 'P'
}
comment_user = []
comment_info = []

def url(n):
        global params
        url_init = 'https://movie.douban.com/subject/3168101/comments?start={}&limit=20&sort=new_score&status=P'
        params['start'] = n
        return url_init.format(n)

def get_page(page_url):
        global params
        ht = requests.get(page_url, headers=head, params=params)
        ht.encoding = 'utf-8'
        return ht.text

def parse_with_re(response):
    global comment_info, comment_user
    commenttext = []
    author = re.findall(r'', response)
    commenttextt = re.findall(r'class="short">((.|\n)*?)', response)
    # 获取评论内容，注意换行符和其他字符在正则中的问题
    for each in commenttextt:
        commenttext.append(each[0])
    temp_num = len(commenttextt)
    for every in range(temp_num):
        comment_user.append(author[every])
        # 将用户名存入一个列表
        comment_info.append((commenttime[every], commenttext[every]))
        # 将评论日期和内容组成元组后存入一个列表
    return

def parse_with_bs(response):
    global comment_user, comment_info
    # 声明全局变量
    com_user = []
    # 暂时存放用户名的列表
    respsoup = BeautifulSoup(response, 'lxml')
    temp_user = respsoup.find_all('div', class_="avatar")
    # 获取的是一个标签的列表
    for every in temp_user:
        com_user.append(every.a.get('title'))
        # 对标签列表进行遍历，对每一项提取出子节点a的title属性，即用户名的值
    temp_time = respsoup.find_all('span', class_="comment-time ")
    # 同样也是标签列表
    temp_comment = respsoup.find_all('span', class_="short")
    temp_num = len(temp_comment)
    for each in range(temp_num):
        # 遍历标签列表，对每一项提取所需要的元素
        # 提取文本的话，直接 tag.text
        comment_user.append(com_user[each])
        comment_info.append((temp_time[each].get('title'), temp_comment[each].text))
    return

def parse_with_lxml(response):
    # 感觉xpath比bs写得顺手些
    global comment_info, comment_user
    resplx = etree.HTML(response)
    temp_user = resplx.xpath('//div[@class="avatar"]/a/@title')
    temp_time = resplx.xpath('//span[@class="comment-time "]/@title')
    temp_comment = resplx.xpath('//span[@class="short"]/text()')
    temp_num = len(temp_comment)
    for each in range(temp_num):
        comment_user.append(temp_user[each])
        comment_info.append((temp_time[each], temp_comment[each]))
    return

if __name__ == '__main__':
    start_time = time.time()
    for i in range(1):
        # range() 里可以设置需要爬取的页数
        print('正在爬取第 %d 页' % (i+1))
        resp = get_page(url(i))
        parse_with_bs(resp)
    length = len(comment_info)
    with open('venom.csv', 'w', encoding='utf-8') as f:
        for i in range(length):
            f.write('"用户名",{0},"评论时间",{1},"评论内容",{2}\n'.format(
                comment_user[i],
                comment_info[i][0],
                comment_info[i][1]))
    # with open('venom.csv', 'w') as csvfile:
    #     csvfile.write(codecs.BOM_UTF8)
    #     file = csv.writer(csvfile, dialect='excel')
    #     for i in range(length):
    #         file.writerow('"用户名",{0},"评论时间",{1},"评论内容",{2}\n'.format(
    #             comment_user[i],
    #             comment_info[i][0],
    #             comment_info[i][1]))
    # print(comment_info[4][1])  # 检查是否乱码
    # for nn in comment_info:
    #     print(nn[1])
    end_time = time.time()
    all_time = end_time - start_time
    print(all_time)


from messages import Message
import fields

class OutputMessage(Message):
    """
    A Message from the GPS unit to the host.
    """

    def __init__(self, input_bytes):
        super().__init__()

        if input_bytes[0] != 0xa0 or input_bytes[1] != 0xa1:
            raise ValueError("Malformed message: Start bytes are wrong.")

        if input_bytes[-1] != 0x0a or input_bytes[-2] != 0x0d:
            raise ValueError("Malformed message: End bytes are wrong.")

        payload_length = int.from_bytes(input_bytes[2:4], byteorder='big', signed=False)
        if len(input_bytes) != payload_length + 7:
            raise ValueError("Malformed message: Message length is wrong.")

        self.payload = input_bytes[4:4 + payload_length]
        if input_bytes[-3] != self.calculate_checksum()[0]:
            raise ValueError("Malformed message: Checksum is wrong.")

    def __str__(self):
        s = "GPS > Host: {}".format(type(self).name)
        for v in self.values:
            s += "\n  {}: {}".format(v.name, v)
        return s

    def get_payload(self):
        return self.payload

    def interpret(self):
        payload = self.get_payload()
        return output_message_types[payload[0]](payload)

class SoftwareVersionMessage(OutputMessage):
    msg_id = 0x80
    name = 'Software Version'
    description = '''
This is a response message which provides the software version of the GPS receiver. This message is sent from the GPS
receiver to host. The example below output the SkyTraq software version as 01.01.01-01.03.14-07.01.18 on System image.
The payload length is 14 bytes.

Structure:
<0xA0,0xA1>< PL><80>< message body><CS><0x0D,0x0A>
'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        if len(payload) != 14:
            raise AttributeError("Payload length should be 14.")

        self.payload = payload
        self.values = [
            fields.SoftwareTypeField(payload[1]),
            fields.KernelVersionField(payload[2:6]),
            fields.OdmVersionField(payload[6:10]),
            fields.RevisionField(payload[10:14]),
        ]

class SoftwareCrcMessage(OutputMessage):
    msg_id = 0x81
    name = 'Software CRC'
    description = '''
This is a response message which provides the software CRC of the GPS receiver. This message is sent from the GPS
receiver to host. The payload length is 4 bytes.

Structure:
<0xA0,0xA1>< PL><81>< message body><CS><0x0D,0x0A>
'''
    # fields = [
    #     SoftwareTypeField,
    #     CrcField
    # ]

class AckMessage(OutputMessage):
    msg_id = 0x83
    name = 'ACK'
    description = '''
This is a response message which is an acknowledgement to a request message. The payload length is 2 bytes.

Structure:
<0xA0,0xA1>< PL><83>< message body><CS><0x0D,0x0A>
'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        if len(payload) != 2:
            raise AttributeError("Payload length should be 2.")

        self.payload = payload
        self.values = [
            fields.AckIdField(payload[1])
        ]

    def __str__(self):
        from input_messages import input_message_types
        try:
            ack_id = self.values[0].value
            ack_name = input_message_types[ack_id].name
            return "GPS acknowleges '{}' (0x{:02x})".format(ack_name, ack_id)
        except KeyError:
            return "GPS acknowleges '?' (0x{:02x})".format(ack_id)

class NackMessage(OutputMessage):
    msg_id = 0x84
    name = 'NACK'
    description = '''
This is a response message which is a response to an unsuccessful request message. This is used to notify the Host that
the request message has been rejected. The payload length is 2 bytes.

Structure:
<0xA0,0xA1>< PL><84>< message body><CS><0x0D,0x0A>
'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        if len(payload) != 2:
            raise AttributeError("Payload length should be 2.")

        self.payload = payload
        self.values = [
            fields.AckIdField(payload[1])
        ]

    def __str__(self):
        from input_messages import input_message_types
        try:
            ack_id = self.values[0].value
            ack_name = input_message_types[ack_id].name
            return "GPS rejects '{}' (0x{:02x})".format(ack_name, ack_id)
        except KeyError:
            return "GPS rejects '?' (0x{:02x})".format(ack_id)

class PositionUpdateRateMessage(OutputMessage):
    msg_id = 0x86
    name = 'Position Update Rate'
    description = '''
This is a response message to QUERY POSITION UPDATE RATE which provides the position update rate of the GPS receiver.
This message is sent from the GPS receiver to host. The payload length is 2 bytes.

Structure:
<0xA0,0xA1>< PL><86>< message body><CS><0x0D,0x0A>
'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        if len(payload) != 2:
            raise AttributeError("Payload length should be 2.")

        self.payload = payload
        self.values = [
            fields.UpdateRateField(payload[1])
        ]

    def __str__(self):
        return "GPS update rate is {:d}Hz".format(self.values[0].value)

class GpsEphemerisDataMessage(OutputMessage):
    msg_id = 0xB1
    name = 'TODO'
    description = '''

'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        # if len(payload) != :
        #     raise AttributeError("Payload length should be .")

        self.payload = payload
        # self.values = [
        #
        # ]

    def __str__(self):
        return "TODO"
        # return "{}".format(self.values)

class GpsDatumMessage(OutputMessage):
    msg_id = 0xAE
    name = 'GPS datum'
    description = '''
This is a response message which provides the datum information of the GPS receiver. This message is sent from the GPS
receiver to host. The payload length is 3 bytes.

Structure:
<0xA0,0xA1>< PL><AE>< message body><CS><0x0D,0x0A>
'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        if len(payload) != 3:
            raise AttributeError("Payload length should be 3.")

        self.payload = payload
        self.values = [
            fields.DatumIndexField(payload[1:3])
        ]

class GpsWaasStatusMessage(OutputMessage):
    msg_id = 0xB3
    name = 'gps waas status'
    description = '''
This is a response message which provides the status of the WAAS receiver. This message is sent from the GPS receiver
to host. The payload length is 2 bytes.

Structure:
<0xA0,0xA1>< PL><B3>< message body><CS><0x0D,0x0A>
'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        # if len(payload) != :
        #     raise AttributeError("Payload length should be .")

        self.payload = payload
        # self.values = [
        #
        # ]

    def __str__(self):
        return "TODO"
        # return "{}".format(self.values)

class GpsPositionPinningStatusMessage(OutputMessage):
    msg_id = 0xb4
    name = 'GPS position pinning status'
    description = '''
NOTE: This description is incorrect in the application note.
'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        if len(payload) != 12:
            raise AttributeError("Payload length should be 12.")

        self.payload = payload
        self.values = [
            fields.PositionPinningField(payload[1]),
            fields.PinningSpeedField(payload[2:4]),
            fields.PinningCountField(payload[4:6]),
            fields.UnpinningSpeedField(payload[6:8]),
            fields.UnpinningCountField(payload[8:10]),
            fields.UnpinningDistanceField(payload[10:12]),
        ]

    def __str__(self):
        return """GPS position pinning is {}.
  {}: {}
  {}: {}
  {}: {}
  {}: {}
  {}: {}""".format(
            'on' if self.values[0].value else 'off',
            self.values[1].name, self.values[1],
            self.values[2].name, self.values[2],
            self.values[3].name, self.values[3],
            self.values[4].name, self.values[4],
            self.values[5].name, self.values[5],
        )

class GpsNavigationModeMessage(OutputMessage):
    msg_id = 0xb5
    name = 'TODO'
    description = '''

'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        # if len(payload) != :
        #     raise AttributeError("Payload length should be .")

        self.payload = payload
        # self.values = [
        #
        # ]

    def __str__(self):
        return "TODO"
        # return "{}".format(self.values)

class GpsPpsModeMessage(OutputMessage):
    msg_id = 0xb6
    name = 'TODO'
    description = '''

'''

    # noinspection PyMissingConstructor
    def __init__(self, payload):
        # Don't call super init.

        if payload[0] != type(self).msg_id:
            raise AttributeError("This is the wrong message class for the given payload. Expected {}, got {}.".format(
                type(self).msg_id,
                payload[0]
            ))

        # if len(payload) != :
        #     raise AttributeError("Payload length should be .")

        self.payload = payload
        # self.values = [
        #
        # ]

    def __str__(self):
        return "TODO"
        # return "{}".format(self.values)

output_message_types = {
    0x80: SoftwareVersionMessage,
    0x81: SoftwareCrcMessage,
    0x83: AckMessage,
    0x84: NackMessage,
    0x86: PositionUpdateRateMessage,
    0xae: GpsDatumMessage,
    0xb3: GpsWaasStatusMessage,
    0xb4: GpsPositionPinningStatusMessage,
    0xb5: GpsNavigationModeMessage,
    0xb6: GpsPpsModeMessage,
}


""" Plugin for server moderation

The bot will perform different tasks when some settings are enabled in a server:

_____________________________________NSFW Filter_____________________________________
    If enabled on the server, spots any text containing the keyword nsfw and a link.
    Then tries to delete their message, and post a link to the dedicated nsfw channel.

Commands:
    moderate
    mute
    unmute
    timeout
"""

from collections import defaultdict

import discord
import asyncio

from pcbot import Config, utils, Annotate
import plugins

moderate = Config("moderate", data=defaultdict(dict))
default_config = {}  # Used by add_setting helper function

def setup_default_config(server: discord.Server):
    """ Setup default settings for a server. """
    # Set to defaults if there is no config for the server
    if server.id not in moderate.data:
        moderate.data[server.id] = default_config
        moderate.save()
        return

    # Set to defaults if server's config is missing values
    if not all(k in moderate.data[server.id].keys() for k in default_config):
        moderate.data[server.id] = default_config
        moderate.save()

@plugins.command(name="moderate")
def moderate_(client, message, _: utils.placeholder):
    """ Change moderation settings. """
    pass

def add_setting(setting: str, default=True, name=None, permissions=None):
    """ Create a set of subcommands for the given setting (True or False).

    :param setting: display name for the setting.
    :param default: The default value for this setting.
    :param name: optionally set the name of the subcommand.
    :param permissions: what permissions are required to change this setting (list of str). """
    if not name:
        name = setting.lower().replace("\n", "").replace(" ", "")

    default_config[name] = default

    @moderate_.command(name=name,
                       description="Display current {} setting or enable/disable it.".format(setting),
                       usage="[on | off]")
    def display_setting(client: discord.Client, message: discord.Message):
        """ The command to display the current setting. """
        setup_default_config(message.server)
        current = moderate.data[message.server.id][name]
        yield from client.say(message, "{} is **{}**.".format(setting, "enabled" if current else "disabled"))

    @display_setting.command(hidden=True, aliases="true set enable")
    @utils.permission(*permissions)
    def on(client: discord.Client, message: discord.Message):
        """ The command to enable this setting. """
        moderate.data[message.server.id][name] = True
        moderate.save()
        yield from client.say(message, "{} **enabled**.".format(setting))

    @display_setting.command(hidden=True, aliases="true set enable")
    @utils.permission(*permissions)
    def off(client: discord.Client, message: discord.Message):
        """ The command to enable this setting. """
        moderate.data[message.server.id][name] = False
        moderate.save()
        yield from client.say(message, "{} **disabled**.".format(setting))

add_setting("NSFW filter", permissions=["manage_server"])
add_setting("Changelog", permissions=["manage_server"], default=False)

@asyncio.coroutine
def manage_mute(client: discord.Client, message: discord.Message, function, *members: discord.Member):
    """ Add or remove Muted role for given members.

    :param function: either client.add_roles or client.remove_roles
    :return: list of muted/unmuted members or None """
    # Manage Roles is required to add or remove the Muted role
    assert message.server.me.permissions_in(message.channel).manage_roles, \
        "I need `Manage Roles` permission to use this command."

    muted_role = discord.utils.get(message.server.roles, name="Muted")

    # The server needs to properly manage the Muted role
    assert muted_role, "No role assigned for muting. Please create a `Muted` role."

    muted_members = []

    # Try giving members the Muted role
    for member in members:
        if member is message.server.me:
            yield from client.say(message, "I refuse to mute myself!")
            continue

        while True:
            try:
                yield from function(member, muted_role)
            except discord.errors.Forbidden:
                yield from client.say(message, "I do not have permission to give members the `Muted` role.")
                return None
            except discord.errors.HTTPException:
                continue
            else:
                muted_members.append(member)
                break

    return muted_members or None

@plugins.command(pos_check=True)
@utils.permission("manage_messages")
def mute(client: discord.Client, message: discord.Message, *members: Annotate.Member):
    """ Mute the specified members. """
    muted_members = yield from manage_mute(client, message, client.add_roles, *members)

    # Some members were muted, success!
    if muted_members:
        yield from client.say(message, "Muted {}".format(utils.format_members(*muted_members)))

@plugins.command(pos_check=True)
@utils.permission("manage_messages")
def unmute(client: discord.Client, message: discord.Message, *members: Annotate.Member):
    """ Unmute the specified members. """
    muted_members = yield from manage_mute(client, message, client.remove_roles, *members)

    # Some members were unmuted, success!
    if muted_members:
        yield from client.say(message, "Unmuted {}".format(utils.format_members(*muted_members)))

@plugins.command(pos_check=True)
@utils.permission("manage_messages")
def timeout(client: discord.Client, message: discord.Message, *members: Annotate.Member, minutes: int):
    """ Timeout the specified members for given minutes. """
    muted_members = yield from manage_mute(client, message, client.add_roles, *members)

    # Do not progress if the members were not successfully muted
    # At this point, manage_mute will have reported any errors
    if not muted_members:
        return

    yield from client.say(message, "Timed out {} for `{}` minutes.".format(utils.format_members(*muted_members),
                                                                           minutes))

    # Sleep for the given minutes and unmute the member
    yield from asyncio.sleep(minutes * 60)  # Since asyncio.sleep takes seconds, multiply by 60
    yield from manage_mute(client, message, client.remove_roles, *muted_members)

@asyncio.coroutine
def check_nsfw(client: discord.Client, message: discord.Message):
    """ Check if the message is NSFW (very rough check). """
    # Check if this server has nsfwfilter enabled
    if not moderate.data[message.server.id]["nsfwfilter"]:
        return False

    # Do not check if the channel is designed for nsfw content
    if "nsfw" in message.channel.name:
        return False

    # Check if message includes keyword nsfw and a link
    msg = message.content.lower()
    if "nsfw" in msg and ("http://" in msg or "https://" in msg):
        if message.server.me.permissions_in(message.channel).manage_messages:
            yield from client.delete_message(message)

        nsfw_channel = discord.utils.find(lambda c: "nsfw" in c.name, message.server.channels)

        if nsfw_channel:
            yield from client.say(message, "{0.mention}: **Please post NSFW content in {1.mention}**".format(
                message.author, nsfw_channel) )

        return True

@plugins.event()
def on_message(client: discord.Client, message: discord.Message):
    """ Check plugin settings. """
    # Do not check in private messages
    if message.channel.is_private:
        return False

    setup_default_config(message.server)

    nsfw_success = yield from check_nsfw(client, message)
    if nsfw_success is True:
        return True

def get_changelog_channel(server: discord.Server):
    """ Return the changelog channel for a server. """
    setup_default_config(server)
    if not moderate.data[server.id]["changelog"]:
        return

    channel = discord.utils.get(server.channels, name="changelog")
    if not channel:
        return

    permissions = channel.permissions_for(server.me)
    if not permissions.send_messages or not permissions.read_messages:
        return

    return channel

@plugins.event()
def on_message_delete(client: discord.Client, message: discord.Message):
    """ Update the changelog with deleted messages. """
    changelog_channel = get_changelog_channel(message.server)
    if not changelog_channel:
        return

    if message.channel == changelog_channel:
        return

    if message.author == client.user:
        return

    if message.content.startswith("|"):  # Custom check for pastas
        return

    yield from client.send_message(
        changelog_channel,
        "{0.author.mention}'s message was deleted in {0.channel.mention}:\n{0.clean_content}".format(message)
    )

@plugins.event()
def on_channel_create(client: discord.Client, channel: discord.Channel):
    """ Update the changelog with created channels. """
    if channel.is_private:
        return

    changelog_channel = get_changelog_channel(channel.server)
    if not changelog_channel:
        return

    # Differ between voice channels and text channels
    if channel.type == discord.ChannelType.text:
        yield from client.send_message(changelog_channel, "Channel {0.mention} was created.".format(channel))
    else:
        yield from client.send_message(changelog_channel, "Voice channel **{0.name}** was created.".format(channel))

@plugins.event()
def on_channel_delete(client: discord.Client, channel: discord.Channel):
    """ Update the changelog with deleted channels. """
    if channel.is_private:
        return

    changelog_channel = get_changelog_channel(channel.server)
    if not changelog_channel:
        return

    # Differ between voice channels and text channels
    if channel.type == discord.ChannelType.text:
        yield from client.send_message(changelog_channel, "Channel #{0.name} was deleted.".format(channel))
    else:
        yield from client.send_message(changelog_channel, "Voice channel **{0.name}** was deleted.".format(channel))

@plugins.event()
def on_member_join(client: discord.Client, member: discord.Member):
    """ Update the changelog with members joined. """
    changelog_channel = get_changelog_channel(member.server)
    if not changelog_channel:
        return

    yield from client.send_message(changelog_channel, "{0.mention} joined the server.".format(member))

@plugins.event()
def on_member_remove(client: discord.Client, member: discord.Member):
    """ Update the changelog with deleted channels. """
    changelog_channel = get_changelog_channel(member.server)
    if not changelog_channel:
        return

    yield from client.send_message(changelog_channel, "{0.mention} ({0.name}) left the server.".format(member))

@plugins.event()
def on_member_update(client: discord.Client, before: discord.Member, after: discord.Member):
    """ Update the changelog with any changed names. """
    name_change = not before.name == after.name
    nick_change = not before.nick == after.nick

    if not name_change and not nick_change:
        return

    changelog_channel = get_changelog_channel(after.server)
    if not changelog_channel:
        return

    # Format the nickname or username changed
    if name_change:
        m = "{0.mention} (previously **{0.name}**) changed their username to **{1.name}**."
    else:
        if not before.nick:
            m = "{0.mention} got the nickname **{1.nick}**."
        elif not after.nick:
            m = "{0.mention} (previously **{0.nick}**), no longer has a nickname."
        else:
            m = "{0.mention} (previously **{0.nick}**) got the nickname **{1.nick}**."

    yield from client.send_message(changelog_channel, m.format(before, after))

@plugins.event()
def on_member_ban(client: discord.Client, member: discord.Member):
    """ Update the changelog with banned members. """
    changelog_channel = get_changelog_channel(member.server)
    if not changelog_channel:
        return

    yield from client.send_message(changelog_channel,
                                   "{0.mention} ({0.name}) was banned from the server.".format(member))

@plugins.event()
def on_member_unban(client: discord.Client, member: discord.Member):
    """ Update the changelog with unbanned members. """
    changelog_channel = get_changelog_channel(member.server)
    if not changelog_channel:
        return

    yield from client.send_message(changelog_channel, "{0.mention} was unbanned from the server.".format(member))


"""Werkzeugkasten zur Verarbeitung von OSM Daten.
https://wiki.openstreetmap.org/wiki/Key:highway
https://stackoverflow.com/questions/14716497/how-can-i-find-a-list-of-street-intersections-from-openstreetmap-data?rq=1
"""

import xml.etree.ElementTree as ET

def load_osm_file(filename):
    """Lade einen OSM-Datensatz ein."""
    tree = ET.parse(filename)
    root = tree.getroot()

    return root

def get_node_lat_lon(root):
    """Erstelle Woerterbuch mit allen Knoten und deren Laengen- /Breitengraden"""
    node_lat_lon = dict()
    for node in root.findall('node'):
        nodeID = node.get('id')
        Breitengrad = node.get('lat')
        Laengengrad = node.get('lon')
        node_lat_lon[nodeID] = [Breitengrad, Laengengrad]

    return node_lat_lon

if __name__ == '__main__':
    osm_root = load_osm_file('./HHG-BHF.osm')
    node_lat_lon = get_node_lat_lon(osm_root)

    ways = osm_root.findall('way')  # List mit Weg Elementen

    # ein Weg Element hat Eigenschaften und wiederum Kinder
#    <way id="14357125" visible="true" version="26" changeset="56091012" timestamp="2018-02-05T17:37:29Z" user="devbrain" uid="58073">
#  <nd ref="139466306"/>
#  <nd ref="1201451389"/>
#  <tag k="cycleway" v="lane"/>
#  <tag k="highway" v="primary"/>
#  <tag k="maxspeed" v="50"/>
#  <tag k="name" v="Eisenbahnstraße"/>
#  <tag k="ref" v="B 37"/>
#  <tag k="surface" v="asphalt"/>
# </way>
    einWeg = ways[0]
    print(einWeg.get('id'))
    print(einWeg.get('timestamp'))

    Kinder = einWeg.getchildren()
    einKind = Kinder[0]
    einKind.attrib
    einKind.tag


from knowledge_model import Base, Knowledge

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///knowledge.db')
Base.metadata.create_all(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()

def add_article(topic, title, rating):
	article_object = Knowledge(
		topic = topic,
		title = title,
		rating = rating)
	session.add(article_object)
	session.commit()

add_article("theatre", "acting", 5)
# add_article("love", "peace", 8)

def query_all_articles():
	articles = session.query(Knowledge).all()
	return articles
# print(query_all_articles())
print(query_all_articles())

def query_article_by_topic(topic):
	article = session.query(Knowledge).filter_by(topic=topic).all()
	return(article)

# print(query_article_by_topic("theatre"))

# def query_article_by_rating(rating):
# 	b = session.query(Knowledge).filter_by(rating=rating).all()
# 	trsh = input("Give an input between 1 and 10")
def delete_article_by_topic(topic):
	session.query(Knowledge).filter_by(topic=topic).delete()
	session.commit()

def delete_all_articles():
	session.query(Knowledge).delete()
	session.commit()

def edit_article_rating():
	title = input("What is the title of the article you want to change?")
	new_rating = input("What is the new rating of the article?")
	articles = session.query(Knowledge).filter_by(title=title).all()
	for article in articles:
		article.rating = new_rating
	session.commit()

# delete_article_by_topic("theatre")
# print(query_all_articles())
# delete_all_articles()
# print(query_all_articles())
def delete_articles_by_rating(threshold):
	session.query(Knowledge).filter(Knowledge.rating<threshold).delete()

# edit_article_rating()
delete_articles_by_rating(7)
print(query_all_articles())


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

import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.nn import functional as F

class CNNClassifier(nn.Module):
    def __init__(self, batch_size, output_size, in_channels, out_channels, kernel_heights, stride, padding, keep_probab,
                  embedding_length):
        super(CNNClassifier, self).__init__()
        self.batch_size = batch_size
        self.output_size = output_size
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_heights = kernel_heights
        self.stride = stride
        self.padding = padding
        self.embedding_length = embedding_length

        self.conv1 = nn.Conv2d(in_channels, out_channels, (kernel_heights[0], embedding_length),padding=(kernel_heights[0] - 1, 0))
        self.conv2 = nn.Conv2d(in_channels, out_channels, (kernel_heights[1], embedding_length),padding=(kernel_heights[1] - 1, 0))
        self.conv3 = nn.Conv2d(in_channels, out_channels, (kernel_heights[2], embedding_length),padding=(kernel_heights[2] - 1, 0))

        self.dropout = nn.Dropout(keep_probab)
        self.label = nn.Linear(len(kernel_heights) * out_channels, output_size)
        self.sofmax = nn.Softmax(dim=1)

    def conv_block(self, input, conv_layer):

        conv_out = conv_layer(input)
        activation = torch.squeeze(F.relu(conv_out),-1)
        max_out = F.max_pool1d(activation, activation.size(2))

        return max_out

    def forward(self, input_sentences, batch_size=None):
        input = torch.unsqueeze(input_sentences, 1)
        max_out1 = self.conv_block(input, self.conv1)
        max_out2 = self.conv_block(input, self.conv2)
        max_out3 = self.conv_block(input, self.conv3)
        all_out = torch.cat((max_out1, max_out2, max_out3), 2)
        all_out = all_out.view(all_out.size(0), -1)
        fc_in = self.dropout(all_out)
        logits = self.label(fc_in)
        logits = self.sofmax(logits)

        return logits


import discord, random, datetime
from discord.ext import commands

class Events(commands.Cog):

    def __init__(self, bot):
        self.bot = bot

    @commands.Cog.listener()
    async def on_ready(self):
        print("+ Events Cog loaded")

    @commands.Cog.listener()
    async def on_member_join(self, member):
        role = discord.utils.get(member.guild.roles, name='Member')
        await member.add_roles(role)

    @commands.Cog.listener()
    async def on_command_error(self, ctx, error):
        # Ignored errors
        ignored = (commands.CommandNotFound, commands.MissingRequiredArgument)#, commands.UserInputError commands.BadArgument
        if isinstance(error, ignored):
            return

        # Error handling
        if isinstance(error, commands.CommandOnCooldown):
            m, s = divmod(error.retry_after, 60)
            h, m = divmod(m, 60)
            if int(h) == 0 and int(m) == 0:
                await ctx.send(f':stopwatch: You must wait **{int(s)} seconds** to use this command again.')
            elif int(h) == 0 and int(m) != 0:
                await ctx.send(f':stopwatch: You must wait **{int(m)} minutes and {int(s)} seconds** to use this command again.')
            else:
                await ctx.send(f':stopwatch: You must wait **{int(h)} hours, {int(m)} minutes and {int(s)} seconds** to use this command again.')
            return

        elif isinstance(error, commands.CheckFailure):
            return await ctx.send("Insufficient permissions.")

        elif isinstance(error, commands.CommandInvokeError):
            if str(error.original) == "403 Forbidden (error code: 50013): Missing Permissions":
                print("===== Attempting to handle in ctx =====")
                try:
                    embed = discord.Embed(title=":x: Missing Permissions", color=discord.Color.red())
                    await ctx.send(embed=embed)
                    return print("Success")
                except Exception:
                    print("Failed")

        print("\n----------\n")
        raise error
        print("\n----------\n")

def setup(bot):
    bot.add_cog(Events(bot))


import logging
import os
import time

import numpy as np
import pandas as pd
import seaborn as sns
from pathlib import Path
import torch
from matplotlib import pyplot as plt
from sklearn.metrics import f1_score, precision_score, recall_score
from torch.nn import CrossEntropyLoss
from tqdm import tqdm
from transformers.optimization import AdamW, get_linear_schedule_with_warmup

from dataloaders.semeval_dataloader import SemEvalDataloader
from logger import LOG_DATETIME_FORMAT, LOG_FORMAT, LOG_LEVEL
from model.bert import BertModel
from model.relation_extractor import RelationExtractor

logging.basicConfig(
    format=LOG_FORMAT, datefmt=LOG_DATETIME_FORMAT, level=LOG_LEVEL
)
logger = logging.getLogger(__name__)

sns.set(font_scale=2.2)

class SemEvalModel(RelationExtractor):
    def __init__(self, config: dict):
        """
        SemEval Model using Transformers.

        Args:
            config: configuration parameters
        """
        super().__init__()
        self.gradient_acc_steps = config.get("gradient_acc_steps")
        self.transformer = config.get("transformer")
        self.config = config
        self.data_loader = SemEvalDataloader(self.config)
        logger.info(
            f"Loaded {len(self.data_loader.train_generator)} fine-tuning samples."
        )

        self.tokenizer = self.data_loader.tokenizer
        self.tokenizer.convert_tokens_to_ids("[E1]")
        self.tokenizer.convert_tokens_to_ids("[E2]")

        self.model = BertModel.from_pretrained(
            model_size=self.config.get("transformer"),
            force_download=False,
            pretrained_model_name_or_path=self.config.get("transformer"),
            task="classification",
            n_classes=self.data_loader.n_classes,
        )
        self.model.resize_token_embeddings(len(self.tokenizer))
        pretrained_mtb_model = self.config.get("pretrained_mtb_model", None)
        if pretrained_mtb_model and os.path.isfile(pretrained_mtb_model):
            self._load_pretrained_model(pretrained_mtb_model)

        self.train_on_gpu = torch.cuda.is_available() and config.get(
            "use_gpu", True
        )
        if self.train_on_gpu:
            self.model.cuda()

        self.criterion = CrossEntropyLoss(reduction="sum")

        self._start_epoch = 0
        self._train_loss = []
        self._train_acc = []
        self._test_f1 = []
        self._test_acc = []
        self._best_test_f1 = 0
        self.checkpoint_dir = os.path.join(
            "models", "finetuning", "sem_eval", self.transformer
        )
        Path(self.checkpoint_dir).mkdir(parents=True, exist_ok=True)

        self._points_seen = 0

    def _load_pretrained_model(self, pretrained_mtb_model):
        logger.info(
            "Loading pre-trained MTB model from {0}.".format(
                pretrained_mtb_model
            )
        )
        checkpoint = torch.load(pretrained_mtb_model)
        model_dict = self.model.state_dict()
        pretrained_dict = {
            k: v
            for k, v in checkpoint["state_dict"].items()
            if k in model_dict.keys()
        }
        model_dict.update(pretrained_dict)
        self.model.load_state_dict(pretrained_dict, strict=False)
        self.tokenizer = checkpoint["tokenizer"]

    def train(self, epochs):
        """
        Runs the training.
        """
        pretrained_model = self.config.get("pretrained_mtb_model", None)
        pretrained_model = (
            "pretrained" if pretrained_model else "no_pretraining"
        )
        no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"]
        optimizer_grouped_parameters = [
            {
                "params": [
                    p
                    for n, p in self.model.named_parameters()
                    if not any(nd in n for nd in no_decay)
                ],
                "weight_decay": 0.01,
            },
            {
                "params": [
                    p
                    for n, p in self.model.named_parameters()
                    if any(nd in n for nd in no_decay)
                ],
                "weight_decay": 0.0,
            },
        ]
        optimizer = AdamW(
            optimizer_grouped_parameters, lr=self.config.get("lr")
        )
        ovr_steps = (
            epochs
            * len(self.data_loader.train_generator)
            * self.config.get("mini_batch_size")
            / self.config.get("batch_size")
        )
        scheduler = get_linear_schedule_with_warmup(
            optimizer, ovr_steps // 10, ovr_steps
        )

        results_path = os.path.join(
            "results", "sem_eval", pretrained_model, str(epochs)
        )
        best_model_path = os.path.join(
            self.checkpoint_dir, "best_model.pth.tar"
        )
        resume = self.config.get("resume", False)
        if resume and os.path.exists(best_model_path):
            (
                self._start_epoch,
                self._best_test_f1,
                self._train_loss,
                self._train_acc,
                self._test_f1,
                self._train_acc,
            ) = self.load_best_model(self.checkpoint_dir)

        logger.info("Starting training process")
        pad_id = self.tokenizer.pad_token_id
        for epoch in range(self._start_epoch, epochs):
            self._train_epoch(epoch, pad_id, optimizer, scheduler)
            data = self._write_kpis(results_path)
            self._plot_results(data, results_path)

        logger.info("Finished Training.")
        return self.model

    def _train_epoch(self, epoch, pad_id, optimizer, scheduler):
        start_time = super()._train_epoch(epoch)

        train_loss, train_acc = [], []

        for i, data in enumerate(tqdm(self.data_loader.train_generator)):
            x, e1_e2_start, labels = data
            classification_logits, labels, loss = self._train_on_batch(
                e1_e2_start, labels, pad_id, x, optimizer, scheduler
            )

            train_loss.append(loss)
            train_acc.append(
                SemEvalModel.evaluate_inner(classification_logits, labels)[0]
            )

        self._train_loss.append(np.mean(train_loss))
        self._train_acc.append(np.mean(train_acc))

        self.on_epoch_end(epoch, self._test_f1, self._best_test_f1)

        logger.info(f"Train Loss: {self._train_loss[-1]}")
        logger.info(f"Train Accuracy: {self._train_acc[-1]}")
        logger.info(f"Test Accuracy: {self._test_acc[-1]}")
        logger.info(f"Test F1: {self._test_f1[-1]}")

        logger.info(
            "Epoch finished, took {0} seconds.".format(
                time.time() - start_time
            )
        )

    def on_epoch_end(self, epoch, benchmark, baseline):
        """
        Function to run at the end of an epoch.

        Runs the evaluation method, increments the scheduler, sets a new baseline and appends the KPIS.ä

        Args:
            epoch: Current epoch
            benchmark: List of benchmark results
            baseline: Current baseline. Best model performance so far
        """
        eval_result = self.evaluate()
        self._best_test_f1 = (
            eval_result.get("f1")
            if eval_result.get("f1") > self._best_test_f1
            else self._best_test_f1
        )
        self._test_f1.append(eval_result["f1"])
        self._test_acc.append(eval_result["accuracy"])

    def _train_on_batch(
        self, e1_e2_start, labels, pad_id, x, optimizer, scheduler
    ):
        attention_mask = (x != pad_id).float()
        token_type_ids = torch.zeros((x.shape[0], x.shape[1])).long()
        if self.train_on_gpu:
            x = x.cuda()
            labels = labels.cuda()
            attention_mask = attention_mask.cuda()
            token_type_ids = token_type_ids.cuda()
        classification_logits = self.model(
            x,
            token_type_ids=token_type_ids,
            attention_mask=attention_mask,
            e1_e2_start=e1_e2_start,
        )
        loss = self.criterion(classification_logits, labels.squeeze(1))
        loss_p = loss.item()
        loss = loss / self.config.get("batch_size")
        loss.backward()
        self._points_seen += len(x)
        if self._points_seen >= self.config.get("batch_size"):
            optimizer.step()
            scheduler.step()
            optimizer.zero_grad()
            self._points_seen = 0
        return classification_logits, labels, loss_p / len(x)

    def _plot_results(self, data, save_at):
        fig, ax = plt.subplots(figsize=(20, 20))
        sns.lineplot(x="Epoch", y="Train Loss", ax=ax, data=data, linewidth=4)
        ax.set_title("Training Loss")
        plt.savefig(
            os.path.join(
                save_at, "train_loss_{0}.png".format(self.transformer)
            )
        )
        plt.close(fig)

        fig, ax = plt.subplots(figsize=(20, 20))
        sns.lineplot(x="Epoch", y="Test F1", ax=ax, data=data, linewidth=4)
        ax.set_title("Test F1 Score")
        plt.savefig(
            os.path.join(save_at, "test_f1_{0}.png".format(self.transformer))
        )
        plt.close(fig)

        tmp = data[["Epoch", "Train Accuracy", "Test Accuracy"]].melt(
            id_vars="Epoch", var_name="Set", value_name="Accuracy"
        )
        fig, ax = plt.subplots(figsize=(20, 20))
        sns.lineplot(
            x="Epoch",
            y="Accuracy",
            hue="Set",
            ax=ax,
            data=tmp,
            linewidth=4,
        )
        ax.set_title("Accuracy")
        plt.savefig(
            os.path.join(save_at, "acc_{0}.png".format(self.transformer))
        )
        plt.close(fig)

    def _write_kpis(self, results_path):
        Path(results_path).mkdir(parents=True, exist_ok=True)
        data = pd.DataFrame(
            {
                "Epoch": np.arange(len(self._train_loss)),
                "Train Loss": self._train_loss,
                "Train Accuracy": self._train_acc,
                "Test Accuracy": self._test_acc,
                "Test F1": self._test_f1,
            }
        )
        data.to_csv(
            os.path.join(
                results_path, "kpis_{0}.csv".format(self.transformer)
            ),
            index=False,
        )
        return data

    def load_best_model(self, checkpoint_dir: str):
        """
        Loads the current best model in the checkpoint directory.

        Args:
            checkpoint_dir: Checkpoint directory path
        """
        checkpoint = super().load_best_model(checkpoint_dir)
        return (
            checkpoint["epoch"],
            checkpoint["best_f1"],
            checkpoint["losses_per_epoch"],
            checkpoint["accuracy_per_epoch"],
            checkpoint["test_acc"],
            checkpoint["test_f1"],
        )

    @classmethod
    def evaluate_inner(cls, output, labels):
        l = labels.squeeze()
        o = output.max(1)[1]
        return (l == o).float().mean().item(), (o, l)

    def evaluate(self):
        logger.info("Evaluating test samples")
        out_labels = []
        true_labels = []
        self.model.eval()
        with torch.no_grad():
            for i, data in tqdm(
                enumerate(self.data_loader.test_generator),
                total=len(self.data_loader.test_generator),
            ):
                x, e1_e2_start, labels = data
                attention_mask = (x != self.tokenizer.pad_token_id).float()
                token_type_ids = torch.zeros((x.shape[0], x.shape[1])).long()

                if self.train_on_gpu:
                    x = x.cuda()
                    labels = labels.cuda()
                    attention_mask = attention_mask.cuda()
                    token_type_ids = token_type_ids.cuda()

                classification_logits = self.model(
                    x,
                    token_type_ids=token_type_ids,
                    attention_mask=attention_mask,
                    e1_e2_start=e1_e2_start,
                )

                _acc, (o, l) = SemEvalModel.evaluate_inner(
                    classification_logits, labels
                )
                out_labels.extend([int(i) for i in o])
                true_labels.extend([int(i) for i in l])

        out_labels = np.array(out_labels)
        true_labels = np.array(true_labels)
        accuracy = np.mean([o == t for t, o in zip(out_labels, true_labels)])
        results = {
            "accuracy": accuracy,
            "precision": precision_score(
                true_labels, out_labels, average="micro"
            ),
            "recall": recall_score(true_labels, out_labels, average="micro"),
            "f1": f1_score(true_labels, out_labels, average="micro"),
        }
        logger.info("***** Eval results *****")
        for key in sorted(results.keys()):
            logger.info("  %s = %s", key, str(results[key]))

        return results

    def _save_model(self, path, epoch, best_model: bool = False):
        if best_model:
            model_path = os.path.join(path, "best_model.pth.tar")
        else:
            model_path = os.path.join(
                path, "checkpoint_epoch_{0}.pth.tar"
            ).format(epoch + 1)
        torch.save(
            {
                "epoch": epoch + 1,
                "state_dict": self.model.state_dict(),
                "best_f1": self._best_test_f1,
                "losses_per_epoch": self._train_loss,
                "accuracy_per_epoch": self._train_acc,
                "test_acc": self._test_acc,
                "test_f1": self._test_f1,
            },
            model_path,
        )


# -*- coding: utf-8 -*-
u"""PyTest for :mod:`sirepo.template.srw.py`

:copyright: Copyright (c) 2016 RadiaSoft LLC.  All Rights Reserved.
:license: http://www.apache.org/licenses/LICENSE-2.0.html
"""
from __future__ import absolute_import, division, print_function
import pytest

pytest.importorskip('srwl_bl')

from pykern import pkresource
from pykern import pkunit
from sirepo import srunit

@srunit.wrap_in_request(sim_types='srw')
def test_model_defaults():
    from pykern import pkconfig
    from sirepo.template import template_common
    from sirepo import simulation_db
    res = template_common.model_defaults('trajectoryReport', simulation_db.get_schema('srw'))
    assert res == {
        'notes': '',
        'plotAxisY2': 'None',
        'timeMomentEstimation': 'auto',
        'magneticField': '2',
        'initialTimeMoment': 0.0,
        'numberOfPoints': 10000,
        'plotAxisY': 'X',
        'plotAxisX': 'Z',
        'finalTimeMoment': 0.0,
    }
    model = {
        'numberOfPoints': 10,
        'finalTimeMoment': 1.0,
    }
    template_common.update_model_defaults(model, 'trajectoryReport', simulation_db.get_schema('srw'))
    assert model['numberOfPoints'] == 10
    assert model['finalTimeMoment'] == 1.0
    assert model['plotAxisX'] == 'Z'

@srunit.wrap_in_request(sim_types='srw')
def test_prepare_aux_files():
    from sirepo.template import template_common
    from pykern.pkdebug import pkdp
    from pykern import pkcollections
    import sirepo.auth
    import sirepo.auth.guest

    sirepo.auth.login(sirepo.auth.guest)

    # Needed to initialize simulation_db
    data = pkcollections.json_load_any('''{
        "simulationType": "srw",
        "models": {
            "simulation": {
                "sourceType": "t"
            },
            "tabulatedUndulator": {
                "undulatorType": "u_t",
                "magneticFile": "magnetic_measurements.zip"
            },
            "beamline": { }
        },
        "report": "intensityReport"
    }''')
    d = pkunit.work_dir()
    template_common.copy_lib_files(data, None, d)


import markdown
import markdown.extensions.codehilite
from sys import argv

# Mathjax Extension
class MathJaxPattern(markdown.inlinepatterns.Pattern):
    def __init__(self):
        markdown.inlinepatterns.Pattern.__init__(self,
                                                 r'(?<!\\)(\$\$?)(.+?)\2')

    def handleMatch(self, m):
        node = markdown.util.etree.Element('mathjax')
        node.text = markdown.util.AtomicString(
            m.group(2) + m.group(3) + m.group(2))
        return node

class MathJaxExtension(markdown.Extension):
    def extendMarkdown(self, md, md_globals):
        # Needs to come before escape matching because \ is pretty important in
        # LaTeX
        md.inlinePatterns.add('mathjax', MathJaxPattern(), '<escape')

def latex_friendly(configs=[]):
    return MathJaxExtension()

input_file = open(argv[1])
text = input_file.read()
file_name = input_file.name
file_name = file_name[:len(file_name) - 3] + '.html'
text = markdown.markdown(text,
                         extensions=[
                             'markdown.extensions.tables',
                             'markdown.extensions.fenced_code',
                             'markdown.extensions.codehilite',
                             latex_friendly()
                         ])

text = text.replace('<table', '<table class="table"')
text = text.replace('<img', '<img style="max-width:100%"')
text = text.replace(
    '
',
    '

')
text = text.replace('
', '

')

print(text)

# with open(file_name, 'w') as f:
#    f.write(text)


# encoding: utf-8
"""
@project = javbus_crawler
@file = downloadtest
@author = ThinkPad
@create_time = 2019-10-2921:55
"""
import requests
import re
from bs4 import BeautifulSoup

url = 'https://www.dmmsee.icu/EIKI-087'
homeres = requests.get(url)
soup = BeautifulSoup(homeres.text)

paramsElems = soup.find_all('script')[8]
text = paramsElems.text
gid = text[12:24]
reqUrl = 'https://www.dmmsee.icu/ajax/uncledatoolsbyajax.php'
user_agent = 'Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, ' \
             'like Gecko) Chrome / 64.0.3282.140 Safari / 537.36 Edge / 18.17763 '
header = {'User-agent': user_agent, 'referer': url}

payload = {'gid': gid, 'uc': 0}

tableRes = requests.get(reqUrl, params=payload, headers=header, timeout=20)
tableRes.raise_for_status()

magnetSoup = BeautifulSoup(tableRes.text, 'html.parser')
magnetElems = magnetSoup.select('a[style="color:#333"]')
m = 0
pattern = r'\r|\n| |\t'
# links = []

while m < len(magnetElems):
    magnetLink = magnetElems[m].get('href')
    movieFile = re.sub(pattern, '', magnetElems[m].text)
    m += 1
    movieSize = re.sub(pattern, '', magnetElems[m].text)
    m += 1
    movieDate = re.sub(pattern, '', magnetElems[m].text)
    print(movieDate)
    if movieDate == '0000-00-00':
        movieDate = '1900-01-01'
    m += 1
    print(m, 'movieFile:{} movieSize{} movieDate{}\n'.format(movieFile, movieSize, movieDate))


from django.core.exceptions import ObjectDoesNotExist
from rest_framework.permissions import IsAuthenticated
from django.contrib.auth.models import Group
from knowledge.models import KbKnowledge
from .models import SysUser
from rest_framework.views import APIView
from rest_framework import serializers, status
from rest_framework.response import Response
from .services import get_user_activity, add_subscriber, is_developer
from rest_framework.authtoken.models import Token
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
import sys

def compressImage(uploadedImage):
    imageTemproary = Image.open(uploadedImage).convert('RGB')
    outputIoStream = BytesIO()
    imageTemproaryResized = imageTemproary.resize((200, 200))
    imageTemproaryResized.save(outputIoStream, format='JPEG', quality=60)
    outputIoStream.seek(0)
    uploadedImage = InMemoryUploadedFile(outputIoStream, 'ImageField', "%s.jpg" % uploadedImage.name.split('.')[0],
                                         'image/jpeg', sys.getsizeof(outputIoStream), None)
    return uploadedImage

class GetUserDetailViewSet(APIView):
    class GetUserDetailFromTokenSerializer(serializers.ModelSerializer):
        class Meta:
            model = SysUser
            fields = ('id_name', 'first_name', 'last_name', 'profile_pic', 'profile', 'header_image', 'email',
                      'about', 'header_image', 'facebook_profile_link', 'instagram_profile_link',
                      'twitter_profile_link', 'external_website_link', 'linkedin_profile', 'public', 'groups')

    def get(self, request, format=None):
        serializer = self.GetUserDetailFromTokenSerializer(request.user, many=False)
        # request.user.groups
        moderator = author = False
        if request.user.groups.filter(name="Moderators").exists():
            moderator = True
        if request.user.groups.filter(name="Authors").exists():
            author = True
        response = {'user': serializer.data, 'author': author, 'moderator': moderator}
        return Response(response, status=status.HTTP_200_OK)

    def post(self, request, format=None):
        if request.data["delete"]:
            request.user.delete()
            response = {"message": "User Deleted Successfully"}
            return Response(response, status=status.HTTP_200_OK)

class EditUserDetailViewSet(APIView):

    def post(self, request, format=None):
        # breakpoint()
        # user : SysUser
        user = request.user
        user.first_name = request.data['first_name']
        user.last_name = request.data['last_name']
        user.external_website_link = request.data['external_link']
        user.instagram_profile_link = request.data['instagram_link']
        user.linkedin_profile = request.data['linkedin_link']
        user.facebook_profile_link = request.data['facebook_link']
        user.public = request.data['public']
        user.twitter_profile_link = request.data['twitter_link']
        # user.profile = request.data['profile']
        user.about = request.data['about']
        user.save()
        return Response({"message": "changed"}, status=status.HTTP_200_OK)

class EditImageOnlyViewSet(APIView):
    class EditImageOnlySerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = SysUser
            fields = ['profile']

    def post(self, request, format=None):
        # breakpoint()
        user = Token.objects.get(key=request.data['token']).user
        user.profile = compressImage(request.data['profile'])
        user.save()
        return Response({"message": "changed the image mofo"}, status=status.HTTP_200_OK)

class GetUserActivity(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request, requested_type, start, end, format=None):
        result = get_user_activity(request, requested_type, start, end)
        # breakpoint()
        return Response(result, status=status.HTTP_200_OK)

class AddSubscriberViewSet(APIView):

    def post(self, request, format=None):
        result = add_subscriber(request)
        return Response(result, status=status.HTTP_200_OK)

class IsDeveloperViewSet(APIView):
    # permission_classes = (IsAuthenticated,)

    def post(self, request, format=None):
        # breakpoint()
        result = is_developer(request.data['passcode'], request.data['username'])
        return Response(result, status=status.HTTP_200_OK)

class IsPartOfTheGroup(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request, group_name, format=None):
        # request.user
        # breakpoint()
        if request.user.groups.filter(name=group_name).exists():
            response = True
        else:
            response = False
        return Response(response, status=status.HTTP_200_OK)

class GetUserAuthoredArticles(APIView):
    permission_classes = (IsAuthenticated,)

    class GetUserAuthoreArticlesSerializer(serializers.ModelSerializer):
        class Meta:
            model = KbKnowledge
            fields = ['id', 'title', 'sys_created_on', 'sys_updated_on', 'workflow', 'article_url', 'get_category',
                      'get_knowledge_base']

    def get(self, request, sort_by, state, format=None):
        # breakpoint()

        if state == 'all':
            articles = KbKnowledge.objects.filter(author=request.user, active=True).order_by(sort_by)
            articles_count = KbKnowledge.objects.filter(author=request.user, active=True).count()
            articles_data = self.GetUserAuthoreArticlesSerializer(articles, many=True)
            response = {
                'articles': articles_data.data,
                'total_count': articles_count,
            }
        if state != 'all':
            articles = KbKnowledge.objects.filter(author=request.user, active=True, workflow=state).order_by(sort_by)
            articles_count = KbKnowledge.objects.filter(author=request.user, active=True, workflow=state).count()
            articles_data = self.GetUserAuthoreArticlesSerializer(articles, many=True)
            response = {
                'articles': articles_data.data,
                'total_count': articles_count,
            }
        return Response(response, status=status.HTTP_200_OK)

class MakeUserAuthor(APIView):
    permission_classes = (IsAuthenticated, )

    def post(self, request, format=None):
        my_group = Group.objects.get(name='Authors')
        my_group.user_set.add(request.user)
        return Response('success', status=status.HTTP_201_CREATED)

class PublicUserData(APIView):
    class GetUserDetailFromTokenSerializer(serializers.ModelSerializer):
        class Meta:
            model = SysUser
            fields = ('id_name', 'first_name', 'last_name', 'profile_pic', 'profile', 'header_image',
                      'about', 'header_image', 'facebook_profile_link', 'instagram_profile_link',
                      'twitter_profile_link', 'external_website_link', 'linkedin_profile', 'public', 'groups')

    def get(self, request, id, format=None):
        try:
            user = SysUser.objects.get(id_name=id, is_active=True, public=True)
        except ObjectDoesNotExist:
            return Response('', status=status.HTTP_404_NOT_FOUND)
        serializer = self.GetUserDetailFromTokenSerializer(user, many=False)
        # request.user.groups
        moderator = author = False
        if user:
            if user.groups.filter(name="Moderators").exists():
                moderator = True
            if user.groups.filter(name="Authors").exists():
                author = True
            response = {'user': serializer.data, 'author': author, 'moderator': moderator}
            return Response(response, status=status.HTTP_200_OK)
        else:
            return Response('User has deactivated his account!', status=status.HTTP_404_NOT_FOUND)

class GetPublicAuthorArticles(APIView):
    class GetUserAuthoreArticlesSerializer(serializers.ModelSerializer):
        class Meta:
            model = KbKnowledge
            fields = ['id', 'title', 'get_knowledge_base', 'get_category', 'article_url']

    def get(self, request, id_name, sort_by, format=None):
        # breakpoint()
        try:
            user = SysUser.objects.get(id_name=id_name, public=True, is_active=True)
        except ObjectDoesNotExist:
            return Response('User has deactivated his account', status=status.HTTP_404_NOT_FOUND)
        articles = KbKnowledge.objects.filter(author=user, active=True, workflow="published").order_by(sort_by)
        articles_count = KbKnowledge.objects.filter(author=user, active=True).count()
        articles_data = self.GetUserAuthoreArticlesSerializer(articles, many=True)
        response = {
            'articles': articles_data.data,
            'total_count': articles_count,
        }
        return Response(response, status=status.HTTP_200_OK)