<commit_before>__author__ = 'jameskreft'

import requests
import feedparser
from bs4 import BeautifulSoup
import re
from operator import itemgetter

def call_api(baseapiurl, index_id):
    r = requests.get(baseapiurl+'/publication/'+index_id)
    pubreturn = r.json()
    pubdata = pubreturn['pub']
    return pubdata

def pubdetails(pubdata):
    """
    build the ordered list to make the 'Publications details' box

    :param pubdata: the data pulled from the pubs warehouse web service
    :return: pubdata with an additional "details" element
    """

    pubdata['details'] = []
    #details list element has len of 2 or 3.  If 2, the item is coming back as a simple Key:value object, but if three
    # there are either lists or dicts. the first item in the list is the param in pubdata, the 2nd or 3rd is the display
    # descriptor and the second if it exists is the secondary key needed to get the text.
    detailslist = [
        ['publicationType', 'text', 'Publication type:'],
        ['publicationSubtype', 'text', 'Publication Subtype:'],
        ['seriesName', 'Series name:'],
        ['seriesNumber', 'Series number:'],
        ['issn', 'online', 'ISSN (online):'],
        ['issn', 'print', 'ISSN (print):'],
        ['isbn', 'ISBN:'],
        ['doi', 'DOI:'],
        ['language', 'Language:'],
        ['publisher', 'Publisher:'],
        ['publisherLocation', 'Publisher location:'],
        ['costCenters', 'text', 'Contributing office(s):'],
        ['productDescription', 'Description:'],
        ['numberofPages', 'Number of pages:'],
        ['startPage', 'Start page:'],
        ['endPage', 'End page:'],
        ['temporalStart', 'Time Range Start:'],
        ['temporalEnd', 'Time Range End:'],
        ['conferenceTitle', 'Conference Title:'],
        ['conferenceLocation', 'Conference Location'],
        ['conferenceDate', 'Conference Date:']
    ]
    for detail in detailslist:

        if len(detail) == 3:
            #if the detail exists and is a dict with a couple key:value pairs, get the right value
            if pubdata.get(detail[0]) is not None and isinstance(pubdata.get(detail[0]), dict):
                pubdata['details'].append({detail[2]: pubdata[detail[0]].get(detail[1])})
            #if the thing is a list of dicts and if there is something in the list, concatenate the values into a string
            elif pubdata.get(detail[0]) is not None and isinstance(pubdata.get(detail[0]), list) \
                    and len(pubdata.get(detail[0])) > 0:
                dd = []
                for det in pubdata.get(detail[0]):
                    dd.append(det.get(detail[1]))
                dd = ', '.join(dd)
                pubdata['details'].append({detail[2]: dd})
        elif len(detail) == 2 and pubdata.get(detail[0]) is not None and len(pubdata.get(detail[0])) > 0:
            pubdata['details'].append({detail[1]: pubdata.get(detail[0])})
    return pubdata

def create_display_links(pubdata):
    """
    restructures links from the API so that they are easy to display in a jinja template
    :param pubdata:
    :return: pubdata with new displayLinks array
    """
    display_links = {
        'Thumbnail': [],
        'Index Page': [],
        'Document': [],
        'Plate': []
    }
    links = pubdata.get("links")
    for linktype in display_links:
        rankcounter = 1
        for link in links:
            if link['type']['text'] == linktype:
                if link.get('rank') is None:
                    link['rank'] = rankcounter
                    rankcounter += 1
                display_links[linktype].append(link)
    display_links = manipulate_plate_links(display_links)
    pubdata["displayLinks"] = display_links
    return pubdata

def manipulate_plate_links(display_links):
    """
    This function rejiggers plate link displays for plate links that are named regularly but do not have display text or
    a proper order
    :param display_links:
    :return: display links with rejiggered plate link order
    """
    if len(display_links.get("Plate")) > 0:
        for link in display_links["Plate"]:
            url = link["url"]
            file_name = url.split("/")[-1].split(".")
            text = file_name[0]
            if link.get("text") is None:
                if len(file_name[0].title().split('-')) > 1:
                    try:
                        text = file_name[0].title().split('-')
                        text[1] = int(text[1])
                    except (ValueError, IndexError):
                        text = file_name[0].title().split('-')
                if len(file_name[0].split("_")) > 1:
                    try:
                        text = file_name[0].split("_")[-1]
                        text = re.split('(\d+)', text)[0:2]
                        text[1] = int(text[1])
                    except (ValueError, IndexError):
                        try:
                            text = file_name[0].split("_")[0]
                            text = re.split('(\d+)', text)[0:2]
                            text[1] = int(text[1])
                        except (ValueError, IndexError):
                            text = file_name[0].split("_")

                link["text"] = text
            if link.get('linkFileType') is None:
                link['linkFileType'] = {'text': file_name[1]}
        display_links["Plate"] = sorted(display_links["Plate"], key=itemgetter('text'))
        rankcounter = 1
        for link in display_links["Plate"]:
            link['rank'] = rankcounter
            rankcounter += 1
            link['text'][1] = str(link['text'][1])
            link['text'] = " ".join(link['text']).title()
    return display_links

def pull_feed(feed_url):
    """
    pull page data from a my.usgs.gov confluence wiki feed
    :param feed_url: the url of the feed, created in confluence feed builder
    :return: the html of the page itself, stripped of header and footer
    """
    feed = feedparser.parse(feed_url)

    # Process html to remove unwanted mark-up and fix links
    post = ''
    if len(feed['entries']) > 0:
        soup = BeautifulSoup(feed['entries'][0].summary)

        # Remove edited by paragraph
        soup.p.extract()

        # Remove final div in the feed
        feed_div = soup.find('div', class_='feed')
        children_divs = feed_div.findAll('div')
        children_divs[len(children_divs) - 1].extract()

        # Translate any in page links to use relative URL
        base = feed['entries'][0].summary_detail.base
        links = feed_div.select('a[href^="' + base + '"]')
        for link in links:
            link['href'] = link['href'].replace(base, '')
        post = unicode(soup)

    return post

def supersedes(supersedes_url, index_id):
    """
    pull supersede info for a pub from legacy "extras" endpoint
    :param supersedes_url:url for extras endpoint
    :param index_id: index_id of pub
    :return: dict of relevant supersede info
    """

    supersede_array = requests.get(supersedes_url,
                                   params={'prod_id': index_id}).json()['modsCollection']['mods'][0]['relatedItem'][0]
    #TODO: deal with pubs with more than one relationship
    return {'type': supersede_array['@type'], 'index_id': supersede_array['identifier']['#text'],
            'title': supersede_array['titleInfo']['title']}

def getbrowsecontent(browseurl):
    """
    Gets
    :param browseurl: url of legacy browse interface
    :return: html content of links, breadcrumb, and title
    """
    content = requests.get(browseurl).text
    soup = BeautifulSoup(content)
    browse_content = {'links':soup.find('div', {"id": "pubs-browse-links"}).contents}
    browse_content['breadcrumbs'] = soup.find('div', {"id": "pubs-browse-breadcrumbs"}).contents
    browse_content['header'] = soup.find('div', {"id": "pubs-browse-header"}).contents

    return browse_content

class SearchPublications(object):

    """
    Methods for executing various types
    of searches against the backend
    Pubs API.

    :param str search_url: URL without any search parameters appended
    """

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

    def get_pubs_search_results(self, params):
        """
        Searches Pubs API for a specified query parameter

        :param str search_url: URL without any search parameters appended
        :param dict params: dictionary of form {'key1': 'value1', 'key2': 'value2}
        :return: query results (or None) and response status code.
        :rtype: tuple
        """
        search_result_obj = requests.get(url=self.search_url, params=params)
        try:
            search_result_json = search_result_obj.json()
            for record in search_result_json['records']:
                if record.get("authors") is not None:
                    record["authorList"] = make_contributor_list(record["authors"])
        except ValueError:
            search_result_json = None
        resp_status_code = search_result_obj.status_code
        return search_result_json, resp_status_code

def make_contributor_list(contributors):
    """
    Makes a list of names for contributors

    :param list contributors: a list of dicts of a contributor type (authors, editors, etc)
    :return list of concatenated author names
    :rtype: list
    """
    sorted_contributors = sorted(contributors, key=itemgetter('rank'))
    contributor_list = []
    for contributor in sorted_contributors:
        if contributor['corporation'] == False:
            contributor_name_list = []
            if contributor.get("given") is not None:
                contributor_name_list.append(contributor['given'])
            if contributor.get("family") is not None:
                contributor_name_list.append(contributor['family'])
            if contributor.get("suffix") is not None:
                contributor_name_list.append(contributor['suffix'])
            contributor_text = " ".join(contributor_name_list)
        else:
            contributor_text = contributor['organization']
        contributor_list.append(contributor_text)
    return contributor_list

def summation(a, b):
    """
    Silly little function to exam test running with
    the nose library.
    """
    sum_value = a + b
    return sum_value
<commit_msg>removed silly summation function<commit_after>__author__ = 'jameskreft'

import requests
import feedparser
from bs4 import BeautifulSoup
import re
from operator import itemgetter

def call_api(baseapiurl, index_id):
    r = requests.get(baseapiurl+'/publication/'+index_id)
    pubreturn = r.json()
    pubdata = pubreturn['pub']
    return pubdata

