<commit_before>from columns import ColumnList
from preferences import BomPref
import units
from sort import natural_sort
import re

DNF = ["dnf", "do not fit", "nofit", "no stuff", "nostuff", "noload", "do not load"]

class Component():
    """Class for a component, aka 'comp' in the xml netlist file.
    This component class is implemented by wrapping an xmlElement instance
    with accessors.  The xmlElement is held in field 'element'.
    """

    def __init__(self, xml_element, prefs=None):
        self.element = xml_element
        self.libpart = None

        if not prefs:
            prefs = BomPref()

        self.prefs = prefs

        # Set to true when this component is included in a component group
        self.grouped = False

    #compare the value of this part, to the value of another part (see if they match)
    def compareValue(self, other):
        #simple string comparison
        if self.getValue().lower() == other.getValue().lower(): return True

        #otherwise, perform a more complicated value comparison
        if units.compareValues(self.getValue(), other.getValue()): return True

        #no match, return False
        return False

    #determine if two parts have the same name
    def comparePartName(self, other):
        pn1 = self.getPartName().lower()
        pn2 = other.getPartName().lower()

        #simple direct match
        if pn1 == pn2: return True

        #compare part aliases e.g. "c" to "c_small"
        for alias in self.prefs.aliases:
            if pn1 in alias and pn2 in alias:
                return True

        return False

    def compareField(self, other, field):

        this_field = self.getField(field).lower()
        other_field = other.getField(field).lower()

        if this_field == other_field: return True

        #if blank comparisons are allowed
        if self.prefs.mergeBlankFields:
            if this_field == "" or other_field == "":
                return True

        return False

    #Equivalency operator is used to determine if two parts are 'equal'
    def __eq__(self, other):
        """Equlivalency operator, remember this can be easily overloaded"""

        results = []

        #'fitted' value must be the same for both parts
        results.append(self.isFitted() == other.isFitted())

        for c in self.prefs.groups:
            #perform special matches
            if c.lower() == ColumnList.COL_VALUE.lower():
                results.append(self.compareValue(other))
            #match part name
            elif c.lower() == ColumnList.COL_PART.lower():
                results.append(self.comparePartName(other))

            #generic match
            else:
                results.append(self.compareField(other, c))

        return all(results)

    def setLibPart(self, part):
        self.libpart = part

    def getPrefix(self): #return the reference prefix
        #e.g. if this component has a reference U12, will return "U"
        prefix = ""

        for c in self.getRef():
            if c.isalpha(): prefix += c
            else: break

        return prefix

    def getLibPart(self):
        return self.libpart

    def getPartName(self):
        return self.element.get("libsource", "part")

    def getLibName(self):
        return self.element.get("libsource", "lib")

    def setValue(self, value):
        """Set the value of this component"""
        v = self.element.getChild("value")
        if v:
            v.setChars(value)

    def getValue(self):
        return self.element.get("value")

    def getField(self, name, ignoreCase=True, libraryToo=True):
        """Return the value of a field named name. The component is first
        checked for the field, and then the components library part is checked
        for the field. If the field doesn't exist in either, an empty string is
        returned

        Keywords:
        name -- The name of the field to return the value for
        libraryToo --   look in the libpart's fields for the same name if not found
                        in component itself
        """

        #special fields

        if name.lower() == ColumnList.COL_REFERENCE.lower():
            return self.getRef().strip()

        if name.lower() == ColumnList.COL_DESCRIPTION.lower():
            return self.getDescription().strip()

        if name.lower() == ColumnList.COL_DATASHEET.lower():
            return self.getDatasheet().strip()

        fp = self.getFootprint().split(":")

        if len(fp) > 1:
            if name.lower() == ColumnList.COL_FP_LIB.lower():
                return fp[0].strip()

            if name.lower() == ColumnList.COL_FP.lower():
                return fp[1].strip()

        if name.lower() == ColumnList.COL_VALUE.lower():
            return self.getValue().strip()

        if name.lower() == ColumnList.COL_PART.lower():
            return self.getPartName().strip()

        if name.lower() == ColumnList.COL_PART_LIB.lower():
            return self.getLibName().strip()

        #other fields (case insensitive)
        for f in self.getFieldNames():
            if f.lower() == name.lower():
                field = self.element.get("field", "name", f)

                if field == "" and libraryToo:
                    field = self.libpart.getField(f)

                return field.strip()

        #could not find
        return ""

    def getFieldNames(self):
        """Return a list of field names in play for this component.  Mandatory
        fields are not included, and they are: Value, Footprint, Datasheet, Ref.
        The netlist format only includes fields with non-empty values.  So if a field
        is empty, it will not be present in the returned list.
        """
        fieldNames = []
        fields = self.element.getChild('fields')
        if fields:
            for f in fields.getChildren():
                fieldNames.append( f.get('field','name') )
        return fieldNames

    def getRef(self):
        return self.element.get("comp", "ref")

    #determine if a component is FITTED or not
    def isFitted(self):

        check = self.getField(self.prefs.configField).lower()

        #check the value field first
        if self.getValue().lower() in DNF or check.lower() in DNF:
            return False

        if check == "":
            return True #empty is fitted

        opts = check.split(",")

        result = True

        for opt in opts:
            #options that start with '-' are explicitly removed from certain configurations
            if opt.startswith('-') and opt[1:].lower() == self.prefs.pcbConfig.lower():
                result = False
                break
            if opt.startswith("+"):
                if opt.lower() == self.prefs.pcbConfig.lower():
                    result = True

        #by default, part is fitted
        return result

    #test if this part should be included, based on any regex expressions provided in the preferences
    def testRegExclude(self):

        for reg in self.prefs.regExcludes:

            if type(reg) == list and len(reg) == 2:
                field_name, regex = reg
                field_value = self.getField(field_name)

                regex = regex.decode("unicode_escape")

                if re.search(regex, field_value, flags=re.IGNORECASE) is not None:
                    if self.prefs.verbose:
                        print("Excluding '{ref}': Field '{field}' ({value}) matched '{reg}'".format(
                            ref = self.getRef(),
                            field = field_name,
                            value = field_value,
                            reg = regex))

                    return True #found a match

        #default, could not find any matches
        return False

    def testRegInclude(self):

        if len(self.prefs.regIncludes) == 0: #nothing to match against
            return True

        for reg in self.prefs.regIncludes:

            if type(reg) == list and len(reg) == 2:
                field_name, regex = reg
                field_value = self.getField(field_name)

                print(field_name,field_value,regex)

                if re.search(regex, field_value, flags=re.IGNORECASE) is not None:
                    if self.prefs.verbose:
                        print("")

                    #found a match
                    return True

        #default, could not find a match
        return False

    def getFootprint(self, libraryToo=True):
        ret = self.element.get("footprint")
        if ret =="" and libraryToo:
            ret = self.libpart.getFootprint()
        return ret

    def getDatasheet(self, libraryToo=True):
        return self.libpart.getDatasheet()

    def getTimestamp(self):
        return self.element.get("tstamp")

    def getDescription(self):
        return self.libpart.getDescription()

class ComponentGroup():

    """
    Initialize the group with no components, and default fields
    """
    def __init__(self, prefs=None):
        self.components = []
        self.fields = dict.fromkeys(ColumnList._COLUMNS_DEFAULT)    #columns loaded from KiCAD

        if not prefs:
            prefs = BomPref()

        self.prefs = prefs

    def getField(self, field):

        if not field in self.fields.keys(): return ""
        if not self.fields[field]: return ""
        return u''.join((self.fields[field]))

    def getCount(self):
        return len(self.components)

    #Test if a given component fits in this group
    def matchComponent(self, c):
        if len(self.components) == 0: return True
        if c == self.components[0]: return True

        return False

    #test if a given component is already contained in this grop
    def containsComponent(self, c):
        if self.matchComponent(c) == False: return False

        for comp in self.components:
            if comp.getRef() == c.getRef(): return True

        return False

    #add a component to the group
    def addComponent(self, c):

        if len(self.components) == 0:
            self.components.append(c)
        elif self.containsComponent(c):
            return
        elif self.matchComponent(c):
            self.components.append(c)

    def isFitted(self):
        return any([c.isFitted() for c in self.components])

    #return a list of the components
    def getRefs(self):
        #print([c.getRef() for c in self.components]))
        #return " ".join([c.getRef() for c in self.components])
        return " ".join([c.getRef() for c in self.components])

    #sort the components in correct order
    def sortComponents(self):
        self.components = sorted(self.components, key=lambda c: natural_sort(c.getRef()))

    #update a given field, based on some rules and such
    def updateField(self, field, fieldData):

        #protected fields cannot be overwritten
        if field in ColumnList._COLUMNS_PROTECTED: return

        if (field == None or field == ""): return
        elif fieldData == "" or fieldData == None:
            return

        if (not field in self.fields.keys()) or (self.fields[field] == None) or (self.fields[field] == ""):
            self.fields[field] = fieldData
        elif fieldData.lower() in self.fields[field].lower():
            return
        else:
            print("Conflict:",self.fields[field],",",fieldData)
            self.fields[field] += " " + fieldData

    def updateFields(self):

        for c in self.components:
            for f in c.getFieldNames():

                #these columns are handled explicitly below
                if f in ColumnList._COLUMNS_PROTECTED:
                    continue

                self.updateField(f, c.getField(f))

        #update 'global' fields
        self.fields[ColumnList.COL_REFERENCE] = self.getRefs()

        q = self.getCount()
        self.fields[ColumnList.COL_GRP_QUANTITY] = "{n}{dnf}".format(
            n=q,
            dnf = " (DNF)" if not self.isFitted() else "")

        self.fields[ColumnList.COL_GRP_BUILD_QUANTITY] = str(q * self.prefs.boards) if self.isFitted() else "0"
        self.fields[ColumnList.COL_VALUE] = self.components[0].getValue()
        self.fields[ColumnList.COL_PART] = self.components[0].getPartName()
        self.fields[ColumnList.COL_PART_LIB] = self.components[0].getLibName()
        self.fields[ColumnList.COL_DESCRIPTION] = self.components[0].getDescription()
        self.fields[ColumnList.COL_DATASHEET] = self.components[0].getDatasheet()

        if len(self.components[0].getFootprint().split(":")) >= 2:
            self.fields[ColumnList.COL_FP] = self.components[0].getFootprint().split(":")[-1]
            self.fields[ColumnList.COL_FP_LIB] = self.components[0].getFootprint().split(":")[0]
        else:
            self.fields[ColumnList.COL_FP] = ""
            self.fields[ColumnList.COL_FP_LIB] = ""

    #return a dict of the KiCAD data based on the supplied columns
    #NOW WITH UNICODE SUPPORT!
    def getRow(self, columns):
        row = []
        for key in columns:
            val = self.getField(key)

            if val is None:
                val = ""
            else:
                val = u'' + val
                val = val.encode('utf-8')

            row.append(val)

        return row<commit_msg>Fixed field grabbing<commit_after>from columns import ColumnList
