<commit_before>import copy
import numpy
from .gqrs import time_to_sample_number, Conf, Peak, Annotation
from .basic import smooth

def find_peaks(x):
    # Definitions:
    # * Hard peak: a peak that is either /\ or \/
    # * Soft peak: a peak that is either /-*\ or \-*/ (In that cas we define the middle of it as the peak)

    # Returns two numpy arrays:
    # * hard_peaks contains the indexes of the Hard peaks
    # * soft_peaks contains the indexes of the Soft peaks

    if len(x) == 0:
        return numpy.empty([0]), numpy.empty([0])

    tmp = x[1:]
    tmp = numpy.append(tmp, [x[-1]])
    tmp = x-tmp
    tmp[numpy.where(tmp>0)] = +1
    tmp[numpy.where(tmp==0)] = 0
    tmp[numpy.where(tmp<0)] = -1
    tmp2 = tmp[1:]
    tmp2 = numpy.append(tmp2, [0])
    tmp = tmp-tmp2
    hard_peaks = numpy.where(numpy.logical_or(tmp==-2,tmp==+2))[0]+1
    soft_peaks = []
    for iv in numpy.where(numpy.logical_or(tmp==-1,tmp==+1))[0]:
        t = tmp[iv]
        i = iv+1
        while True:
            if i==len(tmp) or tmp[i] == -t or tmp[i] == -2 or tmp[i] == 2:
                break
            if tmp[i] == t:
                soft_peaks.append(int(iv+(i-iv)/2))
                break
            i += 1
    soft_peaks = numpy.asarray(soft_peaks)+1
    return hard_peaks, soft_peaks

def find_peaks(x):
    # Definitions:
    # * Hard peak: a peak that is either /\ or \/
    # * Soft peak: a peak that is either /-*\ or \-*/ (In that cas we define the middle of it as the peak)

    # Returns two numpy arrays:
    # * hard_peaks contains the indexes of the Hard peaks
    # * soft_peaks contains the indexes of the Soft peaks

    if len(x) == 0:
        return numpy.empty([0]), numpy.empty([0])

    tmp = x[1:]
    tmp = numpy.append(tmp, [x[-1]])
    tmp = x-tmp
    tmp[numpy.where(tmp>0)] = +1
    tmp[numpy.where(tmp==0)] = 0
    tmp[numpy.where(tmp<0)] = -1
    tmp2 = tmp[1:]
    tmp2 = numpy.append(tmp2, [0])
    tmp = tmp-tmp2
    hard_peaks = numpy.where(numpy.logical_or(tmp==-2,tmp==+2))[0]+1
    soft_peaks = []
    for iv in numpy.where(numpy.logical_or(tmp==-1,tmp==+1))[0]:
        t = tmp[iv]
        i = iv+1
        while True:
            if i==len(tmp) or tmp[i] == -t or tmp[i] == -2 or tmp[i] == 2:
                break
            if tmp[i] == t:
                soft_peaks.append(int(iv+(i-iv)/2))
                break
            i += 1
    soft_peaks = numpy.asarray(soft_peaks)+1
    return hard_peaks, soft_peaks

def correct_peaks(x, peaks_indexes, min_gap, max_gap, smooth_window):
    N = x.shape[0]

    rpeaks = numpy.zeros(N)
    rpeaks[peaks_indexes] = 1.0

    rpeaks = rpeaks.astype('int32')

    # 1- Extract ranges where we have one or many ones side by side
    rpeaks_ranges = []
    tmp_idx = 0
    for i in range(1, len(rpeaks)):
        if rpeaks[i-1] == 1:
            if rpeaks[i] == 0:
                rpeaks_ranges.append((tmp_idx, i-1))
        else:
            if rpeaks[i] == 1:
                tmp_idx = i

    smoothed = smooth(x, smooth_window)

    # Compute signal's peaks
    hard_peaks, soft_peaks = find_peaks(x=x)
    all_peak_idxs = numpy.concatenate((hard_peaks, soft_peaks)).astype('int64')

    # Replace each range of ones by the index of the best value in it
    tmp = set()
    for rp_range in rpeaks_ranges:
        r = numpy.arange(rp_range[0], rp_range[1]+1, dtype='int64')
        vals = x[r]
        smoothed_vals = smoothed[r]
        p = r[numpy.argmax(numpy.absolute(numpy.asarray(vals)-smoothed_vals))]
        tmp.add(p)

    # Replace all peaks by the peak within x-max_gap < x < x+max_gap which have the bigget distance from smooth curve
    dist = numpy.absolute(x-smoothed) # Peak distance from the smoothed mean
    rpeaks_indexes = set()
    for p in tmp:
        a = max(0, p-max_gap)
        b = min(N, p+max_gap)
        r = numpy.arange(a, b, dtype='int64')
        idx_best = r[numpy.argmax(dist[r])]
        rpeaks_indexes.add(idx_best)

    rpeaks_indexes = list(rpeaks_indexes)

    # Prevent multiple peaks to appear in the max bpm range (max_gap)
    # If we found more than one peak in this interval, then we choose the peak with the maximum amplitude compared to the mean of the signal
    tmp = numpy.asarray(rpeaks_indexes)
    to_remove = {}
    for idx in rpeaks_indexes:
        if idx in to_remove:
            continue
        r = tmp[numpy.where(numpy.absolute(tmp-idx)<=max_gap)[0]]
        if len(r) == 1:
            continue
        rr = r.astype('int64')
        vals = x[rr]
        smoo = smoothed[rr]
        the_one = r[numpy.argmax(numpy.absolute(vals-smoo))]
        for i in r:
            if i != the_one:
                to_remove[i] = True
    for v, _ in to_remove.items():
        rpeaks_indexes.remove(v)

    return sorted(rpeaks_indexes)
<commit_msg>Remove duplicated code in peaks.py<commit_after>import copy
import numpy
from .gqrs import time_to_sample_number, Conf, Peak, Annotation
from .basic import smooth

def find_peaks(x):
    # Definitions:
    # * Hard peak: a peak that is either /\ or \/
    # * Soft peak: a peak that is either /-*\ or \-*/ (In that cas we define the middle of it as the peak)

    # Returns two numpy arrays:
    # * hard_peaks contains the indexes of the Hard peaks
    # * soft_peaks contains the indexes of the Soft peaks

    if len(x) == 0:
        return numpy.empty([0]), numpy.empty([0])

    tmp = x[1:]
    tmp = numpy.append(tmp, [x[-1]])
    tmp = x-tmp
    tmp[numpy.where(tmp>0)] = +1
    tmp[numpy.where(tmp==0)] = 0
    tmp[numpy.where(tmp<0)] = -1
    tmp2 = tmp[1:]
    tmp2 = numpy.append(tmp2, [0])
    tmp = tmp-tmp2
    hard_peaks = numpy.where(numpy.logical_or(tmp==-2,tmp==+2))[0]+1
    soft_peaks = []
    for iv in numpy.where(numpy.logical_or(tmp==-1,tmp==+1))[0]:
        t = tmp[iv]
        i = iv+1
        while True:
            if i==len(tmp) or tmp[i] == -t or tmp[i] == -2 or tmp[i] == 2:
                break
            if tmp[i] == t:
                soft_peaks.append(int(iv+(i-iv)/2))
                break
            i += 1
    soft_peaks = numpy.asarray(soft_peaks)+1
    return hard_peaks, soft_peaks

def correct_peaks(x, peaks_indexes, min_gap, max_gap, smooth_window):
    N = x.shape[0]

    rpeaks = numpy.zeros(N)
    rpeaks[peaks_indexes] = 1.0

    rpeaks = rpeaks.astype('int32')

    # 1- Extract ranges where we have one or many ones side by side
    rpeaks_ranges = []
    tmp_idx = 0
    for i in range(1, len(rpeaks)):
        if rpeaks[i-1] == 1:
            if rpeaks[i] == 0:
                rpeaks_ranges.append((tmp_idx, i-1))
        else:
            if rpeaks[i] == 1:
                tmp_idx = i

    smoothed = smooth(x, smooth_window)

    # Compute signal's peaks
    hard_peaks, soft_peaks = find_peaks(x=x)
    all_peak_idxs = numpy.concatenate((hard_peaks, soft_peaks)).astype('int64')

    # Replace each range of ones by the index of the best value in it
    tmp = set()
    for rp_range in rpeaks_ranges:
        r = numpy.arange(rp_range[0], rp_range[1]+1, dtype='int64')
        vals = x[r]
        smoothed_vals = smoothed[r]
        p = r[numpy.argmax(numpy.absolute(numpy.asarray(vals)-smoothed_vals))]
        tmp.add(p)

    # Replace all peaks by the peak within x-max_gap < x < x+max_gap which have the bigget distance from smooth curve
    dist = numpy.absolute(x-smoothed) # Peak distance from the smoothed mean
    rpeaks_indexes = set()
    for p in tmp:
        a = max(0, p-max_gap)
        b = min(N, p+max_gap)
        r = numpy.arange(a, b, dtype='int64')
        idx_best = r[numpy.argmax(dist[r])]
        rpeaks_indexes.add(idx_best)

    rpeaks_indexes = list(rpeaks_indexes)

    # Prevent multiple peaks to appear in the max bpm range (max_gap)
    # If we found more than one peak in this interval, then we choose the peak with the maximum amplitude compared to the mean of the signal
    tmp = numpy.asarray(rpeaks_indexes)
    to_remove = {}
    for idx in rpeaks_indexes:
        if idx in to_remove:
            continue
        r = tmp[numpy.where(numpy.absolute(tmp-idx)<=max_gap)[0]]
        if len(r) == 1:
            continue
        rr = r.astype('int64')
        vals = x[rr]
        smoo = smoothed[rr]
        the_one = r[numpy.argmax(numpy.absolute(vals-smoo))]
        for i in r:
            if i != the_one:
                to_remove[i] = True
    for v, _ in to_remove.items():
        rpeaks_indexes.remove(v)

    return sorted(rpeaks_indexes)
<|endoftext|>


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

import json
import requests
import platform
import re

from .version import __version__

