<commit_before>"""A board is a list of list of str. For example, the board
    ANTT
    XSOB
is represented as the list
    [['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']]

A word list is a list of str. For example, the list of words
    ANT
    BOX
    SOB
    TO
is represented as the list
    ['ANT', 'BOX', 'SOB', 'TO']
"""

def is_valid_word(wordlist, word):
    """ (list of str, str) -> bool

    Return True if and only if word is an element of wordlist.

    >>> is_valid_word(['ANT', 'BOX', 'SOB', 'TO'], 'TO')
    True
    """

    for lst_word in wordlist:
        if word == lst_word:
            return True

    return False

def make_str_from_row(board, row_index):
    """ (list of list of str, int) -> str

    Return the characters from the row of the board with index row_index
    as a single string.

    >>> make_str_from_row([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 0)
    'ANTT'
    """

    row = board[row_index]

    return ''.join(row)

def make_str_from_column(board, column_index):
    """ (list of list of str, int) -> str

    Return the characters from the column of the board with index column_index
    as a single string.

    >>> make_str_from_column([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 1)
    'NS'
    """

    column = ''

    for i in range(len(board)):
        column = column + board[i][column_index]

    return column

def board_contains_word_in_row(board, word):
    """ (list of list of str, str) -> bool

    Return True if and only if one or more of the rows of the board contains
    word.

    Precondition: board has at least one row and one column, and word is a
    valid word.

    >>> board_contains_word_in_row([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 'SOB')
    True
    """

    for row_index in range(len(board)):
        if word in make_str_from_row(board, row_index):
            return True

    return False

def board_contains_word_in_column(board, word):
    """ (list of list of str, str) -> bool

    Return True if and only if one or more of the columns of the board
    contains word.

    Precondition: board has at least one row and one column, and word is a
    valid word.

    >>> board_contains_word_in_column([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 'NO')
    False
    """

    for i in range(len(board[0])):
        if make_str_from_column(board,i) == word:
            return True

    return False

def board_contains_word(board, word):
    """ (list of list of str, str) -> bool

    Return True if and only if word appears in board.

    Precondition: board has at least one row and one column.

    >>> board_contains_word([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 'ANT')
    True
    """

    return board_contains_word_in_row(board,word) or board_contains_word_in_column(board,word)

def word_score(word):
    """ (str) -> int

    Return the point value the word earns.

    Word length: < 3: 0 points
                 3-6: 1 point per character for all characters in word
                 7-9: 2 points per character for all characters in word
                 10+: 3 points per character for all characters in word

    >>> word_score('DRUDGERY')
    16
    """

    score = 0

    if len(word) < 3:
        score = 0
    elif len(word) >=3 and len(word) <= 6:
        score = len(word)
    elif len(word) >=7 and len(word) <= 9:
        score = 2 * len(word)
    else:
        score = 3 * len(word)

    return score

def update_score(player_info, word):
    """ ([str, int] list, str) -> NoneType

    player_info is a list with the player's name and score. Update player_info
    by adding the point value word earns to the player's score.

    >>> update_score(['Jonathan', 4], 'ANT')
    """

    player_info[1] = player_info[1] + word_score(word)

    return None

def num_words_on_board(board, words):
    """ (list of list of str, list of str) -> int

    Return how many words appear on board.

    >>> num_words_on_board([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], ['ANT', 'BOX', 'SOB', 'TO'])
    3
    """

    num_words = 0

    for word in words:
        if board_contains_word(board, word):
            num_words = num_words + 1

    return num_words

def read_words(words_file):
    """ (file open for reading) -> list of str

    Return a list of all words (with newlines removed) from open file
    words_file.

    Precondition: Each line of the file contains a word in uppercase characters
    from the standard English alphabet.

    >>read_words(words.txt)
    """

def read_board(board_file):
    """ (file open for reading) -> list of list of str

    Return a board read from open file board_file. The board file will contain
    one row of the board per line. Newlines are not included in the board.
    """

<commit_msg>Update a3.py<commit_after>"""A board is a list of list of str. For example, the board
    ANTT
    XSOB
is represented as the list
    [['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']]

A word list is a list of str. For example, the list of words
    ANT
    BOX
    SOB
    TO
is represented as the list
    ['ANT', 'BOX', 'SOB', 'TO']
"""

def is_valid_word(wordlist, word):
    """ (list of str, str) -> bool

    Return True if and only if word is an element of wordlist.

    >>> is_valid_word(['ANT', 'BOX', 'SOB', 'TO'], 'TO')
    True
    """

    for lst_word in wordlist:
        if word == lst_word:
            return True

    return False

def make_str_from_row(board, row_index):
    """ (list of list of str, int) -> str

    Return the characters from the row of the board with index row_index
    as a single string.

    >>> make_str_from_row([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 0)
    'ANTT'
    """

    row = board[row_index]

    return ''.join(row)

def make_str_from_column(board, column_index):
    """ (list of list of str, int) -> str

    Return the characters from the column of the board with index column_index
    as a single string.

    >>> make_str_from_column([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 1)
    'NS'
    """

    column = ''

    for i in range(len(board)):
        column = column + board[i][column_index]

    return column

def board_contains_word_in_row(board, word):
    """ (list of list of str, str) -> bool

    Return True if and only if one or more of the rows of the board contains
    word.

    Precondition: board has at least one row and one column, and word is a
    valid word.

    >>> board_contains_word_in_row([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 'SOB')
    True
    """

    contains = False

    for row_index in range(len(board)):
        if word in make_str_from_row(board, row_index):
            contains = True

    return contains

def board_contains_word_in_column(board, word):
    """ (list of list of str, str) -> bool

    Return True if and only if one or more of the columns of the board
    contains word.

    Precondition: board has at least one row and one column, and word is a
    valid word.

    >>> board_contains_word_in_column([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 'NO')
    False
    """

    contains = False

    for i in range(len(board[0])):
        if make_str_from_column(board,i) == word:
            contains = True

    return contains

def board_contains_word(board, word):
    """ (list of list of str, str) -> bool

    Return True if and only if word appears in board.

    Precondition: board has at least one row and one column.

    >>> board_contains_word([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], 'ANT')
    True
    """

    return board_contains_word_in_row(board,word) or board_contains_word_in_column(board,word)

def word_score(word):
    """ (str) -> int

    Return the point value the word earns.

    Word length: < 3: 0 points
                 3-6: 1 point per character for all characters in word
                 7-9: 2 points per character for all characters in word
                 10+: 3 points per character for all characters in word

    >>> word_score('DRUDGERY')
    16
    """

    score = 0

    if len(word) < 3:
        score = 0
    elif len(word) >=3 and len(word) <= 6:
        score = len(word)
    elif len(word) >=7 and len(word) <= 9:
        score = 2 * len(word)
    else:
        score = 3 * len(word)

    return score

def update_score(player_info, word):
    """ ([str, int] list, str) -> NoneType

    player_info is a list with the player's name and score. Update player_info
    by adding the point value word earns to the player's score.

    >>> update_score(['Jonathan', 4], 'ANT')
    """

    player_info[1] = player_info[1] + word_score(word)

    return None

def num_words_on_board(board, words):
    """ (list of list of str, list of str) -> int

    Return how many words appear on board.

    >>> num_words_on_board([['A', 'N', 'T', 'T'], ['X', 'S', 'O', 'B']], ['ANT', 'BOX', 'SOB', 'TO'])
    3
    """

    num_words = 0

    for word in words:
        if board_contains_word(board, word):
            num_words = num_words + 1

    return num_words

def read_words(words_file):
    """ (file open for reading) -> list of str

    Return a list of all words (with newlines removed) from open file
    words_file.

    Precondition: Each line of the file contains a word in uppercase characters
    from the standard English alphabet.

    >>read_words(words.txt)
    """

    L = words_file.readlines()

    for i in range(len(L)):
        L[i] = L[i].rstrip('\n')

    return L

def read_board(board_file):
    """ (file open for reading) -> list of list of str

    Return a board read from open file board_file. The board file will contain
    one row of the board per line. Newlines are not included in the board.
    """

    board = []
    row_element = board_file.read(1)

    while row_element != '':
        board_row = []
        while row_element != '\n':
            board_row.append(row_element)
            row_element = board_file.read(1)
        board.append(board_row)
        row_element = board_file.read(1)

    return board

<|endoftext|>


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

import math
import random
import logging

from pathfinding.finder.a_star import AStarFinder
from pathfinding.core.diagonal_movement import DiagonalMovement

from locals import *

