<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import division #new
from Tkinter import *
from math import *
import webbrowser

'''
TODO:
Create a calculator with a complete graphic user interface (GUI).
Calculator must have the following functionality:

1- Performance of all arithmetic operations.
2- Addition, subtraction, multiplication and division functions.
3- Square root, cube root, cubes, squares and decimals.
4- Generation of mathematical tables in a separate window.
5- CE, C.
6- About and exit dropdown menu.
'''

class Calculator:

    @classmethod
    def __init__(cls, master):
        master.wm_title('Calculator')
        # Create text box which shows input and output
        cls.resultbox = Entry(master, width=22, justify='right',
                              font="Helvetica 15")
        cls.resultbox.bind('<Return>', cls.evalonreturn)
        cls.resultbox.grid(row=0, column=0, columnspan=5, sticky=W)
        cls.resultbox.focus_set()

        # Create dropdown menu
        cls.menu = Menu(master)
        master.config(menu=cls.menu)

        cls.subMenu = Menu(cls.menu, tearoff=0)
        cls.subMenu.add_separator()
        cls.menu.add_cascade(label='Help', menu=cls.subMenu)
        cls.subMenu.add_command(label='About', command=cls.displayAboutMenu)
        cls.subMenu.add_separator()
        cls.subMenu.add_command(label='Exit', command=master.destroy)

        # Add number buttons
        cls.x = '789456123'
        cls.i = 0
        cls.number_buttons = []
        for current_row in xrange(1, 4):
            for current_column in xrange(1, 4):
                cls.b = Button(master, text=cls.x[cls.i], width=7, height=4)
                cls.number_buttons.append(cls.b)
                cls.number_buttons[cls.i].grid(
                    row=current_row, column=current_column, pady=0, sticky='W')
                cls.number_buttons[cls.i]['command'] = lambda currbtn = cls.x[cls.i]: cls.resultbox.insert(
                    END, currbtn)
                cls.i += 1

        # Zero button
        cls.zerobttn = Button(master, text='0', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, '0'))
        cls.zerobttn.grid(row=4, column=2)

        # Clear button
        cls.clearbttn = Button(master, text='C', width=7, height=4,
                               command=lambda: cls.resultbox.delete(0, END))
        cls.clearbttn.grid(row=5, column=4)

        # Raise to power Button
        cls.expbttn = Button(master, text='Power', width=7, height=4,
                             command=lambda: cls.resultbox.insert(END, 'pow()'))
        cls.expbttn.grid(row=5, column=2)

        # Square root Button
        cls.sqrtbttn = Button(master, text='√', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, 'sqrt()'))
        cls.sqrtbttn.grid(row=5, column=3)

        # Decimal button
        cls.decimalbttn = Button(master, text='.', width=7, height=4,
                                 command=lambda: cls.resultbox.insert(END, '.'))
        cls.decimalbttn.grid(row=4, column=1)

        # Math table generator button
        cls.mathtables = Button(master, text='Generate\n Math\n Tables',
                                width=7, height=4, command=cls.mathtablegenwindow)
        cls.mathtables.grid(row=5, column=1)

        # Evaluate button
        cls.equals = Button(master, text='=', width=7, height=4, command=cls.evalonequals)
        cls.equals.grid(row=4, column=3)

        # Addition button
        cls.add = Button(master, text='+', width=7, height=4,
                         command=lambda: cls.resultbox.insert(END, '+'))
        cls.add.grid(row=1, column=4)

        # Subtraction button
        cls.subtract = Button(master, text='-', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, '-'))
        cls.subtract.grid(row=2, column=4)

        # Multiplication button
        cls.multiply = Button(master, text='x', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, '*'))
        cls.multiply.grid(row=3, column=4)
        # Division button
        cls.divide = Button(master, text='÷', width=7, height=4,
                            command=lambda: cls.resultbox.insert(END, '/'))
        cls.divide.grid(row=4, column=4)

    @classmethod
    def evalonreturn(cls, event):
        cls.result = eval(cls.resultbox.get())
        cls.resultbox.delete(0, END)
        cls.resultbox.insert(END, cls.result)

    @classmethod
    def evalonequals(cls):
        cls.resultequals = eval(cls.resultbox.get())
        cls.resultbox.delete(0, END)
        cls.resultbox.insert(END, cls.resultequals)

    @classmethod
    def mathtablegenwindow(cls):
        cls.tablerange = Tk()
        cls.tablerange.wm_title('Math table range')
        cls.intlabel = Label(cls.tablerange, text='Integer')
        cls.intlabel.grid(row=0, column=0)
        cls.intenter = Entry(cls.tablerange)
        cls.intenter.grid(row=0, column=1)
        cls.startlabel = Label(cls.tablerange, text='Starting range\n of table')
        cls.startlabel.grid(row=1, column=0)
        cls.startrangein = Entry(cls.tablerange)
        cls.startrangein.grid(row=1, column=1)
        cls.endlabel = Label(cls.tablerange, text='End range\n of table')
        cls.endlabel.grid(row=2, column=0)
        cls.endrangein = Entry(cls.tablerange)
        cls.endrangein.grid(row=2, column=1)
        cls.inbutton = Button(cls.tablerange, text='OK', command=cls.mathtablegen)
        cls.inbutton.grid(row=3, columnspan=2)
        cls.tablerange.mainloop()

    @classmethod
    def mathtablegen(cls):
        cls.a = cls.intenter.get()
        cls.b = cls.startrangein.get()
        cls.c = cls.endrangein.get()

        cls.mathtablewin = Tk()
        cls.mathtablewin.wm_title('Math Tables')
        cls.n1 = int(cls.a)
        cls.n2 = int(cls.b)
        cls.n3 = int(cls.c)
        cls.output = ''
        for x in range(cls.n2, (cls.n3 + 1), 1):
            cls.ha = cls.n1 * x
            cls.output = cls.output + "{0} * {1} = {2}\n".format(cls.n1, x, cls.ha)
        mathtab = Label(cls.mathtablewin, text=cls.output)
        mathtab.pack()
        cls.mathtablewin.mainloop()

    @classmethod
    def webevent(cls, event):
        webbrowser.open_new(r"https://docs.python.org/2/library/math.html")

    @classmethod
    def displayAboutMenu(cls):
        cls.about_window = Tk()
        cls.about_window.wm_title('About')
        # cls.about_window.geometry('150x100')
        cls.about_label1 = Label(cls.about_window, text='''
This calculator comes packed with the
built-in python math module, giving it more
accessibilty to mathematical functions.
The proper syntax should merely be entered in
the input box for your expressions to be evaluated.

Documentation for the module can be found at:''', justify='left')
        cls.about_label1.grid(row=0, column=0)
        cls.weblink = Label(
            cls.about_window, text='https://docs.python.org/2/library/math.html', fg='blue', cursor='hand2', justify=LEFT)
        cls.weblink.bind('<Button-1>', cls.webevent)
        cls.weblink.grid(row=1, column=0)
        cls.about_label2 = Label(cls.about_window, text='''
-------------WARNING-------------
This calculator uses python's built-in eval()
function. Do not input any rogue code in the entry
box for the sake of your machine. Use at your
own risk.
-------------------------------------

Made by Babur Ahmed.
16th July, 2017
        ''', justify=LEFT)
        cls.about_label2.grid(row=2, column=0)
        cls.about_window.mainloop()

root = Tk()

# root.geometry('270x350')
calcstart = Calculator(root)

root.mainloop()

<commit_msg>Disabled window resizing<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import division
from Tkinter import *
from math import *
import webbrowser

'''
TODO:
Create a calculator with a complete graphic user interface (GUI).
Calculator must have the following functionality:

1- Performance of all arithmetic operations.
2- Addition, subtraction, multiplication and division functions.
3- Square root, cube root, cubes, squares and decimals.
4- Generation of mathematical tables in a separate window.
5- CE, C.
6- About and exit dropdown menu.
'''