class Geocoder(object):
    """
    What3Words v3 API wrapper
    ==========
    """

    def __init__(self, api_key, language='en',
                 end_point='https://api.what3words.com/v3'):
        """
        Constructor
        Params
        ------
        :param api_key: A valid API key
        :param language: default langauge use with the Geocoder
        :param end_point: what3words api end point
        """

        self.end_point = end_point
        self.api_key = api_key
        self.language = language

    def convert_to_coordinates(self, words, format='json'):
        """
        Take a 3 word address and turn it into a pair of coordinates.

        Params
        ------
        :param string words: A 3 word address as a string
        :param string format: Return data format type; can be one of
                              json (the default), geojson

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#convert-to-coordinates
        """

        params = {
            'words': words,
            'format': format,
        }

        return self._request('/convert-to-coordinates', params)

    def convert_to_3wa(self, coordinates, format='json', language=None):
        """
        Take latitude and longitude coordinates and turn them into
        a 3 word address.

        Params
        ------
        :param Coordinates coordinates: the coordinates of the location to convert to 3 word address
        :param float lng: longitude coordinate
        :param string format: Return data format type; can be one of
                              json (the default), geojson
        :param string language: A supported 3 word address language as an
                            ISO 639-1 2 letter code. Defaults to self.language

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#convert-to-3wa
        """

        params = {
            'coordinates': '{0},{1}'.format(coordinates.lat, coordinates.lng),
            'format': format,
            'language': language or self.language,
        }
        return self._request('/convert-to-3wa', params)

    def grid_section(self, bounding_box, format='json'):
        """
        Take latitude and longitude coordinates and turn them into
        a 3 word address.

        Params
        ------
        :param BoundingBox bbox: Bounding box, specified by the northeast and
                            southwest corner coordinates, for which the grid
                            should be returned.
        :param string format: Return data format type; can be one of
                              json (the default), geojson
        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#grid-section
        """

        params = {
            'bounding-box': '{0},{1},{2},{3}'.format(bounding_box.sw.lat, bounding_box.sw.lng, bounding_box.ne.lat, bounding_box.ne.lng),
            'format': format
        }
        return self._request('/grid-section', params)

    def available_languages(self):
        """
        Retrieve a list of available 3 word languages.

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#available-languages
        """

        return self._request('/available-languages')

    def autosuggest(self,
        input,
        n_results=None,
        focus=None,
        n_focus_results=None,
        clip_to_country=None,
        clip_to_bounding_box=None,
        clip_to_circle=None,
        clip_to_polygon=None,
        input_type=None,
        language=None,
        prefer_land=None):
        """
        Returns a list of 3 word addresses based on user input and other
        parameters.

        Params
        ------
        :param string input: The full or partial 3 word address to obtain
                            suggestions for. At minimum this must be the
                            first two complete words plus at least one
                            character from the third word
        :param int n_results: The number of AutoSuggest results to return. A maximum of 100
                            results can be specified, if a number greater than this is
                            requested, this will be truncated to the maximum. The default is 3
        :param Coordinates focus: A location, specified as a latitude,longitude used
                            to refine the results. If specified, the results
                            will be weighted to give preference to those near
                            the specified location in addition to considering
                            similarity to the suggest string. If omitted the
                            default behaviour is to weight results for
                            similarity to the suggest string only.
        :param int n_focus_results: Specifies the number of results (must be <= n_results)
                            within the results set which will have a focus. Defaults to
                            n_results. This allows you to run autosuggest with a mix of
                            focussed and unfocussed results, to give you a "blend" of the two.
        :param string clip_to_country: Restricts autosuggest to only return results inside the
                            countries specified by comma-separated list of uppercase ISO 3166-1
                            alpha-2 country codes (for example, to restrict to Belgium and the
                            UK, use clip_to_country="GB,BE")
        :param BoundingBox clip_to_bounding_box: Restrict autosuggest results to a bounding
                            box, specified by coordinates.
        :param Circle clip_to_circle: Restrict autosuggest results to a circle, specified by
                            Coordinates representing the center of the circle, and a distance in
                            kilometres which represents the radius. For convenience, longitude
                            is allowed to wrap around 180 degrees. For example 181 is equivalent
                            to -179.
        :param Coordinates[] clip_to_polygon: Restrict autosuggest results to a polygon,
                            specified by a list of Coordinates. The polygon
                            should be closed, i.e. the first element should be repeated as the
                            last element; also the list should contain at least 4 entries.
                            The API is currently limited to accepting up to 25 pairs.
        :param string input_type: For power users, used to specify voice input mode. Can be
                            text (default), vocon-hybrid, nmdp-asr or generic-voice.
        :param string language: A supported 3 word address language as an
                            ISO 639-1 2 letter code. Defaults to self.language
        :param string prefer_land: Makes autosuggest prefer results on land to those in the sea.
                            This setting is on by default. Use false to disable this setting and
                            receive more suggestions in the sea.

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v2/#autosuggest
        """

        params = {
            'input': input,
            'language': language or self.language,
        }
        if n_results:
            params.update({
                'n-results': '{0}'.format(n_results)
            })
        if focus:
            params.update({
                'focus': '{0},{1}'.format(focus.lat, focus.lng)
            })
        if n_focus_results:
            params.update({
                'n-focus-results': '{0}'.format(n_focus_results)
            })
        if clip_to_country:
            params.update({
                'clip-to-country': '{0}'.format(clip_to_country)
            })
        if clip_to_bounding_box:
            params.update({
                'clip-to-bounding-box': '{0},{1},{2},{3}'.format(clip_to_bounding_box.sw.lat, clip_to_bounding_box.sw.lng, clip_to_bounding_box.ne.lat, clip_to_bounding_box.ne.lng)
            })
        if clip_to_circle:
            params.update({
                'clip-to-circle': '{0},{1},{2}'.format(clip_to_circle.center.lat, clip_to_circle.center.lng, clip_to_circle.radius)
            })
        if clip_to_polygon:
            params.update({
                'clip-to-polygon': '{0}'.format(', '.join("{},{}".format(coord.lat, coord.lng) for coord in clip_to_polygon))
            })
        if input_type:
            params.update({
                'input-type': '{0}'.format(input_type)
            })
        if prefer_land is not None:
            params.update({
                'prefer-land': '{0}'.format(prefer_land)
            })

        return self._request('/autosuggest', params)

    def defaultLanguage(self, lang=None):
        """
        Sets/returns default language

        Params
        ------
        :param string lang: new default language

        :retype: string
        """
        if(lang is not None):
            self.language = lang
        return self.language

    def defaultEndpoint(self, end_point=None):
        """
        Sets/returns api endpoint

        Params
        ------
        :param string end_point: new api endpoint

        :retype: string url
        """
        if(end_point is not None):
            self.end_point = end_point
        return self.end_point

    def _request(self, url_path, params=None):
        """
        Executes request

        Params
        ------
        :param string url_path: API method URI
        :param dict params: parameters

        :rtype: dict
        """
        if params is None:
            params = {}

        params.update({
            'key': self.api_key,
        })
        url = self.end_point+url_path

        headers = {'X-W3W-Wrapper': 'what3words-Python/{} (Python {}; {})'.format(__version__, platform.python_version(), platform.platform())}
        r = requests.get(url, params=params, headers=headers)
        response = r.text
        return json.loads(response)

class Coordinates(object):
    """
    A Coordinate represents (latitude, longitude) coordinates encoded according to the World Geodetic System (WGS84).
    ==========
    """

    def __init__(self,lat,lng):
        """
        Constructor
        Params
        ------
        :param lat: the latitude
        :param lng: the longitude
        """
        self.lat = lat
        self.lng = lng

    def __eq__(self, other):
        return self.lng == other.lng and self.lat == other.lat

    def __str__(self):
        return '<{0}, {1}>'.format(self.lat, self.lng)

    def __repr__(self):
        return 'Coordinate({0}, {1})'.format(self.lat, self.lng)

class BoundingBox(object):
    """
    A BoundingBox represents which which represents a range of latitudes and longitudes.
    ==========
    """

    def __init__(self,sw,ne):
        """
        Constructor
        Params
        ------
        :param sw: the coordinates of the southwest corner
        :param ne: the coordinates of the northeast corner
        """
        self.sw = sw
        self.ne = ne

    def __eq__(self, other):
        return self.sw == other.sw and self.ne == other.ne

    def __str__(self):
        return '<{0}, {1}>'.format(self.sw, self.ne)

    def __repr__(self):
        return 'BoundingBox({0}, {1})'.format(repr(self.sw), repr(self.ne))

class Circle(object):
    """
    A Circle represented by center Coordinates, and a radius in kilometres
    ==========
    """

    def __init__(self,center,radius):
        """
        Constructor
        Params
        ------
        :param sw: the coordinates of the southwest corner
        :param ne: the coordinates of the northeast corner
        """
        self.center = center
        self.radius = radius

    def __eq__(self, other):
        return self.center == other.center and self.radius == other.radius

    def __str__(self):
        return '<{0}, {1}>'.format(self.center, self.radius)

    def __repr__(self):
        return 'BoundingBox({0}, {1})'.format(repr(self.center), repr(self.radius))
<commit_msg>added regex functions<commit_after>#!/usr/bin/python
# coding: utf8

import json
import requests
import platform
import re

from .version import __version__