class Random (object):
    '''
    The Random AI would just do some thing at random at predefined
    chance of happening. It may fire, move forward or turn at any
    direction.
    '''

    fire_chance = 1
    moving_chance = 70
    turning_chance = 5
    turns_list = [
        DIRECTION_UP,
        DIRECTION_DOWN,
        DIRECTION_LEFT,
        DIRECTION_RIGHT,
    ]

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

    def do_random_thing(self, enemy):
        firing_roll = random.randint(0, 100)
        if firing_roll <= self.fire_chance:
            enemy.fire()

        turning_roll = random.randint(0, 100)
        if turning_roll <= self.turning_chance:
            turn_index = random.randint(0, 3)
            turn_direction = self.turns_list[turn_index]
            enemy.move(turn_direction)
            return

        moving_roll = random.randint(0, 100)
        if moving_roll <= self.moving_chance:
            enemy.move(enemy.facing)
        else:
            enemy.stop()

    def tick(self, deltat, enemies):
        for enemy in enemies:
            self.do_random_thing(enemy)

class ZombieDriver (Random):
    '''
    The ZombieDriver AI would follow the player relentlessly around without any
    thought or self preservation mechanism!
    '''

    def __init__(self, world):
        self.world = world
        self.map = world.map
        self.pathfinder = AStarFinder(
            diagonal_movement=DiagonalMovement.never,
            time_limit=0.005
        )
        self.path_cache_duration = 1000

    def tick(self, deltat, enemies):
        interesting_objects = []

        for player in self.world.players:
            if player.tank is None:
                continue
            interesting_objects.append(player.tank.rect)

        for enemy in enemies:
            self.process_enemy(enemy, deltat, interesting_objects)

    def process_enemy(self, enemy, deltat, interesting_objects):
        enemy.update_path_duration(deltat)
        own_coords = enemy.rect
        own_grid_pos = self.map.world_to_grid_coords(own_coords.centerx, own_coords.centery)

        if enemy.path_duration <= self.path_cache_duration:
            self.continue_path(enemy, own_grid_pos)
            return

        heat_obj = None
        heat_dist = math.inf
        for obj in interesting_objects:
            dist = math.sqrt(
                abs(own_coords.centerx - obj.centerx) ** 2 +
                abs(own_coords.centery - obj.centery) ** 2
            )
            if dist < heat_dist:
                heat_dist = dist
                heat_obj = obj

        if heat_obj is None:
            logging.error("ZombieDriver AI finds nothing interesting on the map! How can that be!?")
            self.do_random_thing(enemy)
            return

        hobjx, hobjy = heat_obj.centerx, heat_obj.centery
        hobj_grid_pos = self.map.world_to_grid_coords(hobjx, hobjy)

        if hobj_grid_pos == enemy.current_target:
            enemy.reset_path_duration()
            self.continue_path(enemy, own_grid_pos)
            return

        # WTF!? You have to rebiuld the *whole* grid every time find_path is called!
        # Definately change to a better library or write A* myself.
        self.map.build_grid()

        pf_start = self.map.grid.node(*own_grid_pos)
        pf_end = self.map.grid.node(*hobj_grid_pos)

        path, runs = self.pathfinder.find_path(
            pf_start,
            pf_end,
            self.map.grid
        )

        enemy.set_current_path(path)
        self.continue_path(enemy, own_grid_pos)

    def continue_path(self, enemy, grid_pos):
        next_node = enemy.get_path_next(grid_pos)

        if next_node is None:
            self.do_random_thing(enemy)
            return

        direction = self.map.grid_direction(grid_pos, next_node)

        if direction == DIRECTION_NONE:
            logging.error("For some reason the calculate direction returned NONE after pathfinding")
            self.do_random_thing(enemy)
        else:
            enemy.move(direction)
<commit_msg>Increase the aloted time for pathfinding to 0.006s<commit_after>#!/usr/bin/env python
#-*- coding: utf8 -*-

import math
import random
import logging

from pathfinding.finder.a_star import AStarFinder
from pathfinding.core.diagonal_movement import DiagonalMovement

from locals import *

class Random (object):
    '''
    The Random AI would just do some thing at random at predefined
    chance of happening. It may fire, move forward or turn at any
    direction.
    '''

    fire_chance = 1
    moving_chance = 70
    turning_chance = 5
    turns_list = [
        DIRECTION_UP,
        DIRECTION_DOWN,
        DIRECTION_LEFT,
        DIRECTION_RIGHT,
    ]

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

    def do_random_thing(self, enemy):
        firing_roll = random.randint(0, 100)
        if firing_roll <= self.fire_chance:
            enemy.fire()

        turning_roll = random.randint(0, 100)
        if turning_roll <= self.turning_chance:
            turn_index = random.randint(0, 3)
            turn_direction = self.turns_list[turn_index]
            enemy.move(turn_direction)
            return

        moving_roll = random.randint(0, 100)
        if moving_roll <= self.moving_chance:
            enemy.move(enemy.facing)
        else:
            enemy.stop()

    def tick(self, deltat, enemies):
        for enemy in enemies:
            self.do_random_thing(enemy)

class ZombieDriver (Random):
    '''
    The ZombieDriver AI would follow the player relentlessly around without any
    thought or self preservation mechanism!
    '''

    def __init__(self, world):
        self.world = world
        self.map = world.map
        self.pathfinder = AStarFinder(
            diagonal_movement=DiagonalMovement.never,
            time_limit=0.006
        )
        self.path_cache_duration = 1000

    def tick(self, deltat, enemies):
        interesting_objects = []

        for player in self.world.players:
            if player.tank is None:
                continue
            interesting_objects.append(player.tank.rect)

        for enemy in enemies:
            self.process_enemy(enemy, deltat, interesting_objects)

    def process_enemy(self, enemy, deltat, interesting_objects):
        enemy.update_path_duration(deltat)
        own_coords = enemy.rect
        own_grid_pos = self.map.world_to_grid_coords(own_coords.centerx, own_coords.centery)

        if enemy.path_duration <= self.path_cache_duration:
            self.continue_path(enemy, own_grid_pos)
            return

        heat_obj = None
        heat_dist = math.inf
        for obj in interesting_objects:
            dist = math.sqrt(
                abs(own_coords.centerx - obj.centerx) ** 2 +
                abs(own_coords.centery - obj.centery) ** 2
            )
            if dist < heat_dist:
                heat_dist = dist
                heat_obj = obj

        if heat_obj is None:
            logging.error("ZombieDriver AI finds nothing interesting on the map! How can that be!?")
            self.do_random_thing(enemy)
            return

        hobjx, hobjy = heat_obj.centerx, heat_obj.centery
        hobj_grid_pos = self.map.world_to_grid_coords(hobjx, hobjy)

        if hobj_grid_pos == enemy.current_target:
            enemy.reset_path_duration()
            self.continue_path(enemy, own_grid_pos)
            return

        # WTF!? You have to rebiuld the *whole* grid every time find_path is called!
        # Definately change to a better library or write A* myself.
        self.map.build_grid()

        pf_start = self.map.grid.node(*own_grid_pos)
        pf_end = self.map.grid.node(*hobj_grid_pos)

        path, runs = self.pathfinder.find_path(
            pf_start,
            pf_end,
            self.map.grid
        )

        enemy.set_current_path(path)
        self.continue_path(enemy, own_grid_pos)

    def continue_path(self, enemy, grid_pos):
        next_node = enemy.get_path_next(grid_pos)

        if next_node is None:
            self.do_random_thing(enemy)
            return

        direction = self.map.grid_direction(grid_pos, next_node)

        if direction == DIRECTION_NONE:
            logging.error("For some reason the calculate direction returned NONE after pathfinding")
            self.do_random_thing(enemy)
        else:
            enemy.move(direction)
<|endoftext|>


<commit_before># 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 glanceclient.exc import ClientException
from glanceclient.v2 import client as glance_client

class GlanceHealth(object):
    def __init__(self, keystone_instance):
        authtoken = keystone_instance.keystone_return_authtoken()
        glance_endpoint = (keystone_instance
                           .keystone_endpoint_find(service_type='image'))
        self.glanceclient = glance_client.Client(glance_endpoint,
                                                 token=authtoken)

    def glance_image_list(self):
        try:
            image_list = self.glanceclient.images.list()
        except (ClientException, Exception) as e:
            return (404, e.message, [])
        return (200, "success", image_list)

    def glance_image_create(self, image_url,
                            image_name, cont_format, disk_format):
        try:
            ret = self.glanceclient.images.create(location=image_url,
                                                  name=image_name,
                                                  container_format=cont_format,
                                                  disk_format=disk_format)
        except (ClientException, Exception) as e:
            return (404, e.message, [])
        return (200, "success", ret)
<commit_msg>Fix glance api to return appropriate exception<commit_after># 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 glanceclient.exc import ClientException
from glanceclient.v2 import client as glance_client

