<commit_before># Copyright 2009 10gen, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Database level operations."""

import types
import md5

from son import SON
from dbref import DBRef
from son_manipulator import ObjectIdInjector, ObjectIdShuffler
from collection import Collection
from errors import InvalidName, CollectionInvalid, OperationFailure
from code import Code

class Database(object):
    """A Mongo database.
    """
    def __init__(self, connection, name):
        """Get a database by connection and name.

        Raises TypeError if name is not an instance of (str, unicode). Raises
        InvalidName if name is not a valid database name.

        :Parameters:
          - `connection`: a connection to Mongo
          - `name`: database name
        """
        if not isinstance(name, types.StringTypes):
            raise TypeError("name must be an instance of (str, unicode)")

        self.__check_name(name)

        self.__name = unicode(name)
        self.__connection = connection
        self.__manipulators = [ObjectIdInjector(self), ObjectIdShuffler(self)]

    def __check_name(self, name):
        for invalid_char in " .$/\\":
            if invalid_char in name:
                raise InvalidName("database names cannot contain the character %r" % name)
        if not name:
            raise InvalidName("database name cannot be the empty string")

    def add_son_manipulator(self, manipulator, index=-1):
        """Add a new son manipulator to this database.

        :Parameters:
          - `manipulator`: the manipulator to add
          - `index` (optional): the index to add the manipulator on the
            manipulator list
        """
        self.__manipulators[index:index] = [manipulator]

    def connection(self):
        """Get the database connection.
        """
        return self.__connection

    def name(self):
        """Get the database name.
        """
        return self.__name

    def __cmp__(self, other):
        if isinstance(other, Database):
            return cmp((self.__connection, self.__name), (other.__connection, other.__name))
        return NotImplemented

    def __repr__(self):
        return "Database(%r, %r)" % (self.__connection, self.__name)

    def __getattr__(self, name):
        """Get a collection of this database by name.

        Raises InvalidName if an invalid collection name is used.

        :Parameters:
          - `name`: the name of the collection to get
        """
        return Collection(self, name)

    def __getitem__(self, name):
        """Get a collection of this database by name.

        Raises InvalidName if an invalid collection name is used.

        :Parameters:
          - `name`: the name of the collection to get
        """
        return self.__getattr__(name)

    def create_collection(self, name, options={}):
        """Create a new collection in this database.

        Normally collection creation is automatic. This method should only if you
        want to specify options on creation. CollectionInvalid is raised if the
        collection already exists.

        Options should be a dictionary, with any of the following options:

          - "size": desired initial size for the collection. must be less than
            or equal to 10000000000. for capped collections this size is the max
            size of the collection.
          - "capped": if True, this is a capped collection
          - "max": maximum number of objects if capped (optional)

        :Parameters:
          - `name`: the name of the collection to create
          - `options` (optional): options to use on the new collection
        """
        if name in self.collection_names():
            raise CollectionInvalid("collection %s already exists" % name)

        return Collection(self, name, options)

    def _fix_incoming(self, son, collection):
        """Apply manipulators to an incoming SON object before it gets stored.

        :Parameters:
          - `son`: the son object going into the database
          - `collection`: the collection the son object is being saved in
        """
        for manipulator in self.__manipulators:
            son = manipulator.transform_incoming(son, collection)
        return son

    def _fix_outgoing(self, son, collection):
        """Apply manipulators to a SON object as it comes out of the database.

        :Parameters:
          - `son`: the son object coming out of the database
          - `collection`: the collection the son object was saved in
        """
        for manipulator in self.__manipulators:
            son = manipulator.transform_outgoing(son, collection)
        return son

    def _command(self, command, allowable_errors=[], check=True):
        """Issue a DB command.
        """
        result = self["$cmd"].find_one(command)
        if check and result["ok"] != 1:
            if result["errmsg"] in allowable_errors:
                return result
            raise OperationFailure("command %r failed: %s" % (command, result["errmsg"]))
        return result

    def collection_names(self):
        """Get a list of all the collection names in this database.
        """
        results = self.system.namespaces.find()
        names = [r["name"] for r in results]
        names = [n[len(self.__name) + 1:] for n in names
                 if n.startswith(self.__name + ".")]
        names = [n for n in names if "$" not in n]
        return names

    def drop_collection(self, name_or_collection):
        """Drop a collection.

        :Parameters:
          - `name_or_collection`: the name of a collection to drop or the
            collection object itself
        """
        name = name_or_collection
        if isinstance(name, Collection):
            name = name.name()

        if not isinstance(name, types.StringTypes):
            raise TypeError("name_or_collection must be an instance of (Collection, str, unicode)")

        if name not in self.collection_names():
            return

        self[name].drop_indexes() # must manually drop indexes

        self._command({"drop": unicode(name)})

    def validate_collection(self, name_or_collection):
        """Validate a collection.

        Returns a string of validation info. Raises CollectionInvalid if
        validation fails.
        """
        name = name_or_collection
        if isinstance(name, Collection):
            name = name.name()

        if not isinstance(name, types.StringTypes):
            raise TypeError("name_or_collection must be an instance of (Collection, str, unicode)")

        result = self._command({"validate": unicode(name)})

        info = result["result"]
        if info.find("exception") != -1 or info.find("corrupt") != -1:
            raise CollectionInvalid("%s invalid: %s" % (name, info))
        return info

    def profiling_level(self):
        """Get the database's current profiling level.

        Returns one of (`pymongo.OFF`, `pymongo.SLOW_ONLY`, `pymongo.ALL`).
        """
        result = self._command({"profile": -1})

        assert result["was"] >= 0 and result["was"] <= 2
        return result["was"]

    def set_profiling_level(self, level):
        """Set the database's profiling level.

        Raises ValueError if level is not one of (`pymongo.OFF`,
        `pymongo.SLOW_ONLY`, `pymongo.ALL`).

        :Parameters:
          - `level`: the profiling level to use
        """
        if not isinstance(level, types.IntType) or level < 0 or level > 2:
            raise ValueError("level must be one of (OFF, SLOW_ONLY, ALL)")

        self._command({"profile": level})

    def profiling_info(self):
        """Returns a list containing current profiling information.
        """
        return list(self.system.profile.find())

    def error(self):
        """Get a database error if one occured on the last operation.

        Return None if the last operation was error-free. Otherwise return the
        error that occurred.
        """
        error = self._command({"getlasterror": 1})
        if error.get("err", 0) is None:
            return None
        return error

    def previous_error(self):
        """Get the most recent error to have occurred on this database.

        Only returns errors that have occurred since the last call to
        `Database.reset_error_history`. Returns None if no such errors have
        occurred.
        """
        error = self._command({"getpreverror": 1})
        if error.get("err", 0) is None:
            return None
        return error

    def reset_error_history(self):
        """Reset the error history of this database.

        Calls to `Database.previous_error` will only return errors that have
        occurred since the most recent call to this method.
        """
        self._command({"reseterror": 1})

    def __iter__(self):
        return self

    def next(self):
        raise TypeError("'Database' object is not iterable")

    # TODO this should probably be private, but I'm using it for some tests right now...
    def _password_digest(self, username, password):
        """Get a password digest to use for authentication.
        """
        if not isinstance(password, types.StringTypes):
            raise TypeError("password must be an instance of (str, unicode)")
        if not isinstance(username, types.StringTypes):
            raise TypeError("username must be an instance of (str, unicode)")

        return unicode(md5.new(username + ":mongo:" + password).hexdigest())

    def authenticate(self, name, password):
        """Authenticate to use this database.

        Once authenticated, the user has full read and write access to this
        database. Raises TypeError if either name or password is not an instance
        of (str, unicode). Authentication lasts for the life of the database
        connection, or until `Database.logout` is called.

        The "admin" database is special. Authenticating on "admin" gives access
        to *all* databases. Effectively, "admin" access means root access to the
        database.

        :Parameters:
          - `name`: the name of the user to authenticate
          - `password`: the password of the user to authenticate
        """
        if not isinstance(name, types.StringTypes):
            raise TypeError("name must be an instance of (str, unicode)")
        if not isinstance(password, types.StringTypes):
            raise TypeError("password must be an instance of (str, unicode)")

        result = self._command({"getnonce": 1})
        nonce = result["nonce"]
        digest = self._password_digest(name, password)
        key = unicode(md5.new("%s%s%s" % (nonce, unicode(name), digest)
                              ).hexdigest())
        try:
            result = self._command(SON([("authenticate", 1),
                                        ("user", unicode(name)),
                                        ("nonce", nonce),
                                        ("key", key)]))
            return True
        except OperationFailure:
            return False

    def logout(self):
        """Deauthorize use of this database for this connection.

        Note that other databases may still be authorized.
        """
        self._command({"logout": 1})

    def dereference(self, dbref):
        """Dereference a DBRef, getting the SON object it points to.

        Raises TypeError if dbref is not an instance of DBRef. Returns a SON
        object or None if the reference does not point to a valid object.

        :Parameters:
          - `dbref`: the reference
        """
        if not isinstance(dbref, DBRef):
            raise TypeError("cannot dereference a %s" % type(dbref))
        return self[dbref.collection()].find_one(dbref.id())

    def eval(self, code, *args):
        """Evaluate a JavaScript expression on the Mongo server.

        Useful if you need to touch a lot of data lightly; in such a scenario
        the network transfer of the data could be a bottleneck. The `code`
        argument must be a JavaScript function. Additional positional
        arguments will be passed to that function when it is run on the
        server.

        Raises TypeError if `code` is not an instance of (str, unicode). Raises
        OperationFailure if the eval fails. Returns the result of the
        evaluation.

        :Parameters:
          - `code`: string representation of JavaScript code to be evaluated
          - `args` (optional): additional positional arguments are passed to
            the `code` being evaluated
        """
        if not isinstance(code, types.StringTypes):
            raise TypeError("code must be an instance of (str, unicode)")

        command = SON([("$eval", Code(code)), ("args", args)])
        result = self._command(command)
        return result.get("retval", None)