class Geocoder(object):
    """
    What3Words v3 API wrapper
    ==========
    """

    def __init__(self, api_key, language='en',
                 end_point='https://api.what3words.com/v3'):
        """
        Constructor
        Params
        ------
        :param api_key: A valid API key
        :param language: default langauge use with the Geocoder
        :param end_point: what3words api end point
        """

        self.end_point = end_point
        self.api_key = api_key
        self.language = language

    def convert_to_coordinates(self, words, format='json'):
        """
        Take a 3 word address and turn it into a pair of coordinates.

        Params
        ------
        :param string words: A 3 word address as a string
        :param string format: Return data format type; can be one of
                              json (the default), geojson

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#convert-to-coordinates
        """

        params = {
            'words': words,
            'format': format,
        }

        return self._request('/convert-to-coordinates', params)

    def convert_to_3wa(self, coordinates, format='json', language=None):
        """
        Take latitude and longitude coordinates and turn them into
        a 3 word address.

        Params
        ------
        :param Coordinates coordinates: the coordinates of the location to convert to 3 word address
        :param float lng: longitude coordinate
        :param string format: Return data format type; can be one of
                              json (the default), geojson
        :param string language: A supported 3 word address language as an
                            ISO 639-1 2 letter code. Defaults to self.language

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#convert-to-3wa
        """

        params = {
            'coordinates': '{0},{1}'.format(coordinates.lat, coordinates.lng),
            'format': format,
            'language': language or self.language,
        }
        return self._request('/convert-to-3wa', params)

    def grid_section(self, bounding_box, format='json'):
        """
        Take latitude and longitude coordinates and turn them into
        a 3 word address.

        Params
        ------
        :param BoundingBox bbox: Bounding box, specified by the northeast and
                            southwest corner coordinates, for which the grid
                            should be returned.
        :param string format: Return data format type; can be one of
                              json (the default), geojson
        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#grid-section
        """

        params = {
            'bounding-box': '{0},{1},{2},{3}'.format(bounding_box.sw.lat, bounding_box.sw.lng, bounding_box.ne.lat, bounding_box.ne.lng),
            'format': format
        }
        return self._request('/grid-section', params)

    def available_languages(self):
        """
        Retrieve a list of available 3 word languages.

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v3/#available-languages
        """

        return self._request('/available-languages')

    def autosuggest(self,
        input,
        n_results=None,
        focus=None,
        n_focus_results=None,
        clip_to_country=None,
        clip_to_bounding_box=None,
        clip_to_circle=None,
        clip_to_polygon=None,
        input_type=None,
        language=None,
        prefer_land=None):
        """
        Returns a list of 3 word addresses based on user input and other
        parameters.

        Params
        ------
        :param string input: The full or partial 3 word address to obtain
                            suggestions for. At minimum this must be the
                            first two complete words plus at least one
                            character from the third word
        :param int n_results: The number of AutoSuggest results to return. A maximum of 100
                            results can be specified, if a number greater than this is
                            requested, this will be truncated to the maximum. The default is 3
        :param Coordinates focus: A location, specified as a latitude,longitude used
                            to refine the results. If specified, the results
                            will be weighted to give preference to those near
                            the specified location in addition to considering
                            similarity to the suggest string. If omitted the
                            default behaviour is to weight results for
                            similarity to the suggest string only.
        :param int n_focus_results: Specifies the number of results (must be <= n_results)
                            within the results set which will have a focus. Defaults to
                            n_results. This allows you to run autosuggest with a mix of
                            focussed and unfocussed results, to give you a "blend" of the two.
        :param string clip_to_country: Restricts autosuggest to only return results inside the
                            countries specified by comma-separated list of uppercase ISO 3166-1
                            alpha-2 country codes (for example, to restrict to Belgium and the
                            UK, use clip_to_country="GB,BE")
        :param BoundingBox clip_to_bounding_box: Restrict autosuggest results to a bounding
                            box, specified by coordinates.
        :param Circle clip_to_circle: Restrict autosuggest results to a circle, specified by
                            Coordinates representing the center of the circle, and a distance in
                            kilometres which represents the radius. For convenience, longitude
                            is allowed to wrap around 180 degrees. For example 181 is equivalent
                            to -179.
        :param Coordinates[] clip_to_polygon: Restrict autosuggest results to a polygon,
                            specified by a list of Coordinates. The polygon
                            should be closed, i.e. the first element should be repeated as the
                            last element; also the list should contain at least 4 entries.
                            The API is currently limited to accepting up to 25 pairs.
        :param string input_type: For power users, used to specify voice input mode. Can be
                            text (default), vocon-hybrid, nmdp-asr or generic-voice.
        :param string language: A supported 3 word address language as an
                            ISO 639-1 2 letter code. Defaults to self.language
        :param string prefer_land: Makes autosuggest prefer results on land to those in the sea.
                            This setting is on by default. Use false to disable this setting and
                            receive more suggestions in the sea.

        :rtype: dict

        References
        ----------
        API Reference: https://docs.what3words.com/api/v2/#autosuggest
        """

        params = {
            'input': input,
            'language': language or self.language,
        }
        if n_results:
            params.update({
                'n-results': '{0}'.format(n_results)
            })
        if focus:
            params.update({
                'focus': '{0},{1}'.format(focus.lat, focus.lng)
            })
        if n_focus_results:
            params.update({
                'n-focus-results': '{0}'.format(n_focus_results)
            })
        if clip_to_country:
            params.update({
                'clip-to-country': '{0}'.format(clip_to_country)
            })
        if clip_to_bounding_box:
            params.update({
                'clip-to-bounding-box': '{0},{1},{2},{3}'.format(clip_to_bounding_box.sw.lat, clip_to_bounding_box.sw.lng, clip_to_bounding_box.ne.lat, clip_to_bounding_box.ne.lng)
            })
        if clip_to_circle:
            params.update({
                'clip-to-circle': '{0},{1},{2}'.format(clip_to_circle.center.lat, clip_to_circle.center.lng, clip_to_circle.radius)
            })
        if clip_to_polygon:
            params.update({
                'clip-to-polygon': '{0}'.format(', '.join("{},{}".format(coord.lat, coord.lng) for coord in clip_to_polygon))
            })
        if input_type:
            params.update({
                'input-type': '{0}'.format(input_type)
            })
        if prefer_land is not None:
            params.update({
                'prefer-land': '{0}'.format(prefer_land)
            })

        return self._request('/autosuggest', params)

    def defaultLanguage(self, lang=None):
        """
        Sets/returns default language

        Params
        ------
        :param string lang: new default language

        :retype: string
        """
        if(lang is not None):
            self.language = lang
        return self.language

    def defaultEndpoint(self, end_point=None):
        """
        Sets/returns api endpoint

        Params
        ------
        :param string end_point: new api endpoint

        :retype: string url
        """
        if(end_point is not None):
            self.end_point = end_point
        return self.end_point

    def _request(self, url_path, params=None):
        """
        Executes request

        Params
        ------
        :param string url_path: API method URI
        :param dict params: parameters

        :rtype: dict
        """
        if params is None:
            params = {}

        params.update({
            'key': self.api_key,
        })
        url = self.end_point+url_path

        headers = {'X-W3W-Wrapper': 'what3words-Python/{} (Python {}; {})'.format(__version__, platform.python_version(), platform.platform())}
        r = requests.get(url, params=params, headers=headers)
        response = r.text
        return json.loads(response)

    def isPossible3wa(self, text):
        regex_match = "^/*(?:[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}[.｡。･・︒។։။۔።।][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}[.｡。･・︒។։။۔።।][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}|'<,.>?/\";:£§º©®\s]+[.｡。･・︒។։။۔።।][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]+|[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]+([\u0020\u00A0][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]+){1,3}[.｡。･・︒។։။۔።।][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]+([\u0020\u00A0][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]+){1,3}[.｡。･・︒។։။۔።।][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]+([\u0020\u00A0][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]+){1,3})$"
        return not (None == re.match(regex_match, text))

    def findPossible3wa(self, text):
        regex_search = "[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}[.｡。･・︒។։။۔።।][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}[.｡。･・︒។։။۔።।][^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}"
        return re.findall(regex_search, text, flags=re.UNICODE)

    def didYouMean(self, text):
        regex_match = "^/*[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}[.｡。･・︒។։။۔።। ,\\-_/+'&\\:;|　-]{1,2}[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}[.｡。･・︒។։။۔።। ,\\-_/+'&\\:;|　-]{1,2}[^0-9`~!@#$%^&*()+\-_=[{\]}\\|'<,.>?/\";:£§º©®\s]{1,}$"
        return not (None == re.match(regex_match, text))

class Coordinates(object):
    """
    A Coordinate represents (latitude, longitude) coordinates encoded according to the World Geodetic System (WGS84).
    ==========
    """

    def __init__(self,lat,lng):
        """
        Constructor
        Params
        ------
        :param lat: the latitude
        :param lng: the longitude
        """
        self.lat = lat
        self.lng = lng

    def __eq__(self, other):
        return self.lng == other.lng and self.lat == other.lat

    def __str__(self):
        return '<{0}, {1}>'.format(self.lat, self.lng)

    def __repr__(self):
        return 'Coordinate({0}, {1})'.format(self.lat, self.lng)

class BoundingBox(object):
    """
    A BoundingBox represents which which represents a range of latitudes and longitudes.
    ==========
    """

    def __init__(self,sw,ne):
        """
        Constructor
        Params
        ------
        :param sw: the coordinates of the southwest corner
        :param ne: the coordinates of the northeast corner
        """
        self.sw = sw
        self.ne = ne

    def __eq__(self, other):
        return self.sw == other.sw and self.ne == other.ne

    def __str__(self):
        return '<{0}, {1}>'.format(self.sw, self.ne)

    def __repr__(self):
        return 'BoundingBox({0}, {1})'.format(repr(self.sw), repr(self.ne))

class Circle(object):
    """
    A Circle represented by center Coordinates, and a radius in kilometres
    ==========
    """

    def __init__(self,center,radius):
        """
        Constructor
        Params
        ------
        :param sw: the coordinates of the southwest corner
        :param ne: the coordinates of the northeast corner
        """
        self.center = center
        self.radius = radius

    def __eq__(self, other):
        return self.center == other.center and self.radius == other.radius

    def __str__(self):
        return '<{0}, {1}>'.format(self.center, self.radius)

    def __repr__(self):
        return 'BoundingBox({0}, {1})'.format(repr(self.center), repr(self.radius))
<|endoftext|>