class GlanceHealth(object):

    def __init__(self, keystone_instance):
        authtoken = keystone_instance.keystone_return_authtoken()
        glance_endpoint = (keystone_instance
                           .keystone_endpoint_find(service_type='image',
                                                   endpoint_type='internalURL')
                           )
        self.glanceclient = glance_client.Client(glance_endpoint,
                                                 token=authtoken)

    def glance_image_list(self):
        try:
            image_list = self.glanceclient.images.list()
            # The above api doens't generate appropriate exception
            # so we are walking through the generator to raise exception
            for image in image_list:
                pass
        except (ClientException, Exception) as e:
            return (404, "ClientException:" + str(e), [])
        return (200, "success", image_list)

    def glance_image_create(self, image_url,
                            image_name, cont_format, disk_format):
        try:
            ret = self.glanceclient.images.create(location=image_url,
                                                  name=image_name,
                                                  container_format=cont_format,
                                                  disk_format=disk_format)
        except (ClientException, Exception) as e:
            return (404, e.message, [])
        return (200, "success", ret)
<|endoftext|>


<commit_before>#!/usr/bin/env python2.6

#
# Copyright (c) 2008 Richard Cameron
# All rights reserved.
#

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#        This product includes software developed by
#		 CiteULike <http://www.citeulike.org> and its
#		 contributors.
# 4. Neither the name of CiteULike nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY CITEULIKE.ORG AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import urllib, re
import socket, subprocess
from metaheaders import MetaHeaders

socket.setdefaulttimeout(15)

def url_to_id(url, page):

	metaheaders = MetaHeaders(page=page,name='scheme')

	jstoreId = None
	doi = None
	if metaheaders.get_item("jstore-stable"):
		jstoreId = metaheaders.get_item("jstore-stable")
	if metaheaders.get_item("doi"):
		doi = metaheaders.get_item("doi")

	if doi and jstoreId:
		print "doi=%s, id=%s" % (doi,jstoreId)
		return (jstoreId, doi)

	# If there's a DOI then we'll have that
	m = re.search(r'doi=(10.\d\d\d\d/(\d+))', url)
	if m:
		return (int(m.group(2)),m.group(1))

	# If it's the old style SICI then, annoyingly, we'll need to fetch it
	if 'sici=' in url:
		m = re.search(r'Article Information', page)
		if m:
			return (int(m.group(1)), None)
		else:
			return (None, None)

	# Otherwise assume anything which looks like /123123/ is an ID
	#m = re.search(r'https?://.*?jstor.+?/(\d{4,})(/|$|\?|#)', url)
	m = re.search(r'/(10.\d\d\d\d/(\d+))', url)
	if m:
		return (int(m.group(2)), m.group(1))
	else:
		doi = None

	m = re.search(r'/(\d+)', url)
	if m:
		return (int(m.group(1)), None)

	return (None,None)

def grab_bibtex(id):
	url = "http://www.jstor.org/action/downloadCitation?format=bibtex&include=abs"
	params = {
		'noDoi' : 'yesDoi',
		'doi' : '10.2307/%s' % id,
		'suffix' : id,
		'downloadFileName' : id }

	page = get_url("%s?%s" %  (url, urllib.urlencode(params)))

	# Remove the random junk found in the record
	m = re.search(r'@comment{{NUMBER OF CITATIONS : 1}}(.*)@comment{{ These records have been provided', page, re.M|re.DOTALL)
	if m:
		page = m.group(1)

	# This bit is fun. Book reviews come through as [untitled]. Piece things back together for them
	if "title = {Review: [untitled]}," in page and "reviewedwork_1 = {" in page:
		work_title = re.search(r'reviewedwork_1 = {(.+?)},', page).group(1)
		page = page.replace('{Review: [untitled]}', "{Review: [%s]}" % work_title)

	return page