class Calculator:

    @classmethod
    def __init__(cls, master):
        master.wm_title('Calculator')
        # Create text box which shows input and output
        cls.resultbox = Entry(master, width=22, justify='right',
                              font="Helvetica 15")
        cls.resultbox.bind('<Return>', cls.evalonreturn)
        cls.resultbox.grid(row=0, column=0, columnspan=5, sticky=W)
        cls.resultbox.focus_set()

        # Create dropdown menu
        cls.menu = Menu(master)
        master.config(menu=cls.menu)

        cls.subMenu = Menu(cls.menu, tearoff=0)
        cls.subMenu.add_separator()
        cls.menu.add_cascade(label='Help', menu=cls.subMenu)
        cls.subMenu.add_command(label='About', command=cls.displayAboutMenu)
        cls.subMenu.add_separator()
        cls.subMenu.add_command(label='Exit', command=master.destroy)

        # Add number buttons
        cls.x = '789456123'
        cls.i = 0
        cls.number_buttons = []
        for current_row in xrange(1, 4):
            for current_column in xrange(1, 4):
                cls.b = Button(master, text=cls.x[cls.i], width=7, height=4)
                cls.number_buttons.append(cls.b)
                cls.number_buttons[cls.i].grid(
                    row=current_row, column=current_column, pady=0, sticky='W')
                cls.number_buttons[cls.i]['command'] = lambda currbtn = cls.x[cls.i]: cls.resultbox.insert(
                    END, currbtn)
                cls.i += 1

        # Zero button
        cls.zerobttn = Button(master, text='0', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, '0'))
        cls.zerobttn.grid(row=4, column=2)

        # Clear button
        cls.clearbttn = Button(master, text='C', width=7, height=4,
                               command=lambda: cls.resultbox.delete(0, END))
        cls.clearbttn.grid(row=5, column=4)

        # Raise to power Button
        cls.expbttn = Button(master, text='Power', width=7, height=4,
                             command=lambda: cls.resultbox.insert(END, 'pow()'))
        cls.expbttn.grid(row=5, column=2)

        # Square root Button
        cls.sqrtbttn = Button(master, text='√', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, 'sqrt()'))
        cls.sqrtbttn.grid(row=5, column=3)

        # Decimal button
        cls.decimalbttn = Button(master, text='.', width=7, height=4,
                                 command=lambda: cls.resultbox.insert(END, '.'))
        cls.decimalbttn.grid(row=4, column=1)

        # Math table generator button
        cls.mathtables = Button(master, text='Generate\n Math\n Tables',
                                width=7, height=4, command=cls.mathtablegenwindow)
        cls.mathtables.grid(row=5, column=1)

        # Evaluate button
        cls.equals = Button(master, text='=', width=7, height=4, command=cls.evalonequals)
        cls.equals.grid(row=4, column=3)

        # Addition button
        cls.add = Button(master, text='+', width=7, height=4,
                         command=lambda: cls.resultbox.insert(END, '+'))
        cls.add.grid(row=1, column=4)

        # Subtraction button
        cls.subtract = Button(master, text='-', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, '-'))
        cls.subtract.grid(row=2, column=4)

        # Multiplication button
        cls.multiply = Button(master, text='x', width=7, height=4,
                              command=lambda: cls.resultbox.insert(END, '*'))
        cls.multiply.grid(row=3, column=4)
        # Division button
        cls.divide = Button(master, text='÷', width=7, height=4,
                            command=lambda: cls.resultbox.insert(END, '/'))
        cls.divide.grid(row=4, column=4)

    @classmethod
    def evalonreturn(cls, event):
        cls.result = eval(cls.resultbox.get())
        cls.resultbox.delete(0, END)
        cls.resultbox.insert(END, cls.result)

    @classmethod
    def evalonequals(cls):
        cls.resultequals = eval(cls.resultbox.get())
        cls.resultbox.delete(0, END)
        cls.resultbox.insert(END, cls.resultequals)

    @classmethod
    def mathtablegenwindow(cls):
        cls.tablerange = Tk()
        cls.tablerange.wm_title('Math table range')
        cls.intlabel = Label(cls.tablerange, text='Integer')
        cls.intlabel.grid(row=0, column=0)
        cls.intenter = Entry(cls.tablerange)
        cls.intenter.grid(row=0, column=1)
        cls.startlabel = Label(cls.tablerange, text='Starting range\n of table')
        cls.startlabel.grid(row=1, column=0)
        cls.startrangein = Entry(cls.tablerange)
        cls.startrangein.grid(row=1, column=1)
        cls.endlabel = Label(cls.tablerange, text='End range\n of table')
        cls.endlabel.grid(row=2, column=0)
        cls.endrangein = Entry(cls.tablerange)
        cls.endrangein.grid(row=2, column=1)
        cls.inbutton = Button(cls.tablerange, text='OK', command=cls.mathtablegen)
        cls.inbutton.grid(row=3, columnspan=2)
        cls.tablerange.mainloop()

    @classmethod
    def mathtablegen(cls):
        cls.a = cls.intenter.get()
        cls.b = cls.startrangein.get()
        cls.c = cls.endrangein.get()

        cls.mathtablewin = Tk()
        cls.mathtablewin.wm_title('Math Tables')
        cls.n1 = int(cls.a)
        cls.n2 = int(cls.b)
        cls.n3 = int(cls.c)
        cls.output = ''
        for x in range(cls.n2, (cls.n3 + 1), 1):
            cls.ha = cls.n1 * x
            cls.output = cls.output + "{0} * {1} = {2}\n".format(cls.n1, x, cls.ha)
        mathtab = Label(cls.mathtablewin, text=cls.output)
        mathtab.pack()
        cls.mathtablewin.mainloop()

    @classmethod
    def webevent(cls, event):
        webbrowser.open_new(r"https://docs.python.org/2/library/math.html")

    @classmethod
    def displayAboutMenu(cls):
        cls.about_window = Tk()
        cls.about_window.wm_title('About')
        # cls.about_window.geometry('150x100')
        cls.about_label1 = Label(cls.about_window, text='''
This calculator comes packed with the
built-in python math module, giving it more
accessibilty to mathematical functions.
The proper syntax should merely be entered in
the input box for your expressions to be evaluated.

Documentation for the module can be found at:''', justify='left')
        cls.about_label1.grid(row=0, column=0)
        cls.weblink = Label(
            cls.about_window, text='https://docs.python.org/2/library/math.html', fg='blue', cursor='hand2', justify=LEFT)
        cls.weblink.bind('<Button-1>', cls.webevent)
        cls.weblink.grid(row=1, column=0)
        cls.about_label2 = Label(cls.about_window, text='''
-------------WARNING-------------
This calculator uses python's built-in eval()
function. Do not input any rogue code in the entry
box for the sake of your machine. Use at your
own risk.
-------------------------------------

Made by Babur Ahmed.
16th July, 2017
        ''', justify=LEFT)
        cls.about_label2.grid(row=2, column=0)
        cls.about_window.mainloop()

root = Tk()

root.resizable(False, False)  #new - window resizing is disabled
calcstart = Calculator(root)

root.mainloop()

<|endoftext|>


<commit_before>from __future__ import unicode_literals
import pytest

from spacy.orth import is_alpha
from spacy.orth import is_digit
from spacy.orth import is_punct
from spacy.orth import is_space
from spacy.orth import is_ascii
from spacy.orth import is_upper
from spacy.orth import is_lower
from spacy.orth import is_title

@pytest.fixture
def words():
    return ["1997", "19.97", "hello9", "Hello", "HELLO", "Hello9", "\n", "!",
            "!d", "\nd"]

def test_is_alpha(words):
    assert not is_alpha(words[0])
    assert not is_alpha(words[1])
    assert not is_alpha(words[2])
    assert is_alpha(words[3])
    assert is_alpha(words[4])
    assert not is_alpha(words[5])
    assert not is_alpha(words[6])
    assert not is_alpha(words[7])
    assert not is_alpha(words[8])
    assert not is_alpha(words[9])

def test_is_digit(words):
    assert is_digit(words[0])
    assert not is_digit(words[1])
    assert not is_digit(words[2])
    assert not is_digit(words[3])
    assert not is_digit(words[4])
    assert not is_digit(words[5])
    assert not is_digit(words[6])
    assert not is_digit(words[7])
    assert not is_digit(words[8])
    assert not is_digit(words[9])
<commit_msg>* Add stubs to test is_bracket/is_quote/is_left_punct/is_right_punct functions<commit_after>from __future__ import unicode_literals
import pytest

from spacy.orth import is_alpha
from spacy.orth import is_digit
from spacy.orth import is_punct
from spacy.orth import is_space
from spacy.orth import is_ascii
from spacy.orth import is_upper
from spacy.orth import is_lower
from spacy.orth import is_title

@pytest.fixture
def words():
    return ["1997", "19.97", "hello9", "Hello", "HELLO", "Hello9", "\n", "!",
            "!d", "\nd"]

def test_is_alpha(words):
    assert not is_alpha(words[0])
    assert not is_alpha(words[1])
    assert not is_alpha(words[2])
    assert is_alpha(words[3])
    assert is_alpha(words[4])
    assert not is_alpha(words[5])
    assert not is_alpha(words[6])
    assert not is_alpha(words[7])
    assert not is_alpha(words[8])
    assert not is_alpha(words[9])

def test_is_digit(words):
    assert is_digit(words[0])
    assert not is_digit(words[1])
    assert not is_digit(words[2])
    assert not is_digit(words[3])
    assert not is_digit(words[4])
    assert not is_digit(words[5])
    assert not is_digit(words[6])
    assert not is_digit(words[7])
    assert not is_digit(words[8])
    assert not is_digit(words[9])

def test_is_quote(words):
    pass

def test_is_bracket(words):
    pass

def test_is_left_bracket(words):
    pass

def test_is_right_bracket(words):
    pass
<|endoftext|>


<commit_before>#!/usr/bin/python

import HTML
import sqlite3
import os

OUTPUT_DIRECTORY = 'pages'
DATABASE_FILENAME = 'CBRadioPlaylist.db'

def getOutputPath(filename):
	real_output_dir = os.path.realpath(OUTPUT_DIRECTORY)
	if not os.path.exists(real_output_dir):
		os.makedirs(real_output_dir)
	return real_output_dir + os.path.sep + filename

def writeToFile(filename, content):
	output_path = getOutputPath(filename)
	with open(output_path, 'w', encoding='utf-8') as f:
		f.write(content)
		print(str.format("Source output to '{0}'\n", output_path))

def createTable(columns, id=None, class_name=None):
	columns_type = type(columns)
	if columns_type is not list and columns_type is not tuple:
		raise TypeError('Columns must be a list or tuple')

	table = HTML.HTMLElement.table(id, class_name)
	header_row = HTML.HTMLElement.tr()
	for col in columns:
		header_row.appendChild(HTML.HTMLElement.th(col))
	table.tHead.appendChild(header_row)
	return table