<commit_before>"""
api/models.py

Define the objects that will be stored in the database and later served through
the API.

https://docs.djangoproject.com/en/1.11/topics/db/models/
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

# Python stdlib Imports
import datetime

# Django Imports
from django.db import models
from django.contrib.gis.db.models import PointField
from django.contrib.auth.models import User
from django.core.validators import RegexValidator

# Other Imports
from model_utils.models import TimeStampedModel
from autoslug import AutoSlugField
from taggit.managers import TaggableManager

class Category(TimeStampedModel):
    """
    Represents the "category" that a Facility falls under. A Category is a
    grouping of Facilities that serve a common/similar purpose.

    ex.
    - Dining
    - Gyms
    - Study areas (Libraries, The Ridge, JC, etc)
    """
    # The name of the category
    name = models.CharField(max_length=100)

    class Meta:
        verbose_name = "category"
        verbose_name_plural = "categories"
        # Sort by name in admin view.
        ordering = ['name']

    def __str__(self):
        """
        String representation of a Category object.
        """
        return self.name

class Location(TimeStampedModel):
    """
    Represents a specific location that a Facility can be found.
    """
    # The building that the facility is located in (on campus).
    building = models.CharField(max_length=100)
    # The physical address of the facility.
    address = models.CharField(max_length=100)
    # Boolean for whether or not the location is "on campus" or not.
    on_campus = models.BooleanField(default=True)
    # A GeoJson coordinate pair that represents the physical location
    coordinate_location = PointField()

    class Meta:
        verbose_name = "location"
        verbose_name_plural = "locations"

    def __str__(self):
        """
        String representation of a Location object.
        """
        return 'Found in %s at %s | On Campus: %s' % (self.building,
                                                      self.address,
                                                      self.on_campus)

class Facility(TimeStampedModel):
    """
    Represents a specific facility location. A Facility is some type of
    establishment that has a schedule of open hours and a location that serves
    a specific purpose that can be categorized.
    """
    # The name of the Facility
    name = models.CharField(max_length=100)
    # Instead of id
    slug = AutoSlugField(populate_from='name', unique=True)

    # The category that this facility can be grouped with
    facility_category = models.ForeignKey('Category',
                                          related_name="categories")
    # The location object that relates to this facility
    facility_location = models.ForeignKey('Location',
                                          related_name="facilities")

    # The User(s) that claim ownership over this facility
    owners = models.ManyToManyField(User)

    # The schedule that is defaulted to if no special schedule is in effect
    main_schedule = models.ForeignKey('Schedule',
                                      related_name='facility_main')
    # A schedule that has a specific start and end date
    special_schedules = models.ManyToManyField('Schedule',
                                               related_name='facility_special',
                                               blank=True,
                                               help_text="""This schedule will
                                                            come into effect
                                                            only for its
                                                            specified duration.
                                                            """)
    # URL, if it exists, to the Tapingo page that is associated with this
    # facility
    tapingo_url = models.URLField(blank=True, validators=[RegexValidator(regex='^https:\/\/www.tapingo.com\/',
                                                                         message='The link is not a valid tapingo link. Example: https://www.tapingo.com/order/restaurant/starbucks-gmu-johnson/',
                                                                         code='invalid_tapingo_url')])
    # A comma seperate list of words that neatly an aptly describe the product
    # that this facility produces. (ex. for Taco Bell: mexican, taco, cheap)
    facility_product_tags = TaggableManager()

    def is_open(self):
        """
        Return true if this facility is currently open.

        First checks any valid special schedules and then checks the main,
        default, schedule.
        """
        # Get the current date
        today = datetime.datetime.today().date()
        # Check special schedules first, loop through all of them
        for schedule in self.special_schedules.all():
            # Special schedules must have valid_start and valid_end set
            if schedule.valid_start and schedule.valid_end:
                # If a special schedule in in effect
                if schedule.valid_start <= today <= schedule.valid_end:
                    # Check if the facility is open or not based on that
                    # special schedule
                    if schedule.is_open_now():
                        # Open
                        return True
                    else:
                        # Closed
                        return False
        # If no special schedule is in effect then check if the facility is
        # open using the main_schedule
        if self.main_schedule.is_open_now():
            # Open
            return True
        else:
            # Closed
            return False

    class Meta:
        verbose_name = "facility"
        verbose_name_plural = "facilities"
        # Sort by name in admin view
        ordering = ['name']

    def __str__(self):
        """
        String representation of a Facility object.
        """
        return self.name

class Schedule(TimeStampedModel):
    """
    A period of time between two dates that represents the beginning and end of
    a "schedule" or rather, a collection of open times for a facility.
    """
    # The name of the schedule
    name = models.CharField(max_length=100)

    # The start date of the schedule
    # (inclusive)
    valid_start = models.DateField('Start Date', null=True, blank=True,
                                   help_text="""Date that this schedule goes
                                                into effect""")
    # The end date of the schedule
    # (inclusive)
    valid_end = models.DateField('End Date', null=True, blank=True,
                                 help_text="""Last day that this schedule is
                                              in effect""")

    def is_open_now(self):
        """
        Return true if this schedule is open right now.
        """
        # Loop through all the open times that correspond to this schedule
        for open_time in OpenTime.objects.filter(schedule=self):
            # If the current time we are looking at is open, then the schedule
            # will say that the facility is open
            if open_time.is_open_now():
                # Open
                return True
        # Closed (all open times are not open)
        return False

    class Meta:
        # Sort by name in admin view
        ordering = ['name']

    def __str__(self):
        """
        String representation of a Schedule object.
        """
        return self.name

class OpenTime(TimeStampedModel):
    """
    Represents a time period when a Facility is open.

    Monday = 0, Sunday = 6.
    """
    # Define integer constants to represent days of the week
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6

    # Tuple that ties a day of the week with an integer representation
    DAY_CHOICES = (
        (MONDAY, 'Monday'),
        (TUESDAY, 'Tuesday'),
        (WEDNESDAY, 'Wednesday'),
        (THURSDAY, 'Thursday'),
        (FRIDAY, 'Friday'),
        (SATURDAY, 'Saturday'),
        (SUNDAY, 'Sunday'),
    )

    # The schedule that this period of open time is a part of
    schedule = models.ForeignKey('Schedule', related_name='open_times')

    # The day that the open time begins on
    start_day = models.IntegerField(default=0, choices=DAY_CHOICES)
    # The day that the open time ends on
    end_day = models.IntegerField(default=0, choices=DAY_CHOICES)

    # The time of day that the open time begins at
    start_time = models.TimeField()
    # The time of day that the open time ends
    end_time = models.TimeField()

    def is_open_now(self):
        """
        Return true if the current time is this OpenTime's range.
        """
        # Get the current datetime
        today = datetime.datetime.today()
        # Check that the start occurs before the end
        if self.start_day <= self.end_day:
            # If today is the start_day
            if self.start_day == today.weekday():
                # If the start_time has not occurred
                if self.start_time > today.time():
                    # Closed
                    return False
            # If the start_day has not occurred
            elif self.start_day > today.weekday():
                # Closed
                return False
            # If the end_day is today
            if self.end_day == today.weekday():
                # If the end_time has already occurred
                if self.end_time < today.time():
                    # Closed
                    return False
            # If the end_day has already occurred
            elif self.end_day < today.weekday():
                # Closed
                return False
        # The end_day > start_day
        else:
            # If today is the start_day
            if self.start_day == today.weekday():
                # If the start_time has not occurred
                if self.start_time > today.time():
                    # Closed
                    return False
            # If the end_day is today
            if self.end_day == today.weekday():
                # If the end_time has already occurred
                if self.end_time < today.time():
                    # Closed
                    return False
            # If the current date takes place after the end_date but before
            # start_day
            if self.end_day < today.weekday() < self.start_day:
                # Closed
                return False
        # All checks passed, it's Open
        return True

    def __str__(self):
        """
        String representation of a OpenTime object.
        """
        weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
                    'Saturday', 'Sunday']
        return '%s %s to %s %s' % (weekdays[self.start_day],
                                   self.start_time.strftime("%H:%M:%S"),
                                   # to
                                   weekdays[self.end_day],
                                   self.end_time.strftime("%H:%M:%S"))

class Alert(TimeStampedModel):
    """
    Some type of notification that is displayed to clients that conveys a
    message. Past examples include: random closings, modified schedules being
    in effect, election reminder, advertising for other SRCT projects.

    Alerts last for a period of time until the information is no longer dank.
    """
    # Define string constants to represent urgency tag levels
    INFO = 'info'  # SRCT announcements
    MINOR = 'minor'  # Holiday hours are in effect
    MAJOR = 'major'  # The hungry patriot is closed today
    EMERGENCY = 'emergency'  # Extreme weather

    # Tuple that ties a urgency tag with a string representation
    URGENCY_CHOICES = (
        (INFO, 'Info'),
        (MINOR, 'Minor'),
        (MAJOR, 'Major'),
        (EMERGENCY, 'Emergency'),
    )

    # The urgency tag for this Alert
    urgency_tag = models.CharField(max_length=10, default='Info',
                                   choices=URGENCY_CHOICES)

    # The text that is displayed that describes the Alert
    message = models.CharField(max_length=140)

    # The date + time that the alert will be start being served
    start_datetime = models.DateTimeField()

    # The date + time that the alert will stop being served
    end_datetime = models.DateTimeField()

    def __str__(self):
        """
        String representation of an Alert object.
        """
        return "%s" % (self.message)
<commit_msg>Add function that cleans old schedules<commit_after>"""
api/models.py

Define the objects that will be stored in the database and later served through
the API.

https://docs.djangoproject.com/en/1.11/topics/db/models/
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

# Python stdlib Imports
import datetime

# Django Imports
from django.db import models
from django.contrib.gis.db.models import PointField
from django.contrib.auth.models import User
from django.core.validators import RegexValidator

# Other Imports
from model_utils.models import TimeStampedModel
from autoslug import AutoSlugField
from taggit.managers import TaggableManager

class Category(TimeStampedModel):
    """
    Represents the "category" that a Facility falls under. A Category is a
    grouping of Facilities that serve a common/similar purpose.

    ex.
    - Dining
    - Gyms
    - Study areas (Libraries, The Ridge, JC, etc)
    """
    # The name of the category
    name = models.CharField(max_length=100)

    class Meta:
        verbose_name = "category"
        verbose_name_plural = "categories"
        # Sort by name in admin view.
        ordering = ['name']

    def __str__(self):
        """
        String representation of a Category object.
        """
        return self.name

class Location(TimeStampedModel):
    """
    Represents a specific location that a Facility can be found.
    """
    # The building that the facility is located in (on campus).
    building = models.CharField(max_length=100)
    # The physical address of the facility.
    address = models.CharField(max_length=100)
    # Boolean for whether or not the location is "on campus" or not.
    on_campus = models.BooleanField(default=True)
    # A GeoJson coordinate pair that represents the physical location
    coordinate_location = PointField()

    class Meta:
        verbose_name = "location"
        verbose_name_plural = "locations"

    def __str__(self):
        """
        String representation of a Location object.
        """
        return 'Found in %s at %s | On Campus: %s' % (self.building,
                                                      self.address,
                                                      self.on_campus)