def parse_citation(s):
	re.compile(r'
\s+(.*?), Vol. ([^,]+)')

# JSTOR barfs at normal python urllib, so spawn lynx, which seem to work
def get_url(url):
	#return subprocess.Popen(["lynx", "-source", "-read_timeout", "10", url],stdout=subprocess.PIPE).stdout.read()
	return subprocess.Popen(["lynx", "-source", url],stdout=subprocess.PIPE).stdout.read()

def main(id, doi):
	if not id:
		print "\t".join([ "status", "err", "Could not identify this as being a JSTOR article" ])
		sys.exit(1)

	print "begin_tsv"
	print "\t".join([ "linkout", "JSTR2", "%d"%id, "", "", ""])
	# Sometimes other prefixes
	if doi:
		print "\t".join([ "linkout", "DOI", "", doi, "", ""])
	print "end_tsv"

	print "begin_bibtex"
	print grab_bibtex(id)
	print "end_bibtex"

	print "status\tok"

if __name__=="__main__":
	import sys
	url = sys.stdin.readline().strip()

	page = get_url(url)

	(jstor_id, doi) = url_to_id(url, page)

	main(jstor_id, doi)
<commit_msg>JSTOR needs serious rewrite - this will do for now<commit_after>#!/usr/bin/env python2.6

#
# Copyright (c) 2008 Richard Cameron
# All rights reserved.
#

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#        This product includes software developed by
#		 CiteULike <http://www.citeulike.org> and its
#		 contributors.
# 4. Neither the name of CiteULike nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY CITEULIKE.ORG AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import urllib, re, sys, codecs
import socket, subprocess
from metaheaders import MetaHeaders

socket.setdefaulttimeout(15)
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

def url_to_id(url, page):

	# first try meta headers
	metaheaders = MetaHeaders(page=page,name='scheme')

	jstoreId = metaheaders.get_item("jstore-stable")
	doi = metaheaders.get_item("doi")

	if doi and jstoreId:
		print "doi=%s, id=%s" % (doi,jstoreId)
		return (jstoreId, doi)

	# If there's a doi=DOI in the URL then we'll have that
	m = re.search(r'doi=(10.\d\d\d\d/(\d+))', url)
	if m:
		return (int(m.group(2)),m.group(1))
	m = re.search(r'doi=(10.\d\d\d\d/.+)', url)
	if m:
		return (None,m.group(1))

	# If it's the old style SICI then, annoyingly, we'll need to fetch it
	if 'sici=' in url:
		m = re.search(r'Article Information', page)
		if m:
			return (int(m.group(1)), None)
		else:
			return (None, None)

	# Otherwise assume anything which looks like /123123/ is an ID
	#m = re.search(r'https?://.*?jstor.+?/(\d{4,})(/|$|\?|#)', url)
	m = re.search(r'/(10.\d\d\d\d/(\d+))', url)
	if m:
		return (int(m.group(2)), m.group(1))

	# sometimes there's a general DOI, no jstore ID
	m = re.search(r'/(10.\d\d\d\d/.+)', url)
	if m:
		return (None, m.group(1))

	# plain old jstore ID, at least 4 digits
	m = re.search(r'/(\d{4,})', url)
	if m:
		return (int(m.group(1)), None)

	return (None,None)

def grab_bibtex(id, doi):
	url = "http://www.jstor.org/action/downloadCitation?format=bibtex&include=abs"

	if not doi:
		doi = '10.2307/%s' % id

	params = {
		'noDoi' : 'yesDoi',
		'doi' : doi,
		'suffix' : id,
		'downloadFileName' : id }

	page = get_url("%s?%s" %  (url, urllib.urlencode(params)))

	# Remove the random junk found in the record
	m = re.search(r'@comment{{NUMBER OF CITATIONS : 1}}(.*)@comment{{ These records have been provided', page, re.M|re.DOTALL)
	if m:
		page = m.group(1)

	# This bit is fun. Book reviews come through as [untitled]. Piece things back together for them
	if "title = {Review: [untitled]}," in page and "reviewedwork_1 = {" in page:
		work_title = re.search(r'reviewedwork_1 = {(.+?)},', page).group(1)
		page = page.replace('{Review: [untitled]}', "{Review: [%s]}" % work_title)

	# pages have leading "pp. "
	page = page.replace("{pp. ","{")

	return page

def parse_citation(s):
	re.compile(r'
\s+(.*?), Vol. ([^,]+)')

# JSTOR barfs at normal python urllib, so spawn lynx, which seem to work
def get_url(url):
	#return subprocess.Popen(["lynx", "-source", "-read_timeout", "10", url],stdout=subprocess.PIPE).stdout.read()
	page = subprocess.Popen(["lynx", "-source", url],stdout=subprocess.PIPE).stdout.read()
	page = page.decode("utf-8")
	return page

def main(id, doi, page):

	print "begin_tsv"
	if id:
		print "\t".join([ "linkout", "JSTR2", "%d"%id, "", "", ""])
	# Sometimes other prefixes
	if doi:
		print "\t".join([ "linkout", "DOI", "", doi, "", ""])

	meta = MetaHeaders(page=page)
	title = meta.get_item("dc.Title")

	if doi:
		abstract = meta.get_item("dc.Description")
		if abstract:
			print "abstract\t%s" % re.sub("^ABSTRACT.\s*", "", abstract)
		meta.print_item("publisher","dc.Publisher")
		print "use_crossref\t1"
		print "end_tsv"
	elif id:
		print "end_tsv"
		print "begin_bibtex"
		print grab_bibtex(id, doi)
		print "end_bibtex"
	elif title:
		# look for meta headers
		meta = MetaHeaders(page=page)
		meta.print_item("title","dc.Title")
		abstract = meta.get_item("dc.Description")
		if abstract:
			print "abstract\t%s" % re.sub("^ABSTRACT.\s*", "", abstract)
		meta.print_item("publisher","dc.Publisher")
		# <meta name="dc.Date" scheme="WTN8601" content="Nov 17, 2011" />
		meta.print_date("dc.Date");

		authors = meta.get_multi_item("dc.Creator")
		if authors:
			for au in authors:
				print "author\t%s" % au
		article_type = meta.get_item("dc.Type")
		if article_type:
			if article_type == "book-review":
				print "type\tBOOK"
			else:
				print "type\tJOUR"
		else:
			print "type\tJOUR"

		print "end_tsv"

	print "status\tok"

if __name__=="__main__":
	import sys
	url = sys.stdin.readline().strip()

	page = get_url(url)

	(jstor_id, doi) = url_to_id(url, page)

	main(jstor_id, doi, page)
<|endoftext|>


<commit_before>#  Copyright 2008-2010 Nokia Siemens Networks Oyj
#
#  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.

"""A test library providing dialogs for interacting with users.

`Dialogs` is Robot Framework's standard library that provides means
for pausing the test execution and getting input from users. The
dialogs are slightly different depending on are tests run on Python or
Jython but they provide the same functionality.
"""

import sys

try:
    from robot.version import get_version
except ImportError:
    __version__ = 'unknown'
else:
    __version__ = get_version()

DIALOG_TITLE = 'Robot Framework'

def pause_execution(message='Test execution paused. Press OK to continue.'):
    """Pauses the test execution and shows dialog with the text `message`. """
    MessageDialog(message)

def execute_manual_step(message, default_error=''):
    """Pauses the test execution until user sets the keyword status.

    `message` is the instruction shown in the dialog. User can select
    PASS or FAIL, and in the latter case an additional dialog is
    opened for defining the error message. `default_error` is the
    possible default value shown in the error message dialog.
    """
    if not  PassFailDialog(message).result:
        msg = get_value_from_user('Give error message:', default_error)
        raise AssertionError(msg)

def get_value_from_user(message, default_value=''):
    """Pauses the test execution and asks user to input a value.

    `message` is the instruction shown in the dialog. `default_value` is the
    possible default value shown in the input field. Selecting 'Cancel' fails
    the keyword.
    """
    return _validate_user_input(InputDialog(message, default_value).result)

def get_selection_from_user(message, *values):
    """Pauses the test execution and asks user to select value

    `message` is the instruction shown in the dialog. and `values` are
    the options given to the user. Selecting 'Cancel' fails the keyword.

    This keyword was added into Robot Framework 2.1.2.
    """
    return _validate_user_input(SelectionDialog(message, values).result)

def _validate_user_input(value):
    if value is None:
        raise ValueError('No value provided by user')
    return value

if not sys.platform.startswith('java'):

    from Tkinter import Tk, Toplevel, Frame, Listbox, Label, Button,\
                        BOTH, END, LEFT
    import tkMessageBox
    import tkSimpleDialog

    class _AbstractTkDialog(Toplevel):

        def __init__(self, title):
            parent = Tk()
            parent.withdraw()
            Toplevel.__init__(self, parent)
            self._init_dialog(parent, title)
            self._create_body()
            self._create_buttons()
            self.result = None
            self._initial_focus.focus_set()
            self.wait_window(self)

        def _init_dialog(self, parent, title):
            self.title(title)
            self.grab_set()
            self.protocol("WM_DELETE_WINDOW", self._right_clicked)
            self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
                                      parent.winfo_rooty()+50))

        def _create_body(self):
            frame = Frame(self)
            self._initial_focus = self.create_components(frame)
            frame.pack(padx=5, pady=5, expand=1, fill=BOTH)

        def _create_buttons(self):
            frame = Frame(self)
            self._create_button(frame, self._left_button, self._left_clicked)
            self._create_button(frame, self._right_button, self._right_clicked)
            self.bind("<Escape>", self._right_clicked)
            frame.pack()

        def _create_button(self, parent, label, command):
            w = Button(parent, text=label, width=10, command=command)
            w.pack(side=LEFT, padx=5, pady=5)

        def _left_clicked(self, event=None):
            if not self.validate():
                self._initial_focus.focus_set()
                return
            self.withdraw()
            self.apply()
            self.destroy()

        def _right_clicked(self, event=None):
            self.destroy()

        def create_components(self, parent):
            raise NotImplementedError()

        def validate(self):
            raise NotImplementedError()

        def apply(self):
            raise NotImplementedError()

    class MessageDialog:

        def __init__(self, message):
            Tk().withdraw()
            tkMessageBox.showinfo(DIALOG_TITLE, message)

    class InputDialog:

        def __init__(self, message, default):
            Tk().withdraw()
            self.result = tkSimpleDialog.askstring(DIALOG_TITLE, message,
                                                   initialvalue=default)

    class SelectionDialog(_AbstractTkDialog):
        _left_button = 'OK'
        _right_button = 'Cancel'

        def __init__(self, message, values):
            self._message = message
            self._values = values
            _AbstractTkDialog.__init__(self, "Select one option")

        def create_components(self, parent):
            Label(parent, text=self._message).pack(fill=BOTH)
            self._listbox = Listbox(parent)
            self._listbox.pack(fill=BOTH)
            for item in self._values:
                self._listbox.insert(END, item)
            return self._listbox

        def validate(self):
            return bool(self._listbox.curselection())

        def apply(self):
            self.result = self._listbox.get(self._listbox.curselection())

    class PassFailDialog(_AbstractTkDialog):
        _left_button = 'PASS'
        _right_button = 'FAIL'

        def __init__(self, message):
            self._message = message
            _AbstractTkDialog.__init__(self, DIALOG_TITLE)

        def create_components(self, parent):
            label = Label(parent, text=self._message)
            label.pack(fill=BOTH)
            return label

        def validate(self):
            return True

        def apply(self):
            self.result = True

    #FIXME: Remove when not needed anymore
    def _run_in_thread(callable):
        print "Running in python thread"
        from threading import Thread
        import time
        t = Thread(target=callable)
        t.run()
        while t.isAlive():
            time.sleep(1)

    def _run_in_rf_thread(callable):
        print "Running in rf thread"
        from robot.utils.robotthread import ThreadedRunner
        t = ThreadedRunner(callable)
        t.run_in_thread(10)
        print t.get_result()

    if __name__ == '__main__':
        _run_in_thread(pause_execution)
        _run_in_thread(pause_execution)
        _run_in_thread(pause_execution)
        _run_in_thread(pause_execution)
        _run_in_rf_thread(pause_execution)
        _run_in_rf_thread(pause_execution)

else:

    import time
    from javax.swing import JOptionPane
    from javax.swing.JOptionPane import PLAIN_MESSAGE, UNINITIALIZED_VALUE, \
        YES_NO_OPTION, OK_CANCEL_OPTION, DEFAULT_OPTION

    class _AbstractSwingDialog:

        def __init__(self, message):
            self.result = self._show_dialog(message)

        def _show_dialog(self, message):
            self._init_dialog(message)
            self._show_dialog_and_wait_it_to_be_closed()
            return self._get_value()

        def _show_dialog_and_wait_it_to_be_closed(self):
            dialog = self._pane.createDialog(None, DIALOG_TITLE)
            dialog.setModal(0);
            dialog.show()
            while dialog.isShowing():
                time.sleep(0.2)
            dialog.dispose()

        def _get_value(self):
            value = self._pane.getInputValue()
            if value == UNINITIALIZED_VALUE:
                return None
            return value

    class MessageDialog(_AbstractSwingDialog):

        def _init_dialog(self, message):
            self._pane = JOptionPane(message, PLAIN_MESSAGE, DEFAULT_OPTION)

    class InputDialog(_AbstractSwingDialog):

        def __init__(self, message, default):
            self._default = default
            _AbstractSwingDialog.__init__(self, message)

        def _init_dialog(self, message):
            self._pane = JOptionPane(message, PLAIN_MESSAGE, OK_CANCEL_OPTION)
            self._pane.setWantsInput(True)
            self._pane.setInitialSelectionValue(self._default)

    class SelectionDialog(_AbstractSwingDialog):

        def __init__(self, message, options):
            self._options = options
            _AbstractSwingDialog.__init__(self, message)

        def _init_dialog(self, message):
            self._pane = JOptionPane(message, PLAIN_MESSAGE, OK_CANCEL_OPTION)
            self._pane.setWantsInput(True)
            self._pane.setSelectionValues(self._options)

    class PassFailDialog(_AbstractSwingDialog):

        def _init_dialog(self, message):
            self._buttons = ['PASS', 'FAIL']
            self._pane = JOptionPane(message, PLAIN_MESSAGE, YES_NO_OPTION,
                                     None, self._buttons, 'PASS')

        def _get_value(self):
            value = self._pane.getValue()
            if value in self._buttons and self._buttons.index(value) == 0:
                return True
            return False
