<commit_before>"""The primary game object lives in this module."""
import pygame
from xml.etree import ElementTree
from pygame.color import THECOLORS
from pymunk import Vec2d
import pymunk
from pymunk.pygame_util import to_pygame
from game import SCREEN_HALF, JUMP_THROUGH_COLLISION_TYPE, SPACE

class Tileset(object):
    def __init__(self, filename):
        self.img = pygame.image.load(filename)
        self.tile_width = 32
        self.tile_height = 32

    def draw(self, screen, tile_id, position):
        if not tile_id:
            return  # There was nothing to be drawn
        tile_id -= 1  # we're 1-indexed in Tiled
        tiles_wide = self.img.get_width() // self.tile_width
        source_x = (tile_id % tiles_wide) * self.tile_width
        source_y = (tile_id // tiles_wide) * self.tile_height
        screen.blit(self.img, to_pygame(position, screen),
                    (source_x, source_y, self.tile_width, self.tile_height))

class TileWorld(object):
    def __init__(self, filename):
        # Somebody set up us the camera
        self.camera = Vec2d(0, 0)

        # Load the Tileset
        # TODO: tileset is hardcoded
        self.tileset = Tileset('res/images/mininicular.png')

        # Parse the TMX
        self.tree = ElementTree.parse(filename)
        root = self.tree.getroot()
        dim = Vec2d(int(root.get('width')), int(root.get('height')))
        t_size = Vec2d(int(root.get('tilewidth')), int(root.get('tileheight')))
        self.map_size = Vec2d(t_size.x*dim.x, t_size.y*dim.y)
        layers = []
        for layer in root.findall('layer'):
            # This is the csv of the map data
            csv = layer.findall('data')[0].text
            # pygame is opposite of Tiled, so needs reversed
            new_layer = list(reversed([row for row in csv.split('\n')]))
            layers.append(new_layer)

        # These are the collision objects
        self.platforms = []
        for object_group in root.findall('objectgroup'):
            for obj in object_group.iter('object'):
                jump_through = False
                speed = 1
                waypoints = []
                group_offset = Vec2d(int(obj.get('x')), int(obj.get('y')))
                for p in obj.iter('property'):
                    if p.get('name') == "jump_through":
                        jump_through = True
                    if p.get('name') == "speed":
                        speed = int(p.get('value'))
                    if p.get('name') == "waypoints":
                        waypoints = self.pointify(p.get('value'))
                line = obj.find('polyline')
                points = self.pointify(line.get('points'),
                                       group_offset=group_offset)
                p = Platform(points, jump_through=jump_through, speed=speed,
                             waypoints=waypoints)
                self.platforms.append(p)

        # Pre-generate the surfaces
        self.surfaces = []
        for layer in layers:
            surf = pygame.Surface(self.map_size, pygame.SRCALPHA, 32)
            self.generate_surface(surf, layer)
            self.surfaces.append(surf)

    def pointify(self, csv_string, group_offset=Vec2d.zero()):
        if group_offset.y:
            offset = self.map_size.y - group_offset.y
        else:
            offset = 0
        point_strings = [p.split(',') for p in csv_string.split()]
        return [(group_offset.x + int(p[0]),
                 offset-int(p[1])) for p in point_strings]

    def update(self, dt, players):
        # TODO: damage players outside of the map

        for p in self.platforms:
            p.update(dt, players)

        # Make the camera follow the center of the players
        living_players = [p for p in players if p.health > 0]
        count = len(living_players)
        if not count:
            return
        position = sum(p.feet.body.position for p in living_players) / count
        destination = position
        current_pos = self.camera
        distance = current_pos.get_distance(destination)
        camera_speed = distance**.5 / 3
        t = 1 if distance < camera_speed else camera_speed / distance
        new_pos = current_pos.interpolate_to(destination, t)
        self.camera = new_pos

    def generate_surface(self, surf, data):
        surf.convert_alpha()
        for y in range(len(data)):
            row = data[y].split(',')
            for x in range(len(row)):
                tile_id = row[x]
                if tile_id == '':
                    continue
                pos = Vec2d(x*32, y*32)
                self.tileset.draw(surf, int(tile_id), pos)

    def draw(self, screen):
        position = Vec2d(self.camera.x, 50*32-self.camera.y) - SCREEN_HALF
        count = len(self.surfaces) - 1.

        for i, surf in enumerate(self.surfaces):
            parallax_factor = i / count

            # constrain to the top and left
            temp = position * parallax_factor
            offset = position * (1 - parallax_factor)

            # constrain to the bottom and right
            size_offset = -position + self.map_size

            # Get the part of the surface to blit
            p = (-offset.x if offset.x < 0 else temp.x,
                 -offset.y if offset.y < 0 else temp.y,
                 size_offset.x, size_offset.y)
            screen.blit(surf, (-position.x if offset.x < 0 else 0,
                               -position.y if offset.y < 0 else 0), p)

class Platform(object):
    def __init__(self, points, jump_through=False, waypoints=None, speed=1):
        self.waypoints = waypoints
        self.speed = speed
        self.target_index = 0
        is_moving = bool(self.waypoints)

        # Create the body type
        if is_moving:
            self.body = pymunk.Body(pymunk.inf, pymunk.inf)
        else:
            self.body = SPACE.static_body

        for i in range(len(points)-1):
            # Make and configure the physics object
            seg = pymunk.Segment(self.body, points[i], points[i+1], 5)
            seg.friction = 1
            seg.group = 1
            if jump_through:
                seg.collision_type = JUMP_THROUGH_COLLISION_TYPE
                seg.color = (255, 255, 0, 255)
            if is_moving:
                seg.color = THECOLORS["blue"]
                seg.body.position = Vec2d(self.waypoints[0])
            # Add it to the world
            SPACE.add(seg)

    def update(self, dt, players):
        if not self.waypoints:
            return  # non-moving platforms don't matter
        destination = self.waypoints[self.target_index]
        current_pos = Vec2d(self.body.position)
        distance = current_pos.get_distance(destination)
        if distance < self.speed:
            self.target_index = (self.target_index + 1) % len(self.waypoints)
            t = 1
        else:
            t = self.speed / distance
        new_pos = current_pos.interpolate_to(destination, t)
        self.body.position = new_pos
        self.body.velocity = (new_pos - current_pos) / dt
<commit_msg>Simplified camera code<commit_after>"""The primary game object lives in this module."""
import pygame
from xml.etree import ElementTree
from pygame.color import THECOLORS
from pymunk import Vec2d
import pymunk
from pymunk.pygame_util import to_pygame
from game import SCREEN_HALF, JUMP_THROUGH_COLLISION_TYPE, SPACE

class Tileset(object):
    def __init__(self, filename):
        self.img = pygame.image.load(filename)
        self.tile_width = 32
        self.tile_height = 32

    def draw(self, screen, tile_id, position):
        if not tile_id:
            return  # There was nothing to be drawn
        tile_id -= 1  # we're 1-indexed in Tiled
        tiles_wide = self.img.get_width() // self.tile_width
        source_x = (tile_id % tiles_wide) * self.tile_width
        source_y = (tile_id // tiles_wide) * self.tile_height
        screen.blit(self.img, to_pygame(position, screen),
                    (source_x, source_y, self.tile_width, self.tile_height))

class TileWorld(object):
    def __init__(self, filename):
        # Somebody set up us the camera
        self.camera = Vec2d(0, 0)

        # Load the Tileset
        # TODO: tileset is hardcoded
        self.tileset = Tileset('res/images/mininicular.png')

        # Parse the TMX
        self.tree = ElementTree.parse(filename)
        root = self.tree.getroot()
        dim = Vec2d(int(root.get('width')), int(root.get('height')))
        t_size = Vec2d(int(root.get('tilewidth')), int(root.get('tileheight')))
        self.map_size = Vec2d(t_size.x*dim.x, t_size.y*dim.y)
        layers = []
        for layer in root.findall('layer'):
            # This is the csv of the map data
            csv = layer.findall('data')[0].text
            # pygame is opposite of Tiled, so needs reversed
            new_layer = list(reversed([row for row in csv.split('\n')]))
            layers.append(new_layer)

        # These are the collision objects
        self.platforms = []
        for object_group in root.findall('objectgroup'):
            for obj in object_group.iter('object'):
                jump_through = False
                speed = 1
                waypoints = []
                group_offset = Vec2d(int(obj.get('x')), int(obj.get('y')))
                for p in obj.iter('property'):
                    if p.get('name') == "jump_through":
                        jump_through = True
                    if p.get('name') == "speed":
                        speed = int(p.get('value'))
                    if p.get('name') == "waypoints":
                        waypoints = self.pointify(p.get('value'))
                line = obj.find('polyline')
                points = self.pointify(line.get('points'),
                                       group_offset=group_offset)
                p = Platform(points, jump_through=jump_through, speed=speed,
                             waypoints=waypoints)
                self.platforms.append(p)

        # Pre-generate the surfaces
        self.surfaces = []
        for layer in layers:
            surf = pygame.Surface(self.map_size, pygame.SRCALPHA, 32)
            self.generate_surface(surf, layer)
            self.surfaces.append(surf)

    def pointify(self, csv_string, group_offset=Vec2d.zero()):
        if group_offset.y:
            offset = self.map_size.y - group_offset.y
        else:
            offset = 0
        point_strings = [p.split(',') for p in csv_string.split()]
        return [(group_offset.x + int(p[0]),
                 offset-int(p[1])) for p in point_strings]

    def update(self, dt, players):
        # TODO: damage players outside of the map

        for p in self.platforms:
            p.update(dt, players)

        # Make the camera follow the center of the players
        living_players = [p for p in players if p.health > 0]
        count = len(living_players)
        if not count:
            return
        position = sum(p.feet.body.position for p in living_players) / count
        distance = self.camera.get_distance(position)
        camera_speed = max(distance**.5 / 3, .01)
        self.camera = self.camera.interpolate_to(position, camera_speed / distance)

    def generate_surface(self, surf, data):
        surf.convert_alpha()
        for y in range(len(data)):
            row = data[y].split(',')
            for x in range(len(row)):
                tile_id = row[x]
                if tile_id == '':
                    continue
                pos = Vec2d(x*32, y*32)
                self.tileset.draw(surf, int(tile_id), pos)

    def draw(self, screen):
        position = Vec2d(self.camera.x, 50*32-self.camera.y) - SCREEN_HALF
        count = len(self.surfaces) - 1.

        for i, surf in enumerate(self.surfaces):
            parallax_factor = i / count

            # constrain to the top and left
            temp = position * parallax_factor
            offset = position * (1 - parallax_factor)

            # constrain to the bottom and right
            size_offset = -position + self.map_size

            # Get the part of the surface to blit
            p = (-offset.x if offset.x < 0 else temp.x,
                 -offset.y if offset.y < 0 else temp.y,
                 size_offset.x, size_offset.y)
            screen.blit(surf, (-position.x if offset.x < 0 else 0,
                               -position.y if offset.y < 0 else 0), p)

class Platform(object):
    def __init__(self, points, jump_through=False, waypoints=None, speed=1):
        self.waypoints = waypoints
        self.speed = speed
        self.target_index = 0
        is_moving = bool(self.waypoints)

        # Create the body type
        if is_moving:
            self.body = pymunk.Body(pymunk.inf, pymunk.inf)
        else:
            self.body = SPACE.static_body

        for i in range(len(points)-1):
            # Make and configure the physics object
            seg = pymunk.Segment(self.body, points[i], points[i+1], 5)
            seg.friction = 1
            seg.group = 1
            if jump_through:
                seg.collision_type = JUMP_THROUGH_COLLISION_TYPE
                seg.color = (255, 255, 0, 255)
            if is_moving:
                seg.color = THECOLORS["blue"]
                seg.body.position = Vec2d(self.waypoints[0])
            # Add it to the world
            SPACE.add(seg)

    def update(self, dt, players):
        if not self.waypoints:
            return  # non-moving platforms don't matter
        destination = self.waypoints[self.target_index]
        current_pos = Vec2d(self.body.position)
        distance = current_pos.get_distance(destination)
        if distance < self.speed:
            self.target_index = (self.target_index + 1) % len(self.waypoints)
            t = 1
        else:
            t = self.speed / distance
        new_pos = current_pos.interpolate_to(destination, t)
        self.body.position = new_pos
        self.body.velocity = (new_pos - current_pos) / dt
<|endoftext|>


<commit_before>#################################################################
##
##  'Field' concept is implemented for Soil Moisture component.
##
##  Sai Nudurupati and Erkan Istanbulluoglu - 15May2014
#################################################################

from landlab import Component

import numpy as np

_VALID_METHODS = set(['Grid'])

def assert_method_is_valid(method):
    if method not in _VALID_METHODS:
        raise ValueError('%s: Invalid method name' % method)

class SoilMoisture( Component ):
    """
    This component calculates and updates soil moisture after each storm. Soil
    moisture is represented as a single bucket. Rainfall depth fills the bucket
    and the soil moisture decays as a result of leakage and ET following the
    analytical solution of Laio et al., (2001). This component can operate on
    any raster grid. Input file is named soilmoisture_input.txt and is
    temporarily placed under landlab.components.

    Storms are considered to be instantaneous events.
    Storm duration, depth and interstorm duration are obtained as input
    Storm depth is obtained in mm and storm duration and interstorm duration
    are obtained in hours

    >>> from landlab import RasterModelGrid
    >>> from landlab.components.soil_moisture.soil_moisture_field import SoilMoisture
    >>> import numpy as np
    >>> grid = RasterModelGrid( 5, 4, 0.2 )
    >>> grid['node']['Elevation'] = np.random.rand( grid.number_of_nodes ) * 1000
    >>> rad = Radiation( grid )
    >>> rad.name
        Radiation
    >>> current_time = 0.5
    >>> rad.update( current_time )
    >>>
    """

    _name = 'Soil Moisture'

    _input_var_names = set([
        'VegetationCover',
        'LiveLeafAreaIndex',
        'PotentialEvapotranspiraton',
    ])

    _output_var_names = set([
        'WaterStress',
        'SaturationFraction',
        'Drainage',
        'Runoff',
        'ActualEvapotranspiration',
    ])

    _var_units = {
        'VegetationCover' : 'None',
        'PotentialEvapotranspiraton' : 'mm',
        'WaterStress' : 'Pa',
        'SaturationFraction' : 'None',
        'Drainage' : 'mm',
        'Runoff' : 'mm',
        'ActualEvapotranspiration' : 'mm',
    }

    def __init__( self, grid, **kwds ):
        self._method = kwds.pop('method', 'Grid')
        self._interception_cap = kwds.pop('INTERCEPT_CAP', 1.)
        self._zr = kwds.pop('ZR', 34.0)
        self._runon = kwds.pop('RUNON', 0.)
        self._fbare = kwds.pop('F_BARE', 0.7)
        self._soil_Ib = kwds.pop('I_B', 625)
        self._soil_Iv = kwds.pop('I_V', 625)
        self._soil_Ew = kwds.pop('EW', 0.1)
        self._soil_pc = kwds.pop('PC', 0.35)
        self._soil_fc = kwds.pop('FC', 0.35)
        self._soil_sc = kwds.pop('SC', 0.33)
        self._soil_wp = kwds.pop('WP', 0.09)
        self._soil_hgw = kwds.pop('HGW', 0.08)
        self._soil_beta = kwds.pop('BETA', 12.1)

        assert_method_is_valid(self._method)

        super(SoilMoisture, self).__init__(grid, **kwds)

        for name in self._input_var_names:
            if not name in self.grid.at_cell:
                self.grid.add_zeros('cell', name, units=self._var_units[name])

        for name in self._output_var_names:
            if not name in self.grid.at_cell:
                self.grid.add_zeros('cell', name, units=self._var_units[name])

        self._nodal_values = self.grid['node']

        if not 'InitialSaturationFraction' in self.grid.at_cell:
            self.grid.add_zeros('cell', 'InitialSaturationFraction', units='None' )

        self._cell_values = self.grid['cell']

    def update( self, current_time, **kwds ):
        DEBUGG = 0

        P = kwds.pop('P', 5.)
        Tb = kwds.pop('Tb', 24.)
        Tr = kwds.pop('Tr', 0.01)
        self._PET = self._cell_values['PotentialEvapotranspiration']
        self._SO = self._cell_values['InitialSaturationFraction']
        self._vegcover = self._cell_values['VegetationCover']
        self._water_stress = self._cell_values['WaterStress']
        self._S = self._cell_values['SaturationFraction']
        self._D = self._cell_values['Drainage']
        self._ETA = self._cell_values['ActualEvapotranspiration']
        self._fr = self._cell_values['LiveLeafAreaIndex']/1.44

        fbare = self._fbare
        ZR = self._zr
        pc = self._soil_pc
        fc = self._soil_fc
        sc = self._soil_sc
        wp = self._soil_wp
        hgw = self._soil_hgw
        beta = self._soil_beta

        for cell in range(0,self.grid.number_of_cells):

            s = self._S[cell]

            Inf_cap = self._soil_Ib*(1-self._vegcover[cell]) + self._soil_Iv*self._vegcover[cell] # Infiltration capacity
            Int_cap = min(self._vegcover[cell]*self._interception_cap, P)  # Interception capacity
            Peff = max(P-Int_cap, 0.0)             # Effective precipitation depth
            mu = (Inf_cap/1000.0)/(pc*ZR*(np.exp(beta*(1-fc))-1))
            Ep = max((self._PET[cell]*self._fr[cell]+fbare*self._PET[cell]*(1-self._fr[cell])) - Int_cap, 0.001)  #
            nu = ((Ep/24.0)/1000.0)/(pc*ZR) # Loss function parameter
            nuw = ((Ep*0.1/24)/1000.0)/(pc*ZR) # Loss function parameter
            sini = self._SO[cell] + (Peff/(pc*ZR*1000.0))+self._runon

            if sini>1:
                runoff = (sini-1)*pc*ZR*1000
                print 'Runoff =', runoff
                sini = 1
            else:
                runoff = 0

            if sini>=fc:
                tfc = (1.0/(beta*(mu-nu)))*(beta*(fc-sini)+                \
                        np.log((nu-mu+mu*np.exp(beta*(sini-fc)))/nu))
                if DEBUGG == 1:
                    if nu == 0:
                        print 'nu is zero', nu
                        print 'Veg Cover', self._vegcover[cell]
                        print 'Ep = ', Ep
                        print 'pet = ', self._PET[cell]
                tsc = ((fc-sc)/nu)+tfc
                twp = ((sc-wp)/(nu-nuw))*np.log(nu/nuw)+tsc

                if Tb<tfc:
                    s = abs(sini-(1/beta)*np.log(((nu-mu+mu*                  \
                            np.exp(beta*(sini-fc)))*np.exp(beta*(nu-mu)*Tb)         \
                            -mu*np.exp(beta*(sini-fc)))/(nu-mu)))

                    self._D[cell] = ((pc*ZR*1000)*(sini-s))-(Tb*(Ep/24))
                    self._ETA[cell] = (Tb*(Ep/24))

                elif Tb>=tfc and Tb<tsc:
                    s = fc-(nu*(Tb-tfc))
                    self._D[cell] = ((pc*ZR*1000)*(sini-fc))-((tfc)*(Ep/24))
                    self._ETA[cell] = (Tb*(Ep/24))

                elif Tb>=tsc and Tb<twp:
                    s = wp+(sc-wp)*((nu/(nu-nuw))*np.exp((-1)*((nu-nuw)/(sc-wp))*(Tb-tsc))-(nuw/(nu-nuw)))
                    self._D[cell] = ((pc*ZR*1000)*(sini-fc))-(tfc*Ep/24)
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))-self._D[cell]

                else:
                    s = hgw+(wp-hgw)*np.exp((-1)*(nuw/(wp-hgw))*max(Tb-twp,0))
                    self._D[cell] = ((pc*ZR*1000)*(sini-fc))-(tfc*Ep/24)
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))-self._D[cell]

            elif sini<fc and sini>=sc:
                tfc = 0
                tsc = (sini-sc)/nu
                twp = ((sc-wp)/(nu-nuw))*np.log(nu/nuw)+tsc

                if Tb<tsc:
                    s = sini - nu*Tb
                    self._D[cell] = 0
                    self._ETA[cell] = 1000*ZR*pc*(sini-s)

                elif Tb>=tsc and Tb<twp:
                    s = wp+(sc-wp)*((nu/(nu-nuw))*np.exp((-1)*((nu-nuw)/(sc-wp))*(Tb-tsc))-(nuw/(nu-nuw)))
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

                else:
                    s = hgw+(wp-hgw)*np.exp((-1)*(nuw/(wp-hgw))*(Tb-twp))
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

            elif sini<sc and sini>=wp:
                tfc = 0
                tsc = 0
                twp = ((sc-wp)/(nu-nuw))*np.log(1+(nu-nuw)*(sini-wp)/(nuw*(sc-wp)))

                if Tb<twp:
                    s = wp+((sc-wp)/(nu-nuw))*((np.exp((-1)*((nu-nuw)/(sc-wp))*Tb))*(nuw+((nu-nuw)/(sc-wp))*(sini-wp))-nuw)
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

                else:
                    s = hgw+(wp-hgw)*np.exp((-1)*(nuw/(wp-hgw))*(Tb-twp))
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

            else:
                tfc = 0
                tsc = 0
                twp = 0

                s = hgw+(sini-hgw)*np.exp((-1)*(nuw/(wp-hgw))*Tb)
                self._D[cell] = 0
                self._ETA[cell] = (1000*ZR*pc*(sini-s))

            self._water_stress[cell] = min(max(pow(((sc - ((s+sini)/2)) / (sc - wp)),4),0.0),1.0)
            self._S[cell] = s
            self._SO[cell] = s

        current_time += (Tb+Tr)/(24.*365.25)
        return( current_time )<commit_msg>Fixed doctests.<commit_after>#################################################################