class Facility(TimeStampedModel):
    """
    Represents a specific facility location. A Facility is some type of
    establishment that has a schedule of open hours and a location that serves
    a specific purpose that can be categorized.
    """
    # The name of the Facility
    name = models.CharField(max_length=100)
    # Instead of id
    slug = AutoSlugField(populate_from='name', unique=True)

    # The category that this facility can be grouped with
    facility_category = models.ForeignKey('Category',
                                          related_name="categories")
    # The location object that relates to this facility
    facility_location = models.ForeignKey('Location',
                                          related_name="facilities")

    # The User(s) that claim ownership over this facility
    owners = models.ManyToManyField(User)

    # The schedule that is defaulted to if no special schedule is in effect
    main_schedule = models.ForeignKey('Schedule',
                                      related_name='facility_main')
    # A schedule that has a specific start and end date
    special_schedules = models.ManyToManyField('Schedule',
                                               related_name='facility_special',
                                               blank=True,
                                               help_text="""This schedule will
                                                            come into effect
                                                            only for its
                                                            specified duration.
                                                            """)
    # URL, if it exists, to the Tapingo page that is associated with this
    # facility
    tapingo_url = models.URLField(blank=True, validators=[RegexValidator(regex='^https:\/\/www.tapingo.com\/',
                                                                         message='The link is not a valid tapingo link. Example: https://www.tapingo.com/order/restaurant/starbucks-gmu-johnson/',
                                                                         code='invalid_tapingo_url')])
    # A comma seperate list of words that neatly an aptly describe the product
    # that this facility produces. (ex. for Taco Bell: mexican, taco, cheap)
    facility_product_tags = TaggableManager()

    def is_open(self):
        """
        Return true if this facility is currently open.

        First checks any valid special schedules and then checks the main,
        default, schedule.
        """
        # Get the current date
        today = datetime.datetime.today().date()
        # Check special schedules first, loop through all of them
        for schedule in self.special_schedules.all():
            # Special schedules must have valid_start and valid_end set
            if schedule.valid_start and schedule.valid_end:
                # If a special schedule in in effect
                if schedule.valid_start <= today <= schedule.valid_end:
                    # Check if the facility is open or not based on that
                    # special schedule
                    if schedule.is_open_now():
                        # Open
                        return True
                    else:
                        # Closed
                        return False
        # If no special schedule is in effect then check if the facility is
        # open using the main_schedule
        if self.main_schedule.is_open_now():
            # Open
            return True
        else:
            # Closed
            return False

    def clean_special_schedules(self):
        """
        Loop through every special_schedule and remove entries that have
        expired.
        """
        for special_schedule in self.special_schedules.all():
            # If it ends before today
            if special_schedule.valid_end < datetime.date.today():
                self.special_schedules.remove(special_schedule)

    class Meta:
        verbose_name = "facility"
        verbose_name_plural = "facilities"
        # Sort by name in admin view
        ordering = ['name']

    def __str__(self):
        """
        String representation of a Facility object.
        """
        return self.name

class Schedule(TimeStampedModel):
    """
    A period of time between two dates that represents the beginning and end of
    a "schedule" or rather, a collection of open times for a facility.
    """
    # The name of the schedule
    name = models.CharField(max_length=100)

    # The start date of the schedule
    # (inclusive)
    valid_start = models.DateField('Start Date', null=True, blank=True,
                                   help_text="""Date that this schedule goes
                                                into effect""")
    # The end date of the schedule
    # (inclusive)
    valid_end = models.DateField('End Date', null=True, blank=True,
                                 help_text="""Last day that this schedule is
                                              in effect""")

    def is_open_now(self):
        """
        Return true if this schedule is open right now.
        """
        # Loop through all the open times that correspond to this schedule
        for open_time in OpenTime.objects.filter(schedule=self):
            # If the current time we are looking at is open, then the schedule
            # will say that the facility is open
            if open_time.is_open_now():
                # Open
                return True
        # Closed (all open times are not open)
        return False

    class Meta:
        # Sort by name in admin view
        ordering = ['name']

    def __str__(self):
        """
        String representation of a Schedule object.
        """
        return self.name

class OpenTime(TimeStampedModel):
    """
    Represents a time period when a Facility is open.

    Monday = 0, Sunday = 6.
    """
    # Define integer constants to represent days of the week
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6

    # Tuple that ties a day of the week with an integer representation
    DAY_CHOICES = (
        (MONDAY, 'Monday'),
        (TUESDAY, 'Tuesday'),
        (WEDNESDAY, 'Wednesday'),
        (THURSDAY, 'Thursday'),
        (FRIDAY, 'Friday'),
        (SATURDAY, 'Saturday'),
        (SUNDAY, 'Sunday'),
    )

    # The schedule that this period of open time is a part of
    schedule = models.ForeignKey('Schedule', related_name='open_times')

    # The day that the open time begins on
    start_day = models.IntegerField(default=0, choices=DAY_CHOICES)
    # The day that the open time ends on
    end_day = models.IntegerField(default=0, choices=DAY_CHOICES)

    # The time of day that the open time begins at
    start_time = models.TimeField()
    # The time of day that the open time ends
    end_time = models.TimeField()

    def is_open_now(self):
        """
        Return true if the current time is this OpenTime's range.
        """
        # Get the current datetime
        today = datetime.datetime.today()
        # Check that the start occurs before the end
        if self.start_day <= self.end_day:
            # If today is the start_day
            if self.start_day == today.weekday():
                # If the start_time has not occurred
                if self.start_time > today.time():
                    # Closed
                    return False
            # If the start_day has not occurred
            elif self.start_day > today.weekday():
                # Closed
                return False
            # If the end_day is today
            if self.end_day == today.weekday():
                # If the end_time has already occurred
                if self.end_time < today.time():
                    # Closed
                    return False
            # If the end_day has already occurred
            elif self.end_day < today.weekday():
                # Closed
                return False
        # The end_day > start_day
        else:
            # If today is the start_day
            if self.start_day == today.weekday():
                # If the start_time has not occurred
                if self.start_time > today.time():
                    # Closed
                    return False
            # If the end_day is today
            if self.end_day == today.weekday():
                # If the end_time has already occurred
                if self.end_time < today.time():
                    # Closed
                    return False
            # If the current date takes place after the end_date but before
            # start_day
            if self.end_day < today.weekday() < self.start_day:
                # Closed
                return False
        # All checks passed, it's Open
        return True

    def __str__(self):
        """
        String representation of a OpenTime object.
        """
        weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
                    'Saturday', 'Sunday']
        return '%s %s to %s %s' % (weekdays[self.start_day],
                                   self.start_time.strftime("%H:%M:%S"),
                                   # to
                                   weekdays[self.end_day],
                                   self.end_time.strftime("%H:%M:%S"))

class Alert(TimeStampedModel):
    """
    Some type of notification that is displayed to clients that conveys a
    message. Past examples include: random closings, modified schedules being
    in effect, election reminder, advertising for other SRCT projects.

    Alerts last for a period of time until the information is no longer dank.
    """
    # Define string constants to represent urgency tag levels
    INFO = 'info'  # SRCT announcements
    MINOR = 'minor'  # Holiday hours are in effect
    MAJOR = 'major'  # The hungry patriot is closed today
    EMERGENCY = 'emergency'  # Extreme weather

    # Tuple that ties a urgency tag with a string representation
    URGENCY_CHOICES = (
        (INFO, 'Info'),
        (MINOR, 'Minor'),
        (MAJOR, 'Major'),
        (EMERGENCY, 'Emergency'),
    )

    # The urgency tag for this Alert
    urgency_tag = models.CharField(max_length=10, default='Info',
                                   choices=URGENCY_CHOICES)

    # The text that is displayed that describes the Alert
    message = models.CharField(max_length=140)

    # The date + time that the alert will be start being served
    start_datetime = models.DateTimeField()

    # The date + time that the alert will stop being served
    end_datetime = models.DateTimeField()

    def __str__(self):
        """
        String representation of an Alert object.
        """
        return "%s" % (self.message)
<|endoftext|>


<commit_before>from numpy import inf
from numpy.random import RandomState
from numpy.testing import assert_allclose

import pytest
from glimix_core.lmm import MTLMM
from numpy_sugar.linalg import economic_qs_linear

def test_lmm_mt_interface():
    random = RandomState(0)
    y0 = random.randn(4)
    y1 = random.randn(5)
    X = random.randn(5, 2)
    G = random.randn(5, 6)

    QS = economic_qs_linear(G)
    with pytest.raises(ValueError):
        MTLMM([y0, y1], X, QS)

    with pytest.raises(ValueError):
        MTLMM([y1, y0], X, QS)

    y0 = random.randn(5)

    with pytest.raises(ValueError):
        MTLMM([y1, y0], X, QS)

    X0 = random.randn(5, 2)
    X1 = random.randn(5, 3)

    G1 = random.randn(6, 6)
    QS1 = economic_qs_linear(G1)

    with pytest.raises(ValueError):
        MTLMM([y1, y0], [X0, X1], QS1)

    lmm = MTLMM([y0, y1], [X0, X1], QS)
    lml = lmm.lml()

    assert_allclose(lml, -7.713526905947342)

    lmm = MTLMM(y0, X0, QS)
    lml = lmm.lml()
    assert_allclose(lml, -5.721633217309584)

    y0[3] = inf
    with pytest.raises(ValueError):
        MTLMM([y1, y0], [X0, X1], QS1)

def test_lmm_mt_interface_pandas():
    from pandas import Series, DataFrame

    random = RandomState(0)
    y = Series(random.randn(5))
    X = DataFrame(random.randn(5, 2))
    G = random.randn(5, 6)

    QS = economic_qs_linear(G)
    lmm = MTLMM([y, y], [X, X], QS)
    assert_allclose(lmm.lml(), -10.474952610114517)

def test_lmm_interface_pandas1():
    from glimix_core.lmm import LMM
    from pandas import Series, DataFrame

    random = RandomState(0)
    y = Series(random.randn(5))
    X = DataFrame(random.randn(5, 2))
    G = random.randn(5, 6)

    QS = economic_qs_linear(G)
    lmm = LMM(y, X, QS)

    assert_allclose(lmm.lml(), -6.7985689491)

def test_lmm_interface_pandas2():
    from pandas import Series, DataFrame

    random = RandomState(0)
    y = Series(random.randn(5))
    X = DataFrame(random.randn(5, 2))
    G = random.randn(5, 6)

    QS = economic_qs_linear(G)
    lmm = MTLMM(y, X, QS)

    assert_allclose(lmm.lml(), -6.7985689491)
<commit_msg>prepare to merge into develop<commit_after>from numpy import inf
from numpy.random import RandomState
from numpy.testing import assert_allclose

import pytest
from glimix_core.lmm import MTLMM
from numpy_sugar.linalg import economic_qs_linear

def test_lmm_mt_interface():
    random = RandomState(0)
    y0 = random.randn(4)
    y1 = random.randn(5)
    X = random.randn(5, 2)
    G = random.randn(5, 6)

    QS = economic_qs_linear(G)
    with pytest.raises(ValueError):
        MTLMM([y0, y1], X, QS)

    with pytest.raises(ValueError):
        MTLMM([y1, y0], X, QS)

    y0 = random.randn(5)

    with pytest.raises(ValueError):
        MTLMM([y1, y0], X, QS)

    X0 = random.randn(5, 2)
    X1 = random.randn(5, 3)

    G1 = random.randn(6, 6)
    QS1 = economic_qs_linear(G1)

    with pytest.raises(ValueError):
        MTLMM([y1, y0], [X0, X1], QS1)

    lmm = MTLMM([y0, y1], [X0, X1], QS)
    lml = lmm.lml()

    assert_allclose(lml, -7.713526905947342)

    lmm = MTLMM(y0, X0, QS)
    lml = lmm.lml()
    assert_allclose(lml, -5.721633217309584)

    y0[3] = inf
    with pytest.raises(ValueError):
        MTLMM([y1, y0], [X0, X1], QS1)