<commit_msg>send args as list, not tuple<commit_after># Copyright 2009 10gen, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Database level operations."""

import types
import md5

from son import SON
from dbref import DBRef
from son_manipulator import ObjectIdInjector, ObjectIdShuffler
from collection import Collection
from errors import InvalidName, CollectionInvalid, OperationFailure
from code import Code

class Database(object):
    """A Mongo database.
    """
    def __init__(self, connection, name):
        """Get a database by connection and name.

        Raises TypeError if name is not an instance of (str, unicode). Raises
        InvalidName if name is not a valid database name.

        :Parameters:
          - `connection`: a connection to Mongo
          - `name`: database name
        """
        if not isinstance(name, types.StringTypes):
            raise TypeError("name must be an instance of (str, unicode)")

        self.__check_name(name)

        self.__name = unicode(name)
        self.__connection = connection
        self.__manipulators = [ObjectIdInjector(self), ObjectIdShuffler(self)]

    def __check_name(self, name):
        for invalid_char in " .$/\\":
            if invalid_char in name:
                raise InvalidName("database names cannot contain the character %r" % name)
        if not name:
            raise InvalidName("database name cannot be the empty string")

    def add_son_manipulator(self, manipulator, index=-1):
        """Add a new son manipulator to this database.

        :Parameters:
          - `manipulator`: the manipulator to add
          - `index` (optional): the index to add the manipulator on the
            manipulator list
        """
        self.__manipulators[index:index] = [manipulator]

    def connection(self):
        """Get the database connection.
        """
        return self.__connection

    def name(self):
        """Get the database name.
        """
        return self.__name

    def __cmp__(self, other):
        if isinstance(other, Database):
            return cmp((self.__connection, self.__name), (other.__connection, other.__name))
        return NotImplemented

    def __repr__(self):
        return "Database(%r, %r)" % (self.__connection, self.__name)

    def __getattr__(self, name):
        """Get a collection of this database by name.

        Raises InvalidName if an invalid collection name is used.

        :Parameters:
          - `name`: the name of the collection to get
        """
        return Collection(self, name)

    def __getitem__(self, name):
        """Get a collection of this database by name.

        Raises InvalidName if an invalid collection name is used.

        :Parameters:
          - `name`: the name of the collection to get
        """
        return self.__getattr__(name)

    def create_collection(self, name, options={}):
        """Create a new collection in this database.

        Normally collection creation is automatic. This method should only if you
        want to specify options on creation. CollectionInvalid is raised if the
        collection already exists.

        Options should be a dictionary, with any of the following options:

          - "size": desired initial size for the collection. must be less than
            or equal to 10000000000. for capped collections this size is the max
            size of the collection.
          - "capped": if True, this is a capped collection
          - "max": maximum number of objects if capped (optional)

        :Parameters:
          - `name`: the name of the collection to create
          - `options` (optional): options to use on the new collection
        """
        if name in self.collection_names():
            raise CollectionInvalid("collection %s already exists" % name)

        return Collection(self, name, options)

    def _fix_incoming(self, son, collection):
        """Apply manipulators to an incoming SON object before it gets stored.

        :Parameters:
          - `son`: the son object going into the database
          - `collection`: the collection the son object is being saved in
        """
        for manipulator in self.__manipulators:
            son = manipulator.transform_incoming(son, collection)
        return son

    def _fix_outgoing(self, son, collection):
        """Apply manipulators to a SON object as it comes out of the database.

        :Parameters:
          - `son`: the son object coming out of the database
          - `collection`: the collection the son object was saved in
        """
        for manipulator in self.__manipulators:
            son = manipulator.transform_outgoing(son, collection)
        return son

    def _command(self, command, allowable_errors=[], check=True):
        """Issue a DB command.
        """
        result = self["$cmd"].find_one(command)
        if check and result["ok"] != 1:
            if result["errmsg"] in allowable_errors:
                return result
            raise OperationFailure("command %r failed: %s" % (command, result["errmsg"]))
        return result

    def collection_names(self):
        """Get a list of all the collection names in this database.
        """
        results = self.system.namespaces.find()
        names = [r["name"] for r in results]
        names = [n[len(self.__name) + 1:] for n in names
                 if n.startswith(self.__name + ".")]
        names = [n for n in names if "$" not in n]
        return names

    def drop_collection(self, name_or_collection):
        """Drop a collection.

        :Parameters:
          - `name_or_collection`: the name of a collection to drop or the
            collection object itself
        """
        name = name_or_collection
        if isinstance(name, Collection):
            name = name.name()

        if not isinstance(name, types.StringTypes):
            raise TypeError("name_or_collection must be an instance of (Collection, str, unicode)")

        if name not in self.collection_names():
            return

        self[name].drop_indexes() # must manually drop indexes

        self._command({"drop": unicode(name)})

    def validate_collection(self, name_or_collection):
        """Validate a collection.

        Returns a string of validation info. Raises CollectionInvalid if
        validation fails.
        """
        name = name_or_collection
        if isinstance(name, Collection):
            name = name.name()

        if not isinstance(name, types.StringTypes):
            raise TypeError("name_or_collection must be an instance of (Collection, str, unicode)")

        result = self._command({"validate": unicode(name)})

        info = result["result"]
        if info.find("exception") != -1 or info.find("corrupt") != -1:
            raise CollectionInvalid("%s invalid: %s" % (name, info))
        return info

    def profiling_level(self):
        """Get the database's current profiling level.

        Returns one of (`pymongo.OFF`, `pymongo.SLOW_ONLY`, `pymongo.ALL`).
        """
        result = self._command({"profile": -1})

        assert result["was"] >= 0 and result["was"] <= 2
        return result["was"]

    def set_profiling_level(self, level):
        """Set the database's profiling level.

        Raises ValueError if level is not one of (`pymongo.OFF`,
        `pymongo.SLOW_ONLY`, `pymongo.ALL`).

        :Parameters:
          - `level`: the profiling level to use
        """
        if not isinstance(level, types.IntType) or level < 0 or level > 2:
            raise ValueError("level must be one of (OFF, SLOW_ONLY, ALL)")

        self._command({"profile": level})

    def profiling_info(self):
        """Returns a list containing current profiling information.
        """
        return list(self.system.profile.find())

    def error(self):
        """Get a database error if one occured on the last operation.

        Return None if the last operation was error-free. Otherwise return the
        error that occurred.
        """
        error = self._command({"getlasterror": 1})
        if error.get("err", 0) is None:
            return None
        return error

    def previous_error(self):
        """Get the most recent error to have occurred on this database.

        Only returns errors that have occurred since the last call to
        `Database.reset_error_history`. Returns None if no such errors have
        occurred.
        """
        error = self._command({"getpreverror": 1})
        if error.get("err", 0) is None:
            return None
        return error

    def reset_error_history(self):
        """Reset the error history of this database.

        Calls to `Database.previous_error` will only return errors that have
        occurred since the most recent call to this method.
        """
        self._command({"reseterror": 1})

    def __iter__(self):
        return self

    def next(self):
        raise TypeError("'Database' object is not iterable")

    # TODO this should probably be private, but I'm using it for some tests right now...
    def _password_digest(self, username, password):
        """Get a password digest to use for authentication.
        """
        if not isinstance(password, types.StringTypes):
            raise TypeError("password must be an instance of (str, unicode)")
        if not isinstance(username, types.StringTypes):
            raise TypeError("username must be an instance of (str, unicode)")

        return unicode(md5.new(username + ":mongo:" + password).hexdigest())

    def authenticate(self, name, password):
        """Authenticate to use this database.

        Once authenticated, the user has full read and write access to this
        database. Raises TypeError if either name or password is not an instance
        of (str, unicode). Authentication lasts for the life of the database
        connection, or until `Database.logout` is called.

        The "admin" database is special. Authenticating on "admin" gives access
        to *all* databases. Effectively, "admin" access means root access to the
        database.

        :Parameters:
          - `name`: the name of the user to authenticate
          - `password`: the password of the user to authenticate
        """
        if not isinstance(name, types.StringTypes):
            raise TypeError("name must be an instance of (str, unicode)")
        if not isinstance(password, types.StringTypes):
            raise TypeError("password must be an instance of (str, unicode)")

        result = self._command({"getnonce": 1})
        nonce = result["nonce"]
        digest = self._password_digest(name, password)
        key = unicode(md5.new("%s%s%s" % (nonce, unicode(name), digest)
                              ).hexdigest())
        try:
            result = self._command(SON([("authenticate", 1),
                                        ("user", unicode(name)),
                                        ("nonce", nonce),
                                        ("key", key)]))
            return True
        except OperationFailure:
            return False

    def logout(self):
        """Deauthorize use of this database for this connection.

        Note that other databases may still be authorized.
        """
        self._command({"logout": 1})

    def dereference(self, dbref):
        """Dereference a DBRef, getting the SON object it points to.

        Raises TypeError if dbref is not an instance of DBRef. Returns a SON
        object or None if the reference does not point to a valid object.

        :Parameters:
          - `dbref`: the reference
        """
        if not isinstance(dbref, DBRef):
            raise TypeError("cannot dereference a %s" % type(dbref))
        return self[dbref.collection()].find_one(dbref.id())

    def eval(self, code, *args):
        """Evaluate a JavaScript expression on the Mongo server.

        Useful if you need to touch a lot of data lightly; in such a scenario
        the network transfer of the data could be a bottleneck. The `code`
        argument must be a JavaScript function. Additional positional
        arguments will be passed to that function when it is run on the
        server.

        Raises TypeError if `code` is not an instance of (str, unicode). Raises
        OperationFailure if the eval fails. Returns the result of the
        evaluation.

        :Parameters:
          - `code`: string representation of JavaScript code to be evaluated
          - `args` (optional): additional positional arguments are passed to
            the `code` being evaluated
        """
        if not isinstance(code, types.StringTypes):
            raise TypeError("code must be an instance of (str, unicode)")

        command = SON([("$eval", Code(code)), ("args", list(args))])
        result = self._command(command)
        return result.get("retval", None)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