<commit_msg>Added mechanism to fail in case of timeouts is used on Windows with Python, issue 641. Also added note to the documentation.<commit_after>#  Copyright 2008-2010 Nokia Siemens Networks Oyj
#
#  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.

"""A test library providing dialogs for interacting with users.

`Dialogs` is Robot Framework's standard library that provides means
for pausing the test execution and getting input from users. The
dialogs are slightly different depending on are tests run on Python or
Jython but they provide the same functionality.

Note: Dialogs library cannot be used with timeouts on Windows with Python.
"""

import sys

try:
    from robot.version import get_version
except ImportError:
    __version__ = 'unknown'
else:
    __version__ = get_version()

DIALOG_TITLE = 'Robot Framework'

def pause_execution(message='Test execution paused. Press OK to continue.'):
    """Pauses the test execution and shows dialog with the text `message`. """
    MessageDialog(message)

def execute_manual_step(message, default_error=''):
    """Pauses the test execution until user sets the keyword status.

    `message` is the instruction shown in the dialog. User can select
    PASS or FAIL, and in the latter case an additional dialog is
    opened for defining the error message. `default_error` is the
    possible default value shown in the error message dialog.
    """
    if not  PassFailDialog(message).result:
        msg = get_value_from_user('Give error message:', default_error)
        raise AssertionError(msg)

def get_value_from_user(message, default_value=''):
    """Pauses the test execution and asks user to input a value.

    `message` is the instruction shown in the dialog. `default_value` is the
    possible default value shown in the input field. Selecting 'Cancel' fails
    the keyword.
    """
    return _validate_user_input(InputDialog(message, default_value).result)

def get_selection_from_user(message, *values):
    """Pauses the test execution and asks user to select value

    `message` is the instruction shown in the dialog. and `values` are
    the options given to the user. Selecting 'Cancel' fails the keyword.

    This keyword was added into Robot Framework 2.1.2.
    """
    return _validate_user_input(SelectionDialog(message, values).result)

def _validate_user_input(value):
    if value is None:
        raise ValueError('No value provided by user')
    return value

if not sys.platform.startswith('java'):

    from Tkinter import Tk, Toplevel, Frame, Listbox, Label, Button,\
                        BOTH, END, LEFT
    import tkMessageBox
    import tkSimpleDialog
    import os
    from threading import currentThread

    def _prevent_execution_with_timeouts(method):
        def _check_timeout(*args):
            if os.name == 'nt' and  currentThread().name != 'MainThread':
                raise AssertionError("Dialogs library cannot be used with "
                                     "timeout on Windows with Python.")
            return method(*args)
        return _check_timeout

    class _AbstractTkDialog(Toplevel):

        @_prevent_execution_with_timeouts
        def __init__(self, title):
            parent = Tk()
            parent.withdraw()
            Toplevel.__init__(self, parent)
            self._init_dialog(parent, title)
            self._create_body()
            self._create_buttons()
            self.result = None
            self._initial_focus.focus_set()
            self.wait_window(self)

        def _init_dialog(self, parent, title):
            self.title(title)
            self.grab_set()
            self.protocol("WM_DELETE_WINDOW", self._right_clicked)
            self.geometry("+%d+%d" % (parent.winfo_rootx()+150,
                                      parent.winfo_rooty()+150))

        def _create_body(self):
            frame = Frame(self)
            self._initial_focus = self.create_components(frame)
            frame.pack(padx=5, pady=5, expand=1, fill=BOTH)

        def _create_buttons(self):
            frame = Frame(self)
            self._create_button(frame, self._left_button, self._left_clicked)
            self._create_button(frame, self._right_button, self._right_clicked)
            self.bind("<Escape>", self._right_clicked)
            frame.pack()

        def _create_button(self, parent, label, command):
            w = Button(parent, text=label, width=10, command=command)
            w.pack(side=LEFT, padx=5, pady=5)

        def _left_clicked(self, event=None):
            if not self.validate():
                self._initial_focus.focus_set()
                return
            self.withdraw()
            self.apply()
            self.destroy()

        def _right_clicked(self, event=None):
            self.destroy()

        def create_components(self, parent):
            raise NotImplementedError()

        def validate(self):
            raise NotImplementedError()

        def apply(self):
            raise NotImplementedError()

    class MessageDialog(object):

        @_prevent_execution_with_timeouts
        def __init__(self, message):
            Tk().withdraw()
            tkMessageBox.showinfo(DIALOG_TITLE, message)

    class InputDialog(object):

        @_prevent_execution_with_timeouts
        def __init__(self, message, default):
            Tk().withdraw()
            self.result = tkSimpleDialog.askstring(DIALOG_TITLE, message,
                                                   initialvalue=default)

    class SelectionDialog(_AbstractTkDialog):
        _left_button = 'OK'
        _right_button = 'Cancel'

        def __init__(self, message, values):
            self._message = message
            self._values = values
            _AbstractTkDialog.__init__(self, "Select one option")

        def create_components(self, parent):
            Label(parent, text=self._message).pack(fill=BOTH)
            self._listbox = Listbox(parent)
            self._listbox.pack(fill=BOTH)
            for item in self._values:
                self._listbox.insert(END, item)
            return self._listbox

        def validate(self):
            return bool(self._listbox.curselection())

        def apply(self):
            self.result = self._listbox.get(self._listbox.curselection())

    class PassFailDialog(_AbstractTkDialog):
        _left_button = 'PASS'
        _right_button = 'FAIL'

        def __init__(self, message):
            self._message = message
            _AbstractTkDialog.__init__(self, DIALOG_TITLE)

        def create_components(self, parent):
            label = Label(parent, text=self._message)
            label.pack(fill=BOTH)
            return label

        def validate(self):
            return True

        def apply(self):
            self.result = True

else:

    import time
    from javax.swing import JOptionPane
    from javax.swing.JOptionPane import PLAIN_MESSAGE, UNINITIALIZED_VALUE, \
        YES_NO_OPTION, OK_CANCEL_OPTION, DEFAULT_OPTION

    class _AbstractSwingDialog:

        def __init__(self, message):
            self.result = self._show_dialog(message)

        def _show_dialog(self, message):
            self._init_dialog(message)
            self._show_dialog_and_wait_it_to_be_closed()
            return self._get_value()

        def _show_dialog_and_wait_it_to_be_closed(self):
            dialog = self._pane.createDialog(None, DIALOG_TITLE)
            dialog.setModal(0);
            dialog.show()
            while dialog.isShowing():
                time.sleep(0.2)
            dialog.dispose()

        def _get_value(self):
            value = self._pane.getInputValue()
            if value == UNINITIALIZED_VALUE:
                return None
            return value

    class MessageDialog(_AbstractSwingDialog):

        def _init_dialog(self, message):
            self._pane = JOptionPane(message, PLAIN_MESSAGE, DEFAULT_OPTION)

    class InputDialog(_AbstractSwingDialog):

        def __init__(self, message, default):
            self._default = default
            _AbstractSwingDialog.__init__(self, message)

        def _init_dialog(self, message):
            self._pane = JOptionPane(message, PLAIN_MESSAGE, OK_CANCEL_OPTION)
            self._pane.setWantsInput(True)
            self._pane.setInitialSelectionValue(self._default)

    class SelectionDialog(_AbstractSwingDialog):

        def __init__(self, message, options):
            self._options = options
            _AbstractSwingDialog.__init__(self, message)

        def _init_dialog(self, message):
            self._pane = JOptionPane(message, PLAIN_MESSAGE, OK_CANCEL_OPTION)
            self._pane.setWantsInput(True)
            self._pane.setSelectionValues(self._options)

    class PassFailDialog(_AbstractSwingDialog):

        def _init_dialog(self, message):
            self._buttons = ['PASS', 'FAIL']
            self._pane = JOptionPane(message, PLAIN_MESSAGE, YES_NO_OPTION,
                                     None, self._buttons, 'PASS')

        def _get_value(self):
            value = self._pane.getValue()
            if value in self._buttons and self._buttons.index(value) == 0:
                return True
            return False