def test_lmm_mt_interface_pandas1():
    from pandas import Series, DataFrame

    random = RandomState(0)
    y = Series(random.randn(5))
    X = DataFrame(random.randn(5, 2))
    G = random.randn(5, 6)

    QS = economic_qs_linear(G)
    lmm = MTLMM([y, y], [X, X], QS)
    assert_allclose(lmm.lml(), -10.474952610114517)

def test_lmm_interface_pandas2():
    from pandas import Series, DataFrame

    random = RandomState(0)
    y = Series(random.randn(5))
    X = DataFrame(random.randn(5, 2))
    G = random.randn(5, 6)

    QS = economic_qs_linear(G)
    lmm = MTLMM(y, X, QS)

    assert_allclose(lmm.lml(), -6.7985689491)
<|endoftext|>


<commit_before># Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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

from __future__ import absolute_import
import os
import signal
import tempfile

from eventlet.green import subprocess

from st2common.constants.scheduler import SCHEDULER_ENABLED_LOG_LINE, SCHEDULER_DISABLED_LOG_LINE
from st2tests.base import IntegrationTestCase
from st2tests.base import CleanDbTestCase

__all__ = [
    'SchedulerEnableDisableTestCase'
]

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ST2_CONFIG_PATH = os.path.join(BASE_DIR, '../../../conf/st2.tests.conf')
ST2_CONFIG_PATH = os.path.abspath(ST2_CONFIG_PATH)
BINARY = os.path.join(BASE_DIR, '../../../st2actions/bin/st2notifier')
BINARY = os.path.abspath(BINARY)
CMD = [BINARY, '--config-file']

class SchedulerEnableDisableTestCase(IntegrationTestCase, CleanDbTestCase):
    @classmethod
    def setUpClass(cls):
        super(SchedulerEnableDisableTestCase, cls).setUpClass()

    def setUp(self):
        super(SchedulerEnableDisableTestCase, self).setUp()
        config_text = open(ST2_CONFIG_PATH).read()
        self.cfg_fd, self.cfg_path = tempfile.mkstemp()
        with open(self.cfg_path, 'w') as f:
            f.write(config_text)
        self.cmd = []
        self.cmd.extend(CMD)
        self.cmd.append(self.cfg_path)

    def tearDown(self):
        self.cmd = None
        self._remove_tempfile(self.cfg_fd, self.cfg_path)
        super(SchedulerEnableDisableTestCase, self).tearDown()

    def test_scheduler_enable_implicit(self):
        process = None
        try:
            process = self._start_notifier(cmd=self.cmd)
            lines = 0
            while lines < 100:
                line = process.stdout.readline()
                lines += 1
                if SCHEDULER_ENABLED_LOG_LINE in line:
                    self.assertTrue(True)
                    break
        finally:
            if process:
                process.send_signal(signal.SIGKILL)
                self.remove_process(process=process)

    def test_scheduler_enable_explicit(self):
        self._append_to_cfg_file(cfg_path=self.cfg_path, content='\n[scheduler]\nenable = True')
        process = None
        try:
            process = self._start_notifier(cmd=self.cmd)
            lines = 0
            while lines < 100:
                line = process.stdout.readline()
                lines += 1
                if SCHEDULER_ENABLED_LOG_LINE in line:
                    self.assertTrue(True)
                    break
        finally:
            if process:
                process.send_signal(signal.SIGKILL)
                self.remove_process(process=process)

    def test_scheduler_disable_explicit(self):
        self._append_to_cfg_file(cfg_path=self.cfg_path, content='\n[scheduler]\nenable = False')
        process = None
        try:
            process = self._start_notifier(cmd=self.cmd)
            lines = 0
            while lines < 100:
                line = process.stdout.readline()
                lines += 1
                if SCHEDULER_DISABLED_LOG_LINE in line:
                    self.assertTrue(True)
                    break
        finally:
            if process:
                process.send_signal(signal.SIGKILL)
                self.remove_process(process=process)

    def _start_notifier(self, cmd):
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                   shell=False, preexec_fn=os.setsid)
        self.add_process(process=process)
        return process

    def _append_to_cfg_file(self, cfg_path, content):
        with open(cfg_path, 'a') as f:
            f.write(content)

    def _remove_tempfile(self, fd, path):
        os.close(fd)
        os.unlink(path)
<commit_msg>Remove unused method, use kill_process() to kill the process.<commit_after># Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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

from __future__ import absolute_import

import os
import signal
import tempfile

from eventlet.green import subprocess

from st2common.constants.scheduler import SCHEDULER_ENABLED_LOG_LINE, SCHEDULER_DISABLED_LOG_LINE
from st2common.util.shell import kill_process

from st2tests.base import IntegrationTestCase
from st2tests.base import CleanDbTestCase

__all__ = [
    'SchedulerEnableDisableTestCase'
]

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ST2_CONFIG_PATH = os.path.join(BASE_DIR, '../../../conf/st2.tests.conf')
ST2_CONFIG_PATH = os.path.abspath(ST2_CONFIG_PATH)
BINARY = os.path.join(BASE_DIR, '../../../st2actions/bin/st2notifier')
BINARY = os.path.abspath(BINARY)
CMD = [BINARY, '--config-file']

class SchedulerEnableDisableTestCase(IntegrationTestCase, CleanDbTestCase):
    def setUp(self):
        super(SchedulerEnableDisableTestCase, self).setUp()

        config_text = open(ST2_CONFIG_PATH).read()
        self.cfg_fd, self.cfg_path = tempfile.mkstemp()
        with open(self.cfg_path, 'w') as f:
            f.write(config_text)
        self.cmd = []
        self.cmd.extend(CMD)
        self.cmd.append(self.cfg_path)

    def tearDown(self):
        super(SchedulerEnableDisableTestCase, self).tearDown()
        self.cmd = None
        self._remove_tempfile(self.cfg_fd, self.cfg_path)

    def test_scheduler_enable_implicit(self):
        process = None
        try:
            process = self._start_notifier(cmd=self.cmd)
            lines = 0
            while lines < 100:
                line = process.stdout.readline()
                lines += 1
                if SCHEDULER_ENABLED_LOG_LINE in line:
                    self.assertTrue(True)
                    break
        finally:
            if process:
                kill_process(process)
                self.remove_process(process=process)

    def test_scheduler_enable_explicit(self):
        self._append_to_cfg_file(cfg_path=self.cfg_path, content='\n[scheduler]\nenable = True')
        process = None
        try:
            process = self._start_notifier(cmd=self.cmd)
            lines = 0
            while lines < 100:
                line = process.stdout.readline()
                lines += 1
                if SCHEDULER_ENABLED_LOG_LINE in line:
                    self.assertTrue(True)
                    break
        finally:
            if process:
                kill_process(process)
                self.remove_process(process=process)

    def test_scheduler_disable_explicit(self):
        self._append_to_cfg_file(cfg_path=self.cfg_path, content='\n[scheduler]\nenable = False')
        process = None
        try:
            process = self._start_notifier(cmd=self.cmd)
            lines = 0
            while lines < 100:
                line = process.stdout.readline()
                lines += 1
                if SCHEDULER_DISABLED_LOG_LINE in line:
                    self.assertTrue(True)
                    break
        finally:
            if process:
                process.send_signal(signal.SIGKILL)
                self.remove_process(process=process)

    def _start_notifier(self, cmd):
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                   shell=False, preexec_fn=os.setsid)
        self.add_process(process=process)
        return process

    def _append_to_cfg_file(self, cfg_path, content):
        with open(cfg_path, 'a') as f:
            f.write(content)

    def _remove_tempfile(self, fd, path):
        os.close(fd)
        os.unlink(path)
<|endoftext|>


<commit_before>import copy

from st2common.models.api.rule import RuleAPI
from st2common.models.system.common import ResourceReference
from st2common.persistence.reactor import Trigger
import st2common.services.triggers as trigger_service

from st2tests.base import CleanDbTestCase
from st2tests.fixturesloader import FixturesLoader

class TriggerServiceTests(CleanDbTestCase):

    def test_create_trigger_db_from_rule(self):
        test_fixtures = {
            'rules': ['cron_timer_rule_1.json', 'cron_timer_rule_3.json']
        }
        loader = FixturesLoader()
        fixtures = loader.load_fixtures(fixtures_pack='generic', fixtures_dict=test_fixtures)
        rules = fixtures['rules']

        trigger_db_ret_1 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_1.json']))
        self.assertTrue(trigger_db_ret_1 is not None)
        trigger_db = Trigger.get_by_id(trigger_db_ret_1.id)
        self.assertDictEqual(trigger_db.parameters,
                             rules['cron_timer_rule_1.json']['trigger']['parameters'])

        trigger_db_ret_2 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_3.json']))
        self.assertTrue(trigger_db_ret_2 is not None)
        self.assertTrue(trigger_db_ret_2.id != trigger_db_ret_1.id)

    def test_create_trigger_db_from_rule_duplicate(self):
        test_fixtures = {
            'rules': ['cron_timer_rule_1.json', 'cron_timer_rule_2.json']
        }
        loader = FixturesLoader()
        fixtures = loader.load_fixtures(fixtures_pack='generic', fixtures_dict=test_fixtures)
        rules = fixtures['rules']

        trigger_db_ret_1 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_1.json']))
        self.assertTrue(trigger_db_ret_1 is not None)
        trigger_db_ret_2 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_2.json']))
        self.assertTrue(trigger_db_ret_2 is not None)
        self.assertEqual(trigger_db_ret_1, trigger_db_ret_2, 'Should reuse same trigger.')
        trigger_db = Trigger.get_by_id(trigger_db_ret_1.id)
        self.assertDictEqual(trigger_db.parameters,
                             rules['cron_timer_rule_1.json']['trigger']['parameters'])

    def test_create_trigger_db_from_rule_use_ref_for_trigger(self):
        test_fixtures = {
            'rules': ['cron_timer_rule_1.json']
        }
        loader = FixturesLoader()
        fixtures = loader.load_fixtures(fixtures_pack='generic', fixtures_dict=test_fixtures)
        rules = fixtures['rules']
        rule_api = RuleAPI(**rules['cron_timer_rule_1.json'])
        trigger_db_ret_1 = trigger_service.create_trigger_db_from_rule(rule_api)
        self.assertTrue(trigger_db_ret_1 is not None)
        rule_api2 = copy.copy(rule_api)
        ref = ResourceReference.to_string_reference(name=trigger_db_ret_1.name,
                                                    pack=trigger_db_ret_1.pack)
        rule_api2.trigger = {'ref': ref, 'type': trigger_db_ret_1.type}
        trigger_db_ret_2 = trigger_service.create_trigger_db_from_rule(rule_api2)
        self.assertTrue(trigger_db_ret_2 is not None)
        self.assertTrue(trigger_db_ret_2.id == trigger_db_ret_1.id)

    def test_create_or_update_trigger_db(self):
        test_fixtures = {
            'triggertypes': ['triggertype1.json']
        }
        loader = FixturesLoader()
        fixtures = loader.save_fixtures_to_db(fixtures_pack='generic', fixtures_dict=test_fixtures)
        triggertypes = fixtures['triggertypes']
        trigger_type_ref = ResourceReference.to_string_reference(
            name=triggertypes['triggertype1.json']['name'],
            pack=triggertypes['triggertype1.json']['name'])

        trigger = {
            'name': 'foo',
            'pack': 'st2',
            'type': trigger_type_ref
        }
        trigger_service.create_or_update_trigger_db(trigger)
        triggers = Trigger.get_all()
        self.assertTrue(len(triggers) == 1, 'Only one trigger should be created.')
        self.assertTrue(triggers[0]['name'] == 'foo')

        # Try adding duplicate
        trigger_service.create_or_update_trigger_db(trigger)
        triggers = Trigger.get_all()
        self.assertTrue(len(triggers) == 1, 'Only one trigger should be present.')
        self.assertTrue(triggers[0]['name'] == 'foo')