from preferences import BomPref
import units
from sort import natural_sort
import re

DNF = ["dnf", "do not fit", "nofit", "no stuff", "nostuff", "noload", "do not load"]

class Component():
    """Class for a component, aka 'comp' in the xml netlist file.
    This component class is implemented by wrapping an xmlElement instance
    with accessors.  The xmlElement is held in field 'element'.
    """

    def __init__(self, xml_element, prefs=None):
        self.element = xml_element
        self.libpart = None

        if not prefs:
            prefs = BomPref()

        self.prefs = prefs

        # Set to true when this component is included in a component group
        self.grouped = False

    #compare the value of this part, to the value of another part (see if they match)
    def compareValue(self, other):
        #simple string comparison
        if self.getValue().lower() == other.getValue().lower(): return True

        #otherwise, perform a more complicated value comparison
        if units.compareValues(self.getValue(), other.getValue()): return True

        #no match, return False
        return False

    #determine if two parts have the same name
    def comparePartName(self, other):
        pn1 = self.getPartName().lower()
        pn2 = other.getPartName().lower()

        #simple direct match
        if pn1 == pn2: return True

        #compare part aliases e.g. "c" to "c_small"
        for alias in self.prefs.aliases:
            if pn1 in alias and pn2 in alias:
                return True

        return False

    def compareField(self, other, field):

        this_field = self.getField(field).lower()
        other_field = other.getField(field).lower()

        if this_field == other_field: return True

        #if blank comparisons are allowed
        if self.prefs.mergeBlankFields:
            if this_field == "" or other_field == "":
                return True

        return False

    #Equivalency operator is used to determine if two parts are 'equal'
    def __eq__(self, other):
        """Equlivalency operator, remember this can be easily overloaded"""

        results = []

        #'fitted' value must be the same for both parts
        results.append(self.isFitted() == other.isFitted())

        for c in self.prefs.groups:
            #perform special matches
            if c.lower() == ColumnList.COL_VALUE.lower():
                results.append(self.compareValue(other))
            #match part name
            elif c.lower() == ColumnList.COL_PART.lower():
                results.append(self.comparePartName(other))

            #generic match
            else:
                results.append(self.compareField(other, c))

        return all(results)

    def setLibPart(self, part):
        self.libpart = part

    def getPrefix(self): #return the reference prefix
        #e.g. if this component has a reference U12, will return "U"
        prefix = ""

        for c in self.getRef():
            if c.isalpha(): prefix += c
            else: break

        return prefix

    def getLibPart(self):
        return self.libpart

    def getPartName(self):
        return self.element.get("libsource", "part")

    def getLibName(self):
        return self.element.get("libsource", "lib")

    def setValue(self, value):
        """Set the value of this component"""
        v = self.element.getChild("value")
        if v:
            v.setChars(value)

    def getValue(self):
        return self.element.get("value")

    def getField(self, name, ignoreCase=True, libraryToo=True):
        """Return the value of a field named name. The component is first
        checked for the field, and then the components library part is checked
        for the field. If the field doesn't exist in either, an empty string is
        returned

        Keywords:
        name -- The name of the field to return the value for
        libraryToo --   look in the libpart's fields for the same name if not found
                        in component itself
        """

        #special fields

        if name.lower() == ColumnList.COL_REFERENCE.lower():
            return self.getRef().strip()

        elif name.lower() == ColumnList.COL_DESCRIPTION.lower():
            return self.getDescription().strip()

        elif name.lower() == ColumnList.COL_DATASHEET.lower():
            return self.getDatasheet().strip()

        fp = self.getFootprint().split(":")

        # Footprint library is first element
        elif name.lower() == ColumnList.COL_FP_LIB.lower():
            if len(fp) > 0:
                return fp[0].strip()
            else:
                return "" # explicit empty return

        elif name.lower() == ColumnList.COL_FP.lower():
            if len(fp) > 0:
                return fp[1].strip()
            else:
                return "" # explicit empty return

        elif name.lower() == ColumnList.COL_VALUE.lower():
            return self.getValue().strip()

        elif name.lower() == ColumnList.COL_PART.lower():
            return self.getPartName().strip()

        elif name.lower() == ColumnList.COL_PART_LIB.lower():
            return self.getLibName().strip()

        #other fields (case insensitive)
        for f in self.getFieldNames():
            if f.lower() == name.lower():
                field = self.element.get("field", "name", f)

                if field == "" and libraryToo:
                    field = self.libpart.getField(f)

                return field.strip()

        #could not find a matching field
        return ""

    def getFieldNames(self):
        """Return a list of field names in play for this component.  Mandatory
        fields are not included, and they are: Value, Footprint, Datasheet, Ref.
        The netlist format only includes fields with non-empty values.  So if a field
        is empty, it will not be present in the returned list.
        """
        fieldNames = []
        fields = self.element.getChild('fields')
        if fields:
            for f in fields.getChildren():
                fieldNames.append( f.get('field','name') )
        return fieldNames

    def getRef(self):
        return self.element.get("comp", "ref")

    #determine if a component is FITTED or not
    def isFitted(self):

        check = self.getField(self.prefs.configField).lower()

        #check the value field first
        if self.getValue().lower() in DNF or check.lower() in DNF:
            return False

        if check == "":
            return True #empty is fitted

        opts = check.split(",")

        result = True

        for opt in opts:
            #options that start with '-' are explicitly removed from certain configurations
            if opt.startswith('-') and opt[1:].lower() == self.prefs.pcbConfig.lower():
                result = False
                break
            if opt.startswith("+"):
                if opt.lower() == self.prefs.pcbConfig.lower():
                    result = True

        #by default, part is fitted
        return result

    #test if this part should be included, based on any regex expressions provided in the preferences
    def testRegExclude(self):

        for reg in self.prefs.regExcludes:

            if type(reg) == list and len(reg) == 2:
                field_name, regex = reg
                field_value = self.getField(field_name)

                regex = regex.decode("unicode_escape")

                if re.search(regex, field_value, flags=re.IGNORECASE) is not None:
                    if self.prefs.verbose:
                        print("Excluding '{ref}': Field '{field}' ({value}) matched '{reg}'".format(
                            ref = self.getRef(),
                            field = field_name,
                            value = field_value,
                            reg = regex))

                    return True #found a match

        #default, could not find any matches
        return False

    def testRegInclude(self):

        if len(self.prefs.regIncludes) == 0: #nothing to match against
            return True

        for reg in self.prefs.regIncludes:

            if type(reg) == list and len(reg) == 2:
                field_name, regex = reg
                field_value = self.getField(field_name)

                print(field_name,field_value,regex)

                if re.search(regex, field_value, flags=re.IGNORECASE) is not None:
                    if self.prefs.verbose:
                        print("")

                    #found a match
                    return True

        #default, could not find a match
        return False

    def getFootprint(self, libraryToo=True):
        ret = self.element.get("footprint")
        if ret =="" and libraryToo:
            ret = self.libpart.getFootprint()
        return ret

    def getDatasheet(self, libraryToo=True):
        return self.libpart.getDatasheet()

    def getTimestamp(self):
        return self.element.get("tstamp")

    def getDescription(self):
        return self.libpart.getDescription()