def generatePlaylistsPage(filename, conn):
	print('Generating playlists page...')
	doc = HTML.HTMLDocument('Cape Breton Radio Playlists', 'html')
	doc.addMetaTag(('charset', 'utf-8'))
	doc.addMetaTag([('name', 'viewport'), ('content', 'width=device-width, initial-scale=1')])
	doc.addMetaTag([('name', 'description'), ('content', 'Song list data scraped from a few Cape Breton radio stations.')])
	doc.addMetaTag([('name', 'keywords'), ('content', 'cape breton,radio,stations,scraped,playlist,data,database,sqlite,the cape,94.9,cjcb,fm,the giant,the eagle,max,98.3')])
	doc.addStylesheet('styles.css')

	body = doc.body
	body.appendChild(HTML.HTMLElement.h1('Cape Breton Radio Playlists'))
	body.appendChild(HTML.HTMLElement.p('This is a page of compiled song lists from a few Cape Breton radio stations. The song data was grabbed by a script that ran from December 16th, 2016 to January 1, 2017.'))
	body.appendChild(HTML.HTMLElement.p('Any name errors, misspellings or truncations are not my fault and are how they appeared on their sources. This may lead to some songs or artists having multiple entries in the database with different spelling alterations.', style='font-style: italic'))
	body.appendChild(HTML.HTMLElement('br'))

	cursor = conn.cursor()
	stations = cursor.execute("SELECT * FROM stations").fetchall()
	playlists = cursor.execute("""SELECT playlists.station_id, songs.name as song_name, artists.name as artist_name, playlists.original_time, playlists.formatted_time, playlists.posix_timestamp FROM playlists
								INNER JOIN songs ON playlists.song_id = songs.id
								INNER JOIN artists ON songs.artist_id = artists.id
								INNER JOIN stations ON playlists.station_id = stations.id
								ORDER BY station_id, posix_timestamp DESC""").fetchall()

	# Partition each station into its own list by using a dictionary
	partitioned_playlists = {}
	for station in stations:
		partitioned_playlists[station['id']] = []
	for row in playlists:
		partitioned_playlists[row['station_id']].append(row)
	del playlists

	header_columns = ('#', 'Song', 'Artist', 'Play Time (original)', 'Play Time (formatted)')
	# Using the station list defined above, I can keep the order of the stations as they appear in the query
	# results AND keep the rest of the station data without using a more complex data structure
	for station in stations:
		playlist_header = HTML.HTMLElement.h2('Station: ' + station['name'] + ' (')
		playlist_header.appendChild(HTML.HTMLElement.a(station['playlist_url'], station['playlist_url']))
		playlist_header.appendChild(HTML.TextNode(')'))
		body.appendChild(playlist_header)
		table = createTable(header_columns, class_name='playlist')
		for index, row in enumerate(partitioned_playlists[station['id']]):
			tr = HTML.HTMLElement.tr()
			tr.appendChild(HTML.HTMLElement.td(str(index + 1)))
			tr.appendChild(HTML.HTMLElement.td(row['song_name']))
			tr.appendChild(HTML.HTMLElement.td(row['artist_name']))
			tr.appendChild(HTML.HTMLElement.td(row['original_time']))
			tr.appendChild(HTML.HTMLElement.td(row['formatted_time']))
			table.tBody.appendChild(tr)
		container = HTML.HTMLElement.div(class_name='playlist-container')
		container.appendChild(table)
		body.appendChild(container)

	source = doc.getSource(True, True)
	print('Finished.')
	writeToFile(filename, source)

def getDistinctSongForEachStation(conn):
	"""
	Returns an array of distinct songs played by each station

	Args:
		conn: Database connection
	"""
	pass

def getSongPlayCountForEachStation(conn):
	"""
	Returns the play count for each song by each station.
	Ordered by play count in descending order

	Args:
		conn: Database connection
	"""
	pass

def getDistinctArtistForEachStation(conn):
	"""
	Returns an array of distinct artists played by each station

	Args:
		conn: Database connection
	"""
	pass

def getArtistPlayCountForEachStation(conn):
	"""
	Returns the play count for each artist by each station
	Ordered by play count in descending order

	Args:
		conn: Database connection
	"""
	pass

def getSongPlayCountByKeywordsForEachStation(conn):
	"""
	Returns the play count for each song that contains specific keywords by each station
	Ordered by play count in descending order

	Args:
		conn: Database connection
	"""
	keywords = ('christmas', 'snow', 'bells', 'jingle', 'santa', 'claus', 'winter', 'reindeer', 'rudolph', 'angel', 'noel',
				'sleigh', 'present', 'mistletoe', 'tinsel', 'tree', 'stocking', 'xmas', 'silver', 'saint', 'silent night',
				'cold outside', 'frosty', 'drummer boy', 'mary', 'merry', 'lord', 'sugar plum', 'chestnuts', 'feliz navidad',
				'december', 'holiday', 'wish', 'lights', 'fairy', 'hallelujah')

with sqlite3.connect(DATABASE_FILENAME) as conn:
	conn.row_factory = sqlite3.Row
	generatePlaylistsPage('playlists.html', conn)
<commit_msg>More modularization, did two of the statistic functions<commit_after>#!/usr/bin/python

import HTML
import sqlite3
import os

OUTPUT_DIRECTORY = 'pages'
DATABASE_FILENAME = 'CBRadioPlaylist.db'

def getOutputPath(filename):
	real_output_dir = os.path.realpath(OUTPUT_DIRECTORY)
	if not os.path.exists(real_output_dir):
		os.makedirs(real_output_dir)
	return real_output_dir + os.path.sep + filename

def writeToFile(filename, content):
	output_path = getOutputPath(filename)
	with open(output_path, 'w', encoding='utf-8') as f:
		f.write(content)
		print(str.format("Source output to '{0}'\n", output_path))

def createTable(columns, id=None, class_name=None):
	columns_type = type(columns)
	if columns_type is not list and columns_type is not tuple:
		raise TypeError('Columns must be a list or tuple')

	table = HTML.HTMLElement.table(id, class_name)
	header_row = HTML.HTMLElement.tr()
	for col in columns:
		header_row.appendChild(HTML.HTMLElement.th(col))
	table.tHead.appendChild(header_row)
	return table

def createStationListHeader(station_name, station_playlist_url):
	"""
	Returns a new HTML header element with the station name and playlist URL

	Args:
		station_name: Name of the station
		station_playlist_url: URL of the station's playlist
	"""
	header = HTML.HTMLElement.h2('Station: ' + station_name + '(')
	header.appendChild(HTML.HTMLElement.a(station_playlist_url, station_playlist_url))
	header.appendChild(HTML.TextNode(')'))
	return header

def partitionDataByStation(data):
	"""
	Separates rows of data into a dictionary where the keys are a station id

	Args:
		data: Query results that requires each row to have the 'station_id' key set
	"""
	partitioned_data = {}
	for station in STATIONS:
		partitioned_data[station['id']] = []
	for row in data:
		partitioned_data[row['station_id']].append(row)
	return partitioned_data

def generatePlaylistsPage(filename, conn):
	print('Generating playlists page...')
	doc = HTML.HTMLDocument('Cape Breton Radio Playlists', 'html')
	doc.addMetaTag(('charset', 'utf-8'))
	doc.addMetaTag([('name', 'viewport'), ('content', 'width=device-width, initial-scale=1')])
	doc.addMetaTag([('name', 'description'), ('content', 'Song list data scraped from a few Cape Breton radio stations.')])
	doc.addMetaTag([('name', 'keywords'), ('content', 'cape breton,radio,stations,scraped,playlist,data,database,sqlite,the cape,94.9,cjcb,fm,the giant,the eagle,max,98.3')])
	doc.addStylesheet('styles.css')

	body = doc.body
	body.appendChild(HTML.HTMLElement.h1('Cape Breton Radio Playlists'))
	body.appendChild(HTML.HTMLElement.p('This is a page of compiled song lists from a few Cape Breton radio stations. The song data was grabbed by a script that ran from December 16th, 2016 to January 1, 2017.'))
	body.appendChild(HTML.HTMLElement.p('Any name errors, misspellings or truncations are not my fault and are how they appeared on their sources. This may lead to some songs or artists having multiple entries in the database with different spelling alterations.', style='font-style: italic'))
	body.appendChild(HTML.HTMLElement('br'))

	cursor = conn.cursor()
	playlists = cursor.execute("""SELECT playlists.station_id, songs.name as song_name, artists.name as artist_name, playlists.original_time, playlists.formatted_time, playlists.posix_timestamp FROM playlists
								INNER JOIN songs ON playlists.song_id = songs.id
								INNER JOIN artists ON songs.artist_id = artists.id
								INNER JOIN stations ON playlists.station_id = stations.id
								ORDER BY station_id, posix_timestamp DESC""").fetchall()

	# Partition each station into its own list by using a dictionary
	partitioned_playlists = partitionDataByStation(playlists)
	del playlists

	header_columns = ('#', 'Song', 'Artist', 'Play Time (original)', 'Play Time (formatted)')
	# Using the station list defined above, I can keep the order of the stations as they appear in the query
	# results AND keep the rest of the station data without using a more complex data structure
	for station in STATIONS:
		body.appendChild(createStationListHeader(station['name'], station['playlist_url']))
		table = createTable(header_columns, class_name='playlist')
		for index, row in enumerate(partitioned_playlists[station['id']]):
			tr = HTML.HTMLElement.tr()
			tr.appendChild(HTML.HTMLElement.td(str(index + 1)))
			tr.appendChild(HTML.HTMLElement.td(row['song_name']))
			tr.appendChild(HTML.HTMLElement.td(row['artist_name']))
			tr.appendChild(HTML.HTMLElement.td(row['original_time']))
			tr.appendChild(HTML.HTMLElement.td(row['formatted_time']))
			table.tBody.appendChild(tr)
		container = HTML.HTMLElement.div(class_name='playlist-container')
		container.appendChild(table)
		body.appendChild(container)

	source = doc.getSource(True, True)
	print('Finished.')
	writeToFile(filename, source)

def getDistinctSongForEachStation(conn):
	"""
	Returns an array of distinct songs played by each station

	Args:
		conn: Database connection
	"""
	cursor = conn.cursor()
	results = cursor.execute("""SELECT DISTINCT playlists.song_id, songs.name AS song_name, artists.name AS artist_name, playlists.* FROM playlists
								INNER JOIN songs ON playlists.song_id = songs.id
								INNER JOIN artists ON songs.artist_id = artists.id
								ORDER BY station_id, artist_name""").fetchall()

	return partitionDataByStation(results)

def getSongPlayCountForEachStation(conn):
	"""
	Returns the play count for each song by each station.
	Ordered by play count in descending order

	Args:
		conn: Database connection
	"""
	pass

def getDistinctArtistForEachStation(conn):
	"""
	Returns an array of distinct artists played by each station

	Args:
		conn: Database connection
	"""
	cursor = conn.cursor()
	results = cursor.execute("""SELECT DISTINCT artists.id, artists.name AS artist_name, playlists.station_id FROM playlists
								INNER JOIN songs ON playlists.song_id = songs.id
								INNER JOIN artists ON songs.artist_id = artists.id
								ORDER BY station_id, artists.name""").fetchall()

	return partitionDataByStation(results)

def getArtistPlayCountForEachStation(conn):
	"""
	Returns the play count for each artist by each station
	Ordered by play count in descending order

	Args:
		conn: Database connection
	"""
	pass