<commit_msg>Add test cases to test_trigger_services<commit_after>import copy

from st2common.models.api.rule import RuleAPI
from st2common.models.system.common import ResourceReference
from st2common.persistence.reactor import Trigger
import st2common.services.triggers as trigger_service

from st2tests.base import CleanDbTestCase
from st2tests.fixturesloader import FixturesLoader

class TriggerServiceTests(CleanDbTestCase):

    def test_create_trigger_db_from_rule(self):
        test_fixtures = {
            'rules': ['cron_timer_rule_1.json', 'cron_timer_rule_3.json']
        }
        loader = FixturesLoader()
        fixtures = loader.load_fixtures(fixtures_pack='generic', fixtures_dict=test_fixtures)
        rules = fixtures['rules']

        trigger_db_ret_1 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_1.json']))
        self.assertTrue(trigger_db_ret_1 is not None)
        trigger_db = Trigger.get_by_id(trigger_db_ret_1.id)
        self.assertDictEqual(trigger_db.parameters,
                             rules['cron_timer_rule_1.json']['trigger']['parameters'])

        trigger_db_ret_2 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_3.json']))
        self.assertTrue(trigger_db_ret_2 is not None)
        self.assertTrue(trigger_db_ret_2.id != trigger_db_ret_1.id)

    def test_create_trigger_db_from_rule_duplicate(self):
        test_fixtures = {
            'rules': ['cron_timer_rule_1.json', 'cron_timer_rule_2.json']
        }
        loader = FixturesLoader()
        fixtures = loader.load_fixtures(fixtures_pack='generic', fixtures_dict=test_fixtures)
        rules = fixtures['rules']

        trigger_db_ret_1 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_1.json']))
        self.assertTrue(trigger_db_ret_1 is not None)
        trigger_db_ret_2 = trigger_service.create_trigger_db_from_rule(
            RuleAPI(**rules['cron_timer_rule_2.json']))
        self.assertTrue(trigger_db_ret_2 is not None)
        self.assertEqual(trigger_db_ret_1, trigger_db_ret_2, 'Should reuse same trigger.')
        trigger_db = Trigger.get_by_id(trigger_db_ret_1.id)
        self.assertDictEqual(trigger_db.parameters,
                             rules['cron_timer_rule_1.json']['trigger']['parameters'])

    def test_create_trigger_db_from_rule_use_ref_for_trigger(self):
        test_fixtures = {
            'rules': ['cron_timer_rule_1.json']
        }
        loader = FixturesLoader()
        fixtures = loader.load_fixtures(fixtures_pack='generic', fixtures_dict=test_fixtures)
        rules = fixtures['rules']
        rule_api = RuleAPI(**rules['cron_timer_rule_1.json'])
        trigger_db_ret_1 = trigger_service.create_trigger_db_from_rule(rule_api)
        self.assertTrue(trigger_db_ret_1 is not None)
        rule_api2 = copy.copy(rule_api)
        ref = ResourceReference.to_string_reference(name=trigger_db_ret_1.name,
                                                    pack=trigger_db_ret_1.pack)
        rule_api2.trigger = {'ref': ref, 'type': trigger_db_ret_1.type}
        trigger_db_ret_2 = trigger_service.create_trigger_db_from_rule(rule_api2)
        self.assertTrue(trigger_db_ret_2 is not None)
        self.assertTrue(trigger_db_ret_2.id == trigger_db_ret_1.id)

    def test_create_or_update_trigger_db_simple_triggers(self):
        test_fixtures = {
            'triggertypes': ['triggertype1.json']
        }
        loader = FixturesLoader()
        fixtures = loader.save_fixtures_to_db(fixtures_pack='generic', fixtures_dict=test_fixtures)
        triggertypes = fixtures['triggertypes']
        trigger_type_ref = ResourceReference.to_string_reference(
            name=triggertypes['triggertype1.json']['name'],
            pack=triggertypes['triggertype1.json']['name'])

        trigger = {
            'name': 'foo',
            'pack': 'st2',
            'type': trigger_type_ref
        }
        trigger_service.create_or_update_trigger_db(trigger)
        triggers = Trigger.get_all()
        self.assertTrue(len(triggers) == 1, 'Only one trigger should be created.')
        self.assertTrue(triggers[0]['name'] == 'foo')

        # Try adding duplicate
        trigger_service.create_or_update_trigger_db(trigger)
        triggers = Trigger.get_all()
        self.assertTrue(len(triggers) == 1, 'Only one trigger should be present.')
        self.assertTrue(triggers[0]['name'] == 'foo')

    def test_exception_thrown_when_rule_creation_no_trigger_yes_triggertype(self):
        test_fixtures = {
            'triggertypes': ['triggertype1.json']
        }
        loader = FixturesLoader()
        fixtures = loader.save_fixtures_to_db(fixtures_pack='generic', fixtures_dict=test_fixtures)
        triggertypes = fixtures['triggertypes']
        trigger_type_ref = ResourceReference.to_string_reference(
            name=triggertypes['triggertype1.json']['name'],
            pack=triggertypes['triggertype1.json']['name'])

        rule = {
            'name': 'fancyrule',
            'trigger': {
                'type': trigger_type_ref
            },
            'criteria': {

            },
            'action': {
                'ref': 'core.local',
                'parameters': {
                    'cmd': 'date'
                }
            }
        }
        rule_api = RuleAPI(**rule)
        try:
            trigger_service.create_trigger_db_from_rule(rule_api)
            self.fail('Should have thrown an exception.')
        except:
            pass
<|endoftext|>


<commit_before><commit_msg>changed from 'config' to 'raw_config' but forgot to update the test<commit_after><|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# © 2004-2009 Tiny SPRL (<http://tiny.be>).
# © 2015 Pedro M. Baeza
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from openerp import models, fields, api
import openerp.addons.decimal_precision as dp

class PurchaseOrderLine(models.Model):
    _inherit = "purchase.order.line"

    def _calc_line_base_price(self):
        self.ensure_one()
        res = super(PurchaseOrderLine, self)._calc_line_base_price()
        return res * (1 - self.discount / 100.0)

    @api.depends('discount')
    def _compute_amount(self):
        super(PurchaseOrderLine, self)._compute_amount()

    discount = fields.Float(
        string='Discount (%)', digits_compute=dp.get_precision('Discount'))

    _sql_constraints = [
        ('discount_limit', 'CHECK (discount <= 100.0)',
         'Discount must be lower than 100%.'),
    ]
<commit_msg>Don't rely on missing hooks to compute discount on purchase order line<commit_after># -*- coding: utf-8 -*-
# © 2004-2009 Tiny SPRL (<http://tiny.be>).
# © 2015 Pedro M. Baeza
# © 2016 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import api, fields, models
import openerp.addons.decimal_precision as dp

class PurchaseOrderLine(models.Model):
    _inherit = "purchase.order.line"

    @api.depends('discount')
    def _compute_amount(self):
        prices = {}
        for line in self:
            if line.discount:
                prices[line.id] = line.price_unit
                line.price_unit *= (1 - line.discount / 100.0)
        super(PurchaseOrderLine, self)._compute_amount()
        # restore prices
        for line in self:
            if self.discount:
                line.price_unit = prices[line.id]

    discount = fields.Float(
        string='Discount (%)', digits_compute=dp.get_precision('Discount'))

    _sql_constraints = [
        ('discount_limit', 'CHECK (discount <= 100.0)',
         'Discount must be lower than 100%.'),
    ]
<|endoftext|>


<commit_before>import django.template
import django.template.loader
import pretend

def test_compile_filter(monkeypatch):

    compile_static = pretend.call_recorder(lambda source_path: "compiled")
    monkeypatch.setattr("static_precompiler.utils.compile_static", compile_static)
    template = django.template.loader.get_template_from_string("""{% load compile_static %}{{ "source"|compile }}""")
    assert template.render(django.template.Context({})) == "compiled"

    monkeypatch.setattr("static_precompiler.settings.PREPEND_STATIC_URL", True)
    assert template.render(django.template.Context({})) == "/static/compiled"

    assert compile_static.calls == [pretend.call("source"), pretend.call("source")]

def test_inlinecompile_tag(monkeypatch):
    compiler = pretend.stub(compile_source=pretend.call_recorder(lambda *args: "compiled"))
    get_compiler_by_name = pretend.call_recorder(lambda *args: compiler)

    monkeypatch.setattr("static_precompiler.utils.get_compiler_by_name", get_compiler_by_name)

    template = django.template.loader.get_template_from_string(
        "{% load compile_static %}{% inlinecompile compiler='sass' %}source{% endinlinecompile %}"
    )
    assert template.render(django.template.Context({})) == "compiled"

    assert get_compiler_by_name.calls == [pretend.call("sass")]
    assert compiler.compile_source.calls == [pretend.call("source")]
<commit_msg>Fix tests for Django 1.8<commit_after>import django.template
import pretend

def test_compile_filter(monkeypatch):

    compile_static = pretend.call_recorder(lambda source_path: "compiled")
    monkeypatch.setattr("static_precompiler.utils.compile_static", compile_static)
    template = django.template.Template("""{% load compile_static %}{{ "source"|compile }}""")
    assert template.render(django.template.Context({})) == "compiled"

    monkeypatch.setattr("static_precompiler.settings.PREPEND_STATIC_URL", True)
    assert template.render(django.template.Context({})) == "/static/compiled"

    assert compile_static.calls == [pretend.call("source"), pretend.call("source")]