def pubdetails(pubdata):
    """
    build the ordered list to make the 'Publications details' box

    :param pubdata: the data pulled from the pubs warehouse web service
    :return: pubdata with an additional "details" element
    """

    pubdata['details'] = []
    #details list element has len of 2 or 3.  If 2, the item is coming back as a simple Key:value object, but if three
    # there are either lists or dicts. the first item in the list is the param in pubdata, the 2nd or 3rd is the display
    # descriptor and the second if it exists is the secondary key needed to get the text.
    detailslist = [
        ['publicationType', 'text', 'Publication type:'],
        ['publicationSubtype', 'text', 'Publication Subtype:'],
        ['seriesName', 'Series name:'],
        ['seriesNumber', 'Series number:'],
        ['issn', 'online', 'ISSN (online):'],
        ['issn', 'print', 'ISSN (print):'],
        ['isbn', 'ISBN:'],
        ['doi', 'DOI:'],
        ['language', 'Language:'],
        ['publisher', 'Publisher:'],
        ['publisherLocation', 'Publisher location:'],
        ['costCenters', 'text', 'Contributing office(s):'],
        ['productDescription', 'Description:'],
        ['numberofPages', 'Number of pages:'],
        ['startPage', 'Start page:'],
        ['endPage', 'End page:'],
        ['temporalStart', 'Time Range Start:'],
        ['temporalEnd', 'Time Range End:'],
        ['conferenceTitle', 'Conference Title:'],
        ['conferenceLocation', 'Conference Location'],
        ['conferenceDate', 'Conference Date:']
    ]
    for detail in detailslist:

        if len(detail) == 3:
            #if the detail exists and is a dict with a couple key:value pairs, get the right value
            if pubdata.get(detail[0]) is not None and isinstance(pubdata.get(detail[0]), dict):
                pubdata['details'].append({detail[2]: pubdata[detail[0]].get(detail[1])})
            #if the thing is a list of dicts and if there is something in the list, concatenate the values into a string
            elif pubdata.get(detail[0]) is not None and isinstance(pubdata.get(detail[0]), list) \
                    and len(pubdata.get(detail[0])) > 0:
                dd = []
                for det in pubdata.get(detail[0]):
                    dd.append(det.get(detail[1]))
                dd = ', '.join(dd)
                pubdata['details'].append({detail[2]: dd})
        elif len(detail) == 2 and pubdata.get(detail[0]) is not None and len(pubdata.get(detail[0])) > 0:
            pubdata['details'].append({detail[1]: pubdata.get(detail[0])})
    return pubdata

def create_display_links(pubdata):
    """
    restructures links from the API so that they are easy to display in a jinja template
    :param pubdata:
    :return: pubdata with new displayLinks array
    """
    display_links = {
        'Thumbnail': [],
        'Index Page': [],
        'Document': [],
        'Plate': []
    }
    links = pubdata.get("links")
    for linktype in display_links:
        rankcounter = 1
        for link in links:
            if link['type']['text'] == linktype:
                if link.get('rank') is None:
                    link['rank'] = rankcounter
                    rankcounter += 1
                display_links[linktype].append(link)
    display_links = manipulate_plate_links(display_links)
    pubdata["displayLinks"] = display_links
    return pubdata

def manipulate_plate_links(display_links):
    """
    This function rejiggers plate link displays for plate links that are named regularly but do not have display text or
    a proper order
    :param display_links:
    :return: display links with rejiggered plate link order
    """
    if len(display_links.get("Plate")) > 0:
        for link in display_links["Plate"]:
            url = link["url"]
            file_name = url.split("/")[-1].split(".")
            text = file_name[0]
            if link.get("text") is None:
                if len(file_name[0].title().split('-')) > 1:
                    try:
                        text = file_name[0].title().split('-')
                        text[1] = int(text[1])
                    except (ValueError, IndexError):
                        text = file_name[0].title().split('-')
                if len(file_name[0].split("_")) > 1:
                    try:
                        text = file_name[0].split("_")[-1]
                        text = re.split('(\d+)', text)[0:2]
                        text[1] = int(text[1])
                    except (ValueError, IndexError):
                        try:
                            text = file_name[0].split("_")[0]
                            text = re.split('(\d+)', text)[0:2]
                            text[1] = int(text[1])
                        except (ValueError, IndexError):
                            text = file_name[0].split("_")

                link["text"] = text
            if link.get('linkFileType') is None:
                link['linkFileType'] = {'text': file_name[1]}
        display_links["Plate"] = sorted(display_links["Plate"], key=itemgetter('text'))
        rankcounter = 1
        for link in display_links["Plate"]:
            link['rank'] = rankcounter
            rankcounter += 1
            link['text'][1] = str(link['text'][1])
            link['text'] = " ".join(link['text']).title()
    return display_links

def pull_feed(feed_url):
    """
    pull page data from a my.usgs.gov confluence wiki feed
    :param feed_url: the url of the feed, created in confluence feed builder
    :return: the html of the page itself, stripped of header and footer
    """
    feed = feedparser.parse(feed_url)

    # Process html to remove unwanted mark-up and fix links
    post = ''
    if len(feed['entries']) > 0:
        soup = BeautifulSoup(feed['entries'][0].summary)

        # Remove edited by paragraph
        soup.p.extract()

        # Remove final div in the feed
        feed_div = soup.find('div', class_='feed')
        children_divs = feed_div.findAll('div')
        children_divs[len(children_divs) - 1].extract()

        # Translate any in page links to use relative URL
        base = feed['entries'][0].summary_detail.base
        links = feed_div.select('a[href^="' + base + '"]')
        for link in links:
            link['href'] = link['href'].replace(base, '')
        post = unicode(soup)

    return post

def supersedes(supersedes_url, index_id):
    """
    pull supersede info for a pub from legacy "extras" endpoint
    :param supersedes_url:url for extras endpoint
    :param index_id: index_id of pub
    :return: dict of relevant supersede info
    """

    supersede_array = requests.get(supersedes_url,
                                   params={'prod_id': index_id}).json()['modsCollection']['mods'][0]['relatedItem'][0]
    #TODO: deal with pubs with more than one relationship
    return {'type': supersede_array['@type'], 'index_id': supersede_array['identifier']['#text'],
            'title': supersede_array['titleInfo']['title']}

def getbrowsecontent(browseurl):
    """
    Gets
    :param browseurl: url of legacy browse interface
    :return: html content of links, breadcrumb, and title
    """
    content = requests.get(browseurl).text
    soup = BeautifulSoup(content)
    browse_content = {'links':soup.find('div', {"id": "pubs-browse-links"}).contents}
    browse_content['breadcrumbs'] = soup.find('div', {"id": "pubs-browse-breadcrumbs"}).contents
    browse_content['header'] = soup.find('div', {"id": "pubs-browse-header"}).contents

    return browse_content

class SearchPublications(object):

    """
    Methods for executing various types
    of searches against the backend
    Pubs API.

    :param str search_url: URL without any search parameters appended
    """

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

    def get_pubs_search_results(self, params):
        """
        Searches Pubs API for a specified query parameter

        :param str search_url: URL without any search parameters appended
        :param dict params: dictionary of form {'key1': 'value1', 'key2': 'value2}
        :return: query results (or None) and response status code.
        :rtype: tuple
        """
        search_result_obj = requests.get(url=self.search_url, params=params)
        try:
            search_result_json = search_result_obj.json()
            for record in search_result_json['records']:
                if record.get("authors") is not None:
                    record["authorList"] = make_contributor_list(record["authors"])
        except ValueError:
            search_result_json = None
        resp_status_code = search_result_obj.status_code
        return search_result_json, resp_status_code

def make_contributor_list(contributors):
    """
    Makes a list of names for contributors

    :param list contributors: a list of dicts of a contributor type (authors, editors, etc)
    :return list of concatenated author names
    :rtype: list
    """
    sorted_contributors = sorted(contributors, key=itemgetter('rank'))
    contributor_list = []
    for contributor in sorted_contributors:
        if contributor['corporation'] == False:
            contributor_name_list = []
            if contributor.get("given") is not None:
                contributor_name_list.append(contributor['given'])
            if contributor.get("family") is not None:
                contributor_name_list.append(contributor['family'])
            if contributor.get("suffix") is not None:
                contributor_name_list.append(contributor['suffix'])
            contributor_text = " ".join(contributor_name_list)
        else:
            contributor_text = contributor['organization']
        contributor_list.append(contributor_text)
    return contributor_list
<|endoftext|>


<commit_before>import cgi
import webapp2
from google.appengine.ext import ndb
from data import Candidate

STYLE = """\

         <link rel="stylesheet" href="/stylesheets/style.css">

    """