def getSongPlayCountByKeywordsForEachStation(conn):
	"""
	Returns the play count for each song that contains specific keywords by each station
	Ordered by play count in descending order

	Args:
		conn: Database connection
	"""
	keywords = ('christmas', 'snow', 'bells', 'jingle', 'santa', 'claus', 'winter', 'reindeer', 'rudolph', 'angel', 'noel',
				'sleigh', 'present', 'mistletoe', 'tinsel', 'tree', 'stocking', 'xmas', 'silver', 'saint', 'silent night',
				'cold outside', 'frosty', 'drummer boy', 'mary', 'merry', 'lord', 'sugar plum', 'chestnuts', 'feliz navidad',
				'december', 'holiday', 'wish', 'lights', 'fairy', 'hallelujah')

with sqlite3.connect(DATABASE_FILENAME) as conn:
	conn.row_factory = sqlite3.Row
	cursor = conn.cursor()
	STATIONS = cursor.execute("SELECT * FROM stations").fetchall()		# So the stations only need to be fetched once
	generatePlaylistsPage('playlists.html', conn)
<|endoftext|>


<commit_before>#!/usr/bin/env python

import json, urlparse, sys, os, signal, socket
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from subprocess import call

class GitAutoDeploy(BaseHTTPRequestHandler):

	CONFIG_FILEPATH = './GitAutoDeploy.conf.json'
	config = None
	debug = True
	quiet = False
	daemon = False

	@classmethod
	def getConfig(myClass):
		if(myClass.config == None):
			try:
				configString = open(myClass.CONFIG_FILEPATH).read()
			except:
				sys.exit('Could not load ' + myClass.CONFIG_FILEPATH + ' file')

			try:
				myClass.config = json.loads(configString)
			except:
				sys.exit(myClass.CONFIG_FILEPATH + ' file is not valid json')

			for repository in myClass.config['repositories']:
				if(not os.path.isdir(repository['path'])):
					sys.exit('Directory ' + repository['path'] + ' not found')
				if(not os.path.isdir(repository['path'] + '/.git')):
					sys.exit('Directory ' + repository['path'] + ' is not a Git repository')

		return myClass.config

	def do_POST(self):
		urls = self.parseRequest()
		for url in urls:
			paths = self.getMatchingPaths(url)
			for path in paths:
				self.pull(path)
				self.deploy(path)
		self.respond()

	def parseRequest(self):
		length = int(self.headers.getheader('content-length'))
		body = self.rfile.read(length)
		post = urlparse.parse_qs(body)
		items = []

		# If payload is missing, we assume gitlab syntax.
		if not 'payload' in post and 'repository' in body:
			response = json.loads(body)
			items.append(response['repository']['url'])

		# Otherwise, we assume github syntax.
		else:
			for itemString in post['payload']:
				item = json.loads(itemString)
				items.append(item['repository']['url'])

		return items

	def getMatchingPaths(self, repoUrl):
		res = []
		config = self.getConfig()
		for repository in config['repositories']:
			if(repository['url'] == repoUrl):
				res.append(repository['path'])
		return res

	def respond(self):
		self.send_response(200)
		self.send_header('Content-type', 'text/plain')
		self.end_headers()

	def pull(self, path):
		if(not self.quiet):
			print "\nPost push request received"
			print 'Updating ' + path
		call(['cd "' + path + '" && git fetch origin && git update-index --refresh &> /dev/null && git reset --hard origin/master'], shell=True)

	def deploy(self, path):
		config = self.getConfig()
		for repository in config['repositories']:
			if(repository['path'] == path):
				cmds = []
				if 'deploy' in repository:
					cmds.append(repository['deploy'])

				gd = config['global_deploy']
				if len(gd[0]) is not 0:
					cmds.insert(0, gd[0])
				if len(gd[1]) is not 0:
					cmds.append(gd[1])

				if(not self.quiet):
					print 'Executing deploy command(s)'
				for cmd in cmds:
					call(['cd "' + path + '" && ' + cmd], shell=True)

				break

class GitAutoDeployMain:

	server = None

	def run(self):
		for arg in sys.argv:
			if(arg == '-d' or arg == '--daemon-mode'):
				GitAutoDeploy.daemon = True
				GitAutoDeploy.quiet = True
			if(arg == '-q' or arg == '--quiet'):
				GitAutoDeploy.quiet = True
			if(arg == '--force'):
				print '[KILLER MODE] Warning: The --force option will try to kill any process ' \
					'using %s port. USE AT YOUR OWN RISK' %GitAutoDeploy.getConfig()['port']
				self.kill_them_all()

		if(GitAutoDeploy.daemon):
			pid = os.fork()
			if(pid != 0):
				sys.exit()
			os.setsid()

		self.create_pidfile()

		if(not GitAutoDeploy.quiet):
			print 'Github & Gitlab Autodeploy Service v 0.1 started'
		else:
			print 'Github & Gitlab Autodeploy Service v 0.1 started in daemon mode'

		try:
			self.server = HTTPServer(('', GitAutoDeploy.getConfig()['port']), GitAutoDeploy)
			self.server.serve_forever()
		except socket.error, e:
			if(not GitAutoDeploy.quiet and not GitAutoDeploy.daemon):
				print "Error on socket: %s" % e
				self.debug_diagnosis()
			sys.exit(1)

	def kill_them_all(self):
		pid = self.get_pid_on_port(GitAutoDeploy.getConfig()['port'])
		if pid == False:
			print '[KILLER MODE] I don\'t know the number of pid that is using my configured port\n ' \
				'[KILLER MODE] Maybe no one? Please, use --force option carefully'
			return False

		os.kill(pid, signal.SIGKILL)
		return True

	def create_pidfile(self):
		with open(GitAutoDeploy.getConfig()['pidfilepath'], 'w') as f:
			f.write(str(os.getpid()))

	def read_pidfile(self):
		with open(GitAutoDeploy.getConfig()['pidfilepath'],'r') as f:
			return f.readlines()

	def remove_pidfile(self):
		os.remove(GitAutoDeploy.getConfig()['pidfilepath'])

	def debug_diagnosis(self):
		if GitAutoDeploy.debug == False:
			return

		port = GitAutoDeploy.getConfig()['port']
		pid = self.get_pid_on_port(port)
		if pid == False:
			print 'I don\'t know the number of pid that is using my configured port'
			return

		print 'Process with pid number %s is using port %s' % (pid, port)
		with open("/proc/%s/cmdline" % pid) as f:
			cmdline = f.readlines()
			print 'cmdline ->', cmdline[0].replace('\x00', ' ')

	def get_pid_on_port(self,port):
		with open("/proc/net/tcp",'r') as f:
			filecontent = f.readlines()[1:]

		pids = [int(x) for x in os.listdir('/proc') if x.isdigit()]
		conf_port = str(GitAutoDeploy.getConfig()['port'])
		mpid = False

		for line in filecontent:
			if mpid != False:
				break

			_, laddr, _, _, _, _, _, _, _, inode = line.split()[:10]
			decport = str(int(laddr.split(':')[1], 16))

			if decport != conf_port:
				continue

			for pid in pids:
				try:
					path = "/proc/%s/fd" % pid
					if os.access(path, os.R_OK) is False:
						continue

					for fd in os.listdir(path):
						cinode = os.readlink("/proc/%s/fd/%s" % (pid, fd))
						minode = cinode.split(":")

						if len(minode) == 2 and minode[1][1:-1] == inode:
							mpid = pid
				except Exception as e:
					pass
		return mpid

	def stop(self):
		if(self.server is not None):
			self.server.socket.close()

	def exit(self):
		if(not GitAutoDeploy.quiet):
			print '\nGoodbye'
		self.remove_pidfile()
		sys.exit(0)

	def signal_handler(self, signum, frame):
		self.stop()
		if(signum == 1):
			self.run()
			return
		elif(signum == 2):
			print '\nKeyboard Interrupt!!!'
		elif(signum == 6):
			print 'Requested close by SIGABRT (process abort signal). Code 6.'

		self.exit()

if __name__ == '__main__':
	gadm = GitAutoDeployMain()

	signal.signal(signal.SIGHUP, gadm.signal_handler)
	signal.signal(signal.SIGINT, gadm.signal_handler)
	signal.signal(signal.SIGABRT, gadm.signal_handler)

	gadm.run()
<commit_msg>Avoid SIGPIPE (Broken pipe errno 32) error<commit_after>#!/usr/bin/env python

import json, urlparse, sys, os, signal, socket
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from subprocess import call

class GitAutoDeploy(BaseHTTPRequestHandler):

	CONFIG_FILEPATH = './GitAutoDeploy.conf.json'
	config = None
	debug = True
	quiet = False
	daemon = False

	@classmethod
	def getConfig(myClass):
		if(myClass.config == None):
			try:
				configString = open(myClass.CONFIG_FILEPATH).read()
			except:
				sys.exit('Could not load ' + myClass.CONFIG_FILEPATH + ' file')

			try:
				myClass.config = json.loads(configString)
			except:
				sys.exit(myClass.CONFIG_FILEPATH + ' file is not valid json')

			for repository in myClass.config['repositories']:
				if(not os.path.isdir(repository['path'])):
					sys.exit('Directory ' + repository['path'] + ' not found')
				if(not os.path.isdir(repository['path'] + '/.git')):
					sys.exit('Directory ' + repository['path'] + ' is not a Git repository')

		return myClass.config

	def do_POST(self):
		urls = self.parseRequest()
		for url in urls:
			paths = self.getMatchingPaths(url)
			for path in paths:
				self.pull(path)
				self.deploy(path)
		self.respond()

	def parseRequest(self):
		length = int(self.headers.getheader('content-length'))
		body = self.rfile.read(length)
		post = urlparse.parse_qs(body)
		items = []

		# If payload is missing, we assume gitlab syntax.
		if not 'payload' in post and 'repository' in body:
			response = json.loads(body)
			items.append(response['repository']['url'])

		# Otherwise, we assume github syntax.
		else:
			for itemString in post['payload']:
				item = json.loads(itemString)
				items.append(item['repository']['url'])

		return items

	def getMatchingPaths(self, repoUrl):
		res = []
		config = self.getConfig()
		for repository in config['repositories']:
			if(repository['url'] == repoUrl):
				res.append(repository['path'])
		return res

	def respond(self):
		self.send_response(200)
		self.send_header('Content-type', 'text/plain')
		self.end_headers()

	def pull(self, path):
		if(not self.quiet):
			print "\nPost push request received"
			print 'Updating ' + path
		call(['cd "' + path + '" && git fetch origin && git update-index --refresh &> /dev/null && git reset --hard origin/master'], shell=True)

	def deploy(self, path):
		config = self.getConfig()
		for repository in config['repositories']:
			if(repository['path'] == path):
				cmds = []
				if 'deploy' in repository:
					cmds.append(repository['deploy'])

				gd = config['global_deploy']
				if len(gd[0]) is not 0:
					cmds.insert(0, gd[0])
				if len(gd[1]) is not 0:
					cmds.append(gd[1])

				if(not self.quiet):
					print 'Executing deploy command(s)'
				for cmd in cmds:
					call(['cd "' + path + '" && ' + cmd], shell=True)

				break