##
##  'Field' concept is implemented for Soil Moisture component.
##
##  Sai Nudurupati and Erkan Istanbulluoglu - 15May2014
#################################################################

from landlab import Component

import numpy as np

_VALID_METHODS = set(['Grid'])

def assert_method_is_valid(method):
    if method not in _VALID_METHODS:
        raise ValueError('%s: Invalid method name' % method)

class SoilMoisture( Component ):
    """
    This component calculates and updates soil moisture after each storm. Soil
    moisture is represented as a single bucket. Rainfall depth fills the bucket
    and the soil moisture decays as a result of leakage and ET following the
    analytical solution of Laio et al., (2001). This component can operate on
    any raster grid. Input file is named soilmoisture_input.txt and is
    temporarily placed under landlab.components.

    Storms are considered to be instantaneous events.
    Storm duration, depth and interstorm duration are obtained as input
    Storm depth is obtained in mm and storm duration and interstorm duration
    are obtained in hours

    >>> from landlab import RasterModelGrid
    >>> from landlab.components.radiation.radiation_field import Radiation
    >>> from landlab.components.soil_moisture.soil_moisture_field import SoilMoisture
    >>> import numpy as np
    >>> grid = RasterModelGrid( 5, 4, 0.2 )
    >>> grid['node']['Elevation'] = np.random.rand( grid.number_of_nodes ) * 1000
    >>> rad = Radiation( grid )
    >>> rad.name
    'Radiation'
    >>> current_time = 0.5
    >>> rad.update( current_time )
    >>>
    """

    _name = 'Soil Moisture'

    _input_var_names = set([
        'VegetationCover',
        'LiveLeafAreaIndex',
        'PotentialEvapotranspiraton',
    ])

    _output_var_names = set([
        'WaterStress',
        'SaturationFraction',
        'Drainage',
        'Runoff',
        'ActualEvapotranspiration',
    ])

    _var_units = {
        'VegetationCover' : 'None',
        'PotentialEvapotranspiraton' : 'mm',
        'WaterStress' : 'Pa',
        'SaturationFraction' : 'None',
        'Drainage' : 'mm',
        'Runoff' : 'mm',
        'ActualEvapotranspiration' : 'mm',
    }

    def __init__( self, grid, **kwds ):
        self._method = kwds.pop('method', 'Grid')
        self._interception_cap = kwds.pop('INTERCEPT_CAP', 1.)
        self._zr = kwds.pop('ZR', 34.0)
        self._runon = kwds.pop('RUNON', 0.)
        self._fbare = kwds.pop('F_BARE', 0.7)
        self._soil_Ib = kwds.pop('I_B', 625)
        self._soil_Iv = kwds.pop('I_V', 625)
        self._soil_Ew = kwds.pop('EW', 0.1)
        self._soil_pc = kwds.pop('PC', 0.35)
        self._soil_fc = kwds.pop('FC', 0.35)
        self._soil_sc = kwds.pop('SC', 0.33)
        self._soil_wp = kwds.pop('WP', 0.09)
        self._soil_hgw = kwds.pop('HGW', 0.08)
        self._soil_beta = kwds.pop('BETA', 12.1)

        assert_method_is_valid(self._method)

        super(SoilMoisture, self).__init__(grid, **kwds)

        for name in self._input_var_names:
            if not name in self.grid.at_cell:
                self.grid.add_zeros('cell', name, units=self._var_units[name])

        for name in self._output_var_names:
            if not name in self.grid.at_cell:
                self.grid.add_zeros('cell', name, units=self._var_units[name])

        self._nodal_values = self.grid['node']

        if not 'InitialSaturationFraction' in self.grid.at_cell:
            self.grid.add_zeros('cell', 'InitialSaturationFraction', units='None' )

        self._cell_values = self.grid['cell']

    def update( self, current_time, **kwds ):
        DEBUGG = 0

        P = kwds.pop('P', 5.)
        Tb = kwds.pop('Tb', 24.)
        Tr = kwds.pop('Tr', 0.01)
        self._PET = self._cell_values['PotentialEvapotranspiration']
        self._SO = self._cell_values['InitialSaturationFraction']
        self._vegcover = self._cell_values['VegetationCover']
        self._water_stress = self._cell_values['WaterStress']
        self._S = self._cell_values['SaturationFraction']
        self._D = self._cell_values['Drainage']
        self._ETA = self._cell_values['ActualEvapotranspiration']
        self._fr = self._cell_values['LiveLeafAreaIndex']/1.44

        fbare = self._fbare
        ZR = self._zr
        pc = self._soil_pc
        fc = self._soil_fc
        sc = self._soil_sc
        wp = self._soil_wp
        hgw = self._soil_hgw
        beta = self._soil_beta

        for cell in range(0,self.grid.number_of_cells):

            s = self._S[cell]

            Inf_cap = self._soil_Ib*(1-self._vegcover[cell]) + self._soil_Iv*self._vegcover[cell] # Infiltration capacity
            Int_cap = min(self._vegcover[cell]*self._interception_cap, P)  # Interception capacity
            Peff = max(P-Int_cap, 0.0)             # Effective precipitation depth
            mu = (Inf_cap/1000.0)/(pc*ZR*(np.exp(beta*(1-fc))-1))
            Ep = max((self._PET[cell]*self._fr[cell]+fbare*self._PET[cell]*(1-self._fr[cell])) - Int_cap, 0.001)  #
            nu = ((Ep/24.0)/1000.0)/(pc*ZR) # Loss function parameter
            nuw = ((Ep*0.1/24)/1000.0)/(pc*ZR) # Loss function parameter
            sini = self._SO[cell] + (Peff/(pc*ZR*1000.0))+self._runon

            if sini>1:
                runoff = (sini-1)*pc*ZR*1000
                print 'Runoff =', runoff
                sini = 1
            else:
                runoff = 0

            if sini>=fc:
                tfc = (1.0/(beta*(mu-nu)))*(beta*(fc-sini)+                \
                        np.log((nu-mu+mu*np.exp(beta*(sini-fc)))/nu))
                if DEBUGG == 1:
                    if nu == 0:
                        print 'nu is zero', nu
                        print 'Veg Cover', self._vegcover[cell]
                        print 'Ep = ', Ep
                        print 'pet = ', self._PET[cell]
                tsc = ((fc-sc)/nu)+tfc
                twp = ((sc-wp)/(nu-nuw))*np.log(nu/nuw)+tsc

                if Tb<tfc:
                    s = abs(sini-(1/beta)*np.log(((nu-mu+mu*                  \
                            np.exp(beta*(sini-fc)))*np.exp(beta*(nu-mu)*Tb)         \
                            -mu*np.exp(beta*(sini-fc)))/(nu-mu)))

                    self._D[cell] = ((pc*ZR*1000)*(sini-s))-(Tb*(Ep/24))
                    self._ETA[cell] = (Tb*(Ep/24))

                elif Tb>=tfc and Tb<tsc:
                    s = fc-(nu*(Tb-tfc))
                    self._D[cell] = ((pc*ZR*1000)*(sini-fc))-((tfc)*(Ep/24))
                    self._ETA[cell] = (Tb*(Ep/24))

                elif Tb>=tsc and Tb<twp:
                    s = wp+(sc-wp)*((nu/(nu-nuw))*np.exp((-1)*((nu-nuw)/(sc-wp))*(Tb-tsc))-(nuw/(nu-nuw)))
                    self._D[cell] = ((pc*ZR*1000)*(sini-fc))-(tfc*Ep/24)
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))-self._D[cell]

                else:
                    s = hgw+(wp-hgw)*np.exp((-1)*(nuw/(wp-hgw))*max(Tb-twp,0))
                    self._D[cell] = ((pc*ZR*1000)*(sini-fc))-(tfc*Ep/24)
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))-self._D[cell]

            elif sini<fc and sini>=sc:
                tfc = 0
                tsc = (sini-sc)/nu
                twp = ((sc-wp)/(nu-nuw))*np.log(nu/nuw)+tsc

                if Tb<tsc:
                    s = sini - nu*Tb
                    self._D[cell] = 0
                    self._ETA[cell] = 1000*ZR*pc*(sini-s)

                elif Tb>=tsc and Tb<twp:
                    s = wp+(sc-wp)*((nu/(nu-nuw))*np.exp((-1)*((nu-nuw)/(sc-wp))*(Tb-tsc))-(nuw/(nu-nuw)))
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

                else:
                    s = hgw+(wp-hgw)*np.exp((-1)*(nuw/(wp-hgw))*(Tb-twp))
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

            elif sini<sc and sini>=wp:
                tfc = 0
                tsc = 0
                twp = ((sc-wp)/(nu-nuw))*np.log(1+(nu-nuw)*(sini-wp)/(nuw*(sc-wp)))

                if Tb<twp:
                    s = wp+((sc-wp)/(nu-nuw))*((np.exp((-1)*((nu-nuw)/(sc-wp))*Tb))*(nuw+((nu-nuw)/(sc-wp))*(sini-wp))-nuw)
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

                else:
                    s = hgw+(wp-hgw)*np.exp((-1)*(nuw/(wp-hgw))*(Tb-twp))
                    self._D[cell] = 0
                    self._ETA[cell] = (1000*ZR*pc*(sini-s))

            else:
                tfc = 0
                tsc = 0
                twp = 0

                s = hgw+(sini-hgw)*np.exp((-1)*(nuw/(wp-hgw))*Tb)
                self._D[cell] = 0
                self._ETA[cell] = (1000*ZR*pc*(sini-s))

            self._water_stress[cell] = min(max(pow(((sc - ((s+sini)/2)) / (sc - wp)),4),0.0),1.0)
            self._S[cell] = s
            self._SO[cell] = s

        current_time += (Tb+Tr)/(24.*365.25)
        return( current_time )