class ViewCandidates(webapp2.RequestHandler):

    def get(self):
        candidates = Candidate.query().fetch(20)

        self.response.out.write('' + STYLE + '

Candidates

Name	Email	Major	University
')
        i = 0;
        for candidate in candidates:
            i=i+1
            self.response.out.write('
%s	%s	%s	%s
' %
                                    (candidate.name,
                                    candidate.email,
                                     candidate.major,
                                     candidate.university))
        self.response.out.write('
Main Menu
')
<commit_msg>fixed button<commit_after>import cgi
import webapp2
from google.appengine.ext import ndb
from data import Candidate

STYLE = """\

         <link rel="stylesheet" href="/stylesheets/style.css">

    """

class ViewCandidates(webapp2.RequestHandler):

    def get(self):
        candidates = Candidate.query().fetch(20)

        self.response.out.write('' + STYLE + '

Candidates

Name	Email	Major	University
')
        i = 0;
        for candidate in candidates:
            i=i+1
            self.response.out.write('
%s	%s	%s	%s
' %
                                    (candidate.name,
                                    candidate.email,
                                     candidate.major,
                                     candidate.university))
        self.response.out.write('
<form action="/main" method="post"><input type="submit" value="Main Menu"></form>
')

#Main Menu
<|endoftext|>


<commit_before>import warnings
from Cookie import SimpleCookie

from data_structures import Header
from utils import generate_content_type
from cookies import generate_cookie

class BaseResponse(object):

    # the charset of response
    charset = 'utf-8'

    # the default mimetype if there is not given
    default_mimetype = 'text/html'

    # the default status code if there is not given
    default_status = 200

    def __init__(self, status=default_status, response_body=None, header=None,
                 mimetype=None, content_type=None):
        if isinstance(header, Header):
            self.header = header
        elif not header:
            self.header = Header()
        else:
            self.header = Header(header)

        # Set cookies.
        # cookies have two copies:
        # one is in response object(Type=SimpleCookie): self.cookies = [SimpleCookie_1, SimpleCookie_2]
        # another is in response header object(Type=str): header['Cookies']=['cookie_1', 'cookie_2']
        self._cookies = None
        # self.header['Cookies'] = []

        # Get the Content-Type
        if content_type is None:
            if mimetype is None and 'Content-Type' not in self.header:
                mimetype = self.default_mimetype
            if mimetype is not None:
                mimetype = generate_content_type(mimetype, self.charset)
            content_type = mimetype
        if content_type is not None:
            self.header['Content-Type'] = content_type

        # Get the status code OR status
        if isinstance(status, basestring):
            # Ex: status = '200 OK'
            self.status = status
        else:
            # Ex: status = 200
            try:
                self.status_code = int(status)
            except TypeError:
                self.status_code = self.default_status
                warnings.warn(
                    'Status code is initialized to 200, Because the '
                    'status code should be int.',
                    UserWarning
                )

        # Get the response body
        if response_body is None:
            self.response = {}
        else:
            self.response = response_body

    @property
    def cookies(self):
        if not self._cookies:
            self._create_cookie()
        return self._cookies

    def _create_cookie(self):
        _dict = self.__dict__
        _dict['_cookies'] = SimpleCookie()

    def set_cookies(self, key, value='', expires=None, path=None, domain=None,
                    max_age=None, secure=None, httponly=False):
        """Set a cookie. The params are the same as the Morsel in Cookie which is a
        python standard library.

        :param key: the name of the cookie to be set
        :param value: the value of the cookie
        :param expires: should be a `datetime` object or UNIX timestamp.
        :param path: limit the cookie to the given path.
        :param domain: set a cross-domain cookie. Example:
                       domain=".example.com", it will set the cookie that is
                       readable by the domain "www.example.com"
        :param max_age: should be a number of seconds or None.
        :param secure: The cookie will only be available via HTTPS
        """
        morsel = generate_cookie(
            key, value, expires=expires, path=path, domain=domain,
            max_age=max_age, secure=secure, httponly=httponly
        )
        self.cookies[key] = morsel
        # self._create_cookies(morsel)
        # self.header['Cookies'].append(morsel)   # Update cookies to header.

    def delete_cookies(self, key, path, domain):
        """Delete the cookie from response.

        :param key: the name of the cookie to be delete
        :param path: limit the cookie to the given path. If the cookie was
                     limited to a path, user can use this param.
        :param domain: limit the cookie to the given domain. If the cookie was
                     limited to the domain, user can use this param.
        """
        self.set_cookies(key, path=path, domain=domain, max_age=0)

    # def _create_cookies(self, mosel):
    #     """Create a cookies attribute for response."""
    #     return self.cookies.append(SimpleCookie(mosel.output()))

class Response(BaseResponse):
    pass
<commit_msg>Add response functions.<commit_after># -*- coding: utf-8 -*-
import warnings
from Cookie import SimpleCookie

from .data_structures import Header
from .utils import generate_content_type
from .cookies import generate_cookie
from .constants import HTTP_CODES

class BaseResponse(object):

    # the charset of response
    charset = 'utf-8'

    # the default mimetype if there is not given
    default_mimetype = 'text/html'

    # the default status code if there is not given
    default_status = 200

    def __init__(self, status=default_status, response_body=None, header=None,
                 mimetype=None, content_type=None):
        if isinstance(header, Header):
            self.header = header
        elif not header:
            self.header = Header()
        else:
            self.header = Header(header)

        # Set cookies.
        # cookies have two copies:
        # one is in response object(Type=SimpleCookie): self.cookies = [SimpleCookie_1, SimpleCookie_2]
        # another is in response header object(Type=str): header['Cookies']=['cookie_1', 'cookie_2']
        self._cookies = None
        # self.header['Cookies'] = []

        # Get the Content-Type
        if content_type is None:
            if mimetype is None and 'Content-Type' not in self.header:
                mimetype = self.default_mimetype
            if mimetype is not None:
                mimetype = generate_content_type(mimetype, self.charset)
            content_type = mimetype
        if content_type is not None:
            self.header['Content-Type'] = content_type

        # Get the status code OR status
        if isinstance(status, basestring):
            # Ex: status = '200 OK'
            self.status = status
        else:
            # Ex: status = 200
            try:
                # self.status_code = int(status)
                self.status = '%d %s' % (int(status), HTTP_CODES[int(status)])
            except TypeError:
                # self.status_code = self.default_status
                self.status = '%d %s' % (
                    self.default_status, HTTP_CODES[self.default_status])
                warnings.warn(
                    'Status code is initialized to 200, Because the '
                    'status code should be int.',
                    UserWarning
                )

        # Get the response body
        if response_body is None:
            self.response = []
        else:
            self.response = response_body

    @property
    def is_sequence(self):
        return isinstance(self.response, (list, tuple))

    @property
    def cookies(self):
        if not self._cookies:
            self._create_cookie()
        return self._cookies

    def _create_cookie(self):
        _dict = self.__dict__
        _dict['_cookies'] = SimpleCookie()

    def set_cookies(self, key, value='', expires=None, path=None, domain=None,
                    max_age=None, secure=None, httponly=False):
        """Set a cookie. The params are the same as the Morsel in Cookie which is a
        python standard library.

        :param key: the name of the cookie to be set
        :param value: the value of the cookie
        :param expires: should be a `datetime` object or UNIX timestamp.
        :param path: limit the cookie to the given path.
        :param domain: set a cross-domain cookie. Example:
                       domain=".example.com", it will set the cookie that is
                       readable by the domain "www.example.com"
        :param max_age: should be a number of seconds or None.
        :param secure: The cookie will only be available via HTTPS
        """
        morsel = generate_cookie(
            key, value, expires=expires, path=path, domain=domain,
            max_age=max_age, secure=secure, httponly=httponly
        )
        self.cookies[key] = morsel
        # self._create_cookies(morsel)
        # self.header['Cookies'].append(morsel)   # Update cookies to header.

    def delete_cookies(self, key, path, domain):
        """Delete the cookie from response.

        :param key: the name of the cookie to be delete
        :param path: limit the cookie to the given path. If the cookie was
                     limited to a path, user can use this param.
        :param domain: limit the cookie to the given domain. If the cookie was
                     limited to the domain, user can use this param.
        """
        self.set_cookies(key, path=path, domain=domain, max_age=0)

    # def _create_cookies(self, mosel):
    #     """Create a cookies attribute for response."""
    #     return self.cookies.append(SimpleCookie(mosel.output()))

    def get_header_list(self):
        """Turn the response header into a list. The header is a instance of Header,
        use this func to get attribute which name is ' _list '.
        """
        for item in self.cookies:
            self.header.add('Set-Cookie', item.OutputString())
        if self.is_sequence and 'Content-Length' not in self.header:
            try:
                content_length = sum((len(str(item))) for item in self.response)
            except UnicodeError:
                pass
            else:
                self.header.add('Content-Length', str(content_length))
        return self.header.head_to_list(charset=self.charset)

    def iterable_item(self, environ):
        """ Generate a iterable object, and use this object to return.

        :param environ: the WSGI environment
        :return: a iterable response
        """
        status_code = int(self.status[:3])
        if environ['REQUEST_METHOD'] == 'HEAD' or \
                    100 <= status_code < 200 or status_code in (204, 304):
            yield ()

        for item in self.response:
            if isinstance(item, unicode):
                yield item.encode(self.charset)
            else:
                yield str(item)

    def wsgi_response(self, environ):
        """Return the WSGI response as a tuple.

        :param environ: the WSGI environment
        :return: (obj_iter, status, header_list)
                 the first one is a iterable object, the second one is a string showing
                 the response status, like '200 OK', the last one is the list of the
                 response header.
        """
        obj_iter = self.iterable_item(environ)
        return obj_iter, self.status, self.get_header_list()

    def __call__(self, environ, start_response):
        """Make instance of Response class as a WSGI application.

        :param environ: the WSGI environment
        :param start_response: the response callable provided by the WSGI
                               server.
        :return: an iterable object.
        """
        obj_iter, status, headers = self.wsgi_response(environ)
        start_response(status, headers)

        return obj_iter

class Response(BaseResponse):
    pass
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import logging
import smtplib
import sys
import eventlet
eventlet.monkey_patch()

from email.mime.text import MIMEText
from email.header import Header

session = None
connect_params = {}

def connect(server='smtp.gmail.com', port=587, user='', passwd='', tls=False):
    params = locals()
    global session, connect_params
    session = smtplib.SMTP(server, port)
    session.ehlo()
    if tls:
        session.starttls()
    try:
        result = session.login(user, passwd)
        # check smtp login status (235, 270 = accepted)
        if result[0] != 235 and result[0] != 270 and 'Accept' not in result[1]:
            raise Exception('SMTP verification failed')
        # check smtp connection status
        status = session.noop()[0]
        if status != 250:
            raise Exception('SMTP connection closed')
    except:
        logging.error('SMTP login failed')
        sys.exit(1)
    else:
        connect_params = params
        logging.info('SMTP login success')
        return True

def test_session():
    global session
    try:
        status = session.noop()[0]
    except:  # smtplib.SMTPServerDisconnected
        status = -1
    return True if status == 250 else False

def send(target, title, content, adapter_name):
    global session, connect_params
    if not test_session():
        connect(**connect_params)
    corpo = MIMEText(content.encode('utf-8'), 'html', 'utf-8')
    corpo['From'] = target
    corpo['To'] = target
    corpo['Subject'] = Header(title, 'utf-8')
    session.sendmail(target, [target], corpo.as_string())
    logging.info('Sent an email by %s', adapter_name)
    # delay
    sleep(1)
<commit_msg>Improve log message to better describe<commit_after># -*- coding: utf-8 -*-
import logging
import smtplib
import sys
import eventlet
eventlet.monkey_patch()

from email.mime.text import MIMEText
from email.header import Header

session = None
connect_params = {}

def connect(server='smtp.gmail.com', port=587, user='', passwd='', tls=False):
    params = locals()
    global session, connect_params
    session = smtplib.SMTP(server, port)
    session.ehlo()
    if tls:
        session.starttls()
    try:
        result = session.login(user, passwd)
        # check smtp login status (235, 270 = accepted)
        if result[0] != 235 and result[0] != 270 and 'Accept' not in result[1]:
            raise Exception(
                'The connection to the SMTP server was unsuccessful.')
        # check smtp connection status
        status = session.noop()[0]
        if status != 250:
            raise Exception('The connection to the SMTP server was lost.')
    except:
        logging.error('Unable to logon to the SMTP server.')
        sys.exit(1)
    else:
        connect_params = params
        logging.info('Successfully connected to the SMTP server.')
        return True

def test_session():
    global session
    try:
        status = session.noop()[0]
    except:  # smtplib.SMTPServerDisconnected
        status = -1
    return True if status == 250 else False

def send(target, title, content, adapter_name):
    global session, connect_params
    if not test_session():
        connect(**connect_params)
    corpo = MIMEText(content.encode('utf-8'), 'html', 'utf-8')
    corpo['From'] = target
    corpo['To'] = target
    corpo['Subject'] = Header(title, 'utf-8')
    session.sendmail(target, [target], corpo.as_string())
    logging.info('The mail has been sent successfully originated from "%s".',
        adapter_name)
    # delay
    eventlet.sleep(0.5)
<|endoftext|>


<commit_before># Copyright (c) 2014 Olli Wang. All right reserved.
#
#  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.

import argparse
import os
import sys

from svg2nvg.parser import SVGParser

parser = argparse.ArgumentParser(
    description='Convert SVG files to NVG source code')
parser.add_argument('svg_path', help='path to a SVG file')
parser.add_argument('-c', '--context', default='context',
                    help='the variable name of nanovg context')
parser.add_argument('-d', '--dest', help='the directory for generated file')
parser.add_argument('--header_file', action='store_true',
                    help='generate header file')
parser.add_argument('-i', '--include_path', default='',
                    help='the include path for generated headers')
parser.add_argument('-vg', '--nanovg_include_path', default='',
                    help='the include path for nanovg')
parser.add_argument('--source_file', action='store_true',
                    help='generate source file')
parser.add_argument('-ns', '--namespace', action='store_true',
                    help='add C++ namespace to header file')

def execute_from_command_line():
    if len(sys.argv) == 1:
        parser.print_help()
        return

    args = parser.parse_args()
    svg_parser = SVGParser(args.context)
    svg_parser.parse_file(args.svg_path)

    basename = os.path.splitext(os.path.basename(args.svg_path))[0]
    dest_path = os.path.join(os.path.abspath(args.dest), basename)
    filename = '%s.h' % basename

    if args.source_file:
        result = svg_parser.get_header_file_content(basename,
                                                    args.namespace,
                                                    prototype_only=True)
        if args.dest is not None:
            header_file = file('%s.h' % dest_path, 'w')
            header_file.write(result)
            header_file.close()

        result = svg_parser.get_source_file_content(basename, args.namespace,
                                                    args.include_path,
                                                    args.nanovg_include_path)
        if args.dest is not None:
            source_file = file('%s.cc' % dest_path, 'w')
            source_file.write(result)
            source_file.close()
    elif args.header_file:
        result = svg_parser.get_header_file_content(args.svg_path,
                                                    args.namespace)
        if args.dest is not None:
            header_file = file('%s.h' % dest_path, 'w')
            header_file.write(result)
            header_file.close()
    else:
        result = svg_parser.get_content()
        print(result)
<commit_msg>Generates files to the current directory by default.<commit_after># Copyright (c) 2014 Olli Wang. All right reserved.
#
#  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.

import argparse
import os
import sys

from svg2nvg.parser import SVGParser

parser = argparse.ArgumentParser(
    description='Convert SVG files to NVG source code')
parser.add_argument('svg_path', help='path to a SVG file')
parser.add_argument('-c', '--context', default='context',
                    help='the variable name of nanovg context')
parser.add_argument('-d', '--dest', default=os.curdir,
                    help='the directory to keep generated files')
parser.add_argument('--header_file', action='store_true',
                    help='generate header file')
parser.add_argument('-i', '--include_path', default='',
                    help='the include path for generated headers')
parser.add_argument('-vg', '--nanovg_include_path', default='',
                    help='the include path for nanovg')
parser.add_argument('--source_file', action='store_true',
                    help='generate source file')
parser.add_argument('-ns', '--namespace', action='store_true',
                    help='add C++ namespace to header file')

def execute_from_command_line():
    if len(sys.argv) == 1:
        parser.print_help()
        return

    args = parser.parse_args()
    svg_parser = SVGParser(args.context)
    svg_parser.parse_file(args.svg_path)

    basename = os.path.splitext(os.path.basename(args.svg_path))[0]
    dest_path = os.path.join(os.path.abspath(args.dest), basename)
    filename = '%s.h' % basename

    if args.source_file:
        result = svg_parser.get_header_file_content(basename,
                                                    args.namespace,
                                                    prototype_only=True)
        if args.dest is not None:
            header_file = file('%s.h' % dest_path, 'w')
            header_file.write(result)
            header_file.close()

        result = svg_parser.get_source_file_content(basename, args.namespace,
                                                    args.include_path,
                                                    args.nanovg_include_path)
        if args.dest is not None:
            source_file = file('%s.cc' % dest_path, 'w')
            source_file.write(result)
            source_file.close()
    elif args.header_file:
        result = svg_parser.get_header_file_content(args.svg_path,
                                                    args.namespace)
        if args.dest is not None:
            header_file = file('%s.h' % dest_path, 'w')
            header_file.write(result)
            header_file.close()
    else:
        result = svg_parser.get_content()
        print(result)
<|endoftext|>


<commit_before>import env
import os
import sys
import unittest
import shutil
from robot import run as robot_run

def acceptance_test(options):
    if not options:
        _acceptance_all()
    else:
        if '-s' in options or '--suite' in options:
            _acceptance_include(options[1:])
        else:
            print 'Only "-s" or "--suite" supported'
            _exit(255)

def _acceptance_all():
    robot_run(env.ACCEPTANCE_TEST_DIR,
              outputdir=env.RESULTS_DIR,
              loglevel='trace')

def _acceptance_include(options):
    robot_run(env.ACCEPTANCE_TEST_DIR,
              outputdir=env.RESULTS_DIR,
              suite=options,
              loglevel='trace')

def clean_results():
    print 'Clean: {0}'.format(env.RESULTS_DIR)
    if os.path.exists(env.RESULTS_DIR):
        shutil.rmtree(env.RESULTS_DIR)
    else:
        os.mkdir(env.RESULTS_DIR)

def unit_test():
    print 'Running unit test'
    sys.path.insert(0, env.SRC_DIR)
    suite = unittest.TestLoader().discover(
        start_dir=env.UNIT_TEST_DIR,
        pattern='test*.py')
    return unittest.TextTestRunner(verbosity=2).run(suite)

def _help():
    print 'Usage: python run_test.py [-s suite_name]'
    return 255

def _exit(rc):
    sys.exit(rc)

if __name__ == '__main__':
    if '--help' in sys.argv or '-h' in sys.argv:
        _exit(_help())
    clean_results()
    result = unit_test()
    if result.errors == 0:
        print 'Unit test passed'
    else:
        print 'Unit test failed'
    acceptance_test(sys.argv[1:])
<commit_msg>Added example in comments how to run single unit test<commit_after>import env
import os
import sys
import unittest
import shutil
from robot import run as robot_run

def acceptance_test(options):
    if not options:
        _acceptance_all()
    else:
        if '-s' in options or '--suite' in options:
            _acceptance_include(options[1:])
        else:
            print 'Only "-s" or "--suite" supported'
            _exit(255)

def _acceptance_all():
    robot_run(env.ACCEPTANCE_TEST_DIR,
              outputdir=env.RESULTS_DIR,
              loglevel='trace')

def _acceptance_include(options):
    robot_run(env.ACCEPTANCE_TEST_DIR,
              outputdir=env.RESULTS_DIR,
              suite=options,
              loglevel='trace')

def clean_results():
    print 'Clean: {0}'.format(env.RESULTS_DIR)
    if os.path.exists(env.RESULTS_DIR):
        shutil.rmtree(env.RESULTS_DIR)
    else:
        os.mkdir(env.RESULTS_DIR)

def unit_test():
    print 'Running unit test'
    sys.path.insert(0, env.SRC_DIR)
    sys.path.append(env.UNIT_TEST_DIR)
    # suite = unittest.TestLoader().loadTestsFromName(
    #      'scanner.test_scanner.TestScanner.test_db_created')
    suite = unittest.TestLoader().discover(
         start_dir=env.UNIT_TEST_DIR,
         pattern='test*.py')
    return unittest.TextTestRunner(verbosity=2).run(suite)

def _help():
    print 'Usage: python run_test.py [-s suite_name]'
    return 255

def _exit(rc):
    sys.exit(rc)

if __name__ == '__main__':
    if '--help' in sys.argv or '-h' in sys.argv:
        _exit(_help())
    clean_results()
    result = unit_test()
    if result.errors == 0:
        print 'Unit test passed'
    else:
        print 'Unit test failed'
    acceptance_test(sys.argv[1:])
<|endoftext|>


<commit_before>from django.contrib import admin, auth
from viewflow.models import Process, Task

class TaskInline(admin.TabularInline):
    model = Task
    fields = ['flow_task', 'flow_task_type', 'status',
              'owner', 'owner_permission', 'token',
              'started', 'finished']
    readonly_fields = ['flow_task', 'flow_task_type', 'status',
                       'token', 'started', 'finished']

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

@admin.register(Process)
class ProcessAdmin(admin.ModelAdmin):
    """
    List all of viewflow process
    """
    actions = None
    date_hierarchy = 'created'
    list_display = ['pk', 'created', 'flow_cls', 'get_status_display', 'participants']
    list_display_links = ['pk', 'created', 'flow_cls']
    list_filter = ['status']
    readonly_fields = ['flow_cls', 'status', 'finished']
    inlines = [TaskInline]

    def participants(self, obj):
        user_ids = obj.task_set.exclude(owner__isnull=True).values('owner')
        users = auth.get_user_model()._default_manager.filter(pk__in=user_ids).values_list('username')
        return ', '.join(user[0] for user in users)

@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
    """
    List all of viewflow tasks
    """
    actions = None
    date_hierarchy = 'created'
    list_display = ['pk', 'created', 'process', 'get_status_display',
                    'owner', 'owner_permission', 'token',
                    'started', 'finished']
    list_display_links = ['pk', 'created', 'process']
    list_filter = ['status']
    readonly_fields = ['process', 'flow_task', 'started', 'finished', 'previous', 'token']
<commit_msg>custom user models can override USERNAME_FIELD<commit_after>from django.contrib import admin, auth
from viewflow.models import Process, Task

class TaskInline(admin.TabularInline):
    model = Task
    fields = ['flow_task', 'flow_task_type', 'status',
              'owner', 'owner_permission', 'token',
              'started', 'finished']
    readonly_fields = ['flow_task', 'flow_task_type', 'status',
                       'token', 'started', 'finished']

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

@admin.register(Process)
class ProcessAdmin(admin.ModelAdmin):
    """
    List all of viewflow process
    """
    actions = None
    date_hierarchy = 'created'
    list_display = ['pk', 'created', 'flow_cls', 'get_status_display', 'participants']
    list_display_links = ['pk', 'created', 'flow_cls']
    list_filter = ['status']
    readonly_fields = ['flow_cls', 'status', 'finished']
    inlines = [TaskInline]

    def participants(self, obj):
        user_ids = obj.task_set.exclude(owner__isnull=True).values('owner')
        USER_MODEL = auth.get_user_model()
        username_field = USER_MODEL.USERNAME_FIELD
        users = USER_MODEL._default_manager.filter(pk__in=user_ids).values_list(username_field)
        return ', '.join(user[0] for user in users)

@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
    """
    List all of viewflow tasks
    """
    actions = None
    date_hierarchy = 'created'
    list_display = ['pk', 'created', 'process', 'get_status_display',
                    'owner', 'owner_permission', 'token',
                    'started', 'finished']
    list_display_links = ['pk', 'created', 'process']
    list_filter = ['status']
    readonly_fields = ['process', 'flow_task', 'started', 'finished', 'previous', 'token']
<|endoftext|>


<commit_before># -*- mode: python; coding: utf-8 -*-
# Copyright 2014 Peter Williams <peter@newton.cx> and collaborators.
# Licensed under the MIT License.

"""pwkit.bblocks - Bayesian Blocks analysis, with a few extensions.

Bayesian Blocks analysis for the "time tagged" case described by Scargle+
2013. Inspired by the bayesian_blocks implementation by Jake Vanderplas in the
AstroML package, but that turned out to have some limitations.

We have iterative determination of the best number of blocks (using an ad-hoc
routine described in Scargle+ 2013) and bootstrap-based determination of
uncertainties on the block heights (ditto).

Functions are:

:func:`bin_bblock`
  Bayesian Blocks analysis with counts and bins.
:func:`tt_bblock`
  BB analysis of time-tagged events.
:func:`bs_tt_bblock`
  Like :func:`tt_bblock` with bootstrap-based uncertainty assessment. NOTE:
  the uncertainties are not very reliable!

"""
from __future__ import absolute_import, division, print_function, unicode_literals

__all__ = str ('nlogn bin_bblock tt_bblock bs_tt_bblock').split ()

from six.moves import range
import numpy as np

from . import Holder

def nlogn (n, dt):
    # I really feel like there must be a cleverer way to do this
    # scalar-or-vector possible-bad-value masking.

    if np.isscalar (n):
        if n == 0:
            return 0.
        return n * (np.log (n) - np.log (dt))

    n = np.asarray (n)
    mask = (n == 0)
    r = n * (np.log (np.where (mask, 1, n)) - np.log (dt))
    return np.where (mask, 0, r)

def bin_bblock (widths, counts, p0=0.05):
    """Fundamental Bayesian Blocks algorithm. Arguments:

    widths  - Array of consecutive cell widths.
    counts  - Array of numbers of counts in each cell.
    p0=0.05 - Probability of preferring solutions with additional bins.

    Returns a Holder with:

    blockstarts - Start times of output blocks.
    counts      - Number of events in each output block.
    finalp0     - Final value of p0, after iteration to minimize `nblocks`.
    nblocks     - Number of output blocks.
    ncells      - Number of input cells/bins.
    origp0      - Original value of p0.
    rates       - Event rate associated with each block.
    widths      - Width of each output block.

    """
    widths = np.asarray (widths)
    counts = np.asarray (counts)
    ncells = widths.size
    origp0 = p0

    if np.any (widths <= 0):
        raise ValueError ('bin widths must be positive')
    if widths.size != counts.size:
        raise ValueError ('widths and counts must have same size')
    if p0 < 0 or p0 >= 1.:
        raise ValueError ('p0 must lie within [0, 1)')

    vedges = np.cumsum (np.concatenate (([0], widths))) # size: ncells + 1
    block_remainders = vedges[-1] - vedges # size: nedges = ncells + 1
    ccounts = np.cumsum (np.concatenate (([0], counts)))
    count_remainders = ccounts[-1] - ccounts

    prev_blockstarts = None
    best = np.zeros (ncells, dtype=np.float)
    last = np.zeros (ncells, dtype=np.int)

    for _ in range (10):
        # Pluggable num-change-points prior-weight expression:
        ncp_prior = 4 - np.log (p0 / (0.0136 * ncells**0.478))

        for r in range (ncells):
            tk = block_remainders[:r+1] - block_remainders[r+1]
            nk = count_remainders[:r+1] - count_remainders[r+1]

            # Pluggable fitness expression:
            fit_vec = nlogn (nk, tk)

            # This incrementally penalizes partitions with more blocks:
            tmp = fit_vec - ncp_prior
            tmp[1:] += best[:r]

            imax = np.argmax (tmp)
            last[r] = imax
            best[r] = tmp[imax]

        # different semantics than Scargle impl: our blockstarts is similar to
        # their changepoints, but we always finish with blockstarts[0] = 0.

        work = np.zeros (ncells, dtype=int)
        workidx = 0
        ind = last[-1]

        while True:
            work[workidx] = ind
            workidx += 1
            if ind == 0:
                break
            ind = last[ind - 1]

        blockstarts = work[:workidx][::-1]

        if prev_blockstarts is not None:
            if (blockstarts.size == prev_blockstarts.size and
                (blockstarts == prev_blockstarts).all ()):
                break # converged

        if blockstarts.size == 1:
            break # can't shrink any farther

        # Recommended ad-hoc iteration to favor fewer blocks above and beyond
        # the value of p0:
        p0 = 1. - (1. - p0)**(1. / (blockstarts.size - 1))
        prev_blockstarts = blockstarts

    assert blockstarts[0] == 0
    nblocks = blockstarts.size

    info = Holder ()
    info.ncells = ncells
    info.nblocks = nblocks
    info.origp0 = origp0
    info.finalp0 = p0
    info.blockstarts = blockstarts
    info.counts = np.empty (nblocks, dtype=np.int)
    info.widths = np.empty (nblocks)

    for iblk in range (nblocks):
        cellstart = blockstarts[iblk]
        if iblk == nblocks - 1:
            cellend = ncells - 1
        else:
            cellend = blockstarts[iblk+1] - 1

        info.widths[iblk] = widths[cellstart:cellend+1].sum ()
        info.counts[iblk] = counts[cellstart:cellend+1].sum ()

    info.rates = info.counts / info.widths
    return info

def tt_bblock (tstarts, tstops, times, p0=0.05):
    """Bayesian Blocks for time-tagged events. Arguments:

    tstarts  - Array of input bin start times.
    tstops   - Array of input bin stop times.
    times    - Array of event arrival times.
    p0=0.05  - Probability of preferring solutions with additional bins.

    Returns a Holder with:

    blockstarts - Start times of output blocks.
    counts      - Number of events in each output block.
    finalp0     - Final value of p0, after iteration to minimize `nblocks`.
    ledges      - Times of left edges of output blocks.
    midpoints   - Times of midpoints of output blocks.
    nblocks     - Number of output blocks.
    ncells      - Number of input cells/bins.
    origp0      - Original value of p0.
    rates       - Event rate associated with each block.
    redges      - Times of right edges of output blocks.
    widths      - Width of each output block.

    Bin start/stop times are best derived from a 1D Voronoi tesselation of the
    event arrival times, with some kind of global observation start/stop time
    setting the extreme edges.

    """
    tstarts = np.asarray (tstarts)
    tstops = np.asarray (tstops)
    times = np.asarray (times)

    if tstarts.size != tstops.size:
        raise ValueError ('must have same number of starts and stops')

    ngti = tstarts.size

    if ngti < 1:
        raise ValueError ('must have at least one goodtime interval')
    if np.any ((tstarts[1:] - tstarts[:-1]) <= 0):
        raise ValueError ('tstarts must be ordered and distinct')
    if np.any ((tstops[1:] - tstops[:-1]) <= 0):
        raise ValueError ('tstops must be ordered and distinct')
    if np.any (tstarts >= tstops):
        raise ValueError ('tstarts must come before tstops')
    if np.any ((times[1:] - times[:-1]) < 0):
        raise ValueError ('times must be ordered')
    if times.min () < tstarts[0]:
        raise ValueError ('no times may be smaller than first tstart')
    if times.max () > tstops[-1]:
        raise ValueError ('no times may be larger than last tstop')
    for i in range (1, ngti):
        if np.where ((times > tstops[i-1]) & (times < tstarts[i]))[0].size:
            raise ValueError ('no times may fall in goodtime gap #%d' % i)
    if p0 < 0 or p0 >= 1.:
        raise ValueError ('p0 must lie within [0, 1)')

    utimes, uidxs = np.unique (times, return_index=True)
    nunique = utimes.size

    counts = np.empty (nunique)
    counts[:-1] = uidxs[1:] - uidxs[:-1]
    counts[-1] = times.size - uidxs[-1]
    assert counts.sum () == times.size

    # we grow these arrays with concats, which will perform badly with lots of
    # GTIs. Not expected to be a big deal.
    widths = np.empty (0)
    ledges = np.empty (0)
    redges = np.empty (0)

    for i in range (ngti):
        tstart, tstop = tstarts[i], tstops[i]

        w = np.where ((utimes >= tstart) & (utimes <= tstop))[0]

        if not w.size:
            # No events during this goodtime! We have to insert a zero-count
            # event block. This may break assumptions within bin_bblock()?

            # j = idx of first event after this GTI
            wafter = np.where (utimes > tstop)[0]
            if wafter.size:
                j = wafter[0]
            else:
                j = utimes.size
            assert j == 0 or np.where (utimes < tstart)[0][-1] == j - 1

            counts = np.concatenate ((counts[:j], [0], counts[j:]))
            widths = np.concatenate ((widths, [tstop - tstart]))
            ledges = np.concatenate ((ledges, [tstart]))
            redges = np.concatenate ((redges, [tstop]))
        else:
            gtutimes = utimes[w]
            midpoints = 0.5 * (gtutimes[1:] + gtutimes[:-1]) # size: n - 1
            gtedges = np.concatenate (([tstart], midpoints, [tstop])) # size: n + 1
            gtwidths = gtedges[1:] - gtedges[:-1] # size: n
            assert gtwidths.sum () == tstop - tstart
            widths = np.concatenate ((widths, gtwidths))
            ledges = np.concatenate ((ledges, gtedges[:-1]))
            redges = np.concatenate ((redges, gtedges[1:]))

    assert counts.size == widths.size
    info = bin_bblock (widths, counts, p0=p0)
    info.ledges = ledges[info.blockstarts]
    # The right edge of the i'th block is the right edge of its rightmost
    # bin, which is the bin before the leftmost bin of the (i+1)'th block:
    info.redges = np.concatenate ((redges[info.blockstarts[1:] - 1], [redges[-1]]))
    info.midpoints = 0.5 * (info.ledges + info.redges)
    return info

def bs_tt_bblock (times, tstarts, tstops, p0=0.05, nbootstrap=512):
    """Bayesian Blocks for time-tagged events with bootstrapping uncertainty
    assessment. THE UNCERTAINTIES ARE NOT VERY GOOD! Arguments:

    tstarts        - Array of input bin start times.
    tstops         - Array of input bin stop times.
    times          - Array of event arrival times.
    p0=0.05        - Probability of preferring solutions with additional bins.
    nbootstrap=512 - Number of bootstrap runs to perform.

    Returns a Holder with:

    blockstarts - Start times of output blocks.
    bsrates     - Mean event rate in each bin from bootstrap analysis.
    bsrstds     - ~Uncertainty: stddev of event rate in each bin from bootstrap analysis.
    counts      - Number of events in each output block.
    finalp0     - Final value of p0, after iteration to minimize `nblocks`.
    ledges      - Times of left edges of output blocks.
    midpoints   - Times of midpoints of output blocks.
    nblocks     - Number of output blocks.
    ncells      - Number of input cells/bins.
    origp0      - Original value of p0.
    rates       - Event rate associated with each block.
    redges      - Times of right edges of output blocks.
    widths      - Width of each output block.

    """
    times = np.asarray (times)
    tstarts = np.asarray (tstarts)
    tstops = np.asarray (tstops)

    nevents = times.size
    if nevents < 1:
        raise ValueError ('must be given at least 1 event')

    info = tt_bblock (tstarts, tstops, times, p0)

    # Now bootstrap resample to assess uncertainties on the bin heights. This
    # is the approach recommended by Scargle+.

    bsrsums = np.zeros (info.nblocks)
    bsrsumsqs = np.zeros (info.nblocks)

    for _ in range (nbootstrap):
        bstimes = times[np.random.randint (0, times.size, times.size)]
        bstimes.sort ()
        bsinfo = tt_bblock (tstarts, tstops, bstimes, p0)
        blocknums = np.minimum (np.searchsorted (bsinfo.redges, info.midpoints),
                                bsinfo.nblocks - 1)
        samprates = bsinfo.rates[blocknums]
        bsrsums += samprates
        bsrsumsqs += samprates**2

    bsrmeans = bsrsums / nbootstrap
    mask = bsrsumsqs / nbootstrap <= bsrmeans**2
    bsrstds = np.sqrt (np.where (mask, 0, bsrsumsqs / nbootstrap - bsrmeans**2))
    info.bsrates = bsrmeans
    info.bsrstds = bsrstds
    return info
<commit_msg>pwkit/bblocks.py: add a mode to intersect time-tagged bblocks with GTIs<commit_after># -*- mode: python; coding: utf-8 -*-
# Copyright 2014 Peter Williams <peter@newton.cx> and collaborators.
# Licensed under the MIT License.

"""pwkit.bblocks - Bayesian Blocks analysis, with a few extensions.

Bayesian Blocks analysis for the "time tagged" case described by Scargle+
2013. Inspired by the bayesian_blocks implementation by Jake Vanderplas in the
AstroML package, but that turned out to have some limitations.

We have iterative determination of the best number of blocks (using an ad-hoc
routine described in Scargle+ 2013) and bootstrap-based determination of
uncertainties on the block heights (ditto).

Functions are:

:func:`bin_bblock`
  Bayesian Blocks analysis with counts and bins.
:func:`tt_bblock`
  BB analysis of time-tagged events.
:func:`bs_tt_bblock`
  Like :func:`tt_bblock` with bootstrap-based uncertainty assessment. NOTE:
  the uncertainties are not very reliable!

"""
from __future__ import absolute_import, division, print_function, unicode_literals

__all__ = str ('nlogn bin_bblock tt_bblock bs_tt_bblock').split ()

from six.moves import range
import numpy as np

from . import Holder

def nlogn (n, dt):
    # I really feel like there must be a cleverer way to do this
    # scalar-or-vector possible-bad-value masking.

    if np.isscalar (n):
        if n == 0:
            return 0.
        return n * (np.log (n) - np.log (dt))

    n = np.asarray (n)
    mask = (n == 0)
    r = n * (np.log (np.where (mask, 1, n)) - np.log (dt))
    return np.where (mask, 0, r)

def bin_bblock (widths, counts, p0=0.05):
    """Fundamental Bayesian Blocks algorithm. Arguments:

    widths  - Array of consecutive cell widths.
    counts  - Array of numbers of counts in each cell.
    p0=0.05 - Probability of preferring solutions with additional bins.

    Returns a Holder with:

    blockstarts - Start times of output blocks.
    counts      - Number of events in each output block.
    finalp0     - Final value of p0, after iteration to minimize `nblocks`.
    nblocks     - Number of output blocks.
    ncells      - Number of input cells/bins.
    origp0      - Original value of p0.
    rates       - Event rate associated with each block.
    widths      - Width of each output block.

    """
    widths = np.asarray (widths)
    counts = np.asarray (counts)
    ncells = widths.size
    origp0 = p0

    if np.any (widths <= 0):
        raise ValueError ('bin widths must be positive')
    if widths.size != counts.size:
        raise ValueError ('widths and counts must have same size')
    if p0 < 0 or p0 >= 1.:
        raise ValueError ('p0 must lie within [0, 1)')

    vedges = np.cumsum (np.concatenate (([0], widths))) # size: ncells + 1
    block_remainders = vedges[-1] - vedges # size: nedges = ncells + 1
    ccounts = np.cumsum (np.concatenate (([0], counts)))
    count_remainders = ccounts[-1] - ccounts

    prev_blockstarts = None
    best = np.zeros (ncells, dtype=np.float)
    last = np.zeros (ncells, dtype=np.int)

    for _ in range (10):
        # Pluggable num-change-points prior-weight expression:
        ncp_prior = 4 - np.log (p0 / (0.0136 * ncells**0.478))

        for r in range (ncells):
            tk = block_remainders[:r+1] - block_remainders[r+1]
            nk = count_remainders[:r+1] - count_remainders[r+1]

            # Pluggable fitness expression:
            fit_vec = nlogn (nk, tk)

            # This incrementally penalizes partitions with more blocks:
            tmp = fit_vec - ncp_prior
            tmp[1:] += best[:r]

            imax = np.argmax (tmp)
            last[r] = imax
            best[r] = tmp[imax]

        # different semantics than Scargle impl: our blockstarts is similar to
        # their changepoints, but we always finish with blockstarts[0] = 0.

        work = np.zeros (ncells, dtype=int)
        workidx = 0
        ind = last[-1]

        while True:
            work[workidx] = ind
            workidx += 1
            if ind == 0:
                break
            ind = last[ind - 1]

        blockstarts = work[:workidx][::-1]

        if prev_blockstarts is not None:
            if (blockstarts.size == prev_blockstarts.size and
                (blockstarts == prev_blockstarts).all ()):
                break # converged

        if blockstarts.size == 1:
            break # can't shrink any farther

        # Recommended ad-hoc iteration to favor fewer blocks above and beyond
        # the value of p0:
        p0 = 1. - (1. - p0)**(1. / (blockstarts.size - 1))
        prev_blockstarts = blockstarts

    assert blockstarts[0] == 0
    nblocks = blockstarts.size

    info = Holder ()
    info.ncells = ncells
    info.nblocks = nblocks
    info.origp0 = origp0
    info.finalp0 = p0
    info.blockstarts = blockstarts
    info.counts = np.empty (nblocks, dtype=np.int)
    info.widths = np.empty (nblocks)

    for iblk in range (nblocks):
        cellstart = blockstarts[iblk]
        if iblk == nblocks - 1:
            cellend = ncells - 1
        else:
            cellend = blockstarts[iblk+1] - 1

        info.widths[iblk] = widths[cellstart:cellend+1].sum ()
        info.counts[iblk] = counts[cellstart:cellend+1].sum ()

    info.rates = info.counts / info.widths
    return info

def tt_bblock (tstarts, tstops, times, p0=0.05, intersect_with_bins=False):
    """Bayesian Blocks for time-tagged events. Arguments:

    tstarts  - Array of input bin start times.
    tstops   - Array of input bin stop times.
    times    - Array of event arrival times.
    p0=0.05  - Probability of preferring solutions with additional bins.
    intersect_with_bins=False
             - If true, intersect bblock bins with input bins; can result
               in more bins than bblocks wants; they will have the same
               rate values.

    Returns a Holder with:

    counts      - Number of events in each output block.
    finalp0     - Final value of p0, after iteration to minimize `nblocks`.
    ledges      - Times of left edges of output blocks.
    midpoints   - Times of midpoints of output blocks.
    nblocks     - Number of output blocks.
    ncells      - Number of input cells/bins.
    origp0      - Original value of p0.
    rates       - Event rate associated with each block.
    redges      - Times of right edges of output blocks.
    widths      - Width of each output block.

    Bin start/stop times are best derived from a 1D Voronoi tesselation of the
    event arrival times, with some kind of global observation start/stop time
    setting the extreme edges. Or they can be set from "good time intervals"
    if observations were toggled on or off as in an X-ray telescope.

    If *intersect_with_bins* is True, the true Bayesian Blocks bins (BBBs) are
    intersected with the "good time intervals" (GTIs) defined by the *tstarts*
    and *tstops* variables. One GTI may contain multiple BBBs if the event
    rate appears to change within the GTI, and one BBB may span multiple GTIs
    if the event date does *not* appear to change between the GTIs. The
    intersection will ensure that no BBB intervals cross the edge of a GTI. If
    this would happen, the BBB is split into multiple, partially redundant
    records. Each of these records will have the **same** value for the
    *counts*, *rates*, and *widths* values. However, the *ledges*, *redges*,
    and *midpoints* values will be recalculated. Note that in this mode, it is
    not necessarily true that ``widths = ledges - redges`` as is usually the
    case. When this flag is true, keep in mind that multiple bins are
    therefore *not* necessarily independent statistical samples.

    """
    tstarts = np.asarray (tstarts)
    tstops = np.asarray (tstops)
    times = np.asarray (times)

    if tstarts.size != tstops.size:
        raise ValueError ('must have same number of starts and stops')

    ngti = tstarts.size

    if ngti < 1:
        raise ValueError ('must have at least one goodtime interval')
    if np.any ((tstarts[1:] - tstarts[:-1]) <= 0):
        raise ValueError ('tstarts must be ordered and distinct')
    if np.any ((tstops[1:] - tstops[:-1]) <= 0):
        raise ValueError ('tstops must be ordered and distinct')
    if np.any (tstarts >= tstops):
        raise ValueError ('tstarts must come before tstops')
    if np.any ((times[1:] - times[:-1]) < 0):
        raise ValueError ('times must be ordered')
    if times.min () < tstarts[0]:
        raise ValueError ('no times may be smaller than first tstart')
    if times.max () > tstops[-1]:
        raise ValueError ('no times may be larger than last tstop')
    for i in range (1, ngti):
        if np.where ((times > tstops[i-1]) & (times < tstarts[i]))[0].size:
            raise ValueError ('no times may fall in goodtime gap #%d' % i)
    if p0 < 0 or p0 >= 1.:
        raise ValueError ('p0 must lie within [0, 1)')

    utimes, uidxs = np.unique (times, return_index=True)
    nunique = utimes.size

    counts = np.empty (nunique)
    counts[:-1] = uidxs[1:] - uidxs[:-1]
    counts[-1] = times.size - uidxs[-1]
    assert counts.sum () == times.size

    # we grow these arrays with concats, which will perform badly with lots of
    # GTIs. Not expected to be a big deal.
    widths = np.empty (0)
    ledges = np.empty (0)
    redges = np.empty (0)

    for i in range (ngti):
        tstart, tstop = tstarts[i], tstops[i]

        w = np.where ((utimes >= tstart) & (utimes <= tstop))[0]

        if not w.size:
            # No events during this goodtime! We have to insert a zero-count
            # event block. This may break assumptions within bin_bblock()?

            # j = idx of first event after this GTI
            wafter = np.where (utimes > tstop)[0]
            if wafter.size:
                j = wafter[0]
            else:
                j = utimes.size
            assert j == 0 or np.where (utimes < tstart)[0][-1] == j - 1

            counts = np.concatenate ((counts[:j], [0], counts[j:]))
            widths = np.concatenate ((widths, [tstop - tstart]))
            ledges = np.concatenate ((ledges, [tstart]))
            redges = np.concatenate ((redges, [tstop]))
        else:
            gtutimes = utimes[w]
            midpoints = 0.5 * (gtutimes[1:] + gtutimes[:-1]) # size: n - 1
            gtedges = np.concatenate (([tstart], midpoints, [tstop])) # size: n + 1
            gtwidths = gtedges[1:] - gtedges[:-1] # size: n
            assert gtwidths.sum () == tstop - tstart
            widths = np.concatenate ((widths, gtwidths))
            ledges = np.concatenate ((ledges, gtedges[:-1]))
            redges = np.concatenate ((redges, gtedges[1:]))

    assert counts.size == widths.size
    info = bin_bblock (widths, counts, p0=p0)
    info.ledges = ledges[info.blockstarts]
    # The right edge of the i'th block is the right edge of its rightmost
    # bin, which is the bin before the leftmost bin of the (i+1)'th block:
    info.redges = np.concatenate ((redges[info.blockstarts[1:] - 1], [redges[-1]]))
    info.midpoints = 0.5 * (info.ledges + info.redges)
    del info.blockstarts

    if intersect_with_bins:
        # OK, we now need to intersect the bblock bins with the input bins.
        # This can fracture one bblock bin into multiple ones but shouldn't
        # make any of them disappear, since they're definitionally constrained
        # to contain events.
        #
        # First: sorted list of all timestamps at which *something* changes:
        # either a bblock edge, or a input bin edge. We drop the last entry,
        # giving is a list of left edges of bins in which everything is the
        # same.

        all_times = set(tstarts)
        all_times.update(tstops)
        all_times.update(info.ledges)
        all_times.update(info.redges)
        all_times = np.array(sorted(all_times))[:-1]

        # Now, construct a lookup table of which bblock number each of these
        # bins corresponds to. More than one bin may have the same bblock
        # number, if a GTI change slices a single bblock into more than one
        # piece. We do this in a somewhat non-obvious way since we know that
        # the bblocks completely cover the overall GTI span in order.

        bblock_ids = np.zeros(all_times.size)

        for i in range(1, info.nblocks):
            bblock_ids[all_times >= info.ledges[i]] = i

        # Now, a lookup table of which bins are within a good GTI span. Again,
        # we know that all bins are either entirely in a good GTI or entirely
        # outside, so the logic is simplified but not necessarily obvious.

        good_timeslot = np.zeros(all_times.size, dtype=np.bool)

        for t0, t1 in zip(tstarts, tstops):
            ok = (all_times >= t0) & (all_times < t1)
            good_timeslot[ok] = True

        # Finally, look for contiguous spans that are in a good timeslot *and*
        # have the same underlying bblock number. These are our intersected
        # blocks.

        old_bblock_ids = []
        ledges = []
        redges = []
        cur_bblock_id = -1

        for i in range(all_times.size):
            if bblock_ids[i] != cur_bblock_id or not good_timeslot[i]:
                if cur_bblock_id >= 0:
                    # Ending a previous span.
                    redges.append(all_times[i])
                    cur_bblock_id = -1

                if good_timeslot[i]:
                    # Starting a new span.
                    ledges.append(all_times[i])
                    old_bblock_ids.append(bblock_ids[i])
                    cur_bblock_id = bblock_ids[i]

        if cur_bblock_id >= 0:
            # End the last span.
            redges.append(tstops[-1])

        # Finally, rewrite all of the data as planned.

        old_bblock_ids = np.array(old_bblock_ids, dtype=np.int)
        info.counts = info.counts[old_bblock_ids]
        info.rates = info.rates[old_bblock_ids]
        info.widths = info.widths[old_bblock_ids]

        info.ledges = np.array(ledges)
        info.redges = np.array(redges)
        info.midpoints = 0.5 * (info.ledges + info.redges)
        info.nblocks = info.ledges.size

    return info

def bs_tt_bblock (times, tstarts, tstops, p0=0.05, nbootstrap=512):
    """Bayesian Blocks for time-tagged events with bootstrapping uncertainty
    assessment. THE UNCERTAINTIES ARE NOT VERY GOOD! Arguments:

    tstarts        - Array of input bin start times.
    tstops         - Array of input bin stop times.
    times          - Array of event arrival times.
    p0=0.05        - Probability of preferring solutions with additional bins.
    nbootstrap=512 - Number of bootstrap runs to perform.

    Returns a Holder with:

    blockstarts - Start times of output blocks.
    bsrates     - Mean event rate in each bin from bootstrap analysis.
    bsrstds     - ~Uncertainty: stddev of event rate in each bin from bootstrap analysis.
    counts      - Number of events in each output block.
    finalp0     - Final value of p0, after iteration to minimize `nblocks`.
    ledges      - Times of left edges of output blocks.
    midpoints   - Times of midpoints of output blocks.
    nblocks     - Number of output blocks.
    ncells      - Number of input cells/bins.
    origp0      - Original value of p0.
    rates       - Event rate associated with each block.
    redges      - Times of right edges of output blocks.
    widths      - Width of each output block.

    """
    times = np.asarray (times)
    tstarts = np.asarray (tstarts)
    tstops = np.asarray (tstops)

    nevents = times.size
    if nevents < 1:
        raise ValueError ('must be given at least 1 event')

    info = tt_bblock (tstarts, tstops, times, p0)

    # Now bootstrap resample to assess uncertainties on the bin heights. This
    # is the approach recommended by Scargle+.

    bsrsums = np.zeros (info.nblocks)
    bsrsumsqs = np.zeros (info.nblocks)

    for _ in range (nbootstrap):
        bstimes = times[np.random.randint (0, times.size, times.size)]
        bstimes.sort ()
        bsinfo = tt_bblock (tstarts, tstops, bstimes, p0)
        blocknums = np.minimum (np.searchsorted (bsinfo.redges, info.midpoints),
                                bsinfo.nblocks - 1)
        samprates = bsinfo.rates[blocknums]
        bsrsums += samprates
        bsrsumsqs += samprates**2

    bsrmeans = bsrsums / nbootstrap
    mask = bsrsumsqs / nbootstrap <= bsrmeans**2
    bsrstds = np.sqrt (np.where (mask, 0, bsrsumsqs / nbootstrap - bsrmeans**2))
    info.bsrates = bsrmeans
    info.bsrstds = bsrstds
    return info
<|endoftext|>


<commit_before># svntrac
#
# Copyright (C) 2003 Xyche Software
# Copyright (C) 2003 Jonas Borgstrm <jonas@xyche.com>
#
# svntrac is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# svntrac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Jonas Borgstrm <jonas@xyche.com>

import os
import sys
import cgi
#import cgitb; cgitb.enable()
import warnings
from util import set_cgi_name, set_authcgi_name
from svn import util, repos
import Href

warnings.filterwarnings('ignore', 'DB-API extension cursor.next() used')

import db
from auth import verify_authentication
from perm import cache_permissions, PermissionError

modules = {
#  name module class need_db need_svn
    'log'         : ('Log', 'Log', 1),
    'file'        : ('File', 'File', 1),
    'wiki'        : ('Wiki', 'Wiki', 0),
    'report'      : ('Report', 'Report', 0),
    'ticket'      : ('Ticket', 'Ticket', 0),
    'browser'     : ('Browser', 'Browser', 1),
    'timeline'    : ('Timeline', 'Timeline', 1),
    'changeset'   : ('Changeset', 'Changeset', 1),
    'newticket'   : ('Ticket', 'Newticket', 0),
    }

def main():
    mode = 'wiki' #default module
    db.init()
    config = db.load_config()
    set_cgi_name(config['general']['cgi_name'])
    set_authcgi_name(config['general']['authcgi_name'])
    Href.initialize(config)

    pool = util.svn_pool_create(None)

    _args = cgi.FieldStorage()
    args = {}
    for x in _args.keys():
	args[x] = _args[x].value

    if args.has_key('mode'):
        mode = args['mode']

    module_name, constructor_name, need_svn = modules[mode]
    module = __import__(module_name, globals(),  locals(), [])
    constructor = getattr(module, constructor_name)
    module = constructor(config, args, pool)

    verify_authentication (args)
    cache_permissions()

    if need_svn:
        repos_dir = config['general']['svn_repository']
        rep = repos.svn_repos_open(repos_dir, pool)
        fs_ptr = repos.svn_repos_fs(rep)
        module.repos = rep
        module.fs_ptr = fs_ptr
        db.sync (rep, fs_ptr, pool)

    try:
        module.render()
        module.apply_template()
    except PermissionError, e:
        print 'Content-Type: text/plain\r\n\r\n',
        print 'page render failed:', e

<commit_msg>A bit better python exception handling<commit_after># svntrac
#
# Copyright (C) 2003 Xyche Software
# Copyright (C) 2003 Jonas Borgstrm <jonas@xyche.com>
#
# svntrac is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# svntrac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Jonas Borgstrm <jonas@xyche.com>

import os
import sys
import cgi
import warnings
from util import set_cgi_name, set_authcgi_name
from svn import util, repos, core
import Href

warnings.filterwarnings('ignore', 'DB-API extension cursor.next() used')

import db
from auth import verify_authentication
from perm import cache_permissions, PermissionError

modules = {
#  name module class need_db need_svn
    'log'         : ('Log', 'Log', 1),
    'file'        : ('File', 'File', 1),
    'wiki'        : ('Wiki', 'Wiki', 0),
    'report'      : ('Report', 'Report', 0),
    'ticket'      : ('Ticket', 'Ticket', 0),
    'browser'     : ('Browser', 'Browser', 1),
    'timeline'    : ('Timeline', 'Timeline', 1),
    'changeset'   : ('Changeset', 'Changeset', 1),
    'newticket'   : ('Ticket', 'Newticket', 0),
    }

def main():
    mode = 'wiki' #default module
    db.init()
    config = db.load_config()
    set_cgi_name(config['general']['cgi_name'])
    set_authcgi_name(config['general']['authcgi_name'])
    Href.initialize(config)

    core.apr_initialize()
    pool = core.svn_pool_create(None)

    _args = cgi.FieldStorage()
    args = {}
    for x in _args.keys():
	args[x] = _args[x].value

    if args.has_key('mode'):
        mode = args['mode']

    module_name, constructor_name, need_svn = modules[mode]
    module = __import__(module_name, globals(),  locals(), [])
    constructor = getattr(module, constructor_name)
    module = constructor(config, args, pool)

    verify_authentication (args)
    cache_permissions()

    if need_svn:
        repos_dir = config['general']['svn_repository']
        rep = repos.svn_repos_open(repos_dir, pool)
        fs_ptr = repos.svn_repos_fs(rep)
        module.repos = rep
        module.fs_ptr = fs_ptr
        db.sync (rep, fs_ptr, pool)

    try:
        module.render()
        module.apply_template()
    except Exception, e:
        print 'Content-Type: text/plain\r\n\r\n',
        import traceback
        traceback.print_exc(file=sys.stdout)

    core.svn_pool_destroy(pool)
    core.apr_terminate()

<|endoftext|>