class GitAutoDeployMain:

	server = None

	def run(self):
		for arg in sys.argv:
			if(arg == '-d' or arg == '--daemon-mode'):
				GitAutoDeploy.daemon = True
				GitAutoDeploy.quiet = True
			if(arg == '-q' or arg == '--quiet'):
				GitAutoDeploy.quiet = True
			if(arg == '--force'):
				print '[KILLER MODE] Warning: The --force option will try to kill any process ' \
					'using %s port. USE AT YOUR OWN RISK' %GitAutoDeploy.getConfig()['port']
				self.kill_them_all()

		if(GitAutoDeploy.daemon):
			pid = os.fork()
			if(pid != 0):
				sys.exit()
			os.setsid()

		self.create_pidfile()

		if(not GitAutoDeploy.quiet):
			print 'Github & Gitlab Autodeploy Service v 0.1 started'
		else:
			print 'Github & Gitlab Autodeploy Service v 0.1 started in daemon mode'

		try:
			self.server = HTTPServer(('', GitAutoDeploy.getConfig()['port']), GitAutoDeploy)
			self.server.serve_forever()
		except socket.error, e:
			if(not GitAutoDeploy.quiet and not GitAutoDeploy.daemon):
				print "Error on socket: %s" % e
				self.debug_diagnosis()
			sys.exit(1)

	def kill_them_all(self):
		pid = self.get_pid_on_port(GitAutoDeploy.getConfig()['port'])
		if pid == False:
			print '[KILLER MODE] I don\'t know the number of pid that is using my configured port\n ' \
				'[KILLER MODE] Maybe no one? Please, use --force option carefully'
			return False

		os.kill(pid, signal.SIGKILL)
		return True

	def create_pidfile(self):
		with open(GitAutoDeploy.getConfig()['pidfilepath'], 'w') as f:
			f.write(str(os.getpid()))

	def read_pidfile(self):
		with open(GitAutoDeploy.getConfig()['pidfilepath'],'r') as f:
			return f.readlines()

	def remove_pidfile(self):
		os.remove(GitAutoDeploy.getConfig()['pidfilepath'])

	def debug_diagnosis(self):
		if GitAutoDeploy.debug == False:
			return

		port = GitAutoDeploy.getConfig()['port']
		pid = self.get_pid_on_port(port)
		if pid == False:
			print 'I don\'t know the number of pid that is using my configured port'
			return

		print 'Process with pid number %s is using port %s' % (pid, port)
		with open("/proc/%s/cmdline" % pid) as f:
			cmdline = f.readlines()
			print 'cmdline ->', cmdline[0].replace('\x00', ' ')

	def get_pid_on_port(self,port):
		with open("/proc/net/tcp",'r') as f:
			filecontent = f.readlines()[1:]

		pids = [int(x) for x in os.listdir('/proc') if x.isdigit()]
		conf_port = str(GitAutoDeploy.getConfig()['port'])
		mpid = False

		for line in filecontent:
			if mpid != False:
				break

			_, laddr, _, _, _, _, _, _, _, inode = line.split()[:10]
			decport = str(int(laddr.split(':')[1], 16))

			if decport != conf_port:
				continue

			for pid in pids:
				try:
					path = "/proc/%s/fd" % pid
					if os.access(path, os.R_OK) is False:
						continue

					for fd in os.listdir(path):
						cinode = os.readlink("/proc/%s/fd/%s" % (pid, fd))
						minode = cinode.split(":")

						if len(minode) == 2 and minode[1][1:-1] == inode:
							mpid = pid
				except Exception as e:
					pass
		return mpid

	def stop(self):
		if(self.server is not None):
			self.server.socket.close()

	def exit(self):
		if(not GitAutoDeploy.quiet):
			print '\nGoodbye'
		self.remove_pidfile()
		sys.exit(0)

	def signal_handler(self, signum, frame):
		self.stop()
		if(signum == 1):
			self.run()
			return
		elif(signum == 2):
			print '\nKeyboard Interrupt!!!'
		elif(signum == 6):
			print 'Requested close by SIGABRT (process abort signal). Code 6.'

		self.exit()

if __name__ == '__main__':
	gadm = GitAutoDeployMain()

	signal.signal(signal.SIGHUP, gadm.signal_handler)
	signal.signal(signal.SIGINT, gadm.signal_handler)
	signal.signal(signal.SIGABRT, gadm.signal_handler)
	signal.signal(signal.SIGPIPE, signal.SIG_DFL)

	gadm.run()
<|endoftext|>


<commit_before>import collections

from spock.plugins.base import PluginBase
from spock.plugins.tools import physics_tools
from spock.mcmap import mapdata
from spock.utils import pl_announce, BoundingBox
from spock.vector import Vector3

"""
Cross-product directional vector with bounding box to get increment length
increment and test for check_collision
repeat until end-node is reached
"""

class PathCore(object):
    def __init__(self, plug):
        self.plug = plug
        self.find_valid_nodes = plug.find_valid_nodes

    def pathfind(self, pos, target):
        pos = PathNode(pos).floor() + Vector3(0.5, 0, 0.5)
        target = PathNode(target).floor() + Vector3(0.5, 0, 0.5)
        return self.plug.pathfind(pos, target)

    def build_list_from_node(self, node):
        ret = collections.deque()
        ret.append(node)
        while ret[0].parent is not None:
            ret.appendleft(ret[0].parent)
        return ret

class PathNode(Vector3):
    def __init__(self, *xyz):
        super(PathNode, self).__init__(*xyz)
        self.parent = None
        self.child = None
        self.node_dist = 0
        self.is_jump = False
        self.is_fall = False

@pl_announce('Path')
class PathPlugin(PluginBase):
    requires = ('World', 'Physics', 'ClientInfo')

    def __init__(self, ploader, settings):
        super(PathPlugin, self).__init__(ploader, settings)

        self.bounding_box = BoundingBox(w=0.6, h=1.8)
        self.path = PathCore(self)
        self.col = physics_tools.MTVTest(self.world, self.bounding_box)
        ploader.provides('Path', self.path)

    def pathfind(self, start_node, end_node):
        def calc_f_val(node):
            return node.node_dist + end_node.dist(node)
        open_list = []
        closed_list = []
        open_list.append(start_node)
        while open_list:
            current_node = open_list.pop(0)
            if current_node.parent is not None:
                p = current_node.parent.parent
                if p is not None and self.raycast_bbox(p, current_node):
                    current_node.parent = p
                    current_node.node_dist = p.node_dist + current_node.dist(p)
            if current_node == end_node:
                return current_node
            for valid_node in self.find_valid_nodes(current_node)[0]:
                if valid_node not in (open_list + closed_list):
                    open_list.append(valid_node)
            open_list.sort(key=calc_f_val)
            closed_list.append(current_node)
        return None

    def raycast_bbox(self, start, end):
        pos = PathNode(start)
        pos.x -= self.col.bounding_box.w/2
        pos.z -= self.col.bounding_box.d/2
        path = end - start
        if not path:
            return True
        depth = Vector3(*map(lambda a: a[0] * a[1], zip(path.norm(), self.bounding_box)))
        i, r = divmod(path.dist(), depth.dist())
        for j in range(int(i)):
            pos += depth
            block_pos = pos.floor()
            block_id, meta = self.world.get_block(
                block_pos.x, block_pos.y-1, block_pos.z
            )
            block = mapdata.get_block(block_id, meta)
            if not block.bounding_box:
                return False
            t = self.col.block_collision(pos)
            if any(t):
                return False
        return True

    def find_valid_nodes(self, node):
        if not isinstance(node, PathNode):
            node = PathNode(node)
        block_node = PathNode(node.floor())
        walk_nodes = []
        jump_nodes = []
        fall_nodes = []
        test_positions = [block_node + Vector3(x, 0, 0) for x in (-1, 1)]
        test_positions.extend([block_node + Vector3(0, 0, z) for z in (-1, 1)])
        for block_pos in test_positions:
            player_pos = block_pos + Vector3(0.5, 0, 0.5)
            test = Vector3(player_pos)
            test.x -= self.col.bounding_box.w/2
            test.z -= self.col.bounding_box.d/2
            t = self.col.block_collision(test)
            if not any(t):
                fall_pos = player_pos - Vector3(0, 1, 0)
                test -= Vector3(0,1,0)
                t = self.col.block_collision(test - Vector3(0, 1, 0))
                if any(t):
                    player_pos.parent = node
                    player_pos.node_dist = node.node_dist + player_pos.dist(node)
                    walk_nodes.append(player_pos)
                else:
                    fall_pos.parent = node
                    fall_pos.node_dist = node.node_dist + fall_pos.dist(node)
                    fall_nodes.append(fall_pos)
                continue
            jump_pos = player_pos + Vector3(0, 1, 0)
            t = self.col.block_collision(test + Vector3(0, 1, 0))
            if not any(t):
                jump_pos.parent = node
                jump_pos.node_dist = node.node_dist + jump_pos.dist(node)
                jump_nodes.append(jump_pos)
        return walk_nodes, jump_nodes, fall_nodes