class ComponentGroup():

    """
    Initialize the group with no components, and default fields
    """
    def __init__(self, prefs=None):
        self.components = []
        self.fields = dict.fromkeys(ColumnList._COLUMNS_DEFAULT)    #columns loaded from KiCAD

        if not prefs:
            prefs = BomPref()

        self.prefs = prefs

    def getField(self, field):

        if not field in self.fields.keys(): return ""
        if not self.fields[field]: return ""
        return u''.join((self.fields[field]))

    def getCount(self):
        return len(self.components)

    #Test if a given component fits in this group
    def matchComponent(self, c):
        if len(self.components) == 0: return True
        if c == self.components[0]: return True

        return False

    #test if a given component is already contained in this grop
    def containsComponent(self, c):
        if self.matchComponent(c) == False: return False

        for comp in self.components:
            if comp.getRef() == c.getRef(): return True

        return False

    #add a component to the group
    def addComponent(self, c):

        if len(self.components) == 0:
            self.components.append(c)
        elif self.containsComponent(c):
            return
        elif self.matchComponent(c):
            self.components.append(c)

    def isFitted(self):
        return any([c.isFitted() for c in self.components])

    #return a list of the components
    def getRefs(self):
        #print([c.getRef() for c in self.components]))
        #return " ".join([c.getRef() for c in self.components])
        return " ".join([c.getRef() for c in self.components])

    #sort the components in correct order
    def sortComponents(self):
        self.components = sorted(self.components, key=lambda c: natural_sort(c.getRef()))

    #update a given field, based on some rules and such
    def updateField(self, field, fieldData):

        #protected fields cannot be overwritten
        if field in ColumnList._COLUMNS_PROTECTED: return

        if (field == None or field == ""): return
        elif fieldData == "" or fieldData == None:
            return

        if (not field in self.fields.keys()) or (self.fields[field] == None) or (self.fields[field] == ""):
            self.fields[field] = fieldData
        elif fieldData.lower() in self.fields[field].lower():
            return
        else:
            print("Conflict:",self.fields[field],",",fieldData)
            self.fields[field] += " " + fieldData

    def updateFields(self):

        for c in self.components:
            for f in c.getFieldNames():

                #these columns are handled explicitly below
                if f in ColumnList._COLUMNS_PROTECTED:
                    continue

                self.updateField(f, c.getField(f))

        #update 'global' fields
        self.fields[ColumnList.COL_REFERENCE] = self.getRefs()

        q = self.getCount()
        self.fields[ColumnList.COL_GRP_QUANTITY] = "{n}{dnf}".format(
            n=q,
            dnf = " (DNF)" if not self.isFitted() else "")

        self.fields[ColumnList.COL_GRP_BUILD_QUANTITY] = str(q * self.prefs.boards) if self.isFitted() else "0"
        self.fields[ColumnList.COL_VALUE] = self.components[0].getValue()
        self.fields[ColumnList.COL_PART] = self.components[0].getPartName()
        self.fields[ColumnList.COL_PART_LIB] = self.components[0].getLibName()
        self.fields[ColumnList.COL_DESCRIPTION] = self.components[0].getDescription()
        self.fields[ColumnList.COL_DATASHEET] = self.components[0].getDatasheet()

        if len(self.components[0].getFootprint().split(":")) >= 2:
            self.fields[ColumnList.COL_FP] = self.components[0].getFootprint().split(":")[-1]
            self.fields[ColumnList.COL_FP_LIB] = self.components[0].getFootprint().split(":")[0]
        else:
            self.fields[ColumnList.COL_FP] = ""
            self.fields[ColumnList.COL_FP_LIB] = ""

    #return a dict of the KiCAD data based on the supplied columns
    #NOW WITH UNICODE SUPPORT!
    def getRow(self, columns):
        row = []
        for key in columns:
            val = self.getField(key)

            if val is None:
                val = ""
            else:
                val = u'' + val
                val = val.encode('utf-8')

            row.append(val)

        return row<|endoftext|>


<commit_before># Copyright (c) 2010-2014, GEM Foundation.
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

import os
import copy
from nose.plugins.attrib import attr

from qa_tests import risk
from openquake.qa_tests_data.scenario_damage import (
    case_1, case_2, case_3, case_4, case_5)
from openquake.engine.tests.utils import helpers
from openquake.engine.db import models

class ScenarioDamageTestCase(risk.BaseRiskQATestCase):
    output_type = "gmf_scenario"

    def _test(self, module):
        testcase = copy.copy(self)
        testcase.module = module
        job = testcase._run_test()
        testcase.compare_xml_outputs(
            job,
            ['expected/dmg_dist_per_asset.xml',
             'expected/collapse_map.xml',
             'expected/dmg_dist_per_taxonomy.xml',
             'expected/dmg_dist_total.xml'])

    def get_hazard_job(self):
        job = helpers.get_job(
            helpers.get_data_path("scenario_hazard/job.ini"))
        fname = os.path.join(os.path.dirname(case_1.__file__),
                             'gmf_scenario.csv')
        helpers.create_gmf_from_csv(job, fname, 'gmf_scenario')
        return job

    def test_case_1(self):
        self._test(case_1)

    def test_case_2(self):
        self._test(case_2)

    def test_case_3(self):
        self._test(case_3)

# FIXME(lp). This is a regression test meant to exercise the sd-imt
# logic in the SR calculator. Data has not been validated
class ScenarioDamageRiskCase4TestCase(risk.FixtureBasedQATestCase):
    module = case_4
    output_type = 'gmf_scenario'
    hazard_calculation_fixture = 'Scenario Damage QA Test 4'

    @attr('qa', 'risk', 'scenario_damage')
    def test(self):
        self._run_test()

    def actual_data(self, job):
        per_asset = models.DmgDistPerAsset.objects.filter(
            dmg_state__risk_calculation=job).order_by(
            'exposure_data', 'dmg_state')
        totals = models.DmgDistTotal.objects.filter(
            dmg_state__risk_calculation=job).order_by(
            'dmg_state')
        data = [[[m.mean, m.stddev] for m in per_asset],
                [[total.mean, total.stddev] for total in totals]]
        return data

    def expected_data(self):
        return [
            [[2665.9251, 385.4088], [320.9249, 363.2749],
             [13.1498, 23.8620], [208.2695, 307.3967],
             [533.6744, 177.1909], [258.0559, 251.5760],
             [638.7025, 757.9946], [670.4166, 539.3635],
             [690.8808, 883.1827]],
            [[3512.8972, 1239.1419], [1525.0160, 685.8950],
             [962.0867, 1051.8785]]]

class ScenarioDamageRiskCase5TestCase(risk.FixtureBasedQATestCase):
    module = case_5
    output_type = 'gmf_scenario'
    hazard_calculation_fixture = 'Scenario Damage QA Test 5'

    @attr('qa', 'risk', 'scenario_damage')
    def test(self):
        job = self._run_test()
        self.compare_xml_outputs(
            job,
            ['expected/dmg_dist_per_asset.xml',
             'expected/collapse_map.xml',
             'expected/dmg_dist_per_taxonomy.xml',
             'expected/dmg_dist_total.xml'])
<commit_msg>qa_tests/scenario_damage: add missing decorators to define test group<commit_after># Copyright (c) 2010-2014, GEM Foundation.
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

import os
import copy
from nose.plugins.attrib import attr

from qa_tests import risk
from openquake.qa_tests_data.scenario_damage import (
    case_1, case_2, case_3, case_4, case_5)
from openquake.engine.tests.utils import helpers
from openquake.engine.db import models

class ScenarioDamageTestCase(risk.BaseRiskQATestCase):
    output_type = "gmf_scenario"

    def _test(self, module):
        testcase = copy.copy(self)
        testcase.module = module
        job = testcase._run_test()
        testcase.compare_xml_outputs(
            job,
            ['expected/dmg_dist_per_asset.xml',
             'expected/collapse_map.xml',
             'expected/dmg_dist_per_taxonomy.xml',
             'expected/dmg_dist_total.xml'])

    def get_hazard_job(self):
        job = helpers.get_job(
            helpers.get_data_path("scenario_hazard/job.ini"))
        fname = os.path.join(os.path.dirname(case_1.__file__),
                             'gmf_scenario.csv')
        helpers.create_gmf_from_csv(job, fname, 'gmf_scenario')
        return job

    @attr('qa', 'risk', 'scenario_damage')
    def test_case_1(self):
        self._test(case_1)

    @attr('qa', 'risk', 'scenario_damage')
    def test_case_2(self):
        self._test(case_2)

    @attr('qa', 'risk', 'scenario_damage')
    def test_case_3(self):
        self._test(case_3)

# FIXME(lp). This is a regression test meant to exercise the sd-imt
# logic in the SR calculator. Data has not been validated
class ScenarioDamageRiskCase4TestCase(risk.FixtureBasedQATestCase):
    module = case_4
    output_type = 'gmf_scenario'
    hazard_calculation_fixture = 'Scenario Damage QA Test 4'

    @attr('qa', 'risk', 'scenario_damage')
    def test(self):
        self._run_test()

    def actual_data(self, job):
        per_asset = models.DmgDistPerAsset.objects.filter(
            dmg_state__risk_calculation=job).order_by(
            'exposure_data', 'dmg_state')
        totals = models.DmgDistTotal.objects.filter(
            dmg_state__risk_calculation=job).order_by(
            'dmg_state')
        data = [[[m.mean, m.stddev] for m in per_asset],
                [[total.mean, total.stddev] for total in totals]]
        return data

    def expected_data(self):
        return [
            [[2665.9251, 385.4088], [320.9249, 363.2749],
             [13.1498, 23.8620], [208.2695, 307.3967],
             [533.6744, 177.1909], [258.0559, 251.5760],
             [638.7025, 757.9946], [670.4166, 539.3635],
             [690.8808, 883.1827]],
            [[3512.8972, 1239.1419], [1525.0160, 685.8950],
             [962.0867, 1051.8785]]]

class ScenarioDamageRiskCase5TestCase(risk.FixtureBasedQATestCase):
    module = case_5
    output_type = 'gmf_scenario'
    hazard_calculation_fixture = 'Scenario Damage QA Test 5'

    @attr('qa', 'risk', 'scenario_damage')
    def test(self):
        job = self._run_test()
        self.compare_xml_outputs(
            job,
            ['expected/dmg_dist_per_asset.xml',
             'expected/collapse_map.xml',
             'expected/dmg_dist_per_taxonomy.xml',
             'expected/dmg_dist_total.xml'])