<|endoftext|>


<commit_before>#!/usr/bin/env python3

import sys
import io
import os

import time
from time import sleep

import numpy
import matplotlib
from PIL import Image
from PyQt5.QtCore import *
import PyQt5.QtCore
from PyQt5.QtGui import *

from PyQt5.QtWidgets import *

from pyqt import FreakingQtImageViewer

from skimage import color
#from skimage import data
from skimage.feature import canny
from skimage.transform import hough_ellipse
from skimage.draw import ellipse_perimeter

from skimage.transform import hough_circle
from skimage.feature import peak_local_max
from skimage.draw import circle_perimeter
from skimage.util import img_as_ubyte

import scipy.misc

import picamera
import picamera.array

from cv import circle_detection

class DraughtsGameWindow(QWidget):
	def __init__(self):
		super().__init__()
		self.corners = []
		self.CELLS_PER_ROW = 8
		self.BLACK_COLOR = QColor(0,0,0,255)
		self.WHITE_COLOR = QColor(255,255,255,255)
		self.BLUE_COLOR = QColor(0,0,255,255)
		self.RED_COLOR = QColor(255,0,0,255)
		self.GREEN_COLOR = QColor(0,255,0,255)
		self.DEFAULT_PEN = QPen(self.BLUE_COLOR)
		self.DEFAULT_PEN.setWidth(20)
		self.PIX_PADDING = 75
		self.BORDER_RADIUS = 10

		self.initUI()

	def keyPressEvent(self, event):
		if event.key() == Qt.Key_Escape:
			self.close()
		elif event.key() == Qt.Key_C:
			self.capture()

	def initUI(self):

		self.WIDTH, self.HEIGHT, self.CELL_SIZE = screen_rect.width(), screen_rect.height(), screen_rect.width() / self.CELLS_PER_ROW
		self.pixmap = QPixmap(self.WIDTH-20,self.HEIGHT-20)

		self.upper_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, self.PIX_PADDING)
		self.upper_right = (- self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, self.PIX_PADDING + 0)
		self.lower_right = (-self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())
		self.lower_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())

		self.DRAW_SIZE = self.upper_right[0] - self.upper_left[0]
		print(self.DRAW_SIZE)

		self.painter = QPainter(self.pixmap)
		self.painter.setPen(self.DEFAULT_PEN)

		self.painter.fillRect(0, 0,self.WIDTH, self.HEIGHT, self.WHITE_COLOR)
		self.painter.drawEllipse(self.upper_left[0]-10,self.upper_left[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.BLACK_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.upper_right[0]-10,self.upper_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.RED_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_right[0]-10,self.lower_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.GREEN_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_left[0]-10,self.lower_left[1]-10,20,20)

		self.vbox = QVBoxLayout(self)

		self.lbl = QLabel(self)
		self.lbl.setPixmap(self.pixmap)
		self.vbox.addWidget(self.lbl)

		self.setLayout(self.vbox)
		self.vbox.setAlignment(Qt.AlignHCenter)
		self.showFullScreen()
		self.setWindowTitle('DraughtsCV')
		self.show()
		#capture()

	def checkCoords(self, img, coords):
		self.redC = []
		self.greenC = []
		self.blueC = []
		self.blackC = []
		empty = [False*4]
		for index, coord in enumerate(coords):
			#print(img[coord[0], coord[1]]," - ", )
			pixel = img[coord[0], coord[1]]
			sum = 0
			sum += pixel[0]
			sum += pixel[1]
			sum += pixel[2]
			if sum > 300:
				print("delete ", coord, ' with ', pixel)
			else:
				if pixel[0] < 50 and pixel[1] < 50 and pixel[2] < 50:
					print(coord, ' could be black with', pixel)
					self.blackC.append(coord)
					empty[0] = True
				elif pixel[0] > 110:
					print(coord, ' could be red with', pixel)
					self.redC.append(coord)
					empty[1] = True
				elif pixel[1] > 110:
					print(coord, ' could be green with', pixel)
					self.greenC.append(coord)
					empty[2] = True
				elif pixel[2] > 110:
					print(coord, ' could be blue with', pixel)
					self.blueC.append(coord)
					empty[3] = True

		#final_red = (int(numpy.sum(self.redC[0])/len(self.redC)),int(numpy.sum(self.redC[1])/len(self.redC[])))	#(sum(self.redC[0]), sum(self.redC[1]))
		final_black = numpy.average(self.blackC, axis=0)
		final_red = numpy.average(self.redC, axis=0)
		final_green = numpy.average(self.greenC, axis=0)
		final_blue = numpy.average(self.blueC, axis=0)
		print('avg black ',final_black)
		print('avg red ',final_red)
		print('avg green',final_green)
		print('avg blue ',final_blue)

		self.lbl.setPixmap(self.pixmap)
		finalCoords = []
		finalCoords.append(final_blue)
		finalCoords.append(final_black)
		finalCoords.append(final_red)
		finalCoords.append(final_green)
		if not empty[0] or not empty[1] or not empty[2] or not empty[3] :
			return -1
		return finalCoords

	def capture(self):
		camera = picamera.PiCamera()
		with picamera.array.PiRGBArray(camera) as stream:
			res = -1
			while res == -1:
				camera.capture(stream, format='rgb')
				img = stream.array
				img, coords = circle_detection(img, 20, 25)
				im = Image.fromarray(img) #.convert('LA')
				im.save('./tmp.png')
				camera.close()
				res =  self.checkCoords(img, coords)
			self.corners = res
			print(self.corners)

if __name__ == '__main__':

	app = QApplication(sys.argv)

	screen_rect = app.desktop().screenGeometry()
	viewer = DraughtsGameWindow() # init ui
	# capture
	sys.exit(app.exec_())<commit_msg>plswork<commit_after>#!/usr/bin/env python3

import sys
import io
import os

import time
from time import sleep

import numpy
import matplotlib
from PIL import Image
from PyQt5.QtCore import *
import PyQt5.QtCore
from PyQt5.QtGui import *

from PyQt5.QtWidgets import *

from pyqt import FreakingQtImageViewer

from skimage import color
#from skimage import data
from skimage.feature import canny
from skimage.transform import hough_ellipse
from skimage.draw import ellipse_perimeter

from skimage.transform import hough_circle
from skimage.feature import peak_local_max
from skimage.draw import circle_perimeter
from skimage.util import img_as_ubyte

import scipy.misc

import picamera
import picamera.array

from cv import circle_detection

class DraughtsGameWindow(QWidget):
	def __init__(self):
		super().__init__()
		self.corners = []
		self.CELLS_PER_ROW = 8
		self.BLACK_COLOR = QColor(0,0,0,255)
		self.WHITE_COLOR = QColor(255,255,255,255)
		self.BLUE_COLOR = QColor(0,0,255,255)
		self.RED_COLOR = QColor(255,0,0,255)
		self.GREEN_COLOR = QColor(0,255,0,255)
		self.DEFAULT_PEN = QPen(self.BLUE_COLOR)
		self.DEFAULT_PEN.setWidth(20)
		self.PIX_PADDING = 75
		self.BORDER_RADIUS = 10

		self.initUI()

	def keyPressEvent(self, event):
		if event.key() == Qt.Key_Escape:
			self.close()
		elif event.key() == Qt.Key_C:
			self.capture()

	def initUI(self):

		self.WIDTH, self.HEIGHT, self.CELL_SIZE = screen_rect.width(), screen_rect.height(), screen_rect.width() / self.CELLS_PER_ROW
		self.pixmap = QPixmap(self.WIDTH-20,self.HEIGHT-20)

		self.upper_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, self.PIX_PADDING)
		self.upper_right = (- self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, self.PIX_PADDING + 0)
		self.lower_right = (-self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())
		self.lower_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())

		self.DRAW_SIZE = self.upper_right[0] - self.upper_left[0]
		print(self.DRAW_SIZE)

		self.painter = QPainter(self.pixmap)
		self.painter.setPen(self.DEFAULT_PEN)

		self.painter.fillRect(0, 0,self.WIDTH, self.HEIGHT, self.WHITE_COLOR)
		self.painter.drawEllipse(self.upper_left[0]-10,self.upper_left[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.BLACK_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.upper_right[0]-10,self.upper_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.RED_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_right[0]-10,self.lower_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.GREEN_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_left[0]-10,self.lower_left[1]-10,20,20)

		self.vbox = QVBoxLayout(self)

		self.lbl = QLabel(self)
		self.lbl.setPixmap(self.pixmap)
		self.vbox.addWidget(self.lbl)

		self.setLayout(self.vbox)
		self.vbox.setAlignment(Qt.AlignHCenter)
		self.showFullScreen()
		self.setWindowTitle('DraughtsCV')
		self.show()
		#capture()

	def checkCoords(self, img, coords):
		self.redC = []
		self.greenC = []
		self.blueC = []
		self.blackC = []
		empty = [False*4]
		for index, coord in enumerate(coords):
			#print(img[coord[0], coord[1]]," - ", )
			pixel = img[coord[0], coord[1]]
			sum = 0
			sum += pixel[0]
			sum += pixel[1]
			sum += pixel[2]
			if sum > 300:
				print("delete ", coord, ' with ', pixel)
			else:
				if pixel[0] < 50 and pixel[1] < 50 and pixel[2] < 50:
					print(coord, ' could be black with', pixel)
					self.blackC.append(coord)
					empty.append(True)
				elif pixel[0] > 110:
					print(coord, ' could be red with', pixel)
					self.redC.append(coord)
					empty.append(True)
				elif pixel[1] > 110:
					print(coord, ' could be green with', pixel)
					self.greenC.append(coord)
					empty.append(True)
				elif pixel[2] > 110:
					print(coord, ' could be blue with', pixel)
					self.blueC.append(coord)
					empty.append(True)

		#final_red = (int(numpy.sum(self.redC[0])/len(self.redC)),int(numpy.sum(self.redC[1])/len(self.redC[])))	#(sum(self.redC[0]), sum(self.redC[1]))
		final_black = numpy.average(self.blackC, axis=0)
		final_red = numpy.average(self.redC, axis=0)
		final_green = numpy.average(self.greenC, axis=0)
		final_blue = numpy.average(self.blueC, axis=0)
		print('avg black ',final_black)
		print('avg red ',final_red)
		print('avg green',final_green)
		print('avg blue ',final_blue)

		self.lbl.setPixmap(self.pixmap)
		finalCoords = []
		finalCoords.append(final_blue)
		finalCoords.append(final_black)
		finalCoords.append(final_red)
		finalCoords.append(final_green)
		if not empty[0] or not empty[1] or not empty[2] or not empty[3] :
			return -1
		return finalCoords

	def capture(self):
		camera = picamera.PiCamera()
		with picamera.array.PiRGBArray(camera) as stream:
			res = -1
			while res == -1:
				camera.capture(stream, format='rgb')
				img = stream.array
				img, coords = circle_detection(img, 20, 25)
				im = Image.fromarray(img) #.convert('LA')
				im.save('./tmp.png')
				camera.close()
				res =  self.checkCoords(img, coords)
			self.corners = res
			print(self.corners)

if __name__ == '__main__':

	app = QApplication(sys.argv)

	screen_rect = app.desktop().screenGeometry()
	viewer = DraughtsGameWindow() # init ui
	# capture
	sys.exit(app.exec_())<|endoftext|>


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

import sys
import smtplib
import os.path
import md5

import neo_cgi
import neo_cs
import neo_util

from __init__ import __version__
from util import *
import Environment
import core
import Ticket

def wrap(t, cols=75, initial_indent='', subsequent_indent=''):
    try:
        import textwrap
        t = t.replace('\r\n', '\n').replace('\r', '\n')
        wrapper = textwrap.TextWrapper(cols, replace_whitespace=0,
                                       break_long_words=0,
                                       initial_indent=initial_indent,
                                       subsequent_indent=subsequent_indent)
        wrappedLines = []
        for line in t.split('\n'):
            wrappedLines += wrapper.wrap(line.rstrip())

        return os.linesep.join(wrappedLines)

    except ImportError:
        return t

class Notify:
    """Generic notification class for Trac. Subclass this to implement
    different methods."""

    db = None
    hdf = None
    cs = None

    def __init__(self, env, msg_template):
        self.env = env
        self.db = env.get_db_cnx()
        self.hdf = neo_util.HDF()
        core.populate_hdf(self.hdf, env, self.db, None)
        tmpl = os.path.join(env.get_config('general','templates_dir'), msg_template)
        self.cs = neo_cs.CS(self.hdf)
        self.cs.parseFile(tmpl)

    def notify(self,resid):
        if sys.version_info[0] == 2 and sys.version_info[1] < 2:
            raise TracError, "Email notifications require Python >= 2.2"
        rcpts = self.get_recipients(resid)
        self.begin_send()
        for to in rcpts:
            self.send(to)
        self.finish_send()

    def get_recipients(self, resid):
        """Return a list of subscribers to the resource 'resid'."""
        raise Exception, "Notify::get_recipients not implemented"

    def begin_send(self):
        """Prepare to send messages. Called before sending begins."""
        pass

    def send(self, rcpt):
        """Send message to a recipient 'rcpt'. Called once for each recipient."""
        raise Exception, "Notify::send not implemented"

    def finish_send(self):
        """Clean up after sending all messages. Called after sending all messages."""
        pass

class NotifyEmail(Notify):
    """Baseclass for notification by email."""

    smtp_server = 'localhost'
    from_email = 'trac+tickets@localhost'
    subject = ''
    server = None

    def notify(self, resid, subject):
        self.subject = subject

        enabled = self.env.get_config('notification', 'smtp_enabled', '0')
        if not enabled.lower() in TRUE:
            return
        self.smtp_server = self.env.get_config('notification',
                                               'smtp_server',
                                               self.smtp_server)
        self.from_email = self.env.get_config('notification',
                                              'smtp_from', '')
        self.replyto_email = self.env.get_config('notification',
                                                 'smtp_replyto',
                                                 self.from_email)
        self.from_email = self.from_email or self.replyto_email
        if not self.from_email and not self.replyto_email:
            raise TracError('Unable to send email due to identity crisis.
'
                            'Both notification.from and'
                            ' notification.reply_to are unspecified'
                            ' in configuration.',
                            'SMTP Notification Error')
        Notify.notify(self, resid)

    def get_email_addresses(self, txt):
        import email.Utils
        emails = [x[1] for x in  email.Utils.getaddresses([str(txt)])]
        return filter(lambda x: x.find('@') > -1, emails)

    def begin_send(self):
        self.server = smtplib.SMTP(self.smtp_server)

    def send(self, rcpt, mime_headers={}):
        from email.MIMEText import MIMEText
        from email.Header import Header
        body = self.cs.render()
        msg = MIMEText(body, 'plain', 'utf-8')
        msg['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__
        msg['X-Trac-Version'] =  __version__
        projname = self.env.get_config('project','name')
        msg['X-Trac-Project'] =  projname
        msg['X-URL'] =  self.env.get_config('project','url')
        msg['Subject'] = Header(self.subject, 'utf-8')
        msg['From'] = '%s <%s>' % (projname, self.from_email)
        msg['Sender'] = self.from_email
        msg['Reply-To'] = self.replyto_email
        msg['To'] = rcpt
        for hdr in mime_headers.keys():
            msg[hdr] = mime_headers[hdr]
        self.server.sendmail(self.from_email, rcpt, msg.as_string())

    def finish_send(self):
        self.server.quit()

class TicketNotifyEmail(NotifyEmail):
    """Notification of ticket changes."""

    template_name = "ticket_notify_email.cs"
    ticket = None
    newticket = None
    modtime = 0
    from_email = 'trac+ticket@localhost'
    COLS = 75

    def __init__(self, env):
        NotifyEmail.__init__(self, env, self.template_name)

    def notify(self, tktid, newticket=1, modtime=0):
        self.ticket = Ticket.get_ticket(self.db, tktid)
        self.modtime = modtime
        self.newticket = newticket
        self.ticket['description'] = wrap(self.ticket['description'],
                                          self.COLS,
                                          initial_indent=' ',
                                          subsequent_indent=' ')
        self.ticket['link'] = self.env.abs_href.ticket(tktid)
        add_dict_to_hdf(self.ticket, self.hdf, 'ticket')
        self.hdf.setValue('email.ticket_props', self.format_props())
        self.hdf.setValue('email.ticket_body_hdr', self.format_hdr())
        self.hdf.setValue('ticket.link', self.ticket['link'])
        self.hdf.setValue('ticket.new', self.newticket and '1' or '0')
        subject = self.format_subj()
        if not self.newticket:
            subject = 'Re: ' + subject
        self.hdf.setValue('email.subject', subject)
        changes=''
        if not self.newticket and modtime:  # Ticketchange
            cursor = self.db.cursor()
            cursor.execute('SELECT author, field, oldvalue, newvalue '
                           ' FROM ticket_change WHERE ticket=%s ANd time=%s' %
                           (tktid, modtime))
            rows=cursor.fetchall()
            for r in rows:
                self.hdf.setValue('ticket.change.author', str(r[0]))
                pfx='ticket.change.%s' % r[1]
                if r[1] == 'comment':
                    newv = wrap(r[3], initial_indent=' ',
                                subsequent_indent=' ')
                else:
                    newv = r[3]
                    l = 7 + len(r[1])
                    chg = wrap('%s => %s' % (r[2], r[3]), self.COLS-l,'', l*' ')
                    changes += '  * %s:  %s\n' % (r[1], chg)
                self.hdf.setValue('%s.author' % pfx, str(r[0]))
                self.hdf.setValue('%s.oldvalue' % pfx, str(r[2]))
                self.hdf.setValue('%s.newvalue' % pfx, newv)
            if changes:
                self.hdf.setValue('email.changes_body', changes)
        NotifyEmail.notify(self, tktid, subject)

    def format_props(self):
        tkt = self.ticket
        tkt['id'] = '%s' % tkt['id']
        t = self.modtime or tkt['time']
        tkt['modified'] = time.strftime('%c', time.localtime(t))
        fields = ['id',        'status',
                  'component', 'modified',
                  'severity',  'milestone',
                  'priority',  'version',
                  'owner',     'reporter']
        i = 1
        width = [0,0,0,0]
        for f in fields:
            idx = 2*(i % 2)
            if len(f) > width[idx]:
                width[idx] = len(f)
            if len(tkt[f]) > width[idx+1]:
                width[idx+1] = len(tkt[f])
            i += 1
        format = (' %%%is:  %%-%is\n' % (width[0], width[1]),
                  '%%%is:  %%-%is  |  ' % (width[2], width[3]))
        i = 1
        l = (width[2] + width[3] + 5)
        sep = l*'-' + '+' + (self.COLS-l)*'-'
        txt = sep + '\n'
        for f in fields:
            txt += format[i%2] % (f.capitalize(), tkt[f])
            i += 1
        txt += sep
        return txt

    def format_hdr(self):
        return '#%s: %s' % (self.ticket['id'],
                               wrap(self.ticket['summary'], self.COLS))

    def format_subj(self):
        projname = self.env.get_config('project', 'name')
        return '[%s] #%s: %s' % (projname, self.ticket['id'],
                                     self.ticket['summary'])

    def get_recipients(self, tktid):
        emails = []
        cursor = self.db.cursor()
        cursor.execute('SELECT reporter,cc FROM ticket WHERE id=%s', tktid)
        row = cursor.fetchone()
        if row:
            emails += row[0] and self.get_email_addresses(row[0]) or []
            emails += row[1] and self.get_email_addresses(row[1]) or []
        cursor.execute('SELECT DISTINCT author,ticket FROM ticket_change '
                       ' WHERE ticket=%s', tktid)
        rows = cursor.fetchall()
        for row in rows:
            emails += row[0] and self.get_email_addresses(row[0]) or []

        # Add smtp_always_cc address
        acc = self.env.get_config('notification', 'smtp_always_cc', '')
        if acc:
            emails += self.get_email_addresses(acc)

        result = []
        for e in emails:        # Remove duplicates
            if e not in result:
                result.append(e)
        return result

    def get_message_id(self, rcpt, modtime=0):
        """Generate a predictable, but sufficiently unique message ID."""
        s = '%s.%08d.%d.%s' % (self.env.get_config('project','url'),
                            int(self.ticket['id']), modtime, rcpt)
        dig = md5.new(s).hexdigest()
        host = self.from_email[self.from_email.find('@')+1:]
        msgid = '<%03d.%s@%s>' % (len(s), dig, host)
        return msgid

    def send(self, rcpt):
        hdrs = {}
        hdrs['Message-ID'] = self.get_message_id(rcpt, self.modtime)
        hdrs['X-Trac-Ticket-ID'] = self.ticket['id']
        hdrs['X-Trac-Ticket-URL'] = self.ticket['link']
        if not self.newticket:
            hdrs['In-Reply-To'] = self.get_message_id(rcpt)
            hdrs['References'] = self.get_message_id(rcpt)
        NotifyEmail.send(self, rcpt, hdrs)

# A simple test
if __name__ == '__main__':
    import db
    env = Environment.Environment('/home/daniel/trac/db/tracenv')
    try:
        tktid = int(sys.argv[1])
    except IndexError:
        tktid = 223
    tn = TicketNotifyEmail(env)
#    tn.notify(tktid, 1)
#    tn.notify(223,0,1081476135)
#    tn.notify(223,0,1081476765)
    tn.notify(224,0,1081528294)
#    tn.display_hdf()
<commit_msg>Use os.linesep '''everywhere''' in notification.<commit_after># -*- coding: iso8859-1 -*-
#
# Copyright (C) 2003, 2004 Edgewall Software
# Copyright (C) 2003, 2004 Daniel Lundin <daniel@edgewall.com>
#
# Trac is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Trac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Daniel Lundin <daniel@edgewall.com>

import sys
import smtplib
import os.path
import md5

import neo_cgi
import neo_cs
import neo_util

from __init__ import __version__
from util import *
import Environment
import core
import Ticket

def wrap(t, cols=75, initial_indent='', subsequent_indent=''):
    try:
        import textwrap
        t = t.replace('\r\n', '\n').replace('\r', '\n')
        wrapper = textwrap.TextWrapper(cols, replace_whitespace=0,
                                       break_long_words=0,
                                       initial_indent=initial_indent,
                                       subsequent_indent=subsequent_indent)
        wrappedLines = []
        for line in t.split('\n'):
            wrappedLines += wrapper.wrap(line.rstrip())

        return os.linesep.join(wrappedLines)

    except ImportError:
        return t

class Notify:
    """Generic notification class for Trac. Subclass this to implement
    different methods."""

    db = None
    hdf = None
    cs = None

    def __init__(self, env, msg_template):
        self.env = env
        self.db = env.get_db_cnx()
        self.hdf = neo_util.HDF()
        core.populate_hdf(self.hdf, env, self.db, None)
        tmpl = os.path.join(env.get_config('general','templates_dir'), msg_template)
        self.cs = neo_cs.CS(self.hdf)
        self.cs.parseFile(tmpl)

    def notify(self,resid):
        if sys.version_info[0] == 2 and sys.version_info[1] < 2:
            raise TracError, "Email notifications require Python >= 2.2"
        rcpts = self.get_recipients(resid)
        self.begin_send()
        for to in rcpts:
            self.send(to)
        self.finish_send()

    def get_recipients(self, resid):
        """Return a list of subscribers to the resource 'resid'."""
        raise Exception, "Notify::get_recipients not implemented"

    def begin_send(self):
        """Prepare to send messages. Called before sending begins."""
        pass

    def send(self, rcpt):
        """Send message to a recipient 'rcpt'. Called once for each recipient."""
        raise Exception, "Notify::send not implemented"

    def finish_send(self):
        """Clean up after sending all messages. Called after sending all messages."""
        pass

class NotifyEmail(Notify):
    """Baseclass for notification by email."""

    smtp_server = 'localhost'
    from_email = 'trac+tickets@localhost'
    subject = ''
    server = None

    def notify(self, resid, subject):
        self.subject = subject

        enabled = self.env.get_config('notification', 'smtp_enabled', '0')
        if not enabled.lower() in TRUE:
            return
        self.smtp_server = self.env.get_config('notification',
                                               'smtp_server',
                                               self.smtp_server)
        self.from_email = self.env.get_config('notification',
                                              'smtp_from', '')
        self.replyto_email = self.env.get_config('notification',
                                                 'smtp_replyto',
                                                 self.from_email)
        self.from_email = self.from_email or self.replyto_email
        if not self.from_email and not self.replyto_email:
            raise TracError('Unable to send email due to identity crisis.
'
                            'Both notification.from and'
                            ' notification.reply_to are unspecified'
                            ' in configuration.',
                            'SMTP Notification Error')
        Notify.notify(self, resid)

    def get_email_addresses(self, txt):
        import email.Utils
        emails = [x[1] for x in  email.Utils.getaddresses([str(txt)])]
        return filter(lambda x: x.find('@') > -1, emails)

    def begin_send(self):
        self.server = smtplib.SMTP(self.smtp_server)

    def send(self, rcpt, mime_headers={}):
        from email.MIMEText import MIMEText
        from email.Header import Header
        body = self.cs.render()
        msg = MIMEText(body, 'plain', 'utf-8')
        msg['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__
        msg['X-Trac-Version'] =  __version__
        projname = self.env.get_config('project','name')
        msg['X-Trac-Project'] =  projname
        msg['X-URL'] =  self.env.get_config('project','url')
        msg['Subject'] = Header(self.subject, 'utf-8')
        msg['From'] = '%s <%s>' % (projname, self.from_email)
        msg['Sender'] = self.from_email
        msg['Reply-To'] = self.replyto_email
        msg['To'] = rcpt
        for hdr in mime_headers.keys():
            msg[hdr] = mime_headers[hdr]
        self.server.sendmail(self.from_email, rcpt, msg.as_string())

    def finish_send(self):
        self.server.quit()

class TicketNotifyEmail(NotifyEmail):
    """Notification of ticket changes."""

    template_name = "ticket_notify_email.cs"
    ticket = None
    newticket = None
    modtime = 0
    from_email = 'trac+ticket@localhost'
    COLS = 75

    def __init__(self, env):
        NotifyEmail.__init__(self, env, self.template_name)

    def notify(self, tktid, newticket=1, modtime=0):
        self.ticket = Ticket.get_ticket(self.db, tktid)
        self.modtime = modtime
        self.newticket = newticket
        self.ticket['description'] = wrap(self.ticket['description'],
                                          self.COLS,
                                          initial_indent=' ',
                                          subsequent_indent=' ')
        self.ticket['link'] = self.env.abs_href.ticket(tktid)
        add_dict_to_hdf(self.ticket, self.hdf, 'ticket')
        self.hdf.setValue('email.ticket_props', self.format_props())
        self.hdf.setValue('email.ticket_body_hdr', self.format_hdr())
        self.hdf.setValue('ticket.link', self.ticket['link'])
        self.hdf.setValue('ticket.new', self.newticket and '1' or '0')
        subject = self.format_subj()
        if not self.newticket:
            subject = 'Re: ' + subject
        self.hdf.setValue('email.subject', subject)
        changes=''
        if not self.newticket and modtime:  # Ticketchange
            cursor = self.db.cursor()
            cursor.execute('SELECT author, field, oldvalue, newvalue '
                           ' FROM ticket_change WHERE ticket=%s ANd time=%s' %
                           (tktid, modtime))
            rows=cursor.fetchall()
            for r in rows:
                self.hdf.setValue('ticket.change.author', str(r[0]))
                pfx='ticket.change.%s' % r[1]
                if r[1] == 'comment':
                    newv = wrap(r[3], initial_indent=' ',
                                subsequent_indent=' ')
                else:
                    newv = r[3]
                    l = 7 + len(r[1])
                    chg = wrap('%s => %s' % (r[2], r[3]), self.COLS-l,'', l*' ')
                    changes += '  * %s:  %s%s' % (r[1], chg, os.linesep)
                self.hdf.setValue('%s.author' % pfx, str(r[0]))
                self.hdf.setValue('%s.oldvalue' % pfx, str(r[2]))
                self.hdf.setValue('%s.newvalue' % pfx, newv)
            if changes:
                self.hdf.setValue('email.changes_body', changes)
        NotifyEmail.notify(self, tktid, subject)

    def format_props(self):
        tkt = self.ticket
        tkt['id'] = '%s' % tkt['id']
        t = self.modtime or tkt['time']
        tkt['modified'] = time.strftime('%c', time.localtime(t))
        fields = ['id',        'status',
                  'component', 'modified',
                  'severity',  'milestone',
                  'priority',  'version',
                  'owner',     'reporter']
        i = 1
        width = [0,0,0,0]
        for f in fields:
            idx = 2*(i % 2)
            if len(f) > width[idx]:
                width[idx] = len(f)
            if len(tkt[f]) > width[idx+1]:
                width[idx+1] = len(tkt[f])
            i += 1
        format = (' %%%is:  %%-%is%s' % (width[0], width[1], os.linesep),
                  '%%%is:  %%-%is  |  ' % (width[2], width[3]))
        i = 1
        l = (width[2] + width[3] + 5)
        sep = l*'-' + '+' + (self.COLS-l)*'-'
        txt = sep + os.linesep
        for f in fields:
            txt += format[i%2] % (f.capitalize(), tkt[f])
            i += 1
        txt += sep
        return txt

    def format_hdr(self):
        return '#%s: %s' % (self.ticket['id'],
                               wrap(self.ticket['summary'], self.COLS))

    def format_subj(self):
        projname = self.env.get_config('project', 'name')
        return '[%s] #%s: %s' % (projname, self.ticket['id'],
                                     self.ticket['summary'])

    def get_recipients(self, tktid):
        emails = []
        cursor = self.db.cursor()
        cursor.execute('SELECT reporter,cc FROM ticket WHERE id=%s', tktid)
        row = cursor.fetchone()
        if row:
            emails += row[0] and self.get_email_addresses(row[0]) or []
            emails += row[1] and self.get_email_addresses(row[1]) or []
        cursor.execute('SELECT DISTINCT author,ticket FROM ticket_change '
                       ' WHERE ticket=%s', tktid)
        rows = cursor.fetchall()
        for row in rows:
            emails += row[0] and self.get_email_addresses(row[0]) or []

        # Add smtp_always_cc address
        acc = self.env.get_config('notification', 'smtp_always_cc', '')
        if acc:
            emails += self.get_email_addresses(acc)

        result = []
        for e in emails:        # Remove duplicates
            if e not in result:
                result.append(e)
        return result

    def get_message_id(self, rcpt, modtime=0):
        """Generate a predictable, but sufficiently unique message ID."""
        s = '%s.%08d.%d.%s' % (self.env.get_config('project','url'),
                            int(self.ticket['id']), modtime, rcpt)
        dig = md5.new(s).hexdigest()
        host = self.from_email[self.from_email.find('@')+1:]
        msgid = '<%03d.%s@%s>' % (len(s), dig, host)
        return msgid

    def send(self, rcpt):
        hdrs = {}
        hdrs['Message-ID'] = self.get_message_id(rcpt, self.modtime)
        hdrs['X-Trac-Ticket-ID'] = self.ticket['id']
        hdrs['X-Trac-Ticket-URL'] = self.ticket['link']
        if not self.newticket:
            hdrs['In-Reply-To'] = self.get_message_id(rcpt)
            hdrs['References'] = self.get_message_id(rcpt)
        NotifyEmail.send(self, rcpt, hdrs)

# A simple test
if __name__ == '__main__':
    import db
    env = Environment.Environment('/home/daniel/trac/db/tracenv')
    try:
        tktid = int(sys.argv[1])
    except IndexError:
        tktid = 223
    tn = TicketNotifyEmail(env)
#    tn.notify(tktid, 1)
#    tn.notify(223,0,1081476135)
#    tn.notify(223,0,1081476765)
    tn.notify(224,0,1081528294)
#    tn.display_hdf()
<|endoftext|>