<|endoftext|>


<commit_before>from __future__ import with_statement
import pickle
import sys
import re
import utility
from plugins import Plugin
from commands import Command
import command_catcher

class URL():
	url = ''
	title = ''
	timestamp = ''
	nick = ''
	def is_match(self, searchword):
		if self.url and re.search(searchword, self.url, re.IGNORECASE):
			return True
		if self.title and re.search(searchword, self.title, re.IGNORECASE):
			return True
		if self.nick and re.search(searchword, self.nick, re.IGNORECASE):
			return True
		return False

class TitleReaderPlugin(Command):
	hooks = ['on_privmsg']
	black_urls = []
	urls = {}
	url_list = []

	def __init__(self):
		pass

	def get_options(self):
		return ['black_urls']

	def set_title(self, target):
		import urllib
		if not re.search('http', self.urls[target].url):
			self.urls[target].url = 'http://' + self.urls[target].url

		try:
			data = urllib.urlopen(self.urls[target].url).read()
		except command_catcher.TimeoutException:
			self.urls[target].title = None
			return

		m = re.search('<title>(.+?)<\/title>', data, re.IGNORECASE)

		if m:
			self.urls[target].title = m.group(1)
			self.urls[target].title = re.sub('<.+?>', '', self.urls[target].title)
		else:
			self.urls[target].title = None

	def on_privmsg(self, bot, source, target, tupels):
		message = tupels[5]

		m = re.search('((http:\/\/|www.)\S+)', message, re.IGNORECASE)

		if m:
			self.urls[target] = URL()
			self.urls[target].url = m.group(1)
			self.urls[target].nick = source
			self.urls[target].timestamp = 'test'
			self.set_title(target)
			self.save_last_url(target)

	def save_last_url(self, target):
		file = open('data/urls.txt', 'w')
		self.url_list.append(self.urls[target])
		p = pickle.Pickler(file)
		p.dump(self.url_list)
		file.close()

	def trig_urlsearch(self, bot, source, target, trigger, argument):
		resultlist = []
		match = False

		if len(argument) > 0:
			searchlist = argument.split(' ')
			try:
				for object in self.url_list:
					match = True
					for word in searchlist:
						if not object.is_match(word):
							match = False
							break
					if match:
						resultlist.append(object)

				if len(resultlist) > 0:
					if resultlist[0].title:
						title = resultlist[0].title
					else:
						title = 'N/A'
					bot.tell(target, 'Match 1 of ' + str(len(resultlist)) + ': ' + resultlist[0].url + ' - ' + title)
				else:
					bot.tell(target, 'No match found.')

			except IOError:
				bot.tell(target, 'I have no urls for this channel!')
		else:
			bot.tell(target, 'Usage: .urlsearch <search string>')

	def trig_title(self, bot, source, target, trigger, argument):
		if target in self.urls.keys():
			m = self.urls[target].title

			if m:
				bot.tell(target, m)
			else:
				bot.tell(target, 'I can\'t find a title for ' + self.urls[target].url)
		else:
			bot.tell(target, 'I haven\'t seen any urls here yet.')

	def load_urls(self):
		try:
			file = open('data/urls.txt', 'r')
			self.url_list = pickle.Unpickler(file).load()
			file.close()
		except IOError:
			pass

	def on_load(self):
		del self.black_urls[:]

		self.load_urls()

		file = open('data/black_urls.txt', 'r')

		while True:
			line = file.readline()
			if not line:
				break

			m = re.match('^(.+)$', line)

			if m:
				self.black_urls.append(m.group(1))

	def save(self):
		file = open('data/black_urls.txt', 'w')

		for url in self.black_urls:
			file.write(url)
			file.write('\n')

		file.close()

	def on_modified_options(self):
		self.save()
<commit_msg><commit_after>from __future__ import with_statement
import pickle
import sys
import re
import utility
from plugins import Plugin
from commands import Command
import command_catcher

class URL():
	url = ''
	title = ''
	timestamp = ''
	nick = ''
	def is_match(self, searchword):
		if self.url and re.search(searchword, self.url, re.IGNORECASE):
			return True
		if self.title and re.search(searchword, self.title, re.IGNORECASE):
			return True
		if self.nick and re.search(searchword, self.nick, re.IGNORECASE):
			return True
		return False

class TitleReaderPlugin(Command):
	hooks = ['on_privmsg']
	black_urls = []
	urls = {}
	url_list = []

	def __init__(self):
		pass

	def get_options(self):
		return ['black_urls']

	def set_title(self, target):
		import urllib
		if not re.search('http', self.urls[target].url):
			self.urls[target].url = 'http://' + self.urls[target].url

		try:
			data = urllib.urlopen(self.urls[target].url).read()
		except command_catcher.TimeoutException:
			self.urls[target].title = None
			return

		m = re.search('<title>(.+?)<\/title>', data, re.IGNORECASE)

		if m:
			self.urls[target].title = m.group(1)
			self.urls[target].title = re.sub('<.+?>', '', self.urls[target].title)
		else:
			self.urls[target].title = None

	def on_privmsg(self, bot, source, target, tupels):
		message = tupels[5]

		m = re.search('((http:\/\/|www.)\S+)', message, re.IGNORECASE)

		if m:
			self.urls[target] = URL()
			self.urls[target].url = m.group(1)
			self.urls[target].nick = source
			self.urls[target].timestamp = 'test'
			self.set_title(target)
			self.save_last_url(target)

	def save_last_url(self, target):
		self.url_list.append(self.urls[target])
		self.save_urls()

	def trig_urlsearch(self, bot, source, target, trigger, argument):
		resultlist = []
		match = False

		if len(argument) > 0:
			searchlist = argument.split(' ')

			for object in self.url_list:
				match = True
				for word in searchlist:
					if not object.is_match(word):
						match = False
						break
				if match:
					resultlist.append(object)

			if len(resultlist) > 0:
				if resultlist[0].title:
					title = resultlist[0].title
				else:
					title = 'N/A'
				bot.tell(target, 'Match 1 of ' + str(len(resultlist)) + ': ' + resultlist[0].url + ' - ' + title)
			else:
				bot.tell(target, 'No match found.')
		else:
			bot.tell(target, 'Usage: .urlsearch <search string>')

	def trig_title(self, bot, source, target, trigger, argument):
		if target in self.urls.keys():
			m = self.urls[target].title

			if m:
				bot.tell(target, m)
			else:
				bot.tell(target, 'I can\'t find a title for ' + self.urls[target].url)
		else:
			bot.tell(target, 'I haven\'t seen any urls here yet.')

	def save_urls(self):
		file = open('data/urls.txt', 'w')
		p = pickle.Pickler(file)
		p.dump(self.url_list)
		file.close()

	def load_urls(self):
		try:
			file = open('data/urls.txt', 'r')
			self.url_list = pickle.Unpickler(file).load()
			file.close()
		except IOError:
			pass

	def on_load(self):
		del self.black_urls[:]

		self.load_urls()

		file = open('data/black_urls.txt', 'r')

		while True:
			line = file.readline()
			if not line:
				break

			m = re.match('^(.+)$', line)

			if m:
				self.black_urls.append(m.group(1))

	def save(self):
		file = open('data/black_urls.txt', 'w')

		for url in self.black_urls:
			file.write(url)
			file.write('\n')

		file.close()

	def on_modified_options(self):
		self.save()
<|endoftext|>


<commit_before>import boto
import boto.s3
from boto.s3.key import Key
import boto.s3.connection
import math
import os
import logging

from pmg import db, app

S3_BUCKET = app.config['S3_BUCKET']

logger = logging.getLogger(__name__)

def rounded_megabytes(bytes):

    megabytes = bytes / float(1024 * 1024)
    megabytes = math.ceil(megabytes * 1000) / 1000  # round the float
    return megabytes

def increment_filename(filename):
    """
    Increment a counter on the filename, so that duplicate filenames can be avoided.
    We do this by adding a counter as a path component at the start of the filename,
    so that the original name is not changed.
    """
    if not '/' in filename:
        counter = 0
        rest = filename
    else:
        counter, rest = filename.split('/', 1)
        try:
            counter = int(counter)
        except ValueError:
            # eg. foo/bar.pdf -> 1/foo/bar.pdf
            counter = 0
            rest = filename

    return '%d/%s' % (counter+1, rest)