<|endoftext|>


<commit_before># This file is part of LiSE, a framework for life simulation games.
# Copyright (c) Zachary Spector,  zacharyspector@gmail.com
"""The sort of node that is ultimately located in a Place.

Things may be located in other Things as well, but eventually must be
recursively located in a Place.

There's a subtle distinction between "location" and "containment": a
Thing may be contained by a Portal, but cannot be located there --
only in one of the Portal's endpoints. Things are both located in and
contained by Places, or possibly other Things.

"""
import networkx as nx
from .node import Node
from .util import path_len
from .exc import(
    CacheError,
    TravelException
)

class Thing(Node):
    """The sort of item that has a particular location at any given time.

    If a Thing is in a Place, it is standing still. If it is in a
    Portal, it is moving through that Portal however fast it must in
    order to arrive at the other end when it is scheduled to. If it is
    in another Thing, then it is wherever that is, and moving the
    same.

    """
    extrakeys = {
        'name',
        'character',
        'location',
        'next_location',
        'arrival_time',
        'next_arrival_time'
    }

    def __contains__(self, key):
        if key in self.extrakeys:
            return True
        return super().__contains__(key)

    def __getitem__(self, key):
        """Return one of my stats stored in the database, or a few
        special cases:

        ``name``: return the name that uniquely identifies me within
        my Character

        ``character``: return the name of my character

        ``location``: return the name of my location

        ``arrival_time``: return the tick when I arrived in the
        present location

        ``next_location``: if I'm in transit, return where to, else return None

        ``next_arrival_time``: return the tick when I'm going to
        arrive at ``next_location``

        ``locations``: return a pair of ``(location, next_location)``

        """
        if key == 'name':
            return self.name
        elif key == 'character':
            return self.character.name
        elif key == 'location':
            return self['locations'][0]
        elif key == 'arrival_time':
            cache = self.engine._things_cache[self.character.name][self.name]
            for (branch, tick) in self.engine._active_branches():
                if branch in cache:
                    return cache[branch].rev_before(tick)
            raise CacheError("Locations not cached correctly")
        elif key == 'next_location':
            return self['locations'][1]
        elif key == 'next_arrival_time':
            cache = self.engine._things_cache[self.character.name][self.name]
            for (branch, tick) in self.engine._active_branches():
                if branch in cache:
                    try:
                        return cache[branch].rev_after(tick)
                    except KeyError:
                        return None
            return None
        elif key == 'locations':
            cache = self.engine._things_cache[self.character.name][self.name]
            for (branch, tick) in self.engine._active_branches():
                if branch in cache:
                    return cache[branch][tick]
            raise CacheError("Locations not cached correctly")
        else:
            return super().__getitem__(key)

    def __setitem__(self, key, value):
        """Set ``key``=``value`` for the present game-time."""
        if key == 'name':
            raise ValueError("Can't change names")
        elif key == 'character':
            raise ValueError("Can't change characters")
        elif key == 'location':
            self['locations'] = (value, None)
        elif key == 'arrival_time':
            raise ValueError("Read-only")
        elif key == 'next_location':
            self['locations'] = (self['location'], value)
        elif key == 'next_arrival_time':
            raise ValueError("Read-only")
        elif key == 'locations':
            self._set_loc_and_next(*value)
            self.dispatch('locations', value)
        else:
            super().__setitem__(key, value)

    def __delitem__(self, key):
        """As of now, this key isn't mine."""
        if key in self.extrakeys:
            raise ValueError("Can't delete {}".format(key))
        super().__delitem__(key)

    def __repr__(self):
        """Return my character, name, and location"""
        if self['next_location'] is not None:
            return "{}.thing[{}]@{}->{}".format(
                self['character'],
                self['name'],
                self['location'],
                self['next_location']
            )
        return "{}.thing[{}]@{}".format(
            self['character'],
            self['name'],
            self['location']
        )

    def _get_arrival_time(self):
        """Query the database for when I arrive at my present location."""
        cache = self.engine._things_cache[self.character.name][self.name]
        for (branch, tick) in self.engine._active_branches():
            if branch in cache:
                return cache[branch][tick]
        raise CacheError("Thing seems never to have arrived where it is")

    def _get_next_arrival_time(self):
        """Query the database for when I will arrive at my next location, or
        ``None`` if I'm not traveling.

        """
        cache = self.engine._things_cache[self.character.name][self.name]
        for (branch, tick) in self.engine._active_branches():
            try:
                return min(t for t in cache[branch] if t > tick)
            except (KeyError, ValueError):
                continue

    def delete(self, nochar=False):
        super().delete()
        if not nochar:
            del self.character.thing[self.name]
        (branch, tick) = self.engine.time
        self._set_loc_and_next(None, None)

    def clear(self):
        """Unset everything."""
        for k in list(self.keys()):
            if k not in (
                    'name',
                    'character',
                    'location',
                    'next_location',
                    'arrival_time',
                    'next_arrival_time',
                    'locations'
            ):
                del self[k]

    @property
    def container(self):
        """If I am in transit, this is the Portal I'm moving through. Otherwise
        it's the Thing or Place I'm located in.

        """
        (a, b) = self['locations']
        try:
            return self.character.portal[a][b]
        except KeyError:
            try:
                return self.character.thing[a]
            except KeyError:
                return self.character.place[a]

    @property
    def location(self):
        """The Thing or Place I'm in. If I'm in transit, it's where I
        started.

        """
        if not self['location']:
            return None
        return self.character.node[self['location']]

    @location.setter
    def location(self, v):
        if hasattr(v, 'name'):
            v = v.name
        self['location'] = v

    @property
    def next_location(self):
        """If I'm not in transit, this is None. If I am, it's where I'm
        headed.

        """
        locn = self['next_location']
        if not locn:
            return None
        try:
            return self.character.thing[locn]
        except KeyError:
            return self.character.place[locn]

    @next_location.setter
    def next_location(self, v):
        if hasattr(v, 'name'):
            v = v.name
        self['next_location'] = v

    def _loc_and_next(self):
        """Private method that returns a pair in which the first item is my
        present ``location`` and the second is my ``next_location``,
        to which I am presently travelling.

        """
        cache = self.engine._things_cache[self.character.name][self.name]
        for (branch, tick) in self.engine._active_branches():
            if branch in cache:
                return cache[branch][tick]
        raise CacheError("Thing loc and next weren't cached right")

    def _set_loc_and_next(self, loc, nextloc=None):
        """Private method to simultaneously set ``location`` and
        ``next_location``

        """
        self.engine._set_thing_loc_and_next(
            self.character.name,
            self.name,
            loc,
            nextloc
        )

    def go_to_place(self, place, weight=''):
        """Assuming I'm in a :class:`Place` that has a :class:`Portal` direct
        to the given :class:`Place`, schedule myself to travel to the
        given :class:`Place`, taking an amount of time indicated by
        the ``weight`` stat on the :class:`Portal`, if given; else 1
        tick.

        Return the number of ticks to travel.

        """
        if hasattr(place, 'name'):
            placen = place.name
        else:
            placen = place
        curloc = self["location"]
        orm = self.character.engine
        curtick = orm.tick
        ticks = self.character.portal[curloc][placen].get(weight, 1)
        self['next_location'] = placen
        orm.tick += ticks
        self['locations'] = (placen, None)
        orm.tick = curtick
        return ticks

    def follow_path(self, path, weight=None):
        """Go to several :class:`Place`s in succession, deciding how long to
        spend in each by consulting the ``weight`` stat of the
        :class:`Portal` connecting the one :class:`Place` to the next.

        Return the total number of ticks the travel will take. Raise
        :class:`TravelException` if I can't follow the whole path,
        either because some of its nodes don't exist, or because I'm
        scheduled to be somewhere else.

        """
        curtick = self.character.engine.tick
        prevplace = path.pop(0)
        if prevplace != self['location']:
            raise ValueError("Path does not start at my present location")
        subpath = [prevplace]
        for place in path:
            if (
                    prevplace not in self.character.portal or
                    place not in self.character.portal[prevplace]
            ):
                raise TravelException(
                    "Couldn't follow portal from {} to {}".format(
                        prevplace,
                        place
                    ),
                    path=subpath,
                    traveller=self
                )
            subpath.append(place)
            prevplace = place
        ticks_total = 0
        prevsubplace = subpath.pop(0)
        subsubpath = [prevsubplace]
        for subplace in subpath:
            if prevsubplace != self["location"]:
                l = self["location"]
                fintick = self.character.engine.tick
                self.character.engine.tick = curtick
                raise TravelException(
                    "When I tried traveling to {}, at tick {}, "
                    "I ended up at {}".format(
                        prevsubplace,
                        fintick,
                        l
                    ),
                    path=subpath,
                    followed=subsubpath,
                    branch=self.character.engine.branch,
                    tick=fintick,
                    lastplace=l,
                    traveller=self
                )
            portal = self.character.portal[prevsubplace][subplace]
            tick_inc = portal.get(weight, 1)
            self.go_to_place(subplace, weight)
            self.character.engine.tick += tick_inc
            ticks_total += tick_inc
            subsubpath.append(subplace)
            prevsubplace = subplace
        self.character.engine.tick = curtick
        return ticks_total

    def travel_to(self, dest, weight=None, graph=None):
        """Find the shortest path to the given :class:`Place` from where I am
        now, and follow it.

        If supplied, the ``weight`` stat of the :class:`Portal`s along
        the path will be used in pathfinding, and for deciding how
        long to stay in each Place along the way.

        The ``graph`` argument may be any NetworkX-style graph. It
        will be used for pathfinding if supplied, otherwise I'll use
        my :class:`Character`. In either case, however, I will attempt
        to actually follow the path using my :class:`Character`, which
        might not be possible if the supplied ``graph`` and my
        :class:`Character` are too different. If it's not possible,
        I'll raise a :class:`TravelException`, whose ``subpath``
        attribute holds the part of the path that I *can* follow. To
        make me follow it, pass it to my ``follow_path`` method.

        Return value is the number of ticks the travel will take.

        """
        destn = dest.name if hasattr(dest, 'name') else dest
        graph = self.character if graph is None else graph
        path = nx.shortest_path(graph, self["location"], destn, weight)
        return self.follow_path(path, weight)

    def travel_to_by(self, dest, arrival_tick, weight=None, graph=None):
        """Arrange to travel to ``dest`` such that I arrive there at
        ``arrival_tick``.

        Optional argument ``weight`` indicates what attribute of
        portals will indicate how long they take to go through.

        Optional argument ``graph`` is the graph to perform
        pathfinding with. If it contains a viable path that my
        character does not, you'll get a TravelException.

        """
        curtick = self.character.engine.tick
        if arrival_tick <= curtick:
            raise ValueError("travel always takes positive amount of time")
        destn = dest.name if hasattr(dest, 'name') else dest
        graph = self.character if graph is None else graph
        curloc = self["location"]
        path = nx.shortest_path(graph, curloc, destn, weight)
        travel_time = path_len(graph, path, weight)
        start_tick = arrival_tick - travel_time
        if start_tick <= curtick:
            raise self.TravelException(
                "path too heavy to follow by the specified tick",
                path=path,
                traveller=self
            )
        self.character.engine.tick = start_tick
        self.follow_path(path, weight)
        self.character.engine.tick = curtick

    def _get_json_dict(self):
        (branch, tick) = self.character.engine.time
        return {
            "type": "Thing",
            "version": 0,
            "character": self.character.name,
            "name": self.name,
            "branch": branch,
            "tick": tick,
            "stat": dict(self)
        }

    def dump(self):
        """Return a JSON representation of my present state only, not any of
        my history.

        """
        return self.engine.json_dump(self._get_json_dict())

    def __eq__(self, other):
        return isinstance(other, Thing) and self.character.name == other.character.name and self.name == other.name