<commit_msg>Comment with example for pathfinding<commit_after>import collections

from spock.plugins.base import PluginBase
from spock.plugins.tools import collision
from spock.mcmap import mapdata
from spock.utils import pl_announce, BoundingBox
from spock.vector import Vector3

"""
I wrote a lot of comments explaining whats broken and whats not but
they got lost somewhere in git. So just assume everything is pretty
crappy. Example plugin at:
https://gist.github.com/nickelpro/098e7158019365f67de6
"""

class PathCore(object):
    def __init__(self, plug):
        self.plug = plug
        self.find_valid_nodes = plug.find_valid_nodes

    def pathfind(self, pos, target):
        pos = PathNode(pos).floor() + Vector3(0.5, 0, 0.5)
        target = PathNode(target).floor() + Vector3(0.5, 0, 0.5)
        return self.plug.pathfind(pos, target)

    def build_list_from_node(self, node):
        ret = collections.deque()
        ret.append(node)
        while ret[0].parent is not None:
            ret.appendleft(ret[0].parent)
        return ret

class PathNode(Vector3):
    def __init__(self, *xyz):
        super(PathNode, self).__init__(*xyz)
        self.parent = None
        self.child = None
        self.node_dist = 0
        self.is_jump = False
        self.is_fall = False

@pl_announce('Path')
class PathPlugin(PluginBase):
    requires = ('World', 'Physics', 'ClientInfo')

    def __init__(self, ploader, settings):
        super(PathPlugin, self).__init__(ploader, settings)

        self.bounding_box = BoundingBox(w=0.6, h=1.8)
        self.path = PathCore(self)
        self.col = collision.MTVTest(self.world, self.bounding_box)
        ploader.provides('Path', self.path)

    def pathfind(self, start_node, end_node):
        def calc_f_val(node):
            return node.node_dist + end_node.dist(node)
        open_list = []
        closed_list = []
        open_list.append(start_node)
        while open_list:
            current_node = open_list.pop(0)
            if current_node.parent is not None:
                p = current_node.parent.parent
                if p is not None and self.raycast_bbox(p, current_node):
                    current_node.parent = p
                    current_node.node_dist = p.node_dist + current_node.dist(p)
            if current_node == end_node:
                return current_node
            for valid_node in self.find_valid_nodes(current_node)[0]:
                if valid_node not in (open_list + closed_list):
                    open_list.append(valid_node)
            open_list.sort(key=calc_f_val)
            closed_list.append(current_node)
        return None

    def raycast_bbox(self, start, end):
        pos = PathNode(start)
        pos.x -= self.col.bounding_box.w/2
        pos.z -= self.col.bounding_box.d/2
        path = end - start
        if not path:
            return True
        depth = Vector3(*map(lambda a: a[0] * a[1], zip(path.norm(), self.bounding_box)))
        i, r = divmod(path.dist(), depth.dist())
        for j in range(int(i)):
            pos += depth
            block_pos = pos.floor()
            block_id, meta = self.world.get_block(
                block_pos.x, block_pos.y-1, block_pos.z
            )
            block = mapdata.get_block(block_id, meta)
            if not block.bounding_box:
                return False
            t = self.col.block_collision(pos)
            if any(t):
                return False
        return True

    def find_valid_nodes(self, node):
        if not isinstance(node, PathNode):
            node = PathNode(node)
        block_node = PathNode(node.floor())
        walk_nodes = []
        jump_nodes = []
        fall_nodes = []
        test_positions = [block_node + Vector3(x, 0, 0) for x in (-1, 1)]
        test_positions.extend([block_node + Vector3(0, 0, z) for z in (-1, 1)])
        for block_pos in test_positions:
            player_pos = block_pos + Vector3(0.5, 0, 0.5)
            test = Vector3(player_pos)
            test.x -= self.col.bounding_box.w/2
            test.z -= self.col.bounding_box.d/2
            t = self.col.block_collision(test)
            if not any(t):
                fall_pos = player_pos - Vector3(0, 1, 0)
                test -= Vector3(0,1,0)
                t = self.col.block_collision(test - Vector3(0, 1, 0))
                if any(t):
                    player_pos.parent = node
                    player_pos.node_dist = node.node_dist + player_pos.dist(node)
                    walk_nodes.append(player_pos)
                else:
                    fall_pos.parent = node
                    fall_pos.node_dist = node.node_dist + fall_pos.dist(node)
                    fall_nodes.append(fall_pos)
                continue
            jump_pos = player_pos + Vector3(0, 1, 0)
            t = self.col.block_collision(test + Vector3(0, 1, 0))
            if not any(t):
                jump_pos.parent = node
                jump_pos.node_dist = node.node_dist + jump_pos.dist(node)
                jump_nodes.append(jump_pos)
        return walk_nodes, jump_nodes, fall_nodes
<|endoftext|>


<commit_before>""" Copyright 2012, 2013 UW Information Technology, University of Washington

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

"""

from django import forms
from spotseeker_server.models import Item, ItemExtendedInfo
import re

class DefaultItemExtendedInfoForm(forms.ModelForm):

    class Meta:
        model = ItemExtendedInfo
        fields = ('key', 'value')

    def clean_key(self):
        key = self.cleaned_data['key'].strip()
        if not re.match(r'^[a-z0-9_-]+$', key, re.I):
            raise forms.ValidationError(
                "Key must be only alphanumerics, underscores, and hyphens")
        return key

class DefaultItemForm(forms.ModelForm):

    class Meta:
        model = Item
        fields = ('name', 'item_category', 'item_subcategory')
<commit_msg>Don't limit fields<commit_after>""" Copyright 2012, 2013 UW Information Technology, University of Washington

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

"""

from django import forms
from spotseeker_server.models import Item, ItemExtendedInfo
import re

class DefaultItemExtendedInfoForm(forms.ModelForm):

    class Meta:
        model = ItemExtendedInfo

    def clean_key(self):
        key = self.cleaned_data['key'].strip()
        if not re.match(r'^[a-z0-9_-]+$', key, re.I):
            raise forms.ValidationError(
                "Key must be only alphanumerics, underscores, and hyphens")
        return key

class DefaultItemForm(forms.ModelForm):

    class Meta:
        model = Item
        fields = ('name', 'item_category', 'item_subcategory')
<|endoftext|>


<commit_before># Copyright 2021 UW-IT, University of Washington
# SPDX-License-Identifier: Apache-2.0

""" Copyright 2012, 2013 UW Information Technology, University of Washington

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

from django.conf import settings
from django.test import TestCase
from django.test.client import Client
from spotseeker_server.models import Spot, SpotAvailableHours