class S3Bucket():
    def __init__(self):
        self._bucket = None

    @property
    def bucket(self):
        if self._bucket is None:
            conn = boto.s3.connect_to_region('eu-west-1')
            self._bucket = conn.get_bucket(S3_BUCKET)
        return self._bucket

    def upload_file(self, path, key):
        try:
            # assemble key
            bytes = os.path.getsize(path)
            megabytes = rounded_megabytes(bytes)

            # ensure we've got a unique key
            tmp_key = self.bucket.get_key(key)
            while tmp_key is not None:
                key = increment_filename(key)
                tmp_key = self.bucket.get_key(key)

            logger.debug("uploading " + path + " (" + str(megabytes) + " MB) to S3 at " + bucket + "/" + key)

            # only upload if the key doesn't exist yet
            tmp_key = Key(self.bucket)
            tmp_key.key = key
            tmp_key.set_contents_from_filename(path)

        except Exception as e:
            logger.error("Cannot upload file to S3. Removing file from disk.")
            # remove file from disc
            os.remove(path)
            raise e

        # remove file from disc
        os.remove(path)
        return key
<commit_msg>Fix logging bug<commit_after>import boto
import boto.s3
from boto.s3.key import Key
import boto.s3.connection
import math
import os
import logging

from pmg import db, app

S3_BUCKET = app.config['S3_BUCKET']

logger = logging.getLogger(__name__)

def rounded_megabytes(bytes):

    megabytes = bytes / float(1024 * 1024)
    megabytes = math.ceil(megabytes * 1000) / 1000  # round the float
    return megabytes

def increment_filename(filename):
    """
    Increment a counter on the filename, so that duplicate filenames can be avoided.
    We do this by adding a counter as a path component at the start of the filename,
    so that the original name is not changed.
    """
    if not '/' in filename:
        counter = 0
        rest = filename
    else:
        counter, rest = filename.split('/', 1)
        try:
            counter = int(counter)
        except ValueError:
            # eg. foo/bar.pdf -> 1/foo/bar.pdf
            counter = 0
            rest = filename

    return '%d/%s' % (counter+1, rest)

class S3Bucket():
    def __init__(self):
        self._bucket = None

    @property
    def bucket(self):
        if self._bucket is None:
            conn = boto.s3.connect_to_region('eu-west-1')
            self._bucket = conn.get_bucket(S3_BUCKET)
        return self._bucket

    def upload_file(self, path, key):
        try:
            # assemble key
            bytes = os.path.getsize(path)
            megabytes = rounded_megabytes(bytes)

            # ensure we've got a unique key
            tmp_key = self.bucket.get_key(key)
            while tmp_key is not None:
                key = increment_filename(key)
                tmp_key = self.bucket.get_key(key)

            logger.debug("uploading " + path + " (" + str(megabytes) + " MB) to S3 at " + key)

            # only upload if the key doesn't exist yet
            tmp_key = Key(self.bucket)
            tmp_key.key = key
            tmp_key.set_contents_from_filename(path)

        except Exception as e:
            logger.error("Cannot upload file to S3. Removing file from disk.")
            # remove file from disc
            os.remove(path)
            raise e

        # remove file from disc
        os.remove(path)
        return key
<|endoftext|>


<commit_before>from rest_framework.test import APITestCase, APIClient, force_authenticate
from models.Post import Post
from models.Author import Author
from django.contrib.auth.models import User
import uuid
from django.core import serializers
import json
from unittest import skip

class PostAPITests(APITestCase):
    def setUp(self):
        superuser = User.objects.create_superuser('superuser', 'test@test.com', 'test1234')
        self.client = APIClient()
        #Authenticate as a super user so we can test everything
        self.client.force_authenticate(user=superuser)
        self.author = Author.create(host='local', displayName='testMonkey', user=superuser)
        self.author.save()
        self.post = Post.create(self.author,
            title="A post title about a post about web dev",
            origin="http://whereitcamefrom.com/post/zzzzz",
            description="This post discusses stuff -- brief",
            categories = ["web","tutorial"],
            visibility = "PUBLIC")
        self.post.save()

    def get_posts_by_current_user(self):
        return self.client.get('/author/posts/')
        #http://service/author/posts (posts that are visible to the currently authenticated user)

    def get_public_posts(self):
        # http://service/posts (all posts marked as public on the server)
        return self.client.get('/posts/')

    def get_posts_by_author_id(self, author_id):
        # http://service/author/{AUTHOR_ID}/posts (all posts made by {AUTHOR_ID} visible to the currently authenticated user)
        return self.client.get('/author/'+str(author_id)+'/')

    def get_posts_by_page(self, page_number):
        # GET http://service/author/posts?page=4
        return self.client.get('/author/posts?page='+str(page_number)+'/')

    def get_posts_by_page_and_size(self, page_number, size):
        # GET http://service/author/posts?page=4&size=50
        return self.client.get('/author/posts?page='+str(page_number)+'&size='+str(size)+'/')

    def create_post(self, post_body):
        #a POST should insert the post http://service/posts/postid
        return self.client.post('/posts/', data=post_body, format='json')

    def delete_post(self, post_id):
        #A delete should delete posts with a specific ID
        return self.client.delete('/posts/'+str(post_id)+'/')

    def create_update_post_with_put(self, post_id, put_body):
        #PUT http://service/posts/postid to update/create post
        return self.client.put('/posts/'+str(post_id)+'/', put_body, format='json')

    def create_update_post_with_post(self, post_id, post_body):
        return self.client.post('/posts/'+str(post_id)+'/', post_body, format='json')

    def test_get_posts_by_current_author(self):
        self.get_posts_by_author_id(self.author.id)
        response = self.get_posts_by_current_user()
        self.assertEqual(response.status_code, 200)
        self.assertEqual(str(self.post.id), response.data[0]['id'])
        self.assertEqual(str(self.post.author.id), response.data[0]['author']['id'])

    def test_no_posts_by_current_author(self):
        pass

    def test_get_posts_by_current_admin(self):
        #TODO: Retrieves all the posts made by the currently logged in admin
        pass

    def test_get_public_posts(self):
        #TODO: Retrieve all public posts on the server
        pass

    def test_get_posts_by_author_id(self):
        #TODO: returns all the posts made by an author with a specific ID
        pass

    def test_get_posts_with_invalid_author_id(self):
        #TODO: test getting posts with an invalid author id, or author ID that doesn't exist
        pass

    def get_single_post_by_id(self, post_id):
        # http://service/posts/{POST_ID} access to a single post with id = {POST_ID}
        return self.client.get('/posts/'+str(post_id)+'/')

    def test_get_post_by_id(self):
        response = self.get_single_post_by_id(self.post.id)
        self.assertEqual(200, response.status_code)
        self.assertEqual(str(self.post.id), response.data['id'])
        self.assertEqual(str(self.post.author.id), response.data['author']['id'])

    def test_get_posts_with_invalid_post_id(self):
        #TODO: tests behaviour for when you get posts with incorrectly
        #      formatted ID, or ID that doesn't exist
        pass

    def test_get_posts_by_page(self):
        #TODO: Returns all of posts on a specific page
        pass

    def test_get_full_page_of_posts(self):
        #TODO: test retrieving a full page of posts
        pass

    def test_get_partial_page_of_posts(self):
        #TODO: test retrieving a partial page of posts
        pass

    def test_page_does_not_exist(self):
        #TODO: Tests what is returned if requested page of posts does not exist
        pass

    def test_page_has_no_posts(self):
        #TODO: Tests getting page 1, when the user has made no posts
        #pretty sure this is redundant to above
        pass

    def test_get_posts_by_page_and_size(self):
        #TODO: retrieve a page of posts with specific size of page
        pass

    def test_get_posts_by_page_and_exceeded_size(self):
        #TODO: retrieve a page where there are more posts than the specified size
        pass

    def test_get_posts_by_page_and_partial_size(self):
        #TODO: retrive a page where the size is smaller than the one specified
        pass

    def test_update_post_with_put(self):
        # Create a post using a put method
        put_body = self.get_post_data(self.post, self.author)
        response = self.create_update_post_with_put(self.post.id, put_body)
        self.assertEqual(response.status_code, 200)

    def test_update_nonexistent_post(self):
        # Try to update a post that hasn't been created
        # Maybe it should have the same result as test_create_post_with_put?
        post_id = uuid.uuid4()
        put_body = {"title":"Sample Title"}
        response = self.create_update_post_with_post(post_id, put_body)
        self.assertEqual(response.status_code, 404)

    def test_create_invalid_post(self):
        post_id = "not-a-number"
        put_body = {"title":"Sample Title"}
        response = self.create_update_post_with_post(post_id, put_body)
        self.assertEqual(response.status_code, 400)

    def test_delete_post(self):
        post_id = self.post.id
        response = self.delete_post(post_id)
        self.assertEqual(response.status_code, 204)

    def test_delete_nonexistent_post(self):
        post_id = uuid.uuid4()
        response = self.delete_post(post_id)
        self.assertEqual(response.status_code, 404)

    def test_author_deletes_own_post(self):
        #TODO: check permissions for an author deleting their own post
        pass

    def test_admin_deletes_post(self):
        #TODO: an admin can delete any post, regardless if they authored it
        pass

    def test_create_post_with_post(self):
        # Create a post using a post method
        self.post.id = uuid.uuid4()
        request_body = self.get_post_data(self.post, self.author)
        response = self.create_post(request_body)
        self.assertEqual(response.status_code, 201)

    def test_update_post_with_post(self):
        # Update an existing post
        request_body = self.get_post_data(self.post, self.author)
        response = self.create_update_post_with_post(self.post.id, request_body)
        self.assertEqual(response.status_code, 200)

    def test_creating_public_post(self):
        #TODO: create a public post and successfully query it
        pass

    def test_create_friend_post(self):
        #TODO: create a post that can only be seen by users friends
        pass

    def test_insert_invalid_post(self):
        #TODO: insert an invalidly formatted post
        pass

    def get_post_data(self, post, author):
        return {
			"title": post.title,
			"source": post.source,
			"origin": post.origin,
			"description": post.description,
			"contentType": post.contentType,
			"content": post.content,
			"author":{
				"id": str(author.id),
				"host": author.host,
				"displayName": author.displayName,
			},
			"categories": post.categories,
			"count": str(post.count),
			"size": str(post.size),
			"next": post.next,
			"published":str(post.published),
			"id":str(post.id),
			"visibility":post.visibility
        }