<commit_msg>Allow return of falsy locations for Thing<commit_after># This file is part of LiSE, a framework for life simulation games.
# Copyright (c) Zachary Spector,  zacharyspector@gmail.com
"""The sort of node that is ultimately located in a Place.

Things may be located in other Things as well, but eventually must be
recursively located in a Place.

There's a subtle distinction between "location" and "containment": a
Thing may be contained by a Portal, but cannot be located there --
only in one of the Portal's endpoints. Things are both located in and
contained by Places, or possibly other Things.

"""
import networkx as nx
from .node import Node
from .util import path_len
from .exc import(
    CacheError,
    TravelException
)

class Thing(Node):
    """The sort of item that has a particular location at any given time.

    If a Thing is in a Place, it is standing still. If it is in a
    Portal, it is moving through that Portal however fast it must in
    order to arrive at the other end when it is scheduled to. If it is
    in another Thing, then it is wherever that is, and moving the
    same.

    """
    extrakeys = {
        'name',
        'character',
        'location',
        'next_location',
        'arrival_time',
        'next_arrival_time'
    }

    def __contains__(self, key):
        if key in self.extrakeys:
            return True
        return super().__contains__(key)

    def __getitem__(self, key):
        """Return one of my stats stored in the database, or a few
        special cases:

        ``name``: return the name that uniquely identifies me within
        my Character

        ``character``: return the name of my character

        ``location``: return the name of my location

        ``arrival_time``: return the tick when I arrived in the
        present location

        ``next_location``: if I'm in transit, return where to, else return None

        ``next_arrival_time``: return the tick when I'm going to
        arrive at ``next_location``

        ``locations``: return a pair of ``(location, next_location)``

        """
        if key == 'name':
            return self.name
        elif key == 'character':
            return self.character.name
        elif key == 'location':
            return self['locations'][0]
        elif key == 'arrival_time':
            cache = self.engine._things_cache[self.character.name][self.name]
            for (branch, tick) in self.engine._active_branches():
                if branch in cache:
                    return cache[branch].rev_before(tick)
            raise CacheError("Locations not cached correctly")
        elif key == 'next_location':
            return self['locations'][1]
        elif key == 'next_arrival_time':
            cache = self.engine._things_cache[self.character.name][self.name]
            for (branch, tick) in self.engine._active_branches():
                if branch in cache:
                    try:
                        return cache[branch].rev_after(tick)
                    except KeyError:
                        return None
            return None
        elif key == 'locations':
            cache = self.engine._things_cache[self.character.name][self.name]
            for (branch, tick) in self.engine._active_branches():
                if branch in cache:
                    return cache[branch][tick]
            raise CacheError("Locations not cached correctly")
        else:
            return super().__getitem__(key)

    def __setitem__(self, key, value):
        """Set ``key``=``value`` for the present game-time."""
        if key == 'name':
            raise ValueError("Can't change names")
        elif key == 'character':
            raise ValueError("Can't change characters")
        elif key == 'location':
            self['locations'] = (value, None)
        elif key == 'arrival_time':
            raise ValueError("Read-only")
        elif key == 'next_location':
            self['locations'] = (self['location'], value)
        elif key == 'next_arrival_time':
            raise ValueError("Read-only")
        elif key == 'locations':
            self._set_loc_and_next(*value)
            self.dispatch('locations', value)
        else:
            super().__setitem__(key, value)

    def __delitem__(self, key):
        """As of now, this key isn't mine."""
        if key in self.extrakeys:
            raise ValueError("Can't delete {}".format(key))
        super().__delitem__(key)

    def __repr__(self):
        """Return my character, name, and location"""
        if self['next_location'] is not None:
            return "{}.thing[{}]@{}->{}".format(
                self['character'],
                self['name'],
                self['location'],
                self['next_location']
            )
        return "{}.thing[{}]@{}".format(
            self['character'],
            self['name'],
            self['location']
        )

    def _get_arrival_time(self):
        """Query the database for when I arrive at my present location."""
        cache = self.engine._things_cache[self.character.name][self.name]
        for (branch, tick) in self.engine._active_branches():
            if branch in cache:
                return cache[branch][tick]
        raise CacheError("Thing seems never to have arrived where it is")

    def _get_next_arrival_time(self):
        """Query the database for when I will arrive at my next location, or
        ``None`` if I'm not traveling.

        """
        cache = self.engine._things_cache[self.character.name][self.name]
        for (branch, tick) in self.engine._active_branches():
            try:
                return min(t for t in cache[branch] if t > tick)
            except (KeyError, ValueError):
                continue

    def delete(self, nochar=False):
        super().delete()
        if not nochar:
            del self.character.thing[self.name]
        (branch, tick) = self.engine.time
        self._set_loc_and_next(None, None)

    def clear(self):
        """Unset everything."""
        for k in list(self.keys()):
            if k not in (
                    'name',
                    'character',
                    'location',
                    'next_location',
                    'arrival_time',
                    'next_arrival_time',
                    'locations'
            ):
                del self[k]

    @property
    def container(self):
        """If I am in transit, this is the Portal I'm moving through. Otherwise
        it's the Thing or Place I'm located in.

        """
        (a, b) = self['locations']
        try:
            return self.character.portal[a][b]
        except KeyError:
            try:
                return self.character.thing[a]
            except KeyError:
                return self.character.place[a]

    @property
    def location(self):
        """The Thing or Place I'm in. If I'm in transit, it's where I
        started.

        """
        if self['location'] is None:
            return None
        return self.character.node[self['location']]

    @location.setter
    def location(self, v):
        if hasattr(v, 'name'):
            v = v.name
        self['location'] = v

    @property
    def next_location(self):
        """If I'm not in transit, this is None. If I am, it's where I'm
        headed.

        """
        locn = self['next_location']
        if not locn:
            return None
        try:
            return self.character.thing[locn]
        except KeyError:
            return self.character.place[locn]

    @next_location.setter
    def next_location(self, v):
        if hasattr(v, 'name'):
            v = v.name
        self['next_location'] = v

    def _loc_and_next(self):
        """Private method that returns a pair in which the first item is my
        present ``location`` and the second is my ``next_location``,
        to which I am presently travelling.

        """
        cache = self.engine._things_cache[self.character.name][self.name]
        for (branch, tick) in self.engine._active_branches():
            if branch in cache:
                return cache[branch][tick]
        raise CacheError("Thing loc and next weren't cached right")

    def _set_loc_and_next(self, loc, nextloc=None):
        """Private method to simultaneously set ``location`` and
        ``next_location``

        """
        self.engine._set_thing_loc_and_next(
            self.character.name,
            self.name,
            loc,
            nextloc
        )

    def go_to_place(self, place, weight=''):
        """Assuming I'm in a :class:`Place` that has a :class:`Portal` direct
        to the given :class:`Place`, schedule myself to travel to the
        given :class:`Place`, taking an amount of time indicated by
        the ``weight`` stat on the :class:`Portal`, if given; else 1
        tick.

        Return the number of ticks to travel.

        """
        if hasattr(place, 'name'):
            placen = place.name
        else:
            placen = place
        curloc = self["location"]
        orm = self.character.engine
        curtick = orm.tick
        ticks = self.character.portal[curloc][placen].get(weight, 1)
        self['next_location'] = placen
        orm.tick += ticks
        self['locations'] = (placen, None)
        orm.tick = curtick
        return ticks

    def follow_path(self, path, weight=None):
        """Go to several :class:`Place`s in succession, deciding how long to
        spend in each by consulting the ``weight`` stat of the
        :class:`Portal` connecting the one :class:`Place` to the next.

        Return the total number of ticks the travel will take. Raise
        :class:`TravelException` if I can't follow the whole path,
        either because some of its nodes don't exist, or because I'm
        scheduled to be somewhere else.

        """
        curtick = self.character.engine.tick
        prevplace = path.pop(0)
        if prevplace != self['location']:
            raise ValueError("Path does not start at my present location")
        subpath = [prevplace]
        for place in path:
            if (
                    prevplace not in self.character.portal or
                    place not in self.character.portal[prevplace]
            ):
                raise TravelException(
                    "Couldn't follow portal from {} to {}".format(
                        prevplace,
                        place
                    ),
                    path=subpath,
                    traveller=self
                )
            subpath.append(place)
            prevplace = place
        ticks_total = 0
        prevsubplace = subpath.pop(0)
        subsubpath = [prevsubplace]
        for subplace in subpath:
            if prevsubplace != self["location"]:
                l = self["location"]
                fintick = self.character.engine.tick
                self.character.engine.tick = curtick
                raise TravelException(
                    "When I tried traveling to {}, at tick {}, "
                    "I ended up at {}".format(
                        prevsubplace,
                        fintick,
                        l
                    ),
                    path=subpath,
                    followed=subsubpath,
                    branch=self.character.engine.branch,
                    tick=fintick,
                    lastplace=l,
                    traveller=self
                )
            portal = self.character.portal[prevsubplace][subplace]
            tick_inc = portal.get(weight, 1)
            self.go_to_place(subplace, weight)
            self.character.engine.tick += tick_inc
            ticks_total += tick_inc
            subsubpath.append(subplace)
            prevsubplace = subplace
        self.character.engine.tick = curtick
        return ticks_total

    def travel_to(self, dest, weight=None, graph=None):
        """Find the shortest path to the given :class:`Place` from where I am
        now, and follow it.

        If supplied, the ``weight`` stat of the :class:`Portal`s along
        the path will be used in pathfinding, and for deciding how
        long to stay in each Place along the way.

        The ``graph`` argument may be any NetworkX-style graph. It
        will be used for pathfinding if supplied, otherwise I'll use
        my :class:`Character`. In either case, however, I will attempt
        to actually follow the path using my :class:`Character`, which
        might not be possible if the supplied ``graph`` and my
        :class:`Character` are too different. If it's not possible,
        I'll raise a :class:`TravelException`, whose ``subpath``
        attribute holds the part of the path that I *can* follow. To
        make me follow it, pass it to my ``follow_path`` method.

        Return value is the number of ticks the travel will take.

        """
        destn = dest.name if hasattr(dest, 'name') else dest
        graph = self.character if graph is None else graph
        path = nx.shortest_path(graph, self["location"], destn, weight)
        return self.follow_path(path, weight)

    def travel_to_by(self, dest, arrival_tick, weight=None, graph=None):
        """Arrange to travel to ``dest`` such that I arrive there at
        ``arrival_tick``.

        Optional argument ``weight`` indicates what attribute of
        portals will indicate how long they take to go through.

        Optional argument ``graph`` is the graph to perform
        pathfinding with. If it contains a viable path that my
        character does not, you'll get a TravelException.

        """
        curtick = self.character.engine.tick
        if arrival_tick <= curtick:
            raise ValueError("travel always takes positive amount of time")
        destn = dest.name if hasattr(dest, 'name') else dest
        graph = self.character if graph is None else graph
        curloc = self["location"]
        path = nx.shortest_path(graph, curloc, destn, weight)
        travel_time = path_len(graph, path, weight)
        start_tick = arrival_tick - travel_time
        if start_tick <= curtick:
            raise self.TravelException(
                "path too heavy to follow by the specified tick",
                path=path,
                traveller=self
            )
        self.character.engine.tick = start_tick
        self.follow_path(path, weight)
        self.character.engine.tick = curtick

    def _get_json_dict(self):
        (branch, tick) = self.character.engine.time
        return {
            "type": "Thing",
            "version": 0,
            "character": self.character.name,
            "name": self.name,
            "branch": branch,
            "tick": tick,
            "stat": dict(self)
        }

    def dump(self):
        """Return a JSON representation of my present state only, not any of
        my history.

        """
        return self.engine.json_dump(self._get_json_dict())

    def __eq__(self, other):
        return isinstance(other, Thing) and self.character.name == other.character.name and self.name == other.name