.. :doctest:

"""

import time
import _strptime
from datetime import date as datetime_date

## Copy of ``_strptime()`` from python 2.7.3 (default, Apr 20 2012, 22:39:59)
## except for optional argument ``reference`` so as to add the option of a
## default ground time.

def strptime(data_string, format="%a %b %d %H:%M:%S %Y",
             reference=(1900, 1, 1, 0, 0, 0, -1, -1, -1)):
    """Return time struct and microseconds based on an input and format string

    An optional ``reference`` is set by default to 1900-01-01 00:00:00.

    ``reference`` field is a tuple with:

    (year, month, day, hour, minute, second, weekday, julian, tz), microseconds

    Please note that weekday and julian will be ignored.

        >>> from sact.epoch import strptime
        >>> strptime('13:05', '%H:%M',
        ...          reference=((2000, 1, 1, 0, 0, 0, -1, -1, -1), 5))
        (time.struct_time(tm_year=2000, tm_mon=1, tm_mday=1, tm_hour=13, tm_min=5, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1), 5)

    """
    with _strptime._cache_lock:
        if _strptime._getlang() != _strptime._TimeRE_cache.locale_time.lang:
            _strptime._TimeRE_cache = TimeRE()
            _strptime._regex_cache.clear()
        if len(_strptime._regex_cache) > _strptime._CACHE_MAX_SIZE:
            _strptime._regex_cache.clear()
        locale_time = _strptime._TimeRE_cache.locale_time
        format_regex = _strptime._regex_cache.get(format)
        if not format_regex:
            try:
                format_regex = _strptime._TimeRE_cache.compile(format)
            # KeyError raised when a bad format is found; can be specified as
            # \\, in which case it was a stray % but with a space after it
            except KeyError, err:
                bad_directive = err.args[0]
                if bad_directive == "\\":
                    bad_directive = "%"
                del err
                raise ValueError("'%s' is a bad directive in format '%s'" %
                                    (bad_directive, format))
            # IndexError only occurs when the format string is "%"
            except IndexError:
                raise ValueError("stray %% in format '%s'" % format)
            _strptime._regex_cache[format] = format_regex
    found = format_regex.match(data_string)
    if not found:
        raise ValueError("time data %r does not match format %r" %
                         (data_string, format))
    if len(data_string) != found.end():
        raise ValueError("unconverted data remains: %s" %
                          data_string[found.end():])
    (year, month, day, hour, minute, second, weekday, julian, tz), \
           fraction = reference
    # Force calculation for julian and weekday
    julian = -1
    weekday = -1
    # Default to -1 to signify that values not known; not critical to have,
    # though
    week_of_year = -1
    week_of_year_start = -1
    found_dict = found.groupdict()
    for group_key in found_dict.iterkeys():
        # Directives not explicitly handled below:
        #   c, x, X
        #      handled by making out of other directives
        #   U, W
        #      worthless without day of the week
        if group_key == 'y':
            year = int(found_dict['y'])
            # Open Group specification for strptime() states that a %y
            #value in the range of [00, 68] is in the century 2000, while
            #[69,99] is in the century 1900
            if year <= 68:
                year += 2000
            else:
                year += 1900
        elif group_key == 'Y':
            year = int(found_dict['Y'])
        elif group_key == 'm':
            month = int(found_dict['m'])
        elif group_key == 'B':
            month = locale_time.f_month.index(found_dict['B'].lower())
        elif group_key == 'b':
            month = locale_time.a_month.index(found_dict['b'].lower())
        elif group_key == 'd':
            day = int(found_dict['d'])
        elif group_key == 'H':
            hour = int(found_dict['H'])
        elif group_key == 'I':
            hour = int(found_dict['I'])
            ampm = found_dict.get('p', '').lower()
            # If there was no AM/PM indicator, we'll treat this like AM
            if ampm in ('', locale_time.am_pm[0]):
                # We're in AM so the hour is correct unless we're
                # looking at 12 midnight.
                # 12 midnight == 12 AM == hour 0
                if hour == 12:
                    hour = 0
            elif ampm == locale_time.am_pm[1]:
                # We're in PM so we need to add 12 to the hour unless
                # we're looking at 12 noon.
                # 12 noon == 12 PM == hour 12
                if hour != 12:
                    hour += 12
        elif group_key == 'M':
            minute = int(found_dict['M'])
        elif group_key == 'S':
            second = int(found_dict['S'])
        elif group_key == 'f':
            s = found_dict['f']
            # Pad to always return microseconds.
            s += "0" * (6 - len(s))
            fraction = int(s)
        elif group_key == 'A':
            weekday = locale_time.f_weekday.index(found_dict['A'].lower())
        elif group_key == 'a':
            weekday = locale_time.a_weekday.index(found_dict['a'].lower())
        elif group_key == 'w':
            weekday = int(found_dict['w'])
            if weekday == 0:
                weekday = 6
            else:
                weekday -= 1
        elif group_key == 'j':
            julian = int(found_dict['j'])
        elif group_key in ('U', 'W'):
            week_of_year = int(found_dict[group_key])
            if group_key == 'U':
                # U starts week on Sunday.
                week_of_year_start = 6
            else:
                # W starts week on Monday.
                week_of_year_start = 0
        elif group_key == 'Z':
            # Since -1 is default value only need to worry about setting tz if
            # it can be something other than -1.
            found_zone = found_dict['Z'].lower()
            for value, tz_values in enumerate(locale_time.timezone):
                if found_zone in tz_values:
                    # Deal with bad locale setup where timezone names are the
                    # same and yet time.daylight is true; too ambiguous to
                    # be able to tell what timezone has daylight savings
                    if (time.tzname[0] == time.tzname[1] and
                       time.daylight and found_zone not in ("utc", "gmt")):
                        break
                    else:
                        tz = value
                        break
    # If we know the week of the year and what day of that week, we can figure
    # out the Julian day of the year.
    if julian == -1 and week_of_year != -1 and weekday != -1:
        week_starts_Mon = True if week_of_year_start == 0 else False
        julian = _strptime._calc_julian_from_U_or_W(year, week_of_year, weekday,
                                            week_starts_Mon)
    # Cannot pre-calculate datetime_date() since can change in Julian
    # calculation and thus could have different value for the day of the week
    # calculation.
    if julian == -1:
        # Need to add 1 to result since first day of the year is 1, not 0.
        julian = datetime_date(year, month, day).toordinal() - \
                  datetime_date(year, 1, 1).toordinal() + 1
    else:  # Assume that if they bothered to include Julian day it will
           # be accurate.
        datetime_result = datetime_date.fromordinal(
            (julian - 1) + datetime_date(year, 1, 1).toordinal())
        year = datetime_result.year
        month = datetime_result.month
        day = datetime_result.day
    if weekday == -1:
        weekday = datetime_date(year, month, day).weekday()
    return (time.struct_time((year, month, day,
                              hour, minute, second,
                              weekday, julian, tz)), fraction)
<commit_msg>new: added ``complete_with_zeroes`` option to ``strptime`` to allow defaulting unspecified time data with zeroes.<commit_after># -*- coding: utf-8 -*-
"""
.. :doctest:

"""

import time
import _strptime
from datetime import date as datetime_date

def first(l, predicate):
    for e in l:
        if predicate(e):
            return e
    raise ValueError("No element in list matches given predicate.")

## Copy of ``_strptime()`` from python 2.7.3 (default, Apr 20 2012, 22:39:59)
## except for optional argument ``reference`` so as to add the option of a
## default ground time.

def strptime(data_string, format="%a %b %d %H:%M:%S %Y",
             reference=(1900, 1, 1, 0, 0, 0, -1, -1, -1),
             complete_with_zeroes=True):
    """Return time struct and microseconds based on an input and format string

    An optional ``reference`` is set by default to 1900-01-01 00:00:00.

    ``reference`` field is a tuple with:

    (year, month, day, hour, minute, second, weekday, julian, tz), microseconds

    Please note that weekday and julian will be ignored.

        >>> from sact.epoch import strptime
        >>> strptime('13:05', '%H:%M',
        ...          reference=((2000, 1, 1, 0, 0, 30, -1, -1, -1), 5))
        (time.struct_time(tm_year=2000, tm_mon=1, tm_mday=1, tm_hour=13, tm_min=5, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1), 0)

    Notice how all the left most values (the biggest weight value)
    where used from the given reference, and how the left most value
    (least weight values) was zeroed. The middle part is what was
    parsed.

    You can switch of the zeroing of the left most part:

        >>> strptime('13:05', '%H:%M',
        ...          reference=((2000, 1, 1, 0, 0, 0, -1, -1, -1), 5),
        ...          complete_with_zeroes=False)
        (time.struct_time(tm_year=2000, tm_mon=1, tm_mday=1, tm_hour=13, tm_min=5, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1), 5)

    """
    with _strptime._cache_lock:
        if _strptime._getlang() != _strptime._TimeRE_cache.locale_time.lang:
            _strptime._TimeRE_cache = TimeRE()
            _strptime._regex_cache.clear()
        if len(_strptime._regex_cache) > _strptime._CACHE_MAX_SIZE:
            _strptime._regex_cache.clear()
        locale_time = _strptime._TimeRE_cache.locale_time
        format_regex = _strptime._regex_cache.get(format)
        if not format_regex:
            try:
                format_regex = _strptime._TimeRE_cache.compile(format)
            # KeyError raised when a bad format is found; can be specified as
            # \\, in which case it was a stray % but with a space after it
            except KeyError, err:
                bad_directive = err.args[0]
                if bad_directive == "\\":
                    bad_directive = "%"
                del err
                raise ValueError("'%s' is a bad directive in format '%s'" %
                                    (bad_directive, format))
            # IndexError only occurs when the format string is "%"
            except IndexError:
                raise ValueError("stray %% in format '%s'" % format)
            _strptime._regex_cache[format] = format_regex
    found = format_regex.match(data_string)
    if not found:
        raise ValueError("time data %r does not match format %r" %
                         (data_string, format))
    if len(data_string) != found.end():
        raise ValueError("unconverted data remains: %s" %
                          data_string[found.end():])
    (year, month, day, hour, minute, second, weekday, julian, tz), \
           fraction = reference
    # Force calculation for julian and weekday
    julian = -1
    weekday = -1
    # Default to -1 to signify that values not known; not critical to have,
    # though
    week_of_year = -1
    week_of_year_start = -1
    found_dict = found.groupdict()
    for group_key in found_dict.iterkeys():
        # Directives not explicitly handled below:
        #   c, x, X
        #      handled by making out of other directives
        #   U, W
        #      worthless without day of the week
        if group_key == 'y':
            year = int(found_dict['y'])
            # Open Group specification for strptime() states that a %y
            #value in the range of [00, 68] is in the century 2000, while
            #[69,99] is in the century 1900
            if year <= 68:
                year += 2000
            else:
                year += 1900
        elif group_key == 'Y':
            year = int(found_dict['Y'])
        elif group_key == 'm':
            month = int(found_dict['m'])
        elif group_key == 'B':
            month = locale_time.f_month.index(found_dict['B'].lower())
        elif group_key == 'b':
            month = locale_time.a_month.index(found_dict['b'].lower())
        elif group_key == 'd':
            day = int(found_dict['d'])
        elif group_key == 'H':
            hour = int(found_dict['H'])
        elif group_key == 'I':
            hour = int(found_dict['I'])
            ampm = found_dict.get('p', '').lower()
            # If there was no AM/PM indicator, we'll treat this like AM
            if ampm in ('', locale_time.am_pm[0]):
                # We're in AM so the hour is correct unless we're
                # looking at 12 midnight.
                # 12 midnight == 12 AM == hour 0
                if hour == 12:
                    hour = 0
            elif ampm == locale_time.am_pm[1]:
                # We're in PM so we need to add 12 to the hour unless
                # we're looking at 12 noon.
                # 12 noon == 12 PM == hour 12
                if hour != 12:
                    hour += 12
        elif group_key == 'M':
            minute = int(found_dict['M'])
        elif group_key == 'S':
            second = int(found_dict['S'])
        elif group_key == 'f':
            s = found_dict['f']
            # Pad to always return microseconds.
            s += "0" * (6 - len(s))
            fraction = int(s)
        elif group_key == 'A':
            weekday = locale_time.f_weekday.index(found_dict['A'].lower())
        elif group_key == 'a':
            weekday = locale_time.a_weekday.index(found_dict['a'].lower())
        elif group_key == 'w':
            weekday = int(found_dict['w'])
            if weekday == 0:
                weekday = 6
            else:
                weekday -= 1
        elif group_key == 'j':
            julian = int(found_dict['j'])
        elif group_key in ('U', 'W'):
            week_of_year = int(found_dict[group_key])
            if group_key == 'U':
                # U starts week on Sunday.
                week_of_year_start = 6
            else:
                # W starts week on Monday.
                week_of_year_start = 0
        elif group_key == 'Z':
            # Since -1 is default value only need to worry about setting tz if
            # it can be something other than -1.
            found_zone = found_dict['Z'].lower()
            for value, tz_values in enumerate(locale_time.timezone):
                if found_zone in tz_values:
                    # Deal with bad locale setup where timezone names are the
                    # same and yet time.daylight is true; too ambiguous to
                    # be able to tell what timezone has daylight savings
                    if (time.tzname[0] == time.tzname[1] and
                       time.daylight and found_zone not in ("utc", "gmt")):
                        break
                    else:
                        tz = value
                        break
    # If we know the week of the year and what day of that week, we can figure
    # out the Julian day of the year.
    if julian == -1 and week_of_year != -1 and weekday != -1:
        week_starts_Mon = True if week_of_year_start == 0 else False
        julian = _strptime._calc_julian_from_U_or_W(year, week_of_year, weekday,
                                            week_starts_Mon)
    if complete_with_zeroes:
        idxs = ("j", "Y", "mBb", "UW", "dAaw", "HI", "M", "S", "f")
        specified = [idxs.index(gk)
                     for gk in [first(idxs, lambda key_group: k in key_group)
                                for k in found_dict.iterkeys()]]
        rightmost_specified = max(specified) if len(specified) else 0
        base = (1900, 1, 1, 1, 0, 0, 0, 0)
        values = year, month, week_of_year, day, hour, minute, second, fraction
        year, month, week_of_year, day, hour, minute, second, fraction = \
               values[:rightmost_specified] + base[rightmost_specified:]

    # Cannot pre-calculate datetime_date() since can change in Julian
    # calculation and thus could have different value for the day of the week
    # calculation.
    if julian == -1:
        # Need to add 1 to result since first day of the year is 1, not 0.
        julian = datetime_date(year, month, day).toordinal() - \
                  datetime_date(year, 1, 1).toordinal() + 1
    else:  # Assume that if they bothered to include Julian day it will
           # be accurate.
        datetime_result = datetime_date.fromordinal(
            (julian - 1) + datetime_date(year, 1, 1).toordinal())
        year = datetime_result.year
        month = datetime_result.month
        day = datetime_result.day
    if weekday == -1:
        weekday = datetime_date(year, month, day).weekday()
    return (time.struct_time((year, month, day,
                              hour, minute, second,
                              weekday, julian, tz)), fraction)
<|endoftext|>


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

# Standard library imports
from __future__ import unicode_literals
import re

# Third party imports
from django.core.validators import RegexValidator
from django.db import models
from django.db.models import signals
from django.utils.translation import ugettext_lazy as _

# Local application / specific library imports
from .parser import get_parser

_rendered_content_field_name = lambda name: '_{}_rendered'.format(name)

_smiley_code_re = re.compile(r'^[\w|\S]+$')
validate_smiley_code = RegexValidator(_smiley_code_re, _("Enter a valid 'smiley code' consisting of any character without whitespace characters"), 'invalid')

class BBCodeContent(object):
    def __init__(self, raw, rendered=None):
        self.raw = raw
        self.rendered = rendered

    def __unicode__(self):
        return '{}'.format(self.raw)

class BBCodeTextCreator(object):
    """
    Acts as the Django's default attribute descriptor class (enabled via the SubfieldBase metaclass).
    The main difference is that it does not call to_python() on the BBCodeTextField class. Instead, it
    stores the two different values of a BBCode content (the raw and the rendered data) separately.
    These values can be separately updated when something is assigned. When the field is accessed,
    a BBCodeContent instance will be returned ; this one is built with the current data.
    """
    def __init__(self, field):
        self.field = field
        self.rendered_field_name = _rendered_content_field_name(self.field.name)

    def __get__(self, instance, type=None):
        if instance is None:
            return self.field
        raw_content = instance.__dict__[self.field.name]
        if raw_content is None:
            return None
        else:
            return BBCodeContent(raw_content, rendered=getattr(instance, self.rendered_field_name))

    def __set__(self, instance, value):
        if isinstance(value, BBCodeContent):
            instance.__dict__[self.field.name] = value.raw
            setattr(instance, self.rendered_field_name, value.rendered)
        else:
            # Set only the bbcode content field
            instance.__dict__[self.field.name] = self.field.to_python(value)

class BBCodeTextField(models.TextField):
    """
    A BBCode text field contributes two columns to the model instead of the standard single column.
    The initial column stores the BBCode content and the other one keeps the rendered content returned
    by the BBCode parser.
    """
    def __init__(self, *args, **kwargs):
        # For South FakeORM compatibility: the frozen version of a BBCodeTextField can't try to add a
        # '*_rendered' field, because the '*_rendered' field itself is frozen as well.
        self.add_rendered_field = not kwargs.pop('no_rendered_field', False)
        super(BBCodeTextField, self).__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name):
        self.raw_name = name

        if self.add_rendered_field and not cls._meta.abstract:
            self.rendered_field_name = _rendered_content_field_name(name)

            # Create a hidden 'rendered' field
            rendered = models.TextField(editable=False, null=True, blank=True)
            # Ensure that the 'rendered' field appears before the actual field in
            # the models _meta.fields
            rendered.creation_counter = self.creation_counter
            cls.add_to_class(self.rendered_field_name, rendered)

        # The data will be processed before each save
        signals.pre_save.connect(self.process_bbcodes, sender=cls)

        # Add the default text field
        super(BBCodeTextField, self).contribute_to_class(cls, name)

        self.set_descriptor_class(cls)

    def set_descriptor_class(self, cls):
        setattr(cls, self.name, BBCodeTextCreator(self))

    def get_db_prep_save(self, value, connection):
        if isinstance(value, BBCodeContent):
            value = value.raw
        return super(BBCodeTextField, self).get_db_prep_save(value, connection)

    def process_bbcodes(self, signal, sender, instance=None, **kwargs):
        bbcode_text = getattr(instance, self.raw_name)

        if isinstance(bbcode_text, BBCodeContent):
            bbcode_text = bbcode_text.raw

        rendered = ''
        if bbcode_text:
            parser = get_parser()
            rendered = parser.render(bbcode_text)

        setattr(instance, self.rendered_field_name, rendered)

class SmileyCodeField(models.CharField):
    default_validators = [validate_smiley_code]
    description = _("Smiley code (up to %(max_length)s)")

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = kwargs.get('max_length', 50)
        # Set db_index=True unless it's been set manually.
        if 'db_index' not in kwargs:
            kwargs['db_index'] = True
        super(SmileyCodeField, self).__init__(*args, **kwargs)

# Allow South to handle those fields smoothly
try:
    from south.modelsinspector import add_introspection_rules

    # For a normal BBCodeTextField, the add_rendered_field attribute is always True,
    # which means that the no_rendered_field arg will always be True in a frozen BBCodeTextField,
    # which is what we want. The use of this flag will tell South not to make the _rendered
    # fields again.
    add_introspection_rules(rules=[((BBCodeTextField,),
                                    [],
                                    {'no_rendered_field': ('add_rendered_field',
                                                           {})})],
                            patterns=['precise_bbcode\.fields\.BBCodeTextField'])

    # SmileyCodeField
    add_introspection_rules([], ['^precise_bbcode\.fields\.SmileyCodeField'])
except ImportError:  # pragma: no cover
    pass
<commit_msg>Comments updated in field definition<commit_after># -*- coding: utf-8 -*-

# Standard library imports
from __future__ import unicode_literals
import re

# Third party imports
from django.core.validators import RegexValidator
from django.db import models
from django.db.models import signals
from django.utils.translation import ugettext_lazy as _

# Local application / specific library imports
from .parser import get_parser

_rendered_content_field_name = lambda name: '_{}_rendered'.format(name)

_smiley_code_re = re.compile(r'^[\w|\S]+$')
validate_smiley_code = RegexValidator(_smiley_code_re, _("Enter a valid 'smiley code' consisting of any character without whitespace characters"), 'invalid')

class BBCodeContent(object):
    def __init__(self, raw, rendered=None):
        self.raw = raw
        self.rendered = rendered

    def __unicode__(self):
        return '{}'.format(self.raw)

class BBCodeTextCreator(object):
    """
    Acts as the Django's default attribute descriptor class (enabled via the SubfieldBase metaclass).
    The main difference is that it does not call to_python() on the BBCodeTextField class. Instead, it
    stores the two different values of a BBCode content (the raw and the rendered data) separately.
    These values can be separately updated when something is assigned. When the field is accessed,
    a BBCodeContent instance will be returned ; this one is built with the current data.
    """
    def __init__(self, field):
        self.field = field
        self.rendered_field_name = _rendered_content_field_name(self.field.name)

    def __get__(self, instance, type=None):
        if instance is None:
            return self.field
        raw_content = instance.__dict__[self.field.name]
        if raw_content is None:
            return None
        else:
            return BBCodeContent(raw_content, rendered=getattr(instance, self.rendered_field_name))

    def __set__(self, instance, value):
        if isinstance(value, BBCodeContent):
            instance.__dict__[self.field.name] = value.raw
            setattr(instance, self.rendered_field_name, value.rendered)
        else:
            # Set only the bbcode content field
            instance.__dict__[self.field.name] = self.field.to_python(value)

class BBCodeTextField(models.TextField):
    """
    A BBCode text field contributes two columns to the model instead of the standard single column.
    The initial column stores the BBCode content and the other one keeps the rendered content returned
    by the BBCode parser.
    """
    def __init__(self, *args, **kwargs):
        # For South FakeORM compatibility: the frozen version of a BBCodeTextField can't try to add a
        # '*_rendered' field, because the '*_rendered' field itself is frozen as well.
        self.add_rendered_field = not kwargs.pop('no_rendered_field', False)
        super(BBCodeTextField, self).__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name):
        self.raw_name = name

        if self.add_rendered_field and not cls._meta.abstract:
            self.rendered_field_name = _rendered_content_field_name(name)

            # Create a hidden 'rendered' field
            rendered = models.TextField(editable=False, null=True, blank=True)
            # Ensure that the 'rendered' field appears before the actual field in
            # the models _meta.fields
            rendered.creation_counter = self.creation_counter
            cls.add_to_class(self.rendered_field_name, rendered)

        # The data will be processed before each save
        signals.pre_save.connect(self.process_bbcodes, sender=cls)

        # Add the default text field
        super(BBCodeTextField, self).contribute_to_class(cls, name)

        # Associates the name of this field to a special descriptor that will return
        # an appropriate BBCodeContent object each time the field is accessed
        self.set_descriptor_class(cls)

    def set_descriptor_class(self, cls):
        setattr(cls, self.name, BBCodeTextCreator(self))

    def get_db_prep_save(self, value, connection):
        if isinstance(value, BBCodeContent):
            value = value.raw
        return super(BBCodeTextField, self).get_db_prep_save(value, connection)

    def process_bbcodes(self, signal, sender, instance=None, **kwargs):
        bbcode_text = getattr(instance, self.raw_name)

        if isinstance(bbcode_text, BBCodeContent):
            bbcode_text = bbcode_text.raw

        rendered = ''
        if bbcode_text:
            parser = get_parser()
            rendered = parser.render(bbcode_text)

        setattr(instance, self.rendered_field_name, rendered)

class SmileyCodeField(models.CharField):
    default_validators = [validate_smiley_code]
    description = _("Smiley code (up to %(max_length)s)")

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = kwargs.get('max_length', 50)
        # Set db_index=True unless it's been set manually.
        if 'db_index' not in kwargs:
            kwargs['db_index'] = True
        super(SmileyCodeField, self).__init__(*args, **kwargs)

# Allow South to handle those fields smoothly
try:
    from south.modelsinspector import add_introspection_rules

    # For a normal BBCodeTextField, the add_rendered_field attribute is always True,
    # which means that the no_rendered_field arg will always be True in a frozen BBCodeTextField,
    # which is what we want. The use of this flag will tell South not to make the _rendered
    # fields again.
    add_introspection_rules(rules=[((BBCodeTextField,),
                                    [],
                                    {'no_rendered_field': ('add_rendered_field',
                                                           {})})],
                            patterns=['precise_bbcode\.fields\.BBCodeTextField'])

    # SmileyCodeField
    add_introspection_rules([], ['^precise_bbcode\.fields\.SmileyCodeField'])
except ImportError:  # pragma: no cover
    pass
<|endoftext|>


<commit_before>"""
Module allows the grabbing of q_values (energy per disintegration) for the calculation of decay heat. This currently consists of the nuclide, it's q_value, and the percent of q coming from gammas. This data is from 'ORIGEN-S DECAY DATA LIBRARY AND HALF-LIFE UNCERTAINTIES' (http://web.ornl.gov/~webworks/cppr/y2001/rpt/97914.pdf)
"""

from __future__ import print_function
import csv
import os

import numpy as np
import tables as tb

from pyne import nucname

# Parses data from .csv
def grab_q_values(fname):
    """Parses data from three q_val csv files."""

    # create list
    all_q_vals = []

    # grabs data row by row
    def read_row(row):
        if row[0] == 'Nuclide' or len(row[0].strip()) == 0:
            return
        nuclide = nucname.id(''.join(row[0:2]).replace(' ', ''))
        if len(row[2]) == 0:
            q_val = 0.0
        else:
            q_val = float(row[2])
        if len(row[3]) == 0:
            gamma_frac = 0.0
        else:
            gamma_frac = float(row[3])
        entry = [nuclide, q_val, gamma_frac]
        all_q_vals.append(entry)

    # opens .csv files and parses them
    with open(fname, 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            read_row(row)

    return all_q_vals

#def make_q_value_table(q_data):

def make_q_value():
    """Controller function for adding q-values"""
#    q_values = args.q_values
#    if os.path.exists(q_values):
#        with tb.openFile(q_values, 'r') as f:
#            if '/q_values' in f:
#                print("skipping q_value table creation; already exists.")
#                return

    # Grab the q_values
    print("Grabbing q_values...")
    q_value_files = ['q_val_actinides.csv', 'q_val_fissionproducts.csv',
                     'q_val_light.csv']
    for fname in q_value_files:
        grab_q_values(fname)

    # Make q_value table once we have the array
#    print("Making q_value table...")
#    make_q_value_table(q_values)
<commit_msg>first attempt at a function to make the table and add it to the nuc_data file<commit_after>"""
Module allows the grabbing of q_values (energy per disintegration) for the calculation of decay heat. This currently consists of the nuclide, it's q_value, and the percent of q coming from gammas. This data is from 'ORIGEN-S DECAY DATA LIBRARY AND HALF-LIFE UNCERTAINTIES' (http://web.ornl.gov/~webworks/cppr/y2001/rpt/97914.pdf)
"""

from __future__ import print_function
import csv
import os

import numpy as np
import tables as tb

from pyne import nucname
from pyne.api import nuc_data
#from pyne.api import BASIC_FILTERS

# Parses data from .csv
def grab_q_values(fname):
    """Parses data from three q_val csv files.

    Parameters
    ----------
    fname : str
        Path to q_value file.
    """

    # Create list
    all_q_values = []

    # Grabs data row by row
    def read_row(row):
        if row[0] == 'Nuclide' or len(row[0].strip()) == 0:
            return
        nuclide = nucname.id(''.join(row[0:2]).replace(' ', ''))
        if len(row[2]) == 0:
            q_val = 0.0
        else:
            q_val = float(row[2])
        if len(row[3]) == 0:
            gamma_frac = 0.0
        else:
            gamma_frac = float(row[3])
        entry = [nuclide, q_val, gamma_frac]
        all_q_values.append(entry)

    # Opens .csv files and parses them
    with open(fname, 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            read_row(row)

    return all_q_values

# Sorts and filters list of q_values
def format_q_values(all_q_values):
    """Filters the q_value data for multiple entries then sorts the nuclides.

    Parameters
    ----------
    all_q_values : list of lists
        Array of q_values for all nuclides.
    """

    distinct_all_q_values = []

    # Ensures only one entry per nuclide
    for nuclide in all_q_values:
        if not nuclide in distinct_all_q_values:
            distinct_all_q_values.append(nuclide)

    # Sort in order of nuclide
    distinct_all_q_values.sort(key=lambda nucid: nucid[0])

    return distinct_all_q_values

# Write q_value table to file
def make_q_value_table(all_q_values, nuc_data, build_dir=""):
    """Adds q_value table to the nuc_data.h5 library.

    Parameters
    ----------
    all_q_values: list of lists
        Array of q_values for all nuclides.
    nuc_data : str
        Path to nuclide data file.
    build_dir : str
        Directory to place q_value files in.
    """

    # Sort and filter the q_values
    distinct_all_q_values = format_q_values(all_q_values)

    # Iterate over all nuclides in the q_value table
    for row in distinct_all_q_values:

        row = (nuclide['Nuclide'],
               nuclide['Q_value [MeV per disintegration]'],
               nuclide['Fraction of Q that comes from gammas'])

        distinct_all_q_values.append(row)

    # Converts to numpy array
    q_value_array = np.array(distinct_all_q_values)

    # Open the hdf5 file and create group
    nuc_file = tb.openFile(nuc_data, 'a', filters=BASIC_FILTERS)
    q_val_group = nuc_file.createGroup('/', 'q_values', 'Q_values for nuclides')

    # Make a new table
    q_value_table = nuc_file.createTable(q_val_group, np.empty(0),
                    'Nuclide, Q_value [MeV per disintegration],'
                    'Fraction of Q that comes from gammas')
    q_value_table.append(q_value_array)

    # Ensure that data was written to table
    q_value_table.flush()

    # Close the hdf5 file
    q_values.close()

def make_q_value(args):
    """Controller function for adding q-values"""
#    q_values = args.q_values
#    if os.path.exists(q_values):
#        with tb.openFile(q_values, 'r') as f:
#            if '/q_values' in f:
#                print("skipping q_value table creation; already exists.")
#                return

    nuc_data, build_dir = args.nuc_data, args.build_dir

    # Grab the q_values
    print('Grabbing q_values...')
    q_value_files = ['q_val_actinides.csv', 'q_val_fissionproducts.csv',
                     'q_val_light.csv']
    all_q_values = []
    for fname in q_value_files:
        all_q_values += grab_q_values(fname)

    # Make the q_value table and write to file
    print("Making q_value table...")
    make_q_value_table(all_q_values, nuc_data, build_dir))
<|endoftext|>


<commit_before>from os.path import join as pjoin

# Format expected by setup.py and doc/source/conf.py: string of form "X.Y.Z"
_version_major = 0
_version_minor = 1
_version_micro = ''  # use '' for first of series, number for 1 and above
_version_extra = 'dev'
# _version_extra = ''  # Uncomment this for full releases

# Construct full version string from these.
_ver = [_version_major, _version_minor]
if _version_micro:
    _ver.append(_version_micro)
if _version_extra:
    _ver.append(_version_extra)

__version__ = '.'.join(map(str, _ver))

CLASSIFIERS = ["Development Status :: 3 - Alpha",
               "Environment :: Console",
               "Intended Audience :: Science/Research",
               "License :: OSI Approved :: MIT License",
               "Operating System :: OS Independent",
               "Programming Language :: Python",
               "Topic :: Scientific/Engineering"]

# Description should be a one-liner:
description = "ABCPRC: A package for fitting individual-based models"
# Long description will go up on the pypi page
long_description = """
ABCPRC
========
ABCPRC is a package for fitting individual-based models
using an Approximate Bayesian Computation Particle Rejection Scheme.
The package is designed to be fully-flexible, whilst also allowing fitting
to occur rapidly with minimal set-up.
License
=======
``ABCPRC`` is licensed under the terms of the MIT license. See the file
"LICENSE" for information on the history of this software, terms & conditions
for usage, and a DISCLAIMER OF ALL WARRANTIES.
All trademarks referenced herein are property of their respective holders.
Copyright (c) 2015--, Ariel Rokem, The University of Washington
eScience Institute.
"""

NAME = "ABCPRC"
MAINTAINER = "Mike Irvine (Sempwn)"
MAINTAINER_EMAIL = "michael.irvine.mai@gmail.com"
DESCRIPTION = description
LONG_DESCRIPTION = long_description
URL = ""
DOWNLOAD_URL = ""
LICENSE = "MIT"
AUTHOR = "Mike Irvine (Sempwn)"
AUTHOR_EMAIL = "michael.irvine.mai@gmail.com"
PLATFORMS = "OS Independent"
MAJOR = _version_major
MINOR = _version_minor
MICRO = _version_micro
VERSION = __version__
PACKAGES = ['ABCPRC',
            'ABCPRC.tests']
PACKAGE_DATA = {'ABCPRC': []}
REQUIRES = ["numpy","pandas","seaborn","matplotlib.pyplot"]<commit_msg>Updated version to include all dependencies.<commit_after>from os.path import join as pjoin

# Format expected by setup.py and doc/source/conf.py: string of form "X.Y.Z"
_version_major = 0
_version_minor = 1
_version_micro = ''  # use '' for first of series, number for 1 and above
_version_extra = 'dev'
# _version_extra = ''  # Uncomment this for full releases

# Construct full version string from these.
_ver = [_version_major, _version_minor]
if _version_micro:
    _ver.append(_version_micro)
if _version_extra:
    _ver.append(_version_extra)

__version__ = '.'.join(map(str, _ver))

CLASSIFIERS = ["Development Status :: 3 - Alpha",
               "Environment :: Console",
               "Intended Audience :: Science/Research",
               "License :: OSI Approved :: MIT License",
               "Operating System :: OS Independent",
               "Programming Language :: Python",
               "Topic :: Scientific/Engineering"]

# Description should be a one-liner:
description = "ABCPRC: A package for fitting individual-based models"
# Long description will go up on the pypi page
long_description = """
ABCPRC
========
ABCPRC is a package for fitting individual-based models
using an Approximate Bayesian Computation Particle Rejection Scheme.
The package is designed to be fully-flexible, whilst also allowing fitting
to occur rapidly with minimal set-up.
License
=======
``ABCPRC`` is licensed under the terms of the MIT license. See the file
"LICENSE" for information on the history of this software, terms & conditions
for usage, and a DISCLAIMER OF ALL WARRANTIES.
All trademarks referenced herein are property of their respective holders.
Copyright (c) 2015--, Ariel Rokem, The University of Washington
eScience Institute.
"""

NAME = "ABCPRC"
MAINTAINER = "Mike Irvine (Sempwn)"
MAINTAINER_EMAIL = "michael.irvine.mai@gmail.com"
DESCRIPTION = description
LONG_DESCRIPTION = long_description
URL = ""
DOWNLOAD_URL = ""
LICENSE = "MIT"
AUTHOR = "Mike Irvine (Sempwn)"
AUTHOR_EMAIL = "michael.irvine.mai@gmail.com"
PLATFORMS = "OS Independent"
MAJOR = _version_major
MINOR = _version_minor
MICRO = _version_micro
VERSION = __version__
PACKAGES = ['ABCPRC',
            'ABCPRC.tests']
PACKAGE_DATA = {'ABCPRC': []}
REQUIRES = ["numpy","pandas","seaborn","matplotlib.pyplot","joblib"]
<|endoftext|>


<commit_before>#!/usr/bin/env python
# Full license can be found in License.md
# Full author list can be found in .zenodo.json file
# DOI:10.5281/zenodo.1199703
# ----------------------------------------------------------------------------
"""Date and time handling utilities."""

import datetime as dt
import numpy as np
import pandas as pds
import re

def getyrdoy(date):
    """Return a tuple of year, day of year for a supplied datetime object.

    Parameters
    ----------
    date : datetime.datetime
        Datetime object

    Returns
    -------
    year : int
        Integer year
    doy : int
        Integer day of year

    Raises
    ------
    AttributeError
        If input date does not have `toordinal` method

    """

    try:
        doy = date.toordinal() - dt.datetime(date.year, 1, 1).toordinal() + 1
    except AttributeError:
        raise AttributeError(''.join(("Must supply a datetime object or an ",
                                      "equivalent class object with the ",
                                      "`toordinal` method")))
    else:
        return date.year, doy

def parse_date(str_yr, str_mo, str_day, str_hr='0', str_min='0', str_sec='0',
               century=2000):
    """Convert string dates to dt.datetime.

    Parameters
    ----------
    str_yr : str
        String containing the year (2 or 4 digits)
    str_mo : str
        String containing month digits
    str_day : str
        String containing day of month digits
    str_hr : str
        String containing the hour of day (default='0')
    str_min : str
        String containing the minutes of hour (default='0')
    str_sec : str
        String containing the seconds of minute (default='0')
    century : int
        Century, only used if str_yr is a 2-digit year (default=2000)

    Returns
    -------
    out_date : dt.datetime
        datetime object

    Raises
    ------
    ValueError
        If any input results in an unrealistic datetime object value

    """

    yr = int(str_yr) + century if len(str_yr) == 2 else int(str_yr)
    out_date = dt.datetime(yr, int(str_mo), int(str_day), int(str_hr),
                           int(str_min), np.int64(str_sec))

    return out_date

def calc_res(index, use_mean=False):
    """Determine the resolution for a time index.

    Parameters
    ----------
    index : array-like
        Datetime list, array, or Index
    use_mean : bool
        Use the minimum time difference if False, use the mean time difference
        if True (default=False)

    Returns
    -------
    res_sec : float
       Resolution value in seconds

    Raises
    ------
    ValueError
        If `index` is too short to calculate a time resolution

    """

    # Test the length of the input
    if len(index) < 2:
        raise ValueError("insufficient data to calculate resolution")

    # Calculate the minimum temporal difference
    del_time = (np.array(index[1:]) - np.array(index[:-1]))

    if use_mean:
        del_time = del_time.mean()
    else:
        del_time = del_time.min()

    # Convert time difference to seconds, based on possible data types
    try:
        # First try as timedelta
        res_sec = del_time.total_seconds()
    except AttributeError as aerr:
        # Now try as numpy.timedelta64
        if isinstance(del_time, np.timedelta64):
            res_sec = np.float64(del_time) * 1.0e-9
        else:
            raise AttributeError("Input should be times: {:}".format(aerr))

    return res_sec

def calc_freq(index):
    """Determine the frequency for a time index.

    Parameters
    ----------
    index : array-like
        Datetime list, array, or Index

    Returns
    -------
    freq : str
       Frequency string as described in Pandas Offset Aliases

    Note
    ----
    Calculates the minimum time difference and sets that as the frequency.

    To reduce the amount of calculations done, the returned frequency is
    either in seconds (if no sub-second resolution is found) or nanoseconds.

    See Also
    --------
    pds.offsets.DateOffset

    """
    # Get the frequency of the index in seconds
    freq_sec = calc_res(index, use_mean=False)

    # Format output frequency
    if np.floor(freq_sec) == freq_sec:
        # The frequency is on the order of seconds or greater
        freq = "{:.0f}S".format(freq_sec)
    else:
        # There are sub-seconds.  Go straigt to nanosec for best resoution
        freq = "{:.0f}N".format(freq_sec * 1.0e9)

    return freq

def freq_to_res(freq):
    """Convert a frequency string to a resolution value in seconds.

    Parameters
    ----------
    freq : str
       Frequency string as described in Pandas Offset Aliases

    Returns
    -------
    res_sec : np.float64
       Resolution value in seconds

    See Also
    --------
    pds.offsets.DateOffset

    References
    ----------
    Separating alpha and numeric portions of strings, as described in:
    https://stackoverflow.com/a/12409995

    """
    # Separate the alpha and numeric portions of the string
    regex = re.compile(r'(\d+|\s+)')
    out_str = [sval for sval in regex.split(freq) if len(sval) > 0]

    if len(out_str) > 2:
        raise ValueError('unexpected frequency format: {:s}'.format(freq))

    # Cast the alpha and numeric portions
    freq_str = out_str[-1]
    freq_num = 1.0 if len(out_str) == 1 else np.float64(out_str[0])

    # Calculate the resolution in seconds
    res_sec = pds.Timedelta(freq_num, unit=freq_str).total_seconds()

    return res_sec

def create_date_range(start, stop, freq='D'):
    """Create array of datetime objects using input frequency from start to stop.

    Parameters
    ----------
    start : dt.datetime or list-like of dt.datetime
        The beginning of the date range.  Supports list, tuple, or ndarray of
        start dates.
    stop : dt.datetime or list-like of dt.datetime
        The end of the date range.  Supports list, tuple, or ndarray of
        stop dates.
    freq : str
        The frequency of the desired output.  Codes correspond to pandas
        date_range codes: 'D' daily, 'M' monthly, 'S' secondly

    Returns
    -------
    season : pds.date_range
        Range of dates over desired time with desired frequency.

    """

    if hasattr(start, '__iter__'):
        # missing check for datetime
        season = pds.date_range(start[0], stop[0], freq=freq)
        for (sta, stp) in zip(start[1:], stop[1:]):
            season = season.append(pds.date_range(sta, stp, freq=freq))
    else:
        season = pds.date_range(start, stop, freq=freq)
    return season

def create_datetime_index(year=None, month=None, day=None, uts=None):
    """Create a timeseries index using supplied date and time.

    Parameters
    ----------
        year : array_like or NoneType
            Array of year values as np.int (default=None)
        month : array_like or NoneType
           Array of month values as np.int. Leave None if using day for
           day of year. (default=None)
        day : array_like or NoneType
            Array of number of days as np.int. If month=None then value
            interpreted as day of year, otherwise, day of month. (default=None)
        uts : array-like or NoneType
            Array of UT seconds of minute as np.float64 values (default=None)

    Returns
    -------
        Pandas timeseries index.

    Note
    ----
    Leap seconds have no meaning here.

    """

    # We need a timeseries index for storing satellite data in pandas, but
    # creating a datetime object for everything is too slow.  Instead, we
    # calculate the number of nanoseconds elapsed since first sample and
    # create timeseries index from that.  This yields a factor of 20
    # improvement compared to previous method, which itself was an order of
    # magnitude faster than datetime.

    # Get list of unique year, and month
    if not hasattr(year, '__iter__'):
        raise ValueError('Must provide an iterable for all inputs.')
    if len(year) == 0:
        raise ValueError('Length of array must be larger than 0.')

    # Establish default month
    if month is None:
        # If no month, assume January.  All days will be treated as day of year.
        month = np.ones(len(year))

    # Initial day is first of given month.
    day0 = np.ones(len(year))

    if day is None:
        # If no day, assume first of month.
        day = np.ones(len(year))
    if uts is None:
        # If no seconds, assume start of day.
        uts = np.zeros(len(year))

    # Initialize all dates as first of month and convert to index.
    df = pds.DataFrame({'year': year, 'month': month, 'day': day0})
    index = pds.DatetimeIndex(pds.to_datetime(df))

    # Add days (offset by 1) and seconds to each index.
    index += (day - 1).astype('timedelta64[D]') + uts.astype('timedelta64[s]')

    return index

def filter_datetime_input(date):
    """Create a datetime object that only includes year, month, and day.

    Parameters
    ----------
    date : NoneType, array-like, or datetime
        Single or sequence of datetime inputs

    Returns
    -------
    out_date: NoneType, datetime, or array-like
        NoneType input yeilds NoneType output, array-like yeilds list of
        datetimes, datetime object yeilds like.  All datetime output excludes
        the sub-daily temporal increments (keeps only date information).

    Note
    ----
    Checks for timezone information not in UTC

    """

    if date is None:
        out_date = None
    else:
        # Check for timezone information and remove time of day for
        # single datetimes and iterable containers of datetime objects
        if hasattr(date, '__iter__'):
            out_date = []
            for in_date in date:
                if(in_date.tzinfo is not None
                   and in_date.utcoffset() is not None):
                    in_date = in_date.astimezone(tz=dt.timezone.utc)

                out_date.append(dt.datetime(in_date.year, in_date.month,
                                            in_date.day))
        else:
            if date.tzinfo is not None and date.utcoffset() is not None:
                date = date.astimezone(tz=dt.timezone.utc)

            out_date = dt.datetime(date.year, date.month, date.day)

    return out_date

def today():
    """Obtain today's date (UTC), with no hour, minute, second, etc.

    Returns
    -------
    today_utc: datetime
        Today's date in UTC

    """
    today_utc = filter_datetime_input(dt.datetime.utcnow())

    return today_utc
<commit_msg>DOC: remove comments from previous algorithms<commit_after>#!/usr/bin/env python
# Full license can be found in License.md
# Full author list can be found in .zenodo.json file
# DOI:10.5281/zenodo.1199703
# ----------------------------------------------------------------------------
"""Date and time handling utilities."""

import datetime as dt
import numpy as np
import pandas as pds
import re

def getyrdoy(date):
    """Return a tuple of year, day of year for a supplied datetime object.

    Parameters
    ----------
    date : datetime.datetime
        Datetime object

    Returns
    -------
    year : int
        Integer year
    doy : int
        Integer day of year

    Raises
    ------
    AttributeError
        If input date does not have `toordinal` method

    """

    try:
        doy = date.toordinal() - dt.datetime(date.year, 1, 1).toordinal() + 1
    except AttributeError:
        raise AttributeError(''.join(("Must supply a datetime object or an ",
                                      "equivalent class object with the ",
                                      "`toordinal` method")))
    else:
        return date.year, doy

def parse_date(str_yr, str_mo, str_day, str_hr='0', str_min='0', str_sec='0',
               century=2000):
    """Convert string dates to dt.datetime.

    Parameters
    ----------
    str_yr : str
        String containing the year (2 or 4 digits)
    str_mo : str
        String containing month digits
    str_day : str
        String containing day of month digits
    str_hr : str
        String containing the hour of day (default='0')
    str_min : str
        String containing the minutes of hour (default='0')
    str_sec : str
        String containing the seconds of minute (default='0')
    century : int
        Century, only used if str_yr is a 2-digit year (default=2000)

    Returns
    -------
    out_date : dt.datetime
        datetime object

    Raises
    ------
    ValueError
        If any input results in an unrealistic datetime object value

    """

    yr = int(str_yr) + century if len(str_yr) == 2 else int(str_yr)
    out_date = dt.datetime(yr, int(str_mo), int(str_day), int(str_hr),
                           int(str_min), np.int64(str_sec))

    return out_date

def calc_res(index, use_mean=False):
    """Determine the resolution for a time index.

    Parameters
    ----------
    index : array-like
        Datetime list, array, or Index
    use_mean : bool
        Use the minimum time difference if False, use the mean time difference
        if True (default=False)

    Returns
    -------
    res_sec : float
       Resolution value in seconds

    Raises
    ------
    ValueError
        If `index` is too short to calculate a time resolution

    """

    # Test the length of the input
    if len(index) < 2:
        raise ValueError("insufficient data to calculate resolution")

    # Calculate the minimum temporal difference
    del_time = (np.array(index[1:]) - np.array(index[:-1]))

    if use_mean:
        del_time = del_time.mean()
    else:
        del_time = del_time.min()

    # Convert time difference to seconds, based on possible data types
    try:
        # First try as timedelta
        res_sec = del_time.total_seconds()
    except AttributeError as aerr:
        # Now try as numpy.timedelta64
        if isinstance(del_time, np.timedelta64):
            res_sec = np.float64(del_time) * 1.0e-9
        else:
            raise AttributeError("Input should be times: {:}".format(aerr))

    return res_sec

def calc_freq(index):
    """Determine the frequency for a time index.

    Parameters
    ----------
    index : array-like
        Datetime list, array, or Index

    Returns
    -------
    freq : str
       Frequency string as described in Pandas Offset Aliases

    Note
    ----
    Calculates the minimum time difference and sets that as the frequency.

    To reduce the amount of calculations done, the returned frequency is
    either in seconds (if no sub-second resolution is found) or nanoseconds.

    See Also
    --------
    pds.offsets.DateOffset

    """
    # Get the frequency of the index in seconds
    freq_sec = calc_res(index, use_mean=False)

    # Format output frequency
    if np.floor(freq_sec) == freq_sec:
        # The frequency is on the order of seconds or greater
        freq = "{:.0f}S".format(freq_sec)
    else:
        # There are sub-seconds.  Go straigt to nanosec for best resoution
        freq = "{:.0f}N".format(freq_sec * 1.0e9)

    return freq

def freq_to_res(freq):
    """Convert a frequency string to a resolution value in seconds.

    Parameters
    ----------
    freq : str
       Frequency string as described in Pandas Offset Aliases

    Returns
    -------
    res_sec : np.float64
       Resolution value in seconds

    See Also
    --------
    pds.offsets.DateOffset

    References
    ----------
    Separating alpha and numeric portions of strings, as described in:
    https://stackoverflow.com/a/12409995

    """
    # Separate the alpha and numeric portions of the string
    regex = re.compile(r'(\d+|\s+)')
    out_str = [sval for sval in regex.split(freq) if len(sval) > 0]

    if len(out_str) > 2:
        raise ValueError('unexpected frequency format: {:s}'.format(freq))

    # Cast the alpha and numeric portions
    freq_str = out_str[-1]
    freq_num = 1.0 if len(out_str) == 1 else np.float64(out_str[0])

    # Calculate the resolution in seconds
    res_sec = pds.Timedelta(freq_num, unit=freq_str).total_seconds()

    return res_sec

def create_date_range(start, stop, freq='D'):
    """Create array of datetime objects using input frequency from start to stop.

    Parameters
    ----------
    start : dt.datetime or list-like of dt.datetime
        The beginning of the date range.  Supports list, tuple, or ndarray of
        start dates.
    stop : dt.datetime or list-like of dt.datetime
        The end of the date range.  Supports list, tuple, or ndarray of
        stop dates.
    freq : str
        The frequency of the desired output.  Codes correspond to pandas
        date_range codes: 'D' daily, 'M' monthly, 'S' secondly

    Returns
    -------
    season : pds.date_range
        Range of dates over desired time with desired frequency.

    """

    if hasattr(start, '__iter__'):
        # missing check for datetime
        season = pds.date_range(start[0], stop[0], freq=freq)
        for (sta, stp) in zip(start[1:], stop[1:]):
            season = season.append(pds.date_range(sta, stp, freq=freq))
    else:
        season = pds.date_range(start, stop, freq=freq)
    return season

def create_datetime_index(year=None, month=None, day=None, uts=None):
    """Create a timeseries index using supplied date and time.

    Parameters
    ----------
        year : array_like or NoneType
            Array of year values as np.int (default=None)
        month : array_like or NoneType
           Array of month values as np.int. Leave None if using day for
           day of year. (default=None)
        day : array_like or NoneType
            Array of number of days as np.int. If month=None then value
            interpreted as day of year, otherwise, day of month. (default=None)
        uts : array-like or NoneType
            Array of UT seconds of minute as np.float64 values (default=None)

    Returns
    -------
        Pandas timeseries index.

    Note
    ----
    Leap seconds have no meaning here.

    """

    # Get list of unique year, and month
    if not hasattr(year, '__iter__'):
        raise ValueError('Must provide an iterable for all inputs.')
    if len(year) == 0:
        raise ValueError('Length of array must be larger than 0.')

    # Establish default month
    if month is None:
        # If no month, assume January.  All days will be treated as day of year.
        month = np.ones(len(year))

    # Initial day is first of given month.
    day0 = np.ones(len(year))

    if day is None:
        # If no day, assume first of month.
        day = np.ones(len(year))
    if uts is None:
        # If no seconds, assume start of day.
        uts = np.zeros(len(year))

    # Initialize all dates as first of month and convert to index.
    df = pds.DataFrame({'year': year, 'month': month, 'day': day0})
    index = pds.DatetimeIndex(pds.to_datetime(df))

    # Add days (offset by 1) and seconds to each index.
    index += (day - 1).astype('timedelta64[D]') + uts.astype('timedelta64[s]')

    return index

def filter_datetime_input(date):
    """Create a datetime object that only includes year, month, and day.

    Parameters
    ----------
    date : NoneType, array-like, or datetime
        Single or sequence of datetime inputs

    Returns
    -------
    out_date: NoneType, datetime, or array-like
        NoneType input yeilds NoneType output, array-like yeilds list of
        datetimes, datetime object yeilds like.  All datetime output excludes
        the sub-daily temporal increments (keeps only date information).

    Note
    ----
    Checks for timezone information not in UTC

    """

    if date is None:
        out_date = None
    else:
        # Check for timezone information and remove time of day for
        # single datetimes and iterable containers of datetime objects
        if hasattr(date, '__iter__'):
            out_date = []
            for in_date in date:
                if(in_date.tzinfo is not None
                   and in_date.utcoffset() is not None):
                    in_date = in_date.astimezone(tz=dt.timezone.utc)

                out_date.append(dt.datetime(in_date.year, in_date.month,
                                            in_date.day))
        else:
            if date.tzinfo is not None and date.utcoffset() is not None:
                date = date.astimezone(tz=dt.timezone.utc)

            out_date = dt.datetime(date.year, date.month, date.day)

    return out_date

def today():
    """Obtain today's date (UTC), with no hour, minute, second, etc.

    Returns
    -------
    today_utc: datetime
        Today's date in UTC

    """
    today_utc = filter_datetime_input(dt.datetime.utcnow())

    return today_utc
<|endoftext|>


<commit_before>import matplotlib.pyplot as plt
from functools import wraps

from prettyplotlib.colors import pretty

@wraps(plt.subplots)
@pretty
def subplots(*args, **kwargs):
    return plt.subplots(*args, **kwargs)

@wraps(plt.subplot2grid)
@pretty
def subplot2grid(*args, **kwargs):
    return plt.subplot2grid(*args, **kwargs)
<commit_msg>Keep ppl.subplot{s,2grid}.__module__ correct.<commit_after>import matplotlib.pyplot as plt
from functools import partial, update_wrapper

from prettyplotlib.colors import pretty

@partial(update_wrapper, wrapped=plt.subplots, assigned=["__doc__"])
@pretty
def subplots(*args, **kwargs):
    return plt.subplots(*args, **kwargs)

@partial(update_wrapper, wrapped=plt.subplot2grid, assigned=["__doc__"])
@pretty
def subplot2grid(*args, **kwargs):
    return plt.subplot2grid(*args, **kwargs)
<|endoftext|>