<commit_msg>Making the test a little stronger<commit_after>from rest_framework.test import APITestCase, APIClient, force_authenticate
from models.Post import Post
from models.Author import Author
from django.contrib.auth.models import User
import uuid
from django.core import serializers
import json
from unittest import skip

class PostAPITests(APITestCase):
    def setUp(self):
        superuser = User.objects.create_superuser('superuser', 'test@test.com', 'test1234')
        self.client = APIClient()
        #Authenticate as a super user so we can test everything
        self.client.force_authenticate(user=superuser)
        self.author = Author.create(host='local', displayName='testMonkey', user=superuser)
        self.author.save()
        self.post = Post.create(self.author,
            title="A post title about a post about web dev",
            origin="http://whereitcamefrom.com/post/zzzzz",
            description="This post discusses stuff -- brief",
            categories = ["web","tutorial"],
            visibility = "PUBLIC")
        self.post.save()

    def get_posts_by_current_user(self):
        return self.client.get('/author/posts/')
        #http://service/author/posts (posts that are visible to the currently authenticated user)

    def get_public_posts(self):
        # http://service/posts (all posts marked as public on the server)
        return self.client.get('/posts/')

    def get_posts_by_author_id(self, author_id):
        # http://service/author/{AUTHOR_ID}/posts (all posts made by {AUTHOR_ID} visible to the currently authenticated user)
        return self.client.get('/author/'+str(author_id)+'/')

    def get_posts_by_page(self, page_number):
        # GET http://service/author/posts?page=4
        return self.client.get('/author/posts?page='+str(page_number)+'/')

    def get_posts_by_page_and_size(self, page_number, size):
        # GET http://service/author/posts?page=4&size=50
        return self.client.get('/author/posts?page='+str(page_number)+'&size='+str(size)+'/')

    def create_post(self, post_body):
        #a POST should insert the post http://service/posts/postid
        return self.client.post('/posts/', data=post_body, format='json')

    def delete_post(self, post_id):
        #A delete should delete posts with a specific ID
        return self.client.delete('/posts/'+str(post_id)+'/')

    def create_update_post_with_put(self, post_id, put_body):
        #PUT http://service/posts/postid to update/create post
        return self.client.put('/posts/'+str(post_id)+'/', put_body, format='json')

    def create_update_post_with_post(self, post_id, post_body):
        return self.client.post('/posts/'+str(post_id)+'/', post_body, format='json')

    def test_get_posts_by_current_author(self):
        self.get_posts_by_author_id(self.author.id)
        response = self.get_posts_by_current_user()
        self.assertEqual(response.status_code, 200)
        self.assertEqual(str(self.post.id), response.data[0]['id'])
        self.assertEqual(str(self.post.author.id), response.data[0]['author']['id'])

    def test_no_posts_by_current_author(self):
        pass

    def test_get_posts_by_current_admin(self):
        #TODO: Retrieves all the posts made by the currently logged in admin
        pass

    def test_get_public_posts(self):
        #TODO: Retrieve all public posts on the server
        pass

    def test_get_posts_by_author_id(self):
        #TODO: returns all the posts made by an author with a specific ID
        pass

    def test_get_posts_with_invalid_author_id(self):
        #TODO: test getting posts with an invalid author id, or author ID that doesn't exist
        pass

    def get_single_post_by_id(self, post_id):
        # http://service/posts/{POST_ID} access to a single post with id = {POST_ID}
        return self.client.get('/posts/'+str(post_id)+'/')

    def test_get_post_by_id(self):
        response = self.get_single_post_by_id(self.post.id)
        self.assertEqual(200, response.status_code)
        self.assertEqual(str(self.post.id), response.data['id'])
        self.assertEqual(str(self.post.author.id), response.data['author']['id'])

    def test_get_posts_with_invalid_post_id(self):
        #TODO: tests behaviour for when you get posts with incorrectly
        #      formatted ID, or ID that doesn't exist
        pass

    def test_get_posts_by_page(self):
        #TODO: Returns all of posts on a specific page
        pass

    def test_get_full_page_of_posts(self):
        #TODO: test retrieving a full page of posts
        pass

    def test_get_partial_page_of_posts(self):
        #TODO: test retrieving a partial page of posts
        pass

    def test_page_does_not_exist(self):
        #TODO: Tests what is returned if requested page of posts does not exist
        pass

    def test_page_has_no_posts(self):
        #TODO: Tests getting page 1, when the user has made no posts
        #pretty sure this is redundant to above
        pass

    def test_get_posts_by_page_and_size(self):
        #TODO: retrieve a page of posts with specific size of page
        pass

    def test_get_posts_by_page_and_exceeded_size(self):
        #TODO: retrieve a page where there are more posts than the specified size
        pass

    def test_get_posts_by_page_and_partial_size(self):
        #TODO: retrive a page where the size is smaller than the one specified
        pass

    def test_update_post_with_put(self):
        # Create a post using a put method
        self.post.title = "The new title"
        put_body = self.get_post_data(self.post, self.author)
        response = self.create_update_post_with_put(self.post.id, put_body)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['title'], "The new title")

    def test_update_nonexistent_post(self):
        # Try to update a post that hasn't been created
        # Maybe it should have the same result as test_create_post_with_put?
        post_id = uuid.uuid4()
        put_body = {"title":"Sample Title"}
        response = self.create_update_post_with_post(post_id, put_body)
        self.assertEqual(response.status_code, 404)

    def test_create_invalid_post(self):
        post_id = "not-a-number"
        put_body = {"title":"Sample Title"}
        response = self.create_update_post_with_post(post_id, put_body)
        self.assertEqual(response.status_code, 400)

    def test_delete_post(self):
        post_id = self.post.id
        response = self.delete_post(post_id)
        self.assertEqual(response.status_code, 204)

    def test_delete_nonexistent_post(self):
        post_id = uuid.uuid4()
        response = self.delete_post(post_id)
        self.assertEqual(response.status_code, 404)

    def test_author_deletes_own_post(self):
        #TODO: check permissions for an author deleting their own post
        pass

    def test_admin_deletes_post(self):
        #TODO: an admin can delete any post, regardless if they authored it
        pass

    def test_create_post_with_post(self):
        # Create a post using a post method
        self.post.id = uuid.uuid4()
        request_body = self.get_post_data(self.post, self.author)
        response = self.create_post(request_body)
        self.assertEqual(response.status_code, 201)

    def test_update_post_with_post(self):
        # Update an existing post
        request_body = self.get_post_data(self.post, self.author)
        response = self.create_update_post_with_post(self.post.id, request_body)
        self.assertEqual(response.status_code, 200)

    def test_creating_public_post(self):
        #TODO: create a public post and successfully query it
        pass

    def test_create_friend_post(self):
        #TODO: create a post that can only be seen by users friends
        pass

    def test_insert_invalid_post(self):
        #TODO: insert an invalidly formatted post
        pass

    def get_post_data(self, post, author):
        return {
			"title": post.title,
			"source": post.source,
			"origin": post.origin,
			"description": post.description,
			"contentType": post.contentType,
			"content": post.content,
			"author":{
				"id": str(author.id),
				"host": author.host,
				"displayName": author.displayName,
			},
			"categories": post.categories,
			"count": str(post.count),
			"size": str(post.size),
			"next": post.next,
			"published":str(post.published),
			"id":str(post.id),
			"visibility":post.visibility
        }
<|endoftext|>