<|endoftext|>


<commit_before># Access WeakSet through the weakref module.
# This code is separated-out because it is needed
# by abc.py to load everything else at startup.

from _weakref import ref

__all__ = ['WeakSet']

class _IterationGuard:
    # This context manager registers itself in the current iterators of the
    # weak container, such as to delay all removals until the context manager
    # exits.
    # This technique should be relatively thread-safe (since sets are).

    def __init__(self, weakcontainer):
        # Don't create cycles
        self.weakcontainer = ref(weakcontainer)

    def __enter__(self):
        w = self.weakcontainer()
        if w is not None:
            w._iterating.add(self)
        return self

    def __exit__(self, e, t, b):
        w = self.weakcontainer()
        if w is not None:
            s = w._iterating
            s.remove(self)
            if not s:
                w._commit_removals()

class WeakSet:
    def __init__(self, data=None):
        self.data = set()
        def _remove(item, selfref=ref(self)):
            self = selfref()
            if self is not None:
                if self._iterating:
                    self._pending_removals.append(item)
                else:
                    self.data.discard(item)
        self._remove = _remove
        # A list of keys to be removed
        self._pending_removals = []
        self._iterating = set()
        if data is not None:
            self.update(data)

    def _commit_removals(self):
        l = self._pending_removals
        discard = self.data.discard
        while l:
            discard(l.pop())

    def __iter__(self):
        with _IterationGuard(self):
            for itemref in self.data:
                item = itemref()
                if item is not None:
                    yield item

    def __len__(self):
        return len(self.data) - len(self._pending_removals)

    def __contains__(self, item):
        try:
            wr = ref(item)
        except TypeError:
            return False
        return wr in self.data

    def __reduce__(self):
        return (self.__class__, (list(self),),
                getattr(self, '__dict__', None))

    def add(self, item):
        if self._pending_removals:
            self._commit_removals()
        self.data.add(ref(item, self._remove))

    def clear(self):
        if self._pending_removals:
            self._commit_removals()
        self.data.clear()

    def copy(self):
        return self.__class__(self)

    def pop(self):
        if self._pending_removals:
            self._commit_removals()
        while True:
            try:
                itemref = self.data.pop()
            except KeyError:
                raise KeyError('pop from empty WeakSet')
            item = itemref()
            if item is not None:
                return item

    def remove(self, item):
        if self._pending_removals:
            self._commit_removals()
        self.data.remove(ref(item))

    def discard(self, item):
        if self._pending_removals:
            self._commit_removals()
        self.data.discard(ref(item))

    def update(self, other):
        if self._pending_removals:
            self._commit_removals()
        if isinstance(other, self.__class__):
            self.data.update(other.data)
        else:
            for element in other:
                self.add(element)

    def __ior__(self, other):
        self.update(other)
        return self

    # Helper functions for simple delegating methods.
    def _apply(self, other, method):
        if not isinstance(other, self.__class__):
            other = self.__class__(other)
        newdata = method(other.data)
        newset = self.__class__()
        newset.data = newdata
        return newset

    def difference(self, other):
        return self._apply(other, self.data.difference)
    __sub__ = difference

    def difference_update(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.difference_update(ref(item) for item in other)
    def __isub__(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.difference_update(ref(item) for item in other)
        return self

    def intersection(self, other):
        return self._apply(other, self.data.intersection)
    __and__ = intersection

    def intersection_update(self, other):
        if self._pending_removals:
            self._commit_removals()
        self.data.intersection_update(ref(item) for item in other)
    def __iand__(self, other):
        if self._pending_removals:
            self._commit_removals()
        self.data.intersection_update(ref(item) for item in other)
        return self

    def issubset(self, other):
        return self.data.issubset(ref(item) for item in other)
    __lt__ = issubset

    def __le__(self, other):
        return self.data <= set(ref(item) for item in other)

    def issuperset(self, other):
        return self.data.issuperset(ref(item) for item in other)
    __gt__ = issuperset

    def __ge__(self, other):
        return self.data >= set(ref(item) for item in other)

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return self.data == set(ref(item) for item in other)

    def symmetric_difference(self, other):
        return self._apply(other, self.data.symmetric_difference)
    __xor__ = symmetric_difference

    def symmetric_difference_update(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.symmetric_difference_update(ref(item) for item in other)
    def __ixor__(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.symmetric_difference_update(ref(item) for item in other)
        return self

    def union(self, other):
        return self._apply(other, self.data.union)
    __or__ = union

    def isdisjoint(self, other):
        return len(self.intersection(other)) == 0
<commit_msg>Port 2.7 fix for sporadic failure in test_weakset.<commit_after># Access WeakSet through the weakref module.
# This code is separated-out because it is needed
# by abc.py to load everything else at startup.

from _weakref import ref

__all__ = ['WeakSet']

class _IterationGuard:
    # This context manager registers itself in the current iterators of the
    # weak container, such as to delay all removals until the context manager
    # exits.
    # This technique should be relatively thread-safe (since sets are).

    def __init__(self, weakcontainer):
        # Don't create cycles
        self.weakcontainer = ref(weakcontainer)

    def __enter__(self):
        w = self.weakcontainer()
        if w is not None:
            w._iterating.add(self)
        return self

    def __exit__(self, e, t, b):
        w = self.weakcontainer()
        if w is not None:
            s = w._iterating
            s.remove(self)
            if not s:
                w._commit_removals()

class WeakSet:
    def __init__(self, data=None):
        self.data = set()
        def _remove(item, selfref=ref(self)):
            self = selfref()
            if self is not None:
                if self._iterating:
                    self._pending_removals.append(item)
                else:
                    self.data.discard(item)
        self._remove = _remove
        # A list of keys to be removed
        self._pending_removals = []
        self._iterating = set()
        if data is not None:
            self.update(data)

    def _commit_removals(self):
        l = self._pending_removals
        discard = self.data.discard
        while l:
            discard(l.pop())

    def __iter__(self):
        with _IterationGuard(self):
            for itemref in self.data:
                item = itemref()
                if item is not None:
                    yield item

    def __len__(self):
        return len(self.data) - len(self._pending_removals)

    def __contains__(self, item):
        try:
            wr = ref(item)
        except TypeError:
            return False
        return wr in self.data

    def __reduce__(self):
        return (self.__class__, (list(self),),
                getattr(self, '__dict__', None))

    def add(self, item):
        if self._pending_removals:
            self._commit_removals()
        self.data.add(ref(item, self._remove))

    def clear(self):
        if self._pending_removals:
            self._commit_removals()
        self.data.clear()

    def copy(self):
        return self.__class__(self)

    def pop(self):
        if self._pending_removals:
            self._commit_removals()
        while True:
            try:
                itemref = self.data.pop()
            except KeyError:
                raise KeyError('pop from empty WeakSet')
            item = itemref()
            if item is not None:
                return item

    def remove(self, item):
        if self._pending_removals:
            self._commit_removals()
        self.data.remove(ref(item))

    def discard(self, item):
        if self._pending_removals:
            self._commit_removals()
        self.data.discard(ref(item))

    def update(self, other):
        if self._pending_removals:
            self._commit_removals()
        for element in other:
            self.add(element)

    def __ior__(self, other):
        self.update(other)
        return self

    # Helper functions for simple delegating methods.
    def _apply(self, other, method):
        if not isinstance(other, self.__class__):
            other = self.__class__(other)
        newdata = method(other.data)
        newset = self.__class__()
        newset.data = newdata
        return newset

    def difference(self, other):
        return self._apply(other, self.data.difference)
    __sub__ = difference

    def difference_update(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.difference_update(ref(item) for item in other)
    def __isub__(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.difference_update(ref(item) for item in other)
        return self

    def intersection(self, other):
        return self._apply(other, self.data.intersection)
    __and__ = intersection

    def intersection_update(self, other):
        if self._pending_removals:
            self._commit_removals()
        self.data.intersection_update(ref(item) for item in other)
    def __iand__(self, other):
        if self._pending_removals:
            self._commit_removals()
        self.data.intersection_update(ref(item) for item in other)
        return self

    def issubset(self, other):
        return self.data.issubset(ref(item) for item in other)
    __lt__ = issubset

    def __le__(self, other):
        return self.data <= set(ref(item) for item in other)

    def issuperset(self, other):
        return self.data.issuperset(ref(item) for item in other)
    __gt__ = issuperset

    def __ge__(self, other):
        return self.data >= set(ref(item) for item in other)

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return self.data == set(ref(item) for item in other)

    def symmetric_difference(self, other):
        return self._apply(other, self.data.symmetric_difference)
    __xor__ = symmetric_difference

    def symmetric_difference_update(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.symmetric_difference_update(ref(item) for item in other)
    def __ixor__(self, other):
        if self._pending_removals:
            self._commit_removals()
        if self is other:
            self.data.clear()
        else:
            self.data.symmetric_difference_update(ref(item) for item in other)
        return self

    def union(self, other):
        return self._apply(other, self.data.union)
    __or__ = union

    def isdisjoint(self, other):
        return len(self.intersection(other)) == 0
<|endoftext|>


<commit_before><commit_msg>[sale_stock_availability] Only count stock on selected warehouse<commit_after><|endoftext|>


<commit_before># Copyright (C) 2001,2002 Python Software Foundation
# Author: barry@zope.com (Barry Warsaw)

"""Miscellaneous utilities.
"""

import time
import socket
import re
import random
import os
import warnings
from cStringIO import StringIO
from types import ListType

from rfc822 import unquote, quote
from rfc822 import AddressList as _AddressList
from rfc822 import mktime_tz

# We need wormarounds for bugs in these methods in older Pythons (see below)
from rfc822 import parsedate as _parsedate
from rfc822 import parsedate_tz as _parsedate_tz

try:
    from quopri import decodestring as _qdecode
except ImportError:
    # Python 2.1 doesn't have quopri.decodestring()
    def _qdecode(s):
        import quopri as _quopri

        if not s:
            return s
        hasnewline = (s[-1] == '\n')
        infp = StringIO(s)
        outfp = StringIO()
        _quopri.decode(infp, outfp)
        value = outfp.getvalue()
        if not hasnewline and value[-1] =='\n':
            return value[:-1]
        return value

import base64

# Intrapackage imports
from Encoders import _bencode, _qencode

COMMASPACE = ', '
UEMPTYSTRING = u''
CRLF = '\r\n'

specialsre = re.compile(r'[][\()<>@,:;".]')
escapesre = re.compile(r'[][\()"]')

# Helpers

def _identity(s):
    return s

def _bdecode(s):
    if not s:
        return s
    # We can't quite use base64.encodestring() since it tacks on a "courtesy
    # newline".  Blech!
    if not s:
        return s
    hasnewline = (s[-1] == '\n')
    value = base64.decodestring(s)
    if not hasnewline and value[-1] == '\n':
        return value[:-1]
    return value

def fix_eols(s):
    """Replace all line-ending characters with \r\n."""
    # Fix newlines with no preceding carriage return
    s = re.sub(r'(?<!\r)\n', CRLF, s)
    # Fix carriage returns with no following newline
    s = re.sub(r'\r(?!\n)', CRLF, s)
    return s

def formataddr(pair):
    """The inverse of parseaddr(), this takes a 2-tuple of the form
    (realname, email_address) and returns the string value suitable
    for an RFC 2822 From:, To: or Cc:.

    If the first element of pair is false, then the second element is
    returned unmodified.
    """
    name, address = pair
    if name:
        quotes = ''
        if specialsre.search(name):
            quotes = '"'
        name = escapesre.sub(r'\\\g<0>', name)
        return '%s%s%s <%s>' % (quotes, name, quotes, address)
    return address

# For backwards compatibility
def dump_address_pair(pair):
    warnings.warn('Use email.Utils.formataddr() instead',
                  DeprecationWarning, 2)
    return formataddr(pair)

def getaddresses(fieldvalues):
    """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
    all = COMMASPACE.join(fieldvalues)
    a = _AddressList(all)
    return a.addresslist

ecre = re.compile(r'''
  =\?                   # literal =?
  (?P<charset>[^?]*?)   # non-greedy up to the next ? is the charset
  \?                    # literal ?
  (?P<encoding>[qb])    # either a "q" or a "b", case insensitive
  \?                    # literal ?
  (?P<atom>.*?)         # non-greedy up to the next ?= is the atom
  \?=                   # literal ?=
  ''', re.VERBOSE | re.IGNORECASE)

def decode(s):
    """Return a decoded string according to RFC 2047, as a unicode string.

    NOTE: This function is deprecated.  Use Header.decode_header() instead.
    """
    warnings.warn('Use Header.decode_header() instead.', DeprecationWarning, 2)
    # Intra-package import here to avoid circular import problems.
    from Header import decode_header
    L = decode_header(s)
    if not isinstance(L, ListType):
        # s wasn't decoded
        return s

    rtn = []
    for atom, charset in L:
        if charset is None:
            rtn.append(atom)
        else:
            # Convert the string to Unicode using the given encoding.  Leave
            # Unicode conversion errors to strict.
            rtn.append(unicode(atom, charset))
    # Now that we've decoded everything, we just need to join all the parts
    # together into the final string.
    return UEMPTYSTRING.join(rtn)

def encode(s, charset='iso-8859-1', encoding='q'):
    """Encode a string according to RFC 2047."""
    warnings.warn('Use Header.Header.encode() instead.', DeprecationWarning, 2)
    encoding = encoding.lower()
    if encoding == 'q':
        estr = _qencode(s)
    elif encoding == 'b':
        estr = _bencode(s)
    else:
        raise ValueError, 'Illegal encoding code: ' + encoding
    return '=?%s?%s?%s?=' % (charset.lower(), encoding, estr)

def formatdate(timeval=None, localtime=0):
    """Returns a date string as specified by RFC 2822, e.g.:

    Fri, 09 Nov 2001 01:08:47 -0000

    Optional timeval if given is a floating point time value as accepted by
    gmtime() and localtime(), otherwise the current time is used.

    Optional localtime is a flag that when true, interprets timeval, and
    returns a date relative to the local timezone instead of UTC, properly
    taking daylight savings time into account.
    """
    # Note: we cannot use strftime() because that honors the locale and RFC
    # 2822 requires that day and month names be the English abbreviations.
    if timeval is None:
        timeval = time.time()
    if localtime:
        now = time.localtime(timeval)
        # Calculate timezone offset, based on whether the local zone has
        # daylight savings time, and whether DST is in effect.
        if time.daylight and now[-1]:
            offset = time.altzone
        else:
            offset = time.timezone
        hours, minutes = divmod(abs(offset), 3600)
        # Remember offset is in seconds west of UTC, but the timezone is in
        # minutes east of UTC, so the signs differ.
        if offset > 0:
            sign = '-'
        else:
            sign = '+'
        zone = '%s%02d%02d' % (sign, hours, minutes / 60)
    else:
        now = time.gmtime(timeval)
        # Timezone offset is always -0000
        zone = '-0000'
    return '%s, %02d %s %04d %02d:%02d:%02d %s' % (
        ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now[6]],
        now[2],
        ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
         'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now[1] - 1],
        now[0], now[3], now[4], now[5],
        zone)

def make_msgid(idstring=None):
    """Returns a string suitable for RFC 2822 compliant Message-ID:, e.g:

    <20020201195627.33539.96671@nightshade.la.mastaler.com>

    Optional idstring if given is a string used to strengthen the
    uniqueness of the Message-ID, otherwise an empty string is used.
    """
    timeval = time.time()
    utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval))
    pid = os.getpid()
    randint = random.randrange(100000)
    if idstring is None:
        idstring = ''
    else:
        idstring = '.' + idstring
    idhost = socket.getfqdn()
    msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost)
    return msgid

# These functions are in the standalone mimelib version only because they've
# subsequently been fixed in the latest Python versions.  We use this to worm
# around broken older Pythons.
def parsedate(data):
    if not data:
        return None
    return _parsedate(data)

def parsedate_tz(data):
    if not data:
        return None
    return _parsedate_tz(data)

def parseaddr(addr):
    addrs = _AddressList(addr).addresslist
    if not addrs:
        return '', ''
    return addrs[0]
<commit_msg>Use absolute import paths for intrapackage imports.<commit_after># Copyright (C) 2001,2002 Python Software Foundation
# Author: barry@zope.com (Barry Warsaw)

"""Miscellaneous utilities.
"""

import time
import socket
import re
import random
import os
import warnings
from cStringIO import StringIO
from types import ListType

from rfc822 import unquote, quote
from rfc822 import AddressList as _AddressList
from rfc822 import mktime_tz

# We need wormarounds for bugs in these methods in older Pythons (see below)
from rfc822 import parsedate as _parsedate
from rfc822 import parsedate_tz as _parsedate_tz

try:
    from quopri import decodestring as _qdecode
except ImportError:
    # Python 2.1 doesn't have quopri.decodestring()
    def _qdecode(s):
        import quopri as _quopri

        if not s:
            return s
        hasnewline = (s[-1] == '\n')
        infp = StringIO(s)
        outfp = StringIO()
        _quopri.decode(infp, outfp)
        value = outfp.getvalue()
        if not hasnewline and value[-1] =='\n':
            return value[:-1]
        return value

import base64

# Intrapackage imports
from email.Encoders import _bencode, _qencode

COMMASPACE = ', '
UEMPTYSTRING = u''
CRLF = '\r\n'

specialsre = re.compile(r'[][\()<>@,:;".]')
escapesre = re.compile(r'[][\()"]')

# Helpers

def _identity(s):
    return s

def _bdecode(s):
    if not s:
        return s
    # We can't quite use base64.encodestring() since it tacks on a "courtesy
    # newline".  Blech!
    if not s:
        return s
    hasnewline = (s[-1] == '\n')
    value = base64.decodestring(s)
    if not hasnewline and value[-1] == '\n':
        return value[:-1]
    return value

def fix_eols(s):
    """Replace all line-ending characters with \r\n."""
    # Fix newlines with no preceding carriage return
    s = re.sub(r'(?<!\r)\n', CRLF, s)
    # Fix carriage returns with no following newline
    s = re.sub(r'\r(?!\n)', CRLF, s)
    return s

def formataddr(pair):
    """The inverse of parseaddr(), this takes a 2-tuple of the form
    (realname, email_address) and returns the string value suitable
    for an RFC 2822 From:, To: or Cc:.

    If the first element of pair is false, then the second element is
    returned unmodified.
    """
    name, address = pair
    if name:
        quotes = ''
        if specialsre.search(name):
            quotes = '"'
        name = escapesre.sub(r'\\\g<0>', name)
        return '%s%s%s <%s>' % (quotes, name, quotes, address)
    return address

# For backwards compatibility
def dump_address_pair(pair):
    warnings.warn('Use email.Utils.formataddr() instead',
                  DeprecationWarning, 2)
    return formataddr(pair)

def getaddresses(fieldvalues):
    """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
    all = COMMASPACE.join(fieldvalues)
    a = _AddressList(all)
    return a.addresslist

ecre = re.compile(r'''
  =\?                   # literal =?
  (?P<charset>[^?]*?)   # non-greedy up to the next ? is the charset
  \?                    # literal ?
  (?P<encoding>[qb])    # either a "q" or a "b", case insensitive
  \?                    # literal ?
  (?P<atom>.*?)         # non-greedy up to the next ?= is the atom
  \?=                   # literal ?=
  ''', re.VERBOSE | re.IGNORECASE)

def decode(s):
    """Return a decoded string according to RFC 2047, as a unicode string.

    NOTE: This function is deprecated.  Use Header.decode_header() instead.
    """
    warnings.warn('Use Header.decode_header() instead.', DeprecationWarning, 2)
    # Intra-package import here to avoid circular import problems.
    from email.Header import decode_header
    L = decode_header(s)
    if not isinstance(L, ListType):
        # s wasn't decoded
        return s

    rtn = []
    for atom, charset in L:
        if charset is None:
            rtn.append(atom)
        else:
            # Convert the string to Unicode using the given encoding.  Leave
            # Unicode conversion errors to strict.
            rtn.append(unicode(atom, charset))
    # Now that we've decoded everything, we just need to join all the parts
    # together into the final string.
    return UEMPTYSTRING.join(rtn)

def encode(s, charset='iso-8859-1', encoding='q'):
    """Encode a string according to RFC 2047."""
    warnings.warn('Use Header.Header.encode() instead.', DeprecationWarning, 2)
    encoding = encoding.lower()
    if encoding == 'q':
        estr = _qencode(s)
    elif encoding == 'b':
        estr = _bencode(s)
    else:
        raise ValueError, 'Illegal encoding code: ' + encoding
    return '=?%s?%s?%s?=' % (charset.lower(), encoding, estr)

def formatdate(timeval=None, localtime=0):
    """Returns a date string as specified by RFC 2822, e.g.:

    Fri, 09 Nov 2001 01:08:47 -0000

    Optional timeval if given is a floating point time value as accepted by
    gmtime() and localtime(), otherwise the current time is used.

    Optional localtime is a flag that when true, interprets timeval, and
    returns a date relative to the local timezone instead of UTC, properly
    taking daylight savings time into account.
    """
    # Note: we cannot use strftime() because that honors the locale and RFC
    # 2822 requires that day and month names be the English abbreviations.
    if timeval is None:
        timeval = time.time()
    if localtime:
        now = time.localtime(timeval)
        # Calculate timezone offset, based on whether the local zone has
        # daylight savings time, and whether DST is in effect.
        if time.daylight and now[-1]:
            offset = time.altzone
        else:
            offset = time.timezone
        hours, minutes = divmod(abs(offset), 3600)
        # Remember offset is in seconds west of UTC, but the timezone is in
        # minutes east of UTC, so the signs differ.
        if offset > 0:
            sign = '-'
        else:
            sign = '+'
        zone = '%s%02d%02d' % (sign, hours, minutes / 60)
    else:
        now = time.gmtime(timeval)
        # Timezone offset is always -0000
        zone = '-0000'
    return '%s, %02d %s %04d %02d:%02d:%02d %s' % (
        ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now[6]],
        now[2],
        ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
         'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now[1] - 1],
        now[0], now[3], now[4], now[5],
        zone)

def make_msgid(idstring=None):
    """Returns a string suitable for RFC 2822 compliant Message-ID:, e.g:

    <20020201195627.33539.96671@nightshade.la.mastaler.com>

    Optional idstring if given is a string used to strengthen the
    uniqueness of the Message-ID, otherwise an empty string is used.
    """
    timeval = time.time()
    utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval))
    pid = os.getpid()
    randint = random.randrange(100000)
    if idstring is None:
        idstring = ''
    else:
        idstring = '.' + idstring
    idhost = socket.getfqdn()
    msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost)
    return msgid

# These functions are in the standalone mimelib version only because they've
# subsequently been fixed in the latest Python versions.  We use this to worm
# around broken older Pythons.
def parsedate(data):
    if not data:
        return None
    return _parsedate(data)

def parsedate_tz(data):
    if not data:
        return None
    return _parsedate_tz(data)

def parseaddr(addr):
    addrs = _AddressList(addr).addresslist
    if not addrs:
        return '', ''
    return addrs[0]
<|endoftext|>


<commit_before><commit_msg>Simplified filter to a get in scan ingest kickoff.<commit_after><|endoftext|>