def test_inlinecompile_tag(monkeypatch):
    compiler = pretend.stub(compile_source=pretend.call_recorder(lambda *args: "compiled"))
    get_compiler_by_name = pretend.call_recorder(lambda *args: compiler)

    monkeypatch.setattr("static_precompiler.utils.get_compiler_by_name", get_compiler_by_name)

    template = django.template.Template(
        "{% load compile_static %}{% inlinecompile compiler='sass' %}source{% endinlinecompile %}"
    )
    assert template.render(django.template.Context({})) == "compiled"

    assert get_compiler_by_name.calls == [pretend.call("sass")]
    assert compiler.compile_source.calls == [pretend.call("source")]
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

try:
    import urllib2 as ul
except:
    import urllib as ul

from urllib import urlencode
import re
import  dbm

import lxml.etree as ET

from .log import Logging
from .util import subclasses, fromstring, totext

REDIRECT_REGEX = r"#REDIRECT\s*\[\[(.*)\]\]"
OFFLINE_PAGES = "/tmp/pages.json"

class BaseFetcher(Logging):

    """
    The base fetcher does not really fetch an article, it just assumes
    that an article was given to it. Subclass this to make more
    complex fetchers.
    """

    priority = 0

    def download(self, symbol, get=None):
        return symbol

    def source(self, symbol, redirect=True):
        return symbol

    def caching_fetch(self, dkey, callback, *args, **kwargs):
        """
        If you support caching use this.
        """

        return callback(*args, **kwargs)

class WikipediaSiteFetcher(BaseFetcher):

    priority = 1

    def __init__(self, url="http://en.wikipedia.org", base="w/index.php", **kw):
        self.url = url.strip('/')
        self.base = base.strip('/')

    def get_wikisource(self, soup):
        """
        Get the source from an html soup of the edit page.
        """
        return totext(soup.find(".//*[@id='wpTextbox1']"))

    def download(self, *args, **kwargs):
            return self.urlopen(*args, **kwargs).read()

    def urlopen(self, symbol, get=None, base=None):
        """
        Download a wikipedia article.

        :param symbol: The wikipedia symbol we are interested in. Set
        this to None to do different calls.
        :param get: dictionary of the get request. eg. `{'action':'edit'}`
        :returns: HTML code
        """

        if not get:
            get = dict(title=symbol)
        else:
            get = get.copy()
            if symbol is not None:
                get.update(title=symbol)

        url = "%s/%s?%s" % (self.url, base or self.base,
                            urlencode(get))

        try:
            return ul.urlopen(url)
        except ul.HTTPError:
            raise LookupError("404 - Uropen args: %s" % repr(url))

    def redirect_url(self, symbol):
        return self.urlopen(symbol).geturl()

    def source(self, symbol, get_request=None, redirect=True):
        """
        Get the full wiki markup of the symbol.
        """

        # Edit tag:
        # <textarea tabindex="1" accesskey="," id="wpTextbox1"
        # cols="80" rows="25" style="" lang="en" dir="ltr"
        # name="wpTextbox1">
        get_request = get_request or dict(action="edit")
        html = self.download(symbol=symbol, get=get_request)
        soup = fromstring(html)

        try:
            src = self.get_wikisource(soup)
        except IndexError:
            raise ValueError("Got invalid source page for article '%s'." %
                             symbol)

        # Handle redirecions silently
        redirect_match = re.search(REDIRECT_REGEX, src)

        if redirect and redirect_match:
            self.log().info("Redirecting to '%s'.", redirect_match.group(1))
            src = self.download(symbol=redirect_match.group(1),
                                get=get_request)

        return src

class CachingSiteFetcher(WikipediaSiteFetcher):

    """
    Caches pages in a json file and reads from there.
    """

    priority = 10
    _fname = OFFLINE_PAGES

    def __init__(self, *args, **kw):
        """
        The `offline` keyword arg will stop ot from looking pages up
        online. The default is True.
        """

        self.offline = kw.get("offline", False)
        self._fname = kw.get("fname", self._fname)

        super(CachingSiteFetcher, self).__init__(*args, **kw)

    def redirect_url(self, symbol):
        return self.caching_fetch("REDIRECT:" + symbol,
                         lambda sym: super(CachingSiteFetcher,
                                           self).redirect_url(symbol), symbol)

    def download(self, symbol, get=None, base=None):
        dkey = "%s;%s" % (symbol, get) if not base else \
               "%s:%s;%s" % (base, symbol, get)
        callback = super(CachingSiteFetcher, self).download
        return self.caching_fetch(dkey, callback, symbol, get=get, base=base)

    def caching_fetch(self, dkey, callback, *args, **kwargs):
        if not hasattr(self, 'data'):
            self.data = dbm.open(self._fname, 'c')

        ret = None

        if dkey in self.data:
            ret = self.data[dkey]

        if not self.offline and not ret:
            ret = callback(*args, **kwargs)
            if ret:
                self.data[dkey] = ret

        if ret is not None:
            return ret.decode('utf-8')

        raise LookupError("Failed to find page '%s' (%s online)." %
                          (symbol, "didnt look" if self.offline else "looked"))

class StaticFetcher(BaseFetcher):
    """
    Will always return constant html and markup.
    """

    def __init__(self, html=None, markup=None):
        self.html = html
        self.markup = markup

    def download(self, *args, **kw):
        return self.html

    def source(self, *args, **kw):
        return self.markup

WIKIBASE_FETCHER = CachingSiteFetcher()
<commit_msg>gdbm instead of gdbm because it works<commit_after># -*- coding: utf-8 -*-

try:
    import urllib2 as ul
except:
    import urllib as ul

from urllib import urlencode
import re
import gdbm as dbm

import lxml.etree as ET

from .log import Logging
from .util import subclasses, fromstring, totext

REDIRECT_REGEX = r"#REDIRECT\s*\[\[(.*)\]\]"
OFFLINE_PAGES = "/tmp/pages.json"

class BaseFetcher(Logging):

    """
    The base fetcher does not really fetch an article, it just assumes
    that an article was given to it. Subclass this to make more
    complex fetchers.
    """

    priority = 0

    def download(self, symbol, get=None):
        return symbol

    def source(self, symbol, redirect=True):
        return symbol

    def caching_fetch(self, dkey, callback, *args, **kwargs):
        """
        If you support caching use this.
        """

        return callback(*args, **kwargs)

    def encode(self, txt):
        # return txt.decode('utf-8')
        return txt.decode("ascii", errors='ignore')

class WikipediaSiteFetcher(BaseFetcher):

    priority = 1

    def __init__(self, url="http://en.wikipedia.org", base="w/index.php", **kw):
        self.url = url.strip('/')
        self.base = base.strip('/')

    def get_wikisource(self, soup):
        """
        Get the source from an html soup of the edit page.
        """
        return totext(soup.find(".//*[@id='wpTextbox1']"))

    def download(self, *args, **kwargs):
            return self.urlopen(*args, **kwargs).read()

    def urlopen(self, symbol, get=None, base=None):
        """
        Download a wikipedia article.

        :param symbol: The wikipedia symbol we are interested in. Set
        this to None to do different calls.
        :param get: dictionary of the get request. eg. `{'action':'edit'}`
        :returns: HTML code
        """

        if not get:
            get = dict(title=symbol)
        else:
            get = get.copy()
            if symbol is not None:
                get.update(title=symbol)

        url = "%s/%s?%s" % (self.url, base or self.base,
                            urlencode(get))

        try:
            return ul.urlopen(url)
        except ul.HTTPError:
            raise LookupError("404 - Uropen args: %s" % repr(url))

    def redirect_url(self, symbol):
        return self.urlopen(symbol).geturl()

    def source(self, symbol, get_request=None, redirect=True):
        """
        Get the full wiki markup of the symbol.
        """

        # Edit tag:
        # <textarea tabindex="1" accesskey="," id="wpTextbox1"
        # cols="80" rows="25" style="" lang="en" dir="ltr"
        # name="wpTextbox1">
        get_request = get_request or dict(action="edit")
        html = self.download(symbol=symbol, get=get_request)
        soup = fromstring(html)

        try:
            src = self.get_wikisource(soup)
        except IndexError:
            raise ValueError("Got invalid source page for article '%s'." %
                             symbol)

        # Handle redirecions silently
        redirect_match = re.search(REDIRECT_REGEX, src)

        if redirect and redirect_match:
            self.log().info("Redirecting to '%s'.", redirect_match.group(1))
            src = self.download(symbol=redirect_match.group(1),
                                get=get_request)

        return src

class CachingSiteFetcher(WikipediaSiteFetcher):

    """
    Caches pages in a json file and reads from there.
    """

    priority = 10
    _fname = OFFLINE_PAGES

    def __init__(self, *args, **kw):
        """
        The `offline` keyword arg will stop ot from looking pages up
        online. The default is True.
        """

        self.offline = kw.get("offline", False)
        self._fname = kw.get("fname", self._fname)

        super(CachingSiteFetcher, self).__init__(*args, **kw)

    def redirect_url(self, symbol):
        return self.caching_fetch("REDIRECT:" + symbol,
                         lambda sym: super(CachingSiteFetcher,
                                           self).redirect_url(symbol), symbol)

    def download(self, symbol, get=None, base=None):
        dkey = "%s;%s" % (symbol, get) if not base else \
               "%s:%s;%s" % (base, symbol, get)
        callback = super(CachingSiteFetcher, self).download
        return self.caching_fetch(dkey, callback, symbol, get=get, base=base)

    def caching_fetch(self, dkey, callback, *args, **kwargs):
        if not hasattr(self, 'data'):
            self.data = dbm.open(self._fname, 'n' if os.path.exists else 'w')

        ret = None

        if dkey in self.data:
            ret = self.data[dkey]

        if not self.offline and not ret:
            ret = callback(*args, **kwargs)
            if ret:
                self.data[dkey] = ret

        if ret is not None:
            return self.encode(ret)

        raise LookupError("Failed to find page '%s' (%s online)." %
                          (symbol, "didnt look" if self.offline else "looked"))

class StaticFetcher(BaseFetcher):
    """
    Will just get the html and markup provided in init.
    """

    def __init__(self, html=None, markup=None):
        self.html = html
        self.markup = markup

    def download(self, *args, **kw):
        return self.html

    def source(self, *args, **kw):
        return self.markup

WIKIBASE_FETCHER = CachingSiteFetcher()
<|endoftext|>