class SpotHoursOverlapTest(TestCase):
    """ Tests that when open hours are submitted that overlap with other open
        hours for a Spot, the previous Spot hours are adjusted rather than
        having multiple AvailableHours hanging around.
    """
    def setUp(self):
        spot = Spot.objects.create(name="This spot has overlapping hours")
        self.spot = spot

    def test_early_overlap(self):
        """ Tests adding SpotAvailableHours with a start time earlier than
            an existing start, but end within the current open hours.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="12:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="07:00",
                                                   end_time="10:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_late_overlap(self):
        """ Tests adding SpotAvailableHours with a start time within current
            hours, but an end time later than the current open hours.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="12:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="10:00",
                                                   end_time="14:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_total_overlap(self):
        """ Tests adding SpotAvailableHours with an earlier start time and a
            later end time.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="12:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="07:00",
                                                   end_time="14:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_underlap(self):
        """ Tests adding SpotAvailableHours with a start and end insice of
            currently open hours.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="08:00",
                                                   end_time="14:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="12:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_no_overlap(self):
        """ Tests adding another available hours object that should not get
            merged into an existing one.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="08:00",
                                                   end_time="12:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="14:00",
                                                   end_time="18:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 2, "Two SpotAvailableHours objects")

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        new_hours2 = self.spot.spotavailablehours_set.all()[1]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)
        self.assertEquals(hours2.start_time, new_hours2.start_time)
        self.assertEquals(hours2.end_time, new_hours2.end_time)

    def test_exact_same_start(self):
        """ Tests adding another available hours object that has the exact
            same start time as an existing one, but a later end.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="12:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="14:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_exact_same_end(self):
        """ Tests adding another available hours object that has the exact
            same end time as an existing one, but an earlier start.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="14:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="08:00",
                                                   end_time="14:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_exact_same_hours(self):
        """ Tests adding the escact same available hours for a spot.
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="14:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="14:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_abutting_hours(self):
        """ Tests windows that are next to each other
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="14:00")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="14:00",
                                                   end_time="19:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_almost_abutting_hours(self):
        """ Tests windows that are next to each other
        """
        hours1 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="09:00",
                                                   end_time="13:59:59")
        hours2 = SpotAvailableHours.objects.create(spot=self.spot,
                                                   day="m",
                                                   start_time="14:00",
                                                   end_time="19:00")

        # creating hours2 should get those times merged into hours1
        hours_obj_count = \
            self.spot.spotavailablehours_set.values_list().count()
        self.assertEquals(hours_obj_count, 2, "two SpotAvailableHours objects")

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[1]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)
<commit_msg>Reformat and update copyright.<commit_after># Copyright 2021 UW-IT, University of Washington
# SPDX-License-Identifier: Apache-2.0

from django.conf import settings
from django.test import TestCase
from django.test.client import Client
from spotseeker_server.models import Spot, SpotAvailableHours

class SpotHoursOverlapTest(TestCase):
    """Tests that when open hours are submitted that overlap with other open
    hours for a Spot, the previous Spot hours are adjusted rather than
    having multiple AvailableHours hanging around.
    """

    def setUp(self):
        spot = Spot.objects.create(name="This spot has overlapping hours")
        self.spot = spot

    def test_early_overlap(self):
        """Tests adding SpotAvailableHours with a start time earlier than
        an existing start, but end within the current open hours.
        """
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="12:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="07:00", end_time="10:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_late_overlap(self):
        """Tests adding SpotAvailableHours with a start time within current
        hours, but an end time later than the current open hours.
        """
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="12:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="10:00", end_time="14:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_total_overlap(self):
        """Tests adding SpotAvailableHours with an earlier start time and a
        later end time.
        """
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="12:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="07:00", end_time="14:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_underlap(self):
        """Tests adding SpotAvailableHours with a start and end insice of
        currently open hours.
        """
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="08:00", end_time="14:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="12:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_no_overlap(self):
        """Tests adding another available hours object that should not get
        merged into an existing one.
        """
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="08:00", end_time="12:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="14:00", end_time="18:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 2, "Two SpotAvailableHours objects")

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        new_hours2 = self.spot.spotavailablehours_set.all()[1]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)
        self.assertEquals(hours2.start_time, new_hours2.start_time)
        self.assertEquals(hours2.end_time, new_hours2.end_time)

    def test_exact_same_start(self):
        """Tests adding another available hours object that has the exact
        same start time as an existing one, but a later end.
        """
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="12:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="14:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_exact_same_end(self):
        """Tests adding another available hours object that has the exact
        same end time as an existing one, but an earlier start.
        """
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="14:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="08:00", end_time="14:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_exact_same_hours(self):
        """Tests adding the escact same available hours for a spot."""
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="14:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="14:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

    def test_abutting_hours(self):
        """Tests windows that are next to each other"""
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="14:00"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="14:00", end_time="19:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 1)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)

    def test_almost_abutting_hours(self):
        """Tests windows that are next to each other"""
        hours1 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="09:00", end_time="13:59:59"
        )
        hours2 = SpotAvailableHours.objects.create(
            spot=self.spot, day="m", start_time="14:00", end_time="19:00"
        )

        # creating hours2 should get those times merged into hours1
        hours_obj_count = (
            self.spot.spotavailablehours_set.values_list().count()
        )
        self.assertEquals(hours_obj_count, 2, "two SpotAvailableHours objects")

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[0]
        self.assertEquals(hours1.start_time, new_hours.start_time)
        self.assertEquals(hours1.end_time, new_hours.end_time)

        # check to see that start and end times are correct
        new_hours = self.spot.spotavailablehours_set.all()[1]
        self.assertEquals(hours2.start_time, new_hours.start_time)
        self.assertEquals(hours2.end_time, new_hours.end_time)
<|endoftext|>


<commit_before>import RPi.GPIO as GPIO
import spi as SPI
import signal
import time

class RFID:
    pin_rst = 22

    mode_idle = 0x00
    mode_auth = 0x0E
    mode_receive = 0x08
    mode_transmit = 0x04
    mode_transrec = 0x0C
    mode_reset = 0x0F
    mode_crc = 0x03

    auth_a = 0x60
    auth_b = 0x61

    act_read = 0x30
    act_write = 0xA0
    act_increment = 0xC1
    act_decrement = 0xC0
    act_restore = 0xC2
    act_transfer = 0xB0

    act_reqidl = 0x26
    act_reqall = 0x52
    act_anticl = 0x93
    act_select = 0x93
    act_end = 0x50

    reg_tx_control = 0x14
    length = 16

    authed = False

    def __init__(self, dev='/dev/spidev0.0', speed=1000000, pin_rst=22, pin_ce=0):
        self.pin_rst = pin_rst

        SPI.openSPI(device=dev, speed=speed)
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(pin_rst, GPIO.OUT)
        GPIO.output(pin_rst, 1)
        if pin_ce != 0:
	        GPIO.setup(pin_ce, GPIO.OUT)
	        GPIO.output(pin_ce, 1)
        self.reset()
        self.dev_write(0x2A, 0x8D)
        self.dev_write(0x2B, 0x3E)
        self.dev_write(0x2D, 30)
        self.dev_write(0x2C, 0)
        self.dev_write(0x15, 0x40)
        self.dev_write(0x11, 0x3D)
        self.set_antenna(True)

    def spi_transfer(self, data):
        if pin_ce != 0:
	        GPIO.output(pin_ce, 0)
        SPI.transfer(data)
        if pin_ce != 0:
	        GPIO.output(pin_ce, 1)

    def dev_write(self, address, value):
        spi_transfer(((address << 1) & 0x7E, value))

    def dev_read(self, address):
        return spi_transfer((((address << 1) & 0x7E) | 0x80, 0))[1]

    def set_bitmask(self, address, mask):
        current = self.dev_read(address)
        self.dev_write(address, current | mask)

    def clear_bitmask(self, address, mask):
        current = self.dev_read(address)
        self.dev_write(address, current & (~mask))

    def set_antenna(self, state):
        if state == True:
            current = self.dev_read(self.reg_tx_control)
            if ~(current & 0x03):
                self.set_bitmask(self.reg_tx_control, 0x03)
        else:
            self.clear_bitmask(self.reg_tx_control, 0x03)

    def card_write(self, command, data):
        back_data = []
        back_length = 0
        error = False
        irq = 0x00
        irq_wait = 0x00
        last_bits = None
        n = 0

        if command == self.mode_auth:
            irq = 0x12
            irq_wait = 0x10
        if command == self.mode_transrec:
            irq = 0x77
            irq_wait = 0x30

        self.dev_write(0x02, irq | 0x80)
        self.clear_bitmask(0x04, 0x80)
        self.set_bitmask(0x0A, 0x80)
        self.dev_write(0x01, self.mode_idle)

        for i in range(len(data)):
            self.dev_write(0x09, data[i])

        self.dev_write(0x01, command)

        if command == self.mode_transrec:
            self.set_bitmask(0x0D, 0x80)

        i = 2000
        while True:
            n = self.dev_read(0x04)
            i -= 1
            if ~((i != 0) and ~(n & 0x01) and ~(n & irq_wait)):
                break

        self.clear_bitmask(0x0D, 0x80)

        if i != 0:
            if (self.dev_read(0x06) & 0x1B) == 0x00:
                error = False

                if n & irq & 0x01:
                    print "E1"
                    error = True

                if command == self.mode_transrec:
                    n = self.dev_read(0x0A)
                    last_bits = self.dev_read(0x0C) & 0x07
                    if last_bits != 0:
                        back_length = (n - 1) * 8 + last_bits
                    else:
                        back_length = n * 8

                    if n == 0:
                        n = 1

                    if n > self.length:
                        n = self.length

                    for i in range(n):
                        back_data.append(self.dev_read(0x09))
            else:
                print "E2"
                error = True

        return (error, back_data, back_length)

    def request(self, req_mode=0x26):
        """
        Requests for tag.
        Returns (False, None) if no tag is present, otherwise returns (True, tag type)
        """
        error = True
        back_bits = 0

        self.dev_write(0x0D, 0x07)
        (error, back_data, back_bits) = self.card_write(self.mode_transrec, [req_mode, ])

        if error or (back_bits != 0x10):
            return (True, None)

        return (False, back_bits)

    def anticoll(self):
        """
        Anti-collision detection.
        Returns tuple of (error state, tag ID).
        """
        back_data = []
        serial_number = []

        serial_number_check = 0

        self.dev_write(0x0D, 0x00)
        serial_number.append(self.act_anticl)
        serial_number.append(0x20)

        (error, back_data, back_bits) = self.card_write(self.mode_transrec, serial_number)
        if not error:
            if len(back_data) == 5:
                for i in range(4):
                    serial_number_check = serial_number_check ^ back_data[i]

                if serial_number_check != back_data[4]:
                    error = True
            else:
                error = True

        return (error, back_data)

    def calculate_crc(self, data):
        self.clear_bitmask(0x05, 0x04)
        self.set_bitmask(0x0A, 0x80)

        for i in range(len(data)):
            self.dev_write(0x09, data[i])
        self.dev_write(0x01, self.mode_crc)

        i = 255
        while True:
            n = self.dev_read(0x05)
            i -= 1
            if not ((i != 0) and not (n & 0x04)):
                break

        ret_data = []
        ret_data.append(self.dev_read(0x22))
        ret_data.append(self.dev_read(0x21))

        return ret_data

    def select_tag(self, uid):
        """
        Selects tag for further usage.
        uid -- list or tuple with four bytes tag ID
        Returns error state.
        """
        back_data = []
        buf = []

        buf.append(self.act_select)
        buf.append(0x70)

        for i in range(5):
            buf.append(uid[i])

        crc = self.calculate_crc(buf)
        buf.append(crc[0])
        buf.append(crc[1])

        (error, back_data, back_length) = self.card_write(self.mode_transrec, buf)

        if (not error) and (back_length == 0x18):
            return False
        else:
            return True

    def card_auth(self, auth_mode, block_address, key, uid):
        """
        Authenticates to use specified block address. Tag must be selected using select_tag(uid) before auth.
        auth_mode -- RFID.auth_a or RFID.auth_b
        key -- list or tuple with six bytes key
        uid -- list or tuple with four bytes tag ID
        Returns error state.
        """
        buf = []
        buf.append(auth_mode)
        buf.append(block_address)

        for i in range(len(key)):
            buf.append(key[i])

        for i in range(4):
            buf.append(uid[i])

        (error, back_data, back_length) = self.card_write(self.mode_auth, buf)
        if not (self.dev_read(0x08) & 0x08) != 0:
            error = True

        if not error:
            self.authed = True

        return error

    def stop_crypto(self):
        """Ends operations with Crypto1 usage."""
        self.clear_bitmask(0x08, 0x08)
        self.authed = False

    def read(self, block_address):
        """
        Reads data from block. You should be authenticated before calling read.
        Returns tuple of (error state, read data).
        """
        buf = []
        buf.append(self.act_read)
        buf.append(block_address)
        crc = self.calculate_crc(buf)
        buf.append(crc[0])
        buf.append(crc[1])
        (error, back_data, back_length) = self.card_write(self.mode_transrec, buf)

        if len(back_data) != 16:
            error = True

        return (error, back_data)

    def write(self, block_address, data):
        """
        Writes data to block. You should be authenticated before calling write.
        Returns error state.
        """
        buf = []
        buf.append(self.act_write)
        buf.append(block_address)
        crc = self.calculate_crc(buf)
        buf.append(crc[0])
        buf.append(crc[1])
        (error, back_data, back_length) = self.card_write(self.mode_transrec, buf)
        if not(back_length == 4) or not((back_data[0] & 0x0F) == 0x0A):
            error = True

        if not error:
            buf_w = []
            for i in range(16):
                buf_w.append(data[i])

            crc = self.calculate_crc(buf_w)
            buf_w.append(crc[0])
            buf_w.append(crc[1])
            (error, back_data, back_length) = self.card_write(self.mode_transrec, buf_w)
            if not(back_length == 4) or not((back_data[0] & 0x0F) == 0x0A):
                error = True

        return error

    def reset(self):
        self.dev_write(0x01, self.mode_reset)

    def cleanup(self):
        """
        Calls stop_crypto() if needed and cleanups GPIO.
        """
        if self.authed:
            self.stop_crypto()
        GPIO.cleanup()

    def util(self):
        """
        Creates and returns RFIDUtil object for this RFID instance.
        If module is not present, returns None.
        """
        try:
            import RFIDUtil
            return RFIDUtil.RFIDUtil(self)
        except ImportError:
            return None<commit_msg>Updated print() to works in python3<commit_after>import RPi.GPIO as GPIO
import spi as SPI
import signal
import time

class RFID:
    pin_rst = 22

    mode_idle = 0x00
    mode_auth = 0x0E
    mode_receive = 0x08
    mode_transmit = 0x04
    mode_transrec = 0x0C
    mode_reset = 0x0F
    mode_crc = 0x03

    auth_a = 0x60
    auth_b = 0x61

    act_read = 0x30
    act_write = 0xA0
    act_increment = 0xC1
    act_decrement = 0xC0
    act_restore = 0xC2
    act_transfer = 0xB0

    act_reqidl = 0x26
    act_reqall = 0x52
    act_anticl = 0x93
    act_select = 0x93
    act_end = 0x50

    reg_tx_control = 0x14
    length = 16

    authed = False

    def __init__(self, dev='/dev/spidev0.0', speed=1000000, pin_rst=22, pin_ce=0):
        self.pin_rst = pin_rst

        SPI.openSPI(device=dev, speed=speed)
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(pin_rst, GPIO.OUT)
        GPIO.output(pin_rst, 1)
        if pin_ce != 0:
	        GPIO.setup(pin_ce, GPIO.OUT)
	        GPIO.output(pin_ce, 1)
        self.reset()
        self.dev_write(0x2A, 0x8D)
        self.dev_write(0x2B, 0x3E)
        self.dev_write(0x2D, 30)
        self.dev_write(0x2C, 0)
        self.dev_write(0x15, 0x40)
        self.dev_write(0x11, 0x3D)
        self.set_antenna(True)

    def spi_transfer(self, data):
        if pin_ce != 0:
	        GPIO.output(pin_ce, 0)
        SPI.transfer(data)
        if pin_ce != 0:
	        GPIO.output(pin_ce, 1)

    def dev_write(self, address, value):
        spi_transfer(((address << 1) & 0x7E, value))

    def dev_read(self, address):
        return spi_transfer((((address << 1) & 0x7E) | 0x80, 0))[1]

    def set_bitmask(self, address, mask):
        current = self.dev_read(address)
        self.dev_write(address, current | mask)

    def clear_bitmask(self, address, mask):
        current = self.dev_read(address)
        self.dev_write(address, current & (~mask))

    def set_antenna(self, state):
        if state == True:
            current = self.dev_read(self.reg_tx_control)
            if ~(current & 0x03):
                self.set_bitmask(self.reg_tx_control, 0x03)
        else:
            self.clear_bitmask(self.reg_tx_control, 0x03)

    def card_write(self, command, data):
        back_data = []
        back_length = 0
        error = False
        irq = 0x00
        irq_wait = 0x00
        last_bits = None
        n = 0

        if command == self.mode_auth:
            irq = 0x12
            irq_wait = 0x10
        if command == self.mode_transrec:
            irq = 0x77
            irq_wait = 0x30

        self.dev_write(0x02, irq | 0x80)
        self.clear_bitmask(0x04, 0x80)
        self.set_bitmask(0x0A, 0x80)
        self.dev_write(0x01, self.mode_idle)

        for i in range(len(data)):
            self.dev_write(0x09, data[i])

        self.dev_write(0x01, command)

        if command == self.mode_transrec:
            self.set_bitmask(0x0D, 0x80)

        i = 2000
        while True:
            n = self.dev_read(0x04)
            i -= 1
            if ~((i != 0) and ~(n & 0x01) and ~(n & irq_wait)):
                break

        self.clear_bitmask(0x0D, 0x80)

        if i != 0:
            if (self.dev_read(0x06) & 0x1B) == 0x00:
                error = False

                if n & irq & 0x01:
                    print("E1")
                    error = True

                if command == self.mode_transrec:
                    n = self.dev_read(0x0A)
                    last_bits = self.dev_read(0x0C) & 0x07
                    if last_bits != 0:
                        back_length = (n - 1) * 8 + last_bits
                    else:
                        back_length = n * 8

                    if n == 0:
                        n = 1

                    if n > self.length:
                        n = self.length

                    for i in range(n):
                        back_data.append(self.dev_read(0x09))
            else:
                print("E2")
                error = True

        return (error, back_data, back_length)

    def request(self, req_mode=0x26):
        """
        Requests for tag.
        Returns (False, None) if no tag is present, otherwise returns (True, tag type)
        """
        error = True
        back_bits = 0

        self.dev_write(0x0D, 0x07)
        (error, back_data, back_bits) = self.card_write(self.mode_transrec, [req_mode, ])

        if error or (back_bits != 0x10):
            return (True, None)

        return (False, back_bits)

    def anticoll(self):
        """
        Anti-collision detection.
        Returns tuple of (error state, tag ID).
        """
        back_data = []
        serial_number = []

        serial_number_check = 0

        self.dev_write(0x0D, 0x00)
        serial_number.append(self.act_anticl)
        serial_number.append(0x20)

        (error, back_data, back_bits) = self.card_write(self.mode_transrec, serial_number)
        if not error:
            if len(back_data) == 5:
                for i in range(4):
                    serial_number_check = serial_number_check ^ back_data[i]

                if serial_number_check != back_data[4]:
                    error = True
            else:
                error = True

        return (error, back_data)

    def calculate_crc(self, data):
        self.clear_bitmask(0x05, 0x04)
        self.set_bitmask(0x0A, 0x80)

        for i in range(len(data)):
            self.dev_write(0x09, data[i])
        self.dev_write(0x01, self.mode_crc)

        i = 255
        while True:
            n = self.dev_read(0x05)
            i -= 1
            if not ((i != 0) and not (n & 0x04)):
                break

        ret_data = []
        ret_data.append(self.dev_read(0x22))
        ret_data.append(self.dev_read(0x21))

        return ret_data

    def select_tag(self, uid):
        """
        Selects tag for further usage.
        uid -- list or tuple with four bytes tag ID
        Returns error state.
        """
        back_data = []
        buf = []

        buf.append(self.act_select)
        buf.append(0x70)

        for i in range(5):
            buf.append(uid[i])

        crc = self.calculate_crc(buf)
        buf.append(crc[0])
        buf.append(crc[1])

        (error, back_data, back_length) = self.card_write(self.mode_transrec, buf)

        if (not error) and (back_length == 0x18):
            return False
        else:
            return True

    def card_auth(self, auth_mode, block_address, key, uid):
        """
        Authenticates to use specified block address. Tag must be selected using select_tag(uid) before auth.
        auth_mode -- RFID.auth_a or RFID.auth_b
        key -- list or tuple with six bytes key
        uid -- list or tuple with four bytes tag ID
        Returns error state.
        """
        buf = []
        buf.append(auth_mode)
        buf.append(block_address)

        for i in range(len(key)):
            buf.append(key[i])

        for i in range(4):
            buf.append(uid[i])

        (error, back_data, back_length) = self.card_write(self.mode_auth, buf)
        if not (self.dev_read(0x08) & 0x08) != 0:
            error = True

        if not error:
            self.authed = True

        return error

    def stop_crypto(self):
        """Ends operations with Crypto1 usage."""
        self.clear_bitmask(0x08, 0x08)
        self.authed = False

    def read(self, block_address):
        """
        Reads data from block. You should be authenticated before calling read.
        Returns tuple of (error state, read data).
        """
        buf = []
        buf.append(self.act_read)
        buf.append(block_address)
        crc = self.calculate_crc(buf)
        buf.append(crc[0])
        buf.append(crc[1])
        (error, back_data, back_length) = self.card_write(self.mode_transrec, buf)

        if len(back_data) != 16:
            error = True

        return (error, back_data)

    def write(self, block_address, data):
        """
        Writes data to block. You should be authenticated before calling write.
        Returns error state.
        """
        buf = []
        buf.append(self.act_write)
        buf.append(block_address)
        crc = self.calculate_crc(buf)
        buf.append(crc[0])
        buf.append(crc[1])
        (error, back_data, back_length) = self.card_write(self.mode_transrec, buf)
        if not(back_length == 4) or not((back_data[0] & 0x0F) == 0x0A):
            error = True

        if not error:
            buf_w = []
            for i in range(16):
                buf_w.append(data[i])

            crc = self.calculate_crc(buf_w)
            buf_w.append(crc[0])
            buf_w.append(crc[1])
            (error, back_data, back_length) = self.card_write(self.mode_transrec, buf_w)
            if not(back_length == 4) or not((back_data[0] & 0x0F) == 0x0A):
                error = True

        return error

    def reset(self):
        self.dev_write(0x01, self.mode_reset)

    def cleanup(self):
        """
        Calls stop_crypto() if needed and cleanups GPIO.
        """
        if self.authed:
            self.stop_crypto()
        GPIO.cleanup()

    def util(self):
        """
        Creates and returns RFIDUtil object for this RFID instance.
        If module is not present, returns None.
        """
        try:
            import RFIDUtil
            return RFIDUtil.RFIDUtil(self)
        except ImportError:
            return None<|endoftext|>