<commit_before>from . import fast_ffts
import warnings
import numpy as np
from . import scale
from . import zoom

def dftups(inp,nor=None,noc=None,usfac=1,roff=0,coff=0):
    """
    *translated from matlab*
    http://www.mathworks.com/matlabcentral/fileexchange/18401-efficient-subpixel-image-registration-by-cross-correlation/content/html/efficient_subpixel_registration.html

    Upsampled DFT by matrix multiplies, can compute an upsampled DFT in just
    a small region.
    usfac         Upsampling factor (default usfac = 1)
    [nor,noc]     Number of pixels in the output upsampled DFT, in
                  units of upsampled pixels (default = size(in))
    roff, coff    Row and column offsets, allow to shift the output array to
                  a region of interest on the DFT (default = 0)
    Recieves DC in upper left corner, image center must be in (1,1)
    Manuel Guizar - Dec 13, 2007
    Modified from dftus, by J.R. Fienup 7/31/06

    This code is intended to provide the same result as if the following
    operations were performed
      - Embed the array "in" in an array that is usfac times larger in each
        dimension. ifftshift to bring the center of the image to (1,1).
      - Take the FFT of the larger array
      - Extract an [nor, noc] region of the result. Starting with the
        [roff+1 coff+1] element.

    It achieves this result by computing the DFT in the output array without
    the need to zeropad. Much faster and memory efficient than the
    zero-padded FFT approach if [nor noc] are much smaller than [nr*usfac nc*usfac]
    """
    # this function is translated from matlab, so I'm just going to pretend
    # it is matlab/pylab
    from numpy.fft import ifftshift,fftfreq
    from numpy import pi,newaxis,floor

    nr,nc=np.shape(inp);
    # Set defaults
    if noc is None: noc=nc;
    if nor is None: nor=nr;
    # Compute kernels and obtain DFT by matrix products
    term1c = ( ifftshift(np.arange(nc,dtype='float') - floor(nc/2)).T[:,newaxis] )/nc # fftfreq
    term2c = (( np.arange(noc,dtype='float') - coff  )/usfac)[newaxis,:]              # output points
    kernc=np.exp((-1j*2*pi)*term1c*term2c);

    term1r = ( np.arange(nor,dtype='float').T - roff )[:,newaxis]                # output points
    term2r = ( ifftshift(np.arange(nr,dtype='float')) - floor(nr/2) )[newaxis,:] # fftfreq
    kernr=np.exp((-1j*2*pi/(nr*usfac))*term1r*term2r);
    #kernc=exp((-i*2*pi/(nc*usfac))*( ifftshift([0:nc-1]).' - floor(nc/2) )*( [0:noc-1] - coff ));
    #kernr=exp((-i*2*pi/(nr*usfac))*( [0:nor-1].' - roff )*( ifftshift([0:nr-1]) - floor(nr/2)  ));
    out=np.dot(np.dot(kernr,inp),kernc);
    #return np.roll(np.roll(out,-1,axis=0),-1,axis=1)
    return out

def dftups1d(inp,usfac=1,outsize=None,offset=0, return_xouts=False):
    """
    """
    # this function is translated from matlab, so I'm just going to pretend
    # it is matlab/pylab
    from numpy.fft import ifftshift,fftfreq
    from numpy import pi,newaxis,floor

    insize, = inp.shape
    if outsize is None: outsize=insize

    # Compute kernel and obtain DFT by matrix products
    term1 = fftfreq(insize)[:,newaxis]
    term2 = ((np.arange(outsize,dtype='float') + offset)/usfac)[newaxis,:]
    kern=np.exp((-1j*2*pi)*term1*term2);
    # Without the +1 in term 2, the output is always shifted by 1.
    # But, the actual X-axis starts at zero, so I have to subtract 1 below
    # This is weird... it implies that the FT is indexed from 1, which is not
    # a meaningful statement
    # My best guess is that it's a problem with e^0, the mean case, throwing things
    # off, but that's not a useful statement

    out = np.dot(inp,kern)
    if return_xouts:
        return term2.squeeze(),out
    return out

def upsample_image(image, upsample_factor=1, output_size=None, nthreads=1,
        use_numpy_fft=False, xshift=0, yshift=0):
    """
    Use dftups to upsample an image (but takes an image and returns an image with all reals)
    """
    fftn,ifftn = fast_ffts.get_ffts(nthreads=nthreads, use_numpy_fft=use_numpy_fft)

    imfft = ifftn(image)

    if output_size is None:
        s1 = image.shape[0]*upsample_factor
        s2 = image.shape[1]*upsample_factor
    elif hasattr(output_size,'__len__'):
        s1 = output_size[0]
        s2 = output_size[1]
    else:
        s1 = output_size
        s2 = output_size

    ups = dftups(imfft, s1, s2, upsample_factor, roff=yshift, coff=xshift)

    return np.abs(ups)

def odddftups(inp,nor=None,noc=None,usfac=1,roff=0,coff=0):
    from numpy.fft import ifftshift
    from numpy import pi,newaxis,floor

    nr,nc=np.shape(inp);

    # Set defaults
    if noc is None: noc=nc;
    if nor is None: nor=nr;

    if nr % 2 == 1:
        oddr = True
        nrnew = nr+1
    else:
        oddr = False
    if nr % 2 == 1:
        oddr = True
        nrnew = nr+1
    else:
        oddr = False

    # Compute kernels and obtain DFT by matrix products
    term1c = ( ifftshift(np.arange(nc) - floor(nc/2)).T[:,newaxis] )
    term2c = ( np.arange(noc) - coff  )[newaxis,:]
    kernc=np.exp((-1j*2*pi/(nc*usfac))*term1c*term2c);
    term1r = ( np.arange(nor).T - roff )[:,newaxis]
    term2r = ( ifftshift(np.arange(nr)) - floor(nr/2) )[newaxis,:]
    kernr=np.exp((-1j*2*pi/(nr*usfac))*term1r*term2r);
    #kernc=exp((-i*2*pi/(nc*usfac))*( ifftshift([0:nc-1]).' - floor(nc/2) )*( [0:noc-1] - coff ));
    #kernr=exp((-i*2*pi/(nr*usfac))*( [0:nor-1].' - roff )*( ifftshift([0:nr-1]) - floor(nr/2)  ));
    out=np.dot(np.dot(kernr,inp),kernc);
    #return np.roll(np.roll(out,+1,axis=0),+1,axis=1)
    return out

if __name__ == "__main__" and False:

    # breakdown of the dft upsampling method
    from numpy.fft import ifftshift
    from numpy import pi,newaxis,floor

    from pylab import *

    xx,yy = np.meshgrid(np.linspace(-5,5,100),np.linspace(-5,5,100))
    # a Gaussian image
    data = np.exp(-(xx**2+yy**2)/(0.5**2 * 2.))
    fftn,ifftn = fast_ffts.get_ffts(nthreads=4, use_numpy_fft=False)
    print("input max pixel: ",np.unravel_index(data.argmax(),data.shape))
    inp = ifftn(data)

    nr,nc=np.shape(inp);
    noc,nor = nc,nr # these are the output sizes

    # upsample_factor
    usfac = 20.
    for usfac in [1,2,5,10,20,30,40]:
        # the "virtual image" will have size im.shape[0]*usfac,im.shape[1]*usfac
        # To "zoom in" on the center of the image, we need an offset that identifies
        # the lower-left corner of the new image
        vshape = inp.shape[0]*usfac,inp.shape[1]*usfac
        roff = -(vshape[0] - usfac - nor)/2. -1
        coff = -(vshape[1] - usfac - noc)/2. -1

        # shifts decided automatically now
        # roff,coff = 0,0 # -50,-50

        # Compute kernels and obtain DFT by matrix products
        term1c = ( ifftshift(np.arange(nc) - floor(nc/2)).T[:,newaxis] )
        term2c = ( np.arange(noc) - coff  )[newaxis,:]
        kernc=np.exp((-1j*2*pi/(nc*usfac))*term1c*term2c);

        figure(1)
        clf()
        subplot(121)
        imshow(term1c)
        title("term1 (col)")
        colorbar()
        subplot(122)
        imshow(term2c)
        title("term2 (col)")
        colorbar()

        term1r = ( np.arange(nor).T - roff )[:,newaxis]
        term2r = ( ifftshift(np.arange(nr)) - floor(nr/2) )[newaxis,:]
        kernr=np.exp((-1j*2*pi/(nr*usfac))*term1r*term2r);

        figure(2)
        clf()
        subplot(121)
        imshow(term1r)
        title("term1 (row)")
        colorbar()
        subplot(122)
        imshow(term2r)
        title("term2 (row)")
        colorbar()

        figure(3)
        clf()
        subplot(131)
        imshow(np.abs(kernr))
        subplot(132)
        imshow(kernr.real)
        subplot(133)
        imshow(kernr.imag)

        #kernc=exp((-i*2*pi/(nc*usfac))*( ifftshift([0:nc-1]).' - floor(nc/2) )*( [0:noc-1] - coff ));
        #kernr=exp((-i*2*pi/(nr*usfac))*( [0:nor-1].' - roff )*( ifftshift([0:nr-1]) - floor(nr/2)  ));
        dot1 = np.dot(kernr,inp)
        out=np.dot(dot1,kernc);

        # http://stackoverflow.com/a/9479621/814354
        # wrong from scipy.linalg import fblas as FB
        # wrong out2 = FB.dgemm(alpha=1.0, a=dot1, b=kernc)

        figure(10)
        subplot(121)
        imshow(data)
        title("gaussian")
        subplot(122)
        imshow(np.abs(out))
        title('zoomed')

        print("usfac: ",usfac,"max pixel: ",np.unravel_index(np.abs(out).argmax(),out.shape))

        figure(11)
        clf()
        imshow(np.abs(dftups(inp,inp.shape[0]*2,inp.shape[1]*2,usfac=2)))
<commit_msg>this might resolve issue #6 (https://github.com/keflavich/image_registration/issues/6)<commit_after>from . import fast_ffts
import warnings
import numpy as np
from . import scale
from . import zoom

def dftups(inp,nor=None,noc=None,usfac=1,roff=0,coff=0):
    """
    *translated from matlab*
    http://www.mathworks.com/matlabcentral/fileexchange/18401-efficient-subpixel-image-registration-by-cross-correlation/content/html/efficient_subpixel_registration.html

    Upsampled DFT by matrix multiplies, can compute an upsampled DFT in just
    a small region.
    usfac         Upsampling factor (default usfac = 1)
    [nor,noc]     Number of pixels in the output upsampled DFT, in
                  units of upsampled pixels (default = size(in))
    roff, coff    Row and column offsets, allow to shift the output array to
                  a region of interest on the DFT (default = 0)
    Recieves DC in upper left corner, image center must be in (1,1)
    Manuel Guizar - Dec 13, 2007
    Modified from dftus, by J.R. Fienup 7/31/06

    This code is intended to provide the same result as if the following
    operations were performed
      - Embed the array "in" in an array that is usfac times larger in each
        dimension. ifftshift to bring the center of the image to (1,1).
      - Take the FFT of the larger array
      - Extract an [nor, noc] region of the result. Starting with the
        [roff+1 coff+1] element.

    It achieves this result by computing the DFT in the output array without
    the need to zeropad. Much faster and memory efficient than the
    zero-padded FFT approach if [nor noc] are much smaller than [nr*usfac nc*usfac]
    """
    # this function is translated from matlab, so I'm just going to pretend
    # it is matlab/pylab
    from numpy.fft import ifftshift,fftfreq
    from numpy import pi,newaxis,floor

    nr,nc=np.shape(inp);
    # Set defaults
    if noc is None: noc=nc;
    if nor is None: nor=nr;
    # Compute kernels and obtain DFT by matrix products
    term1c = ( ifftshift(np.arange(nc,dtype='float') - floor(nc/2)).T[:,newaxis] )/nc # fftfreq
    term2c = (( np.arange(noc,dtype='float') - coff  )/usfac)[newaxis,:]              # output points
    kernc=np.exp((-1j*2*pi)*term1c*term2c);

    term1r = ( np.arange(nor,dtype='float').T - roff )[:,newaxis]                # output points
    term2r = ( ifftshift(np.arange(nr,dtype='float')) - floor(nr/2) )[newaxis,:] # fftfreq
    kernr=np.exp((-1j*2*pi/(nr*usfac))*term1r*term2r);
    #kernc=exp((-i*2*pi/(nc*usfac))*( ifftshift([0:nc-1]).' - floor(nc/2) )*( [0:noc-1] - coff ));
    #kernr=exp((-i*2*pi/(nr*usfac))*( [0:nor-1].' - roff )*( ifftshift([0:nr-1]) - floor(nr/2)  ));
    out=np.dot(np.dot(kernr,inp),kernc);
    #return np.roll(np.roll(out,-1,axis=0),-1,axis=1)
    return out

def dftups1d(inp,usfac=1,outsize=None,offset=0, return_xouts=False):
    """
    """
    # this function is translated from matlab, so I'm just going to pretend
    # it is matlab/pylab
    from numpy.fft import ifftshift,fftfreq
    from numpy import pi,newaxis,floor

    insize, = inp.shape
    if outsize is None: outsize=insize

    # Compute kernel and obtain DFT by matrix products
    term1 = fftfreq(insize)[:,newaxis]
    term2 = ((np.arange(outsize,dtype='float') + offset)/usfac)[newaxis,:]
    kern=np.exp((-1j*2*pi)*term1*term2);
    # Without the +1 in term 2, the output is always shifted by 1.
    # But, the actual X-axis starts at zero, so I have to subtract 1 below
    # This is weird... it implies that the FT is indexed from 1, which is not
    # a meaningful statement
    # My best guess is that it's a problem with e^0, the mean case, throwing things
    # off, but that's not a useful statement

    out = np.dot(inp,kern)
    if return_xouts:
        return term2.squeeze(),out
    return out

def upsample_image(image, upsample_factor=1, output_size=None, nthreads=1,
                   use_numpy_fft=False, xshift=0, yshift=0):
    """
    Use dftups to upsample an image (but takes an image and returns an image with all reals)
    """
    fftn,ifftn = fast_ffts.get_ffts(nthreads=nthreads, use_numpy_fft=use_numpy_fft)

    imfft = ifftn(image)

    if output_size is None:
        s1 = image.shape[0]*upsample_factor
        s2 = image.shape[1]*upsample_factor
    elif hasattr(output_size,'__len__'):
        s1 = output_size[0]
        s2 = output_size[1]
    else:
        s1 = output_size
        s2 = output_size

    ups = dftups(imfft, s1, s2, upsample_factor, roff=yshift, coff=xshift)

    return ups

def odddftups(inp,nor=None,noc=None,usfac=1,roff=0,coff=0):
    from numpy.fft import ifftshift
    from numpy import pi,newaxis,floor

    nr,nc=np.shape(inp);

    # Set defaults
    if noc is None: noc=nc;
    if nor is None: nor=nr;

    if nr % 2 == 1:
        oddr = True
        nrnew = nr+1
    else:
        oddr = False
    if nr % 2 == 1:
        oddr = True
        nrnew = nr+1
    else:
        oddr = False

    # Compute kernels and obtain DFT by matrix products
    term1c = ( ifftshift(np.arange(nc) - floor(nc/2)).T[:,newaxis] )
    term2c = ( np.arange(noc) - coff  )[newaxis,:]
    kernc=np.exp((-1j*2*pi/(nc*usfac))*term1c*term2c);
    term1r = ( np.arange(nor).T - roff )[:,newaxis]
    term2r = ( ifftshift(np.arange(nr)) - floor(nr/2) )[newaxis,:]
    kernr=np.exp((-1j*2*pi/(nr*usfac))*term1r*term2r);
    #kernc=exp((-i*2*pi/(nc*usfac))*( ifftshift([0:nc-1]).' - floor(nc/2) )*( [0:noc-1] - coff ));
    #kernr=exp((-i*2*pi/(nr*usfac))*( [0:nor-1].' - roff )*( ifftshift([0:nr-1]) - floor(nr/2)  ));
    out=np.dot(np.dot(kernr,inp),kernc);
    #return np.roll(np.roll(out,+1,axis=0),+1,axis=1)
    return out

if __name__ == "__main__" and False:

    # breakdown of the dft upsampling method
    from numpy.fft import ifftshift
    from numpy import pi,newaxis,floor

    from pylab import *

    xx,yy = np.meshgrid(np.linspace(-5,5,100),np.linspace(-5,5,100))
    # a Gaussian image
    data = np.exp(-(xx**2+yy**2)/(0.5**2 * 2.))
    fftn,ifftn = fast_ffts.get_ffts(nthreads=4, use_numpy_fft=False)
    print("input max pixel: ",np.unravel_index(data.argmax(),data.shape))
    inp = ifftn(data)

    nr,nc=np.shape(inp);
    noc,nor = nc,nr # these are the output sizes

    # upsample_factor
    usfac = 20.
    for usfac in [1,2,5,10,20,30,40]:
        # the "virtual image" will have size im.shape[0]*usfac,im.shape[1]*usfac
        # To "zoom in" on the center of the image, we need an offset that identifies
        # the lower-left corner of the new image
        vshape = inp.shape[0]*usfac,inp.shape[1]*usfac
        roff = -(vshape[0] - usfac - nor)/2. -1
        coff = -(vshape[1] - usfac - noc)/2. -1

        # shifts decided automatically now
        # roff,coff = 0,0 # -50,-50

        # Compute kernels and obtain DFT by matrix products
        term1c = ( ifftshift(np.arange(nc) - floor(nc/2)).T[:,newaxis] )
        term2c = ( np.arange(noc) - coff  )[newaxis,:]
        kernc=np.exp((-1j*2*pi/(nc*usfac))*term1c*term2c);

        figure(1)
        clf()
        subplot(121)
        imshow(term1c)
        title("term1 (col)")
        colorbar()
        subplot(122)
        imshow(term2c)
        title("term2 (col)")
        colorbar()

        term1r = ( np.arange(nor).T - roff )[:,newaxis]
        term2r = ( ifftshift(np.arange(nr)) - floor(nr/2) )[newaxis,:]
        kernr=np.exp((-1j*2*pi/(nr*usfac))*term1r*term2r);

        figure(2)
        clf()
        subplot(121)
        imshow(term1r)
        title("term1 (row)")
        colorbar()
        subplot(122)
        imshow(term2r)
        title("term2 (row)")
        colorbar()

        figure(3)
        clf()
        subplot(131)
        imshow(np.abs(kernr))
        subplot(132)
        imshow(kernr.real)
        subplot(133)
        imshow(kernr.imag)

        #kernc=exp((-i*2*pi/(nc*usfac))*( ifftshift([0:nc-1]).' - floor(nc/2) )*( [0:noc-1] - coff ));
        #kernr=exp((-i*2*pi/(nr*usfac))*( [0:nor-1].' - roff )*( ifftshift([0:nr-1]) - floor(nr/2)  ));
        dot1 = np.dot(kernr,inp)
        out=np.dot(dot1,kernc);

        # http://stackoverflow.com/a/9479621/814354
        # wrong from scipy.linalg import fblas as FB
        # wrong out2 = FB.dgemm(alpha=1.0, a=dot1, b=kernc)

        figure(10)
        subplot(121)
        imshow(data)
        title("gaussian")
        subplot(122)
        imshow(np.abs(out))
        title('zoomed')

        print("usfac: ",usfac,"max pixel: ",np.unravel_index(np.abs(out).argmax(),out.shape))

        figure(11)
        clf()
        imshow(np.abs(dftups(inp,inp.shape[0]*2,inp.shape[1]*2,usfac=2)))
<|endoftext|>


<commit_before>__version_info__ = ('1', '0', '7')
__version__ = '.'.join(__version_info__)
__short_version__ = '.'.join(__version_info__)
__author__ = 'Stormpath, Inc.'
__license__ = 'Apache'
__copyright__ = '(c) 2012 - 2015 Stormpath, Inc.'
<commit_msg>Version bump.<commit_after>__version_info__ = ('1', '1', '0')
__version__ = '.'.join(__version_info__)
__short_version__ = '.'.join(__version_info__)
__author__ = 'Stormpath, Inc.'
__license__ = 'Apache'
__copyright__ = '(c) 2012 - 2015 Stormpath, Inc.'
<|endoftext|>


<commit_before>from django.db import models
from django.utils import timezone

from django_vend.core.managers import BaseVendAPIManager
from django_vend.core.exceptions import VendSyncError
from django_vend.core.utils import parse_date
from django_vend.auth.models import VendRetailer

class VendOutletManager(BaseVendAPIManager):

    resource_collection_url = 'https://{}.vendhq.com/api/2.0/outlets'
    resource_object_url = 'https://{}.vendhq.com/api/2.0/outlets/{}'

    json_collection_name = 'data'
    json_object_name = 'data'

    def parse_json_object(self, json_obj):
        obj = {
            'name': self.get_dict_value(json_obj, 'name'),
            'time_zone': self.get_dict_value(json_obj, 'time_zone'),
            'currency': self.get_dict_value(json_obj, 'currency'),
            'currency_symbol': self.get_dict_value(json_obj, 'currency_symbol'),
            'deleted_at': parse_date(self.get_dict_value(
                                json_obj, 'deleted_at', required=False)),
        }

        inclusive = self.get_dict_value(json_obj, 'display_prices')
        obj['display_prices_tax_inclusive'] = (inclusive == 'inclusive')

        return obj

class VendRegisterManager(BaseVendAPIManager):

    resource_collection_url = 'https://{}.vendhq.com/api/2.0/registers'
    resource_object_url = 'https://{}.vendhq.com/api/2.0/registers/{}'

    json_collection_name = 'data'
    json_object_name = 'data'

    def synchronise(self, retailer, *args, **kwargs):
        VendOutlet.objects.synchronise(retailer)
        super(VendRegisterManager, self).synchronise(retailer, *args, **kwargs)

    def parse_json_object(self, json_obj):
        outlet_id = self.get_dict_value(json_obj, 'outlet_id')
        try:
            outlet = VendOutlet.objects.get(uid=outlet_id)
        except VendOutlet.DoesNotExist:
            raise self.sync_exception('Invalid uid {} for VendOutlet'.format(
                outlet_id))

        obj = {
            'name': self.get_dict_value(json_obj, 'name'),
            'outlet': outlet,
            'invoice_prefix': self.get_dict_value(
                                json_obj, 'invoice_prefix', required=False),
            'invoice_suffix': self.get_dict_value(
                                json_obj, 'invoice_suffix', required=False),
            'invoice_sequence': self.get_dict_value(
                                            json_obj, 'invoice_sequence'),
            'deleted_at': parse_date(self.get_dict_value(
                                json_obj, 'deleted_at', required=False)),
            'is_open': self.get_dict_value(json_obj, 'is_open'),
            'register_open_time': parse_date(self.get_dict_value(
                            json_obj, 'register_open_time', required=False)),
            'register_close_time': parse_date(self.get_dict_value(
                            json_obj, 'register_close_time', required=False)),
        }
        return obj

class VendOutlet(models.Model):
    # /api/outlets AND /api/2.0/outlets
    uid = models.UUIDField(editable=False)
    name = models.CharField(max_length=256)
    # /api/outlets
    #email = models.EmailField()
    #address1 = models.CharField(max_length=256)
    #address2 = models.CharField(max_length=256)
    #city = models.CharField(max_length=256)
    #country = models.CharField(max_length=256)
    #postcode = models.CharField(max_length=256)
    #state = models.CharField(max_length=256)
    #suburb = models.CharField(max_length=256)
    retailer = models.ForeignKey(VendRetailer, editable=False,
        on_delete=models.CASCADE)
    # /api/outlets AND /api/2.0/outlets
    time_zone = models.CharField(max_length=256)
    # /api/2.0/outlets
    currency = models.CharField(max_length=256)
    currency_symbol = models.CharField(max_length=32)
    display_prices_tax_inclusive = models.BooleanField(default=True)
    deleted_at = models.DateTimeField(null=True)

    objects = VendOutletManager()

    def __str__(self):
        return self.name

class VendRegister(models.Model):
    # /api/registers AND /api/2.0/registers
    uid = models.UUIDField(editable=False)
    name = models.CharField(max_length=256)
    outlet = models.ForeignKey(VendOutlet, editable=False,
        on_delete=models.CASCADE)
    invoice_prefix = models.CharField(max_length=32)
    invoice_suffix = models.CharField(max_length=32)
    invoice_sequence = models.PositiveIntegerField()
    # /api/registers
    #register_open_count_sequence = models.PositiveIntegerField(null=True)
    # /api/2.0/registers
    deleted_at = models.DateTimeField(null=True)
    is_open = models.BooleanField(default=False)
    # /api/registers AND /api/2.0/registers
    register_open_time = models.DateTimeField(blank=True, null=True)
    register_close_time = models.DateTimeField(blank=True, null=True)
    # Non-API
    retailer = models.ForeignKey(VendRetailer, editable=False,
        on_delete=models.CASCADE)

    objects = VendRegisterManager()

    def __str__(self):
        return self.name
<commit_msg>Add get_absolute_url method to VendOutlet model<commit_after>from django.db import models
from django.utils import timezone
from django.urls import reverse

from django_vend.core.managers import BaseVendAPIManager
from django_vend.core.exceptions import VendSyncError
from django_vend.core.utils import parse_date
from django_vend.auth.models import VendRetailer

class VendOutletManager(BaseVendAPIManager):

    resource_collection_url = 'https://{}.vendhq.com/api/2.0/outlets'
    resource_object_url = 'https://{}.vendhq.com/api/2.0/outlets/{}'

    json_collection_name = 'data'
    json_object_name = 'data'

    def parse_json_object(self, json_obj):
        obj = {
            'name': self.get_dict_value(json_obj, 'name'),
            'time_zone': self.get_dict_value(json_obj, 'time_zone'),
            'currency': self.get_dict_value(json_obj, 'currency'),
            'currency_symbol': self.get_dict_value(json_obj, 'currency_symbol'),
            'deleted_at': parse_date(self.get_dict_value(
                                json_obj, 'deleted_at', required=False)),
        }

        inclusive = self.get_dict_value(json_obj, 'display_prices')
        obj['display_prices_tax_inclusive'] = (inclusive == 'inclusive')

        return obj

class VendRegisterManager(BaseVendAPIManager):

    resource_collection_url = 'https://{}.vendhq.com/api/2.0/registers'
    resource_object_url = 'https://{}.vendhq.com/api/2.0/registers/{}'

    json_collection_name = 'data'
    json_object_name = 'data'

    def synchronise(self, retailer, *args, **kwargs):
        VendOutlet.objects.synchronise(retailer)
        super(VendRegisterManager, self).synchronise(retailer, *args, **kwargs)

    def parse_json_object(self, json_obj):
        outlet_id = self.get_dict_value(json_obj, 'outlet_id')
        try:
            outlet = VendOutlet.objects.get(uid=outlet_id)
        except VendOutlet.DoesNotExist:
            raise self.sync_exception('Invalid uid {} for VendOutlet'.format(
                outlet_id))

        obj = {
            'name': self.get_dict_value(json_obj, 'name'),
            'outlet': outlet,
            'invoice_prefix': self.get_dict_value(
                                json_obj, 'invoice_prefix', required=False),
            'invoice_suffix': self.get_dict_value(
                                json_obj, 'invoice_suffix', required=False),
            'invoice_sequence': self.get_dict_value(
                                            json_obj, 'invoice_sequence'),
            'deleted_at': parse_date(self.get_dict_value(
                                json_obj, 'deleted_at', required=False)),
            'is_open': self.get_dict_value(json_obj, 'is_open'),
            'register_open_time': parse_date(self.get_dict_value(
                            json_obj, 'register_open_time', required=False)),
            'register_close_time': parse_date(self.get_dict_value(
                            json_obj, 'register_close_time', required=False)),
        }
        return obj

class VendOutlet(models.Model):
    # /api/outlets AND /api/2.0/outlets
    uid = models.UUIDField(editable=False)
    name = models.CharField(max_length=256)
    # /api/outlets
    #email = models.EmailField()
    #address1 = models.CharField(max_length=256)
    #address2 = models.CharField(max_length=256)
    #city = models.CharField(max_length=256)
    #country = models.CharField(max_length=256)
    #postcode = models.CharField(max_length=256)
    #state = models.CharField(max_length=256)
    #suburb = models.CharField(max_length=256)
    retailer = models.ForeignKey(VendRetailer, editable=False,
        on_delete=models.CASCADE)
    # /api/outlets AND /api/2.0/outlets
    time_zone = models.CharField(max_length=256)
    # /api/2.0/outlets
    currency = models.CharField(max_length=256)
    currency_symbol = models.CharField(max_length=32)
    display_prices_tax_inclusive = models.BooleanField(default=True)
    deleted_at = models.DateTimeField(null=True)

    objects = VendOutletManager()

    def get_absolute_url(self):
        return reverse('vend_outlet_detail', args=[str(self.uid)])

    def __str__(self):
        return self.name

class VendRegister(models.Model):
    # /api/registers AND /api/2.0/registers
    uid = models.UUIDField(editable=False)
    name = models.CharField(max_length=256)
    outlet = models.ForeignKey(VendOutlet, editable=False,
        on_delete=models.CASCADE)
    invoice_prefix = models.CharField(max_length=32)
    invoice_suffix = models.CharField(max_length=32)
    invoice_sequence = models.PositiveIntegerField()
    # /api/registers
    #register_open_count_sequence = models.PositiveIntegerField(null=True)
    # /api/2.0/registers
    deleted_at = models.DateTimeField(null=True)
    is_open = models.BooleanField(default=False)
    # /api/registers AND /api/2.0/registers
    register_open_time = models.DateTimeField(blank=True, null=True)
    register_close_time = models.DateTimeField(blank=True, null=True)
    # Non-API
    retailer = models.ForeignKey(VendRetailer, editable=False,
        on_delete=models.CASCADE)

    objects = VendRegisterManager()

    def __str__(self):
        return self.name
<|endoftext|>


<commit_before>from copy import copy

CHUNK_SIZE = 50
MAX_ITEMS = 2**64-1  # XXX

class RestfulModelCollectionIterator():
    def __init__(self, collection, offset=0, limit=MAX_ITEMS, **filters):
        self.collection = collection
        self.offset = offset
        self.limit = limit
        self.cache = []

    def next(self):
        if self.cache:
            return self.cache.pop()
        elif not self.limit:
            raise StopIteration()

        max_results = min(self.limit, CHUNK_SIZE)
        self.cache = self.collection.range(self.offset, max_results)

        if not self.cache:
            raise StopIteration()

        self.limit -= len(self.cache)
        self.offset += len(self.cache)

        return self.cache.pop()

class RestfulModelCollection():
    def __init__(self, cls, api, namespace, filter={}, **filters):
        filters.update(filter)
        from inbox.client import APIClient
        if not isinstance(api, APIClient):
            raise Exception("Provided api was not an APIClient.")

        self.model_class = cls
        self.filters = filters
        self.namespace = namespace
        self.api = api

    def __iter__(self):
        return RestfulModelCollectionIterator(self, **self.filters)

    def items(self):
        offset = 0
        while True:
            items = self._get_model_collection(offset)
            if not items:
                break

            for item in items:
                yield item

            offset += len(items)

    def first(self):
        results = self._get_model_collection(0, 1)
        if len(results):
            return results[0]
        return None

    def all(self):
        return self.range(0, MAX_ITEMS)

    def where(self, filter={}, **filters):
        filters.update(filter)
        collection = copy(self)
        collection.filters = filters
        return collection

    def range(self, offset=0, limit=CHUNK_SIZE):
        accumulated = []
        while len(accumulated) < limit:
            to_fetch = min(limit-len(accumulated), CHUNK_SIZE)
            results = self._get_model_collection(offset + len(accumulated),
                                                 to_fetch)
            accumulated.extend(results)

            # done if more than 'limit' items, less than asked for
            if not results or len(results) % to_fetch:
                break

        return accumulated

    def find(self, id):
        return self._get_model(id)

    def create(self, **args):
        return self.model_class.create(self.api, self.namespace, **args)

    def __getitem__(self, offset):
        return self._get_model_collection(offset, 1)[0]

    # Private functions

    def _get_model_collection(self, offset=0, limit=CHUNK_SIZE):
        filters = copy(self.filters)
        filters['offset'] = offset
        filters['limit'] = limit

        return self.api._get_resources(self.namespace, self.model_class,
                                       **filters)

    def _get_model(self, id):
        return self.api._get_resource(self.namespace, self.model_class, id,
                                      **self.filters)
<commit_msg>[restful_model_collection] Add delete suport.<commit_after>from copy import copy

CHUNK_SIZE = 50
MAX_ITEMS = 2**64-1  # XXX

class RestfulModelCollectionIterator():
    def __init__(self, collection, offset=0, limit=MAX_ITEMS, **filters):
        self.collection = collection
        self.offset = offset
        self.limit = limit
        self.cache = []

    def next(self):
        if self.cache:
            return self.cache.pop()
        elif not self.limit:
            raise StopIteration()

        max_results = min(self.limit, CHUNK_SIZE)
        self.cache = self.collection.range(self.offset, max_results)

        if not self.cache:
            raise StopIteration()

        self.limit -= len(self.cache)
        self.offset += len(self.cache)

        return self.cache.pop()

class RestfulModelCollection():
    def __init__(self, cls, api, namespace, filter={}, **filters):
        filters.update(filter)
        from inbox.client import APIClient
        if not isinstance(api, APIClient):
            raise Exception("Provided api was not an APIClient.")

        self.model_class = cls
        self.filters = filters
        self.namespace = namespace
        self.api = api

    def __iter__(self):
        return RestfulModelCollectionIterator(self, **self.filters)

    def items(self):
        offset = 0
        while True:
            items = self._get_model_collection(offset)
            if not items:
                break

            for item in items:
                yield item

            offset += len(items)

    def first(self):
        results = self._get_model_collection(0, 1)
        if len(results):
            return results[0]
        return None

    def all(self):
        return self.range(0, MAX_ITEMS)

    def where(self, filter={}, **filters):
        filters.update(filter)
        collection = copy(self)
        collection.filters = filters
        return collection

    def range(self, offset=0, limit=CHUNK_SIZE):
        accumulated = []
        while len(accumulated) < limit:
            to_fetch = min(limit-len(accumulated), CHUNK_SIZE)
            results = self._get_model_collection(offset + len(accumulated),
                                                 to_fetch)
            accumulated.extend(results)

            # done if more than 'limit' items, less than asked for
            if not results or len(results) % to_fetch:
                break

        return accumulated

    def find(self, id):
        return self._get_model(id)

    def create(self, **args):
        return self.model_class.create(self.api, self.namespace, **args)

    def delete(self, id):
        return self.api._delete_resource(self.api, self.namespace,
                                         self.model_class, id)

    def __getitem__(self, offset):
        return self._get_model_collection(offset, 1)[0]

    # Private functions

    def _get_model_collection(self, offset=0, limit=CHUNK_SIZE):
        filters = copy(self.filters)
        filters['offset'] = offset
        filters['limit'] = limit

        return self.api._get_resources(self.namespace, self.model_class,
                                       **filters)

    def _get_model(self, id):
        return self.api._get_resource(self.namespace, self.model_class, id,
                                      **self.filters)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""Framework for importing bank statement files."""
import base64

from openerp import api, models, fields
from openerp.tools.translate import _
from openerp.exceptions import Warning

import logging
_logger = logging.getLogger(__name__)

class AccountBankStatementLine(models.Model):
    _inherit = "account.bank.statement.line"

    # Ensure transactions can be imported only once (if the import format
    # provides unique transaction ids)
    unique_import_id = fields.Char('Import ID', readonly=True, copy=False)

    _sql_constraints = [
        ('unique_import_id',
         'unique (unique_import_id)',
         'A bank account transactions can be imported only once !')
    ]

class AccountBankStatementImport(models.TransientModel):
    _name = 'account.bank.statement.import'
    _description = 'Import Bank Statement'

    @api.model
    def _get_hide_journal_field(self):
        """ Return False if the journal_id can't be provided by the parsed
        file and must be provided by the wizard.
        See account_bank_statement_import_qif """
        return True

    journal_id = fields.Many2one(
        'account.journal', string='Journal',
        help='Accounting journal related to the bank statement you\'re '
        'importing. It has be be manually chosen for statement formats which '
        'doesn\'t allow automatic journal detection (QIF for example).')
    hide_journal_field = fields.Boolean(
        string='Hide the journal field in the view',
        compute='_get_hide_journal_field')
    data_file = fields.Binary(
        'Bank Statement File', required=True,
        help='Get you bank statements in electronic format from your bank '
        'and select them here.')

    @api.multi
    def import_file(self):
        """ Process the file chosen in the wizard, create bank statement(s) and
        go to reconciliation."""
        self.ensure_one()
        data_file = base64.b64decode(self.data_file)
        statement_ids, notifications = self.with_context(
            active_id=self.id)._import_file(data_file)
        # dispatch to reconciliation interface
        action = self.env.ref(
            'account.action_bank_reconcile_bank_statements')
        return {
            'name': action.name,
            'tag': action.tag,
            'context': {
                'statement_ids': statement_ids,
                'notifications': notifications
            },
            'type': 'ir.actions.client',
        }

    @api.model
    def _import_file(self, data_file):
        """ Create bank statement(s) from file."""
        # The appropriate implementation module returns the required data
        statement_ids = []
        notifications = []
        statements = self._parse_file(data_file)
        # Check raw data:
        self._check_parsed_data(statements)
        # Import all statements:
        for statement in statements:
            (statement_id, new_notifications) = (
                self._import_statement(statement))
            if statement_id:
                statement_ids.append(statement_id)
            notifications.append(new_notifications)
        if len(statement_ids) == 0:
            raise Warning(_('You have already imported that file.'))
        return statement_ids, notifications

    @api.model
    def _import_statement(self, statement):
        """Import a single bank-statement.

        Return ids of created statements and notifications.
        """
        currency_code = statement.pop('currency_code')
        account_number = statement.pop('account_number')
        # Try to find the bank account and currency in odoo
        currency_id = self._find_currency_id(currency_code)
        bank_account_id = self._find_bank_account_id(account_number)
        # Create the bank account if not already existing
        if not bank_account_id and account_number:
            journal_id = self.env.context.get('journal_id')
            company_id = self.env.user.company_id.id
            if journal_id:
                journal = self.env['account.journal'].browse(journal_id)
                company_id = journal.company_id.id
            bank_account_id = self._create_bank_account(
                account_number, company_id=company_id,
                currency_id=currency_id).id
        # Find or create the bank journal
        journal_id = self._get_journal(currency_id, bank_account_id)
        # By now journal and account_number must be known
        if not journal_id:
            raise Warning(_('Can not determine journal for import.'))
        # Prepare statement data to be used for bank statements creation
        statement = self._complete_statement(
            statement, journal_id, account_number)
        # Create the bank statement
        return self._create_bank_statement(statement)

    @api.model
    def _parse_file(self, data_file):
        """ Each module adding a file support must extends this method. It
        processes the file if it can, returns super otherwise, resulting in a
        chain of responsability.
        This method parses the given file and returns the data required by
        the bank statement import process, as specified below.
        - bank statements data: list of dict containing (optional
                                items marked by o) :
            -o currency code: string (e.g: 'EUR')
                The ISO 4217 currency code, case insensitive
            -o account number: string (e.g: 'BE1234567890')
                The number of the bank account which the statement
                belongs to
            - 'name': string (e.g: '000000123')
            - 'date': date (e.g: 2013-06-26)
            -o 'balance_start': float (e.g: 8368.56)
            -o 'balance_end_real': float (e.g: 8888.88)
            - 'transactions': list of dict containing :
                - 'name': string
                    (e.g: 'KBC-INVESTERINGSKREDIET 787-5562831-01')
                - 'date': date
                - 'amount': float
                - 'unique_import_id': string
                -o 'account_number': string
                    Will be used to find/create the res.partner.bank
                    in odoo
                -o 'note': string
                -o 'partner_name': string
                -o 'ref': string
        """
        raise Warning(_(
            'Could not make sense of the given file.\nDid you '
            'install the module to support this type of file ?'
        ))

    @api.model
    def _check_parsed_data(self, statements):
        """ Basic and structural verifications """
        if len(statements) == 0:
            raise Warning(_('This file doesn\'t contain any statement.'))
        for statement in statements:
            if 'transactions' in statement and statement['transactions']:
                return
        # If we get here, no transaction was found:
        raise Warning(_('This file doesn\'t contain any transaction.'))

    @api.model
    def _find_currency_id(self, currency_code):
        """ Get res.currency ID."""
        if currency_code:
            currency_ids = self.env['res.currency'].search(
                [('name', '=ilike', currency_code)])
            if currency_ids:
                return currency_ids[0].id
            else:
                raise Warning(_(
                    'Statement has invalid currency code %s') % currency_code)
        # if no currency_code is provided, we'll use the company currency
        return self.env.user.company_id.currency_id.id

    @api.model
    def _find_bank_account_id(self, account_number):
        """ Get res.partner.bank ID """
        bank_account_id = None
        if account_number and len(account_number) > 4:
            bank_account_ids = self.env['res.partner.bank'].search(
                [('acc_number', '=', account_number)], limit=1)
            if bank_account_ids:
                bank_account_id = bank_account_ids[0].id
        return bank_account_id

    @api.model
    def _get_journal(self, currency_id, bank_account_id, account_number):
        """ Find the journal """
        bank_model = self.env['res.partner.bank']
        # Find the journal from context, wizard or bank account
        journal_id = self.env.context.get('journal_id') or self.journal_id.id
        if bank_account_id:
            bank_account = bank_model.browse(bank_account_id)
            if journal_id:
                if (bank_account.journal_id.id and
                        bank_account.journal_id.id != journal_id):
                    raise Warning(
                        _('The account of this statement is linked to '
                          'another journal.'))
                if not bank_account.journal_id.id:
                    bank_model.write({'journal_id': journal_id})
            else:
                if bank_account.journal_id.id:
                    journal_id = bank_account.journal_id.id
        # If importing into an existing journal, its currency must be the same
        # as the bank statement. When journal has no currency, currency must
        # be equal to company currency.
        if journal_id and currency_id:
            journal_currency_id = self.env['account.journal'].browse(
                journal_id).currency.id
            if journal_currency_id:
                if currency_id and currency_id != journal_currency_id:
                    raise Warning(_(
                        'The currency of the bank statement is not '
                        'the same as the currency of the journal !'
                    ))
                else:
                    if currency_id != self.env.user.company_id.currency_id.id:
                        raise Warning(_(
                            'The currency of the bank statement is not '
                            'the same as the company currency !'
                        ))
        return journal_id

    @api.model
    @api.returns('res.partner.bank')
    def _create_bank_account(self, account_number, company_id=False,
                             currency_id=False):
        try:
            bank_type = self.env.ref('base.bank_normal')
            bank_code = bank_type.code
        except ValueError:
            bank_code = 'bank'
        vals_acc = {
            'acc_number': account_number,
            'state': bank_code,
        }
        # Odoo users bank accounts (which we import statement from) have
        # company_id and journal_id set while 'counterpart' bank accounts
        # (from which statement transactions originate) don't.
        # Warning : if company_id is set, the method post_write of class
        # bank will create a journal
        if company_id:
            vals = self.env['res.partner.bank'].onchange_company_id(company_id)
            vals_acc.update(vals.get('value', {}))
            vals_acc['company_id'] = company_id

        # When the journal is created at same time of the bank account, we need
        # to specify the currency to use for the account.account and
        # account.journal
        return self.env['res.partner.bank'].with_context(
            default_currency_id=currency_id,
            default_currency=currency_id).create(vals_acc)

    @api.model
    def _complete_statement(self, statement, journal_id, account_number):
        statement['journal_id'] = journal_id
        for line_vals in statement['transactions']:
            unique_import_id = line_vals.get('unique_import_id', False)
            if unique_import_id:
                line_vals['unique_import_id'] = (
                    account_number and account_number + '-' or '') + \
                    unique_import_id

            if not line_vals.get('bank_account_id'):
                # Find the partner and his bank account or create the bank
                # account. The partner selected during the reconciliation
                # process will be linked to the bank when the statement is
                # closed.
                partner_id = False
                bank_account_id = False
                identifying_string = line_vals.get('account_number')
                if identifying_string:
                    bank_model = self.env['res.partner.bank']
                    banks = bank_model.search(
                        [('acc_number', '=', identifying_string)], limit=1)
                    if banks:
                        bank_account_id = banks[0].id
                        partner_id = banks[0].partner_id.id
                    else:
                        bank_account_id = self._create_bank_account(
                            identifying_string).id
                line_vals['partner_id'] = partner_id
                line_vals['bank_account_id'] = bank_account_id
        return statement

    @api.model
    def _create_bank_statement(self, statement):
        """ Create bank statement from imported values, filtering out
        already imported transactions, and return data used by the
        reconciliation widget
        """
        bs_model = self.env['account.bank.statement']
        bsl_model = self.env['account.bank.statement.line']
        # Filter out already imported transactions and create statement
        ignored_statement_lines_import_ids = []
        filtered_st_lines = []
        for line_vals in statement['transactions']:
            unique_id = (
                'unique_import_id' in line_vals and
                line_vals['unique_import_id']
            )
            if not unique_id or not bool(bsl_model.sudo().search(
                    [('unique_import_id', '=', unique_id)], limit=1)):
                filtered_st_lines.append(line_vals)
            else:
                ignored_statement_lines_import_ids.append(unique_id)
        statement_id = False
        if len(filtered_st_lines) > 0:
            # Remove values that won't be used to create records
            statement.pop('transactions', None)
            for line_vals in filtered_st_lines:
                line_vals.pop('account_number', None)
            # Create the statement
            statement['line_ids'] = [
                [0, False, line] for line in filtered_st_lines]
            statement_id = bs_model.create(statement).id
        # Prepare import feedback
        notifications = []
        num_ignored = len(ignored_statement_lines_import_ids)
        if num_ignored > 0:
            notifications += [{
                'type': 'warning',
                'message': _("%d transactions had already been imported and "
                             "were ignored.") % num_ignored
                        if num_ignored > 1
                        else _("1 transaction had already been imported and "
                               "was ignored."),
                'details': {
                    'name': _('Already imported items'),
                    'model': 'account.bank.statement.line',
                    'ids': bsl_model.search(
                        [('unique_import_id', 'in',
                          ignored_statement_lines_import_ids)]).ids
                }
            }]
        return statement_id, notifications
<commit_msg>[FIX] Company and statement currency can be different.<commit_after># -*- coding: utf-8 -*-
"""Framework for importing bank statement files."""
import base64

from openerp import api, models, fields
from openerp.tools.translate import _
from openerp.exceptions import Warning

import logging
_logger = logging.getLogger(__name__)

class AccountBankStatementLine(models.Model):
    _inherit = "account.bank.statement.line"

    # Ensure transactions can be imported only once (if the import format
    # provides unique transaction ids)
    unique_import_id = fields.Char('Import ID', readonly=True, copy=False)

    _sql_constraints = [
        ('unique_import_id',
         'unique (unique_import_id)',
         'A bank account transactions can be imported only once !')
    ]

class AccountBankStatementImport(models.TransientModel):
    _name = 'account.bank.statement.import'
    _description = 'Import Bank Statement'

    @api.model
    def _get_hide_journal_field(self):
        """ Return False if the journal_id can't be provided by the parsed
        file and must be provided by the wizard.
        See account_bank_statement_import_qif """
        return True

    journal_id = fields.Many2one(
        'account.journal', string='Journal',
        help='Accounting journal related to the bank statement you\'re '
        'importing. It has be be manually chosen for statement formats which '
        'doesn\'t allow automatic journal detection (QIF for example).')
    hide_journal_field = fields.Boolean(
        string='Hide the journal field in the view',
        compute='_get_hide_journal_field')
    data_file = fields.Binary(
        'Bank Statement File', required=True,
        help='Get you bank statements in electronic format from your bank '
        'and select them here.')

    @api.multi
    def import_file(self):
        """ Process the file chosen in the wizard, create bank statement(s) and
        go to reconciliation."""
        self.ensure_one()
        data_file = base64.b64decode(self.data_file)
        statement_ids, notifications = self.with_context(
            active_id=self.id)._import_file(data_file)
        # dispatch to reconciliation interface
        action = self.env.ref(
            'account.action_bank_reconcile_bank_statements')
        return {
            'name': action.name,
            'tag': action.tag,
            'context': {
                'statement_ids': statement_ids,
                'notifications': notifications
            },
            'type': 'ir.actions.client',
        }

    @api.model
    def _import_file(self, data_file):
        """ Create bank statement(s) from file."""
        # The appropriate implementation module returns the required data
        statement_ids = []
        notifications = []
        statements = self._parse_file(data_file)
        # Check raw data:
        self._check_parsed_data(statements)
        # Import all statements:
        for statement in statements:
            (statement_id, new_notifications) = (
                self._import_statement(statement))
            if statement_id:
                statement_ids.append(statement_id)
            notifications.append(new_notifications)
        if len(statement_ids) == 0:
            raise Warning(_('You have already imported that file.'))
        return statement_ids, notifications

    @api.model
    def _import_statement(self, statement):
        """Import a single bank-statement.

        Return ids of created statements and notifications.
        """
        currency_code = statement.pop('currency_code')
        account_number = statement.pop('account_number')
        # Try to find the bank account and currency in odoo
        currency_id = self._find_currency_id(currency_code)
        bank_account_id = self._find_bank_account_id(account_number)
        # Create the bank account if not already existing
        if not bank_account_id and account_number:
            journal_id = self.env.context.get('journal_id')
            company_id = self.env.user.company_id.id
            if journal_id:
                journal = self.env['account.journal'].browse(journal_id)
                company_id = journal.company_id.id
            bank_account_id = self._create_bank_account(
                account_number, company_id=company_id,
                currency_id=currency_id).id
        # Find or create the bank journal
        journal_id = self._get_journal(currency_id, bank_account_id)
        # By now journal and account_number must be known
        if not journal_id:
            raise Warning(_('Can not determine journal for import.'))
        # Prepare statement data to be used for bank statements creation
        statement = self._complete_statement(
            statement, journal_id, account_number)
        # Create the bank statement
        return self._create_bank_statement(statement)

    @api.model
    def _parse_file(self, data_file):
        """ Each module adding a file support must extends this method. It
        processes the file if it can, returns super otherwise, resulting in a
        chain of responsability.
        This method parses the given file and returns the data required by
        the bank statement import process, as specified below.
        - bank statements data: list of dict containing (optional
                                items marked by o) :
            -o currency code: string (e.g: 'EUR')
                The ISO 4217 currency code, case insensitive
            -o account number: string (e.g: 'BE1234567890')
                The number of the bank account which the statement
                belongs to
            - 'name': string (e.g: '000000123')
            - 'date': date (e.g: 2013-06-26)
            -o 'balance_start': float (e.g: 8368.56)
            -o 'balance_end_real': float (e.g: 8888.88)
            - 'transactions': list of dict containing :
                - 'name': string
                    (e.g: 'KBC-INVESTERINGSKREDIET 787-5562831-01')
                - 'date': date
                - 'amount': float
                - 'unique_import_id': string
                -o 'account_number': string
                    Will be used to find/create the res.partner.bank
                    in odoo
                -o 'note': string
                -o 'partner_name': string
                -o 'ref': string
        """
        raise Warning(_(
            'Could not make sense of the given file.\nDid you '
            'install the module to support this type of file ?'
        ))

    @api.model
    def _check_parsed_data(self, statements):
        """ Basic and structural verifications """
        if len(statements) == 0:
            raise Warning(_('This file doesn\'t contain any statement.'))
        for statement in statements:
            if 'transactions' in statement and statement['transactions']:
                return
        # If we get here, no transaction was found:
        raise Warning(_('This file doesn\'t contain any transaction.'))

    @api.model
    def _find_currency_id(self, currency_code):
        """ Get res.currency ID."""
        if currency_code:
            currency_ids = self.env['res.currency'].search(
                [('name', '=ilike', currency_code)])
            if currency_ids:
                return currency_ids[0].id
            else:
                raise Warning(_(
                    'Statement has invalid currency code %s') % currency_code)
        # if no currency_code is provided, we'll use the company currency
        return self.env.user.company_id.currency_id.id

    @api.model
    def _find_bank_account_id(self, account_number):
        """ Get res.partner.bank ID """
        bank_account_id = None
        if account_number and len(account_number) > 4:
            bank_account_ids = self.env['res.partner.bank'].search(
                [('acc_number', '=', account_number)], limit=1)
            if bank_account_ids:
                bank_account_id = bank_account_ids[0].id
        return bank_account_id

    @api.model
    def _get_journal(self, currency_id, bank_account_id, account_number):
        """ Find the journal """
        bank_model = self.env['res.partner.bank']
        # Find the journal from context, wizard or bank account
        journal_id = self.env.context.get('journal_id') or self.journal_id.id
        if bank_account_id:
            bank_account = bank_model.browse(bank_account_id)
            if journal_id:
                if (bank_account.journal_id.id and
                        bank_account.journal_id.id != journal_id):
                    raise Warning(
                        _('The account of this statement is linked to '
                          'another journal.'))
                if not bank_account.journal_id.id:
                    bank_model.write({'journal_id': journal_id})
            else:
                if bank_account.journal_id.id:
                    journal_id = bank_account.journal_id.id

        # If importing into an existing journal, its currency must be the same
        # as the bank statement
        if journal_id:
            journal_currency_id = self.env['account.journal'].browse(
                journal_id).currency.id
            if currency_id and currency_id != journal_currency_id:
                raise Warning(_('The currency of the bank statement is not '
                                'the same as the currency of the journal !'))

        # If we couldn't find/create a journal, everything is lost
        if not journal_id:
            raise Warning(_('Cannot find in which journal import this '
                            'statement. Please manually select a journal.'))

        return journal_id

    @api.model
    @api.returns('res.partner.bank')
    def _create_bank_account(self, account_number, company_id=False,
                             currency_id=False):
        try:
            bank_type = self.env.ref('base.bank_normal')
            bank_code = bank_type.code
        except ValueError:
            bank_code = 'bank'
        vals_acc = {
            'acc_number': account_number,
            'state': bank_code,
        }
        # Odoo users bank accounts (which we import statement from) have
        # company_id and journal_id set while 'counterpart' bank accounts
        # (from which statement transactions originate) don't.
        # Warning : if company_id is set, the method post_write of class
        # bank will create a journal
        if company_id:
            vals = self.env['res.partner.bank'].onchange_company_id(company_id)
            vals_acc.update(vals.get('value', {}))
            vals_acc['company_id'] = company_id

        # When the journal is created at same time of the bank account, we need
        # to specify the currency to use for the account.account and
        # account.journal
        return self.env['res.partner.bank'].with_context(
            default_currency_id=currency_id,
            default_currency=currency_id).create(vals_acc)

    @api.model
    def _complete_statement(self, statement, journal_id, account_number):
        statement['journal_id'] = journal_id
        for line_vals in statement['transactions']:
            unique_import_id = line_vals.get('unique_import_id', False)
            if unique_import_id:
                line_vals['unique_import_id'] = (
                    account_number and account_number + '-' or '') + \
                    unique_import_id

            if not line_vals.get('bank_account_id'):
                # Find the partner and his bank account or create the bank
                # account. The partner selected during the reconciliation
                # process will be linked to the bank when the statement is
                # closed.
                partner_id = False
                bank_account_id = False
                identifying_string = line_vals.get('account_number')
                if identifying_string:
                    bank_model = self.env['res.partner.bank']
                    banks = bank_model.search(
                        [('acc_number', '=', identifying_string)], limit=1)
                    if banks:
                        bank_account_id = banks[0].id
                        partner_id = banks[0].partner_id.id
                    else:
                        bank_account_id = self._create_bank_account(
                            identifying_string).id
                line_vals['partner_id'] = partner_id
                line_vals['bank_account_id'] = bank_account_id
        return statement

    @api.model
    def _create_bank_statement(self, statement):
        """ Create bank statement from imported values, filtering out
        already imported transactions, and return data used by the
        reconciliation widget
        """
        bs_model = self.env['account.bank.statement']
        bsl_model = self.env['account.bank.statement.line']
        # Filter out already imported transactions and create statement
        ignored_statement_lines_import_ids = []
        filtered_st_lines = []
        for line_vals in statement['transactions']:
            unique_id = (
                'unique_import_id' in line_vals and
                line_vals['unique_import_id']
            )
            if not unique_id or not bool(bsl_model.sudo().search(
                    [('unique_import_id', '=', unique_id)], limit=1)):
                filtered_st_lines.append(line_vals)
            else:
                ignored_statement_lines_import_ids.append(unique_id)
        statement_id = False
        if len(filtered_st_lines) > 0:
            # Remove values that won't be used to create records
            statement.pop('transactions', None)
            for line_vals in filtered_st_lines:
                line_vals.pop('account_number', None)
            # Create the statement
            statement['line_ids'] = [
                [0, False, line] for line in filtered_st_lines]
            statement_id = bs_model.create(statement).id
        # Prepare import feedback
        notifications = []
        num_ignored = len(ignored_statement_lines_import_ids)
        if num_ignored > 0:
            notifications += [{
                'type': 'warning',
                'message': _("%d transactions had already been imported and "
                             "were ignored.") % num_ignored
                        if num_ignored > 1
                        else _("1 transaction had already been imported and "
                               "was ignored."),
                'details': {
                    'name': _('Already imported items'),
                    'model': 'account.bank.statement.line',
                    'ids': bsl_model.search(
                        [('unique_import_id', 'in',
                          ignored_statement_lines_import_ids)]).ids
                }
            }]
        return statement_id, notifications
<|endoftext|>


<commit_before>from atom.views import (CreateMessageMixin, DeleteMessageMixin,
                        UpdateMessageMixin)
from braces.views import (FormValidMessageMixin, LoginRequiredMixin,
                          PermissionRequiredMixin, PrefetchRelatedMixin,
                          SelectRelatedMixin, UserFormKwargsMixin)
from dal import autocomplete
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, DeleteView, DetailView, UpdateView
from django_filters.views import FilterView

from feder.cases.models import Case
from feder.main.mixins import ExtraListMixin

from .filters import InstitutionFilter
from .forms import InstitutionForm
from .models import Institution, Tag

_('Institutions index')

class InstitutionListView(SelectRelatedMixin, FilterView):
    filterset_class = InstitutionFilter
    model = Institution
    select_related = ['jst', 'jst__category']
    paginate_by = 25

    def get_queryset(self, *args, **kwargs):
        qs = super(InstitutionListView, self).get_queryset(*args, **kwargs)
        return qs.with_case_count()

class InstitutionDetailView(SelectRelatedMixin, ExtraListMixin, PrefetchRelatedMixin, DetailView):
    model = Institution
    prefetch_related = ['tags']
    select_related = []
    extra_list_context = 'case_list'

    @staticmethod
    def get_object_list(obj):
        return (Case.objects.filter(institution=obj).
                select_related('monitoring').
                prefetch_related('task_set').
                order_by('monitoring').all())

class InstitutionCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateMessageMixin,
                            UserFormKwargsMixin, CreateView):
    model = Institution
    form_class = InstitutionForm
    permission_required = "institutions.add_institution"
    raise_exception = True
    redirect_unauthenticated_users = True

class InstitutionUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UserFormKwargsMixin,
                            UpdateMessageMixin, FormValidMessageMixin, UpdateView):
    model = Institution
    form_class = InstitutionForm
    permission_required = "institutions.change_institution"
    raise_exception = True
    redirect_unauthenticated_users = True

class InstitutionDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageMixin,
                            UpdateMessageMixin, DeleteView):
    model = Institution
    success_url = reverse_lazy('institutions:list')
    permission_required = "institutions.delete_institution"
    raise_exception = True
    redirect_unauthenticated_users = True

class InstitutionAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Institution.objects
        if self.q:
            qs = qs.filter(name__icontains=self.q)
        return qs.all()

class TagAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Tag.objects
        if self.q:
            qs = qs.filter(name__icontains=self.q)
        return qs.all()
<commit_msg>Add institutions count in TagAutocomplete<commit_after>from atom.views import (CreateMessageMixin, DeleteMessageMixin,
                        UpdateMessageMixin)
from braces.views import (FormValidMessageMixin, LoginRequiredMixin,
                          PermissionRequiredMixin, PrefetchRelatedMixin,
                          SelectRelatedMixin, UserFormKwargsMixin)
from dal import autocomplete
from django.core.urlresolvers import reverse_lazy
from django.db.models import Count
from django.utils import six
from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, DeleteView, DetailView, UpdateView
from django_filters.views import FilterView
from feder.cases.models import Case
from feder.main.mixins import ExtraListMixin

from .filters import InstitutionFilter
from .forms import InstitutionForm
from .models import Institution, Tag

_('Institutions index')

class InstitutionListView(SelectRelatedMixin, FilterView):
    filterset_class = InstitutionFilter
    model = Institution
    select_related = ['jst', 'jst__category']
    paginate_by = 25

    def get_queryset(self, *args, **kwargs):
        qs = super(InstitutionListView, self).get_queryset(*args, **kwargs)
        return qs.with_case_count()

class InstitutionDetailView(SelectRelatedMixin, ExtraListMixin, PrefetchRelatedMixin, DetailView):
    model = Institution
    prefetch_related = ['tags']
    select_related = []
    extra_list_context = 'case_list'

    @staticmethod
    def get_object_list(obj):
        return (Case.objects.filter(institution=obj).
                select_related('monitoring').
                prefetch_related('task_set').
                order_by('monitoring').all())

class InstitutionCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateMessageMixin,
                            UserFormKwargsMixin, CreateView):
    model = Institution
    form_class = InstitutionForm
    permission_required = "institutions.add_institution"
    raise_exception = True
    redirect_unauthenticated_users = True

class InstitutionUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UserFormKwargsMixin,
                            UpdateMessageMixin, FormValidMessageMixin, UpdateView):
    model = Institution
    form_class = InstitutionForm
    permission_required = "institutions.change_institution"
    raise_exception = True
    redirect_unauthenticated_users = True

class InstitutionDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageMixin,
                            UpdateMessageMixin, DeleteView):
    model = Institution
    success_url = reverse_lazy('institutions:list')
    permission_required = "institutions.delete_institution"
    raise_exception = True
    redirect_unauthenticated_users = True

class InstitutionAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Institution.objects
        if self.q:
            qs = qs.filter(name__icontains=self.q)
        return qs.all()

class TagAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Tag.objects.annotate(institution_count=Count('institution'))
        if self.q:
            qs = qs.filter(name__icontains=self.q)
        return qs.order_by('-institution_count').all()

    def get_result_label(self, result):
        return "%s (%d)" % (six.text_type(result), result.institution_count)
<|endoftext|>


<commit_before>def eazy_photoz(root, force=False, object_only=True):

    import os
    import eazy
    import numpy as np
    from grizli import utils

    if (os.path.exists('{0}.eazypy.self.npy'.format(root))) & (not force):
        self = np.load('{0}.eazypy.self.npy'.format(root))[0]
        zout = utils.read_catalog('{0}.eazypy.zout.fits'.format(root))
        cat = utils.read_catalog('{0}_phot_apcorr.fits'.format(root))
        return self, cat, zout

    trans = {'f105w':202, 'f110w':241, 'f125w':203, 'f140w':204, 'f160w':205, 'f435w':233, 'f606w':236, 'f814w':239}

    cat = utils.read_catalog('{0}_phot.fits'.format(root))
    filters = []
    for c in cat.meta:
        if c.endswith('_ZP'):
            filters.append(c.split('_ZP')[0].lower())

    # Total flux
    apcorr = {}
    for i in range(5):
        if 'flux_aper_{0}'.format(i) in cat.colnames:
            cat['apcorr_{0}'.format(i)] = cat['flux_auto']/cat['flux_aper_{0}'.format(i)]
            for f in filters:
                cat['{0}_corr_{1}'.format(f, i)] = cat['{0}_flux_aper_{1}'.format(f, i)]*cat['apcorr_{0}'.format(i)]
                cat['{0}_ecorr_{1}'.format(f, i)] = cat['{0}_fluxerr_aper_{1}'.format(f, i)]*cat['apcorr_{0}'.format(i)]

                bad = cat['{0}_mask_aper_{1}'.format(f, i)] > 0.2*np.percentile(cat['{0}_mask_aper_{1}'.format(f, i)], 95)
                cat['{0}_corr_{1}'.format(f, i)][bad] = -99
                cat['{0}_ecorr_{1}'.format(f, i)][bad] = -99

    cat.rename_column('number','id')
    cat['z_spec'] = cat['id']*0.-1
    cat.write('{0}_phot_apcorr.fits'.format(root), overwrite=True)

    # Translate
    ix = 0
    fp = open('zphot.translate','w')
    for f in filters:
        if f in trans:
            fp.write('{0}_corr_{1} F{2}\n'.format(f, ix, trans[f]))
            fp.write('{0}_ecorr_{1} E{2}\n'.format(f, ix, trans[f]))
    fp.close()

    params = {}
    params['CATALOG_FILE'] = '{0}_phot_apcorr.fits'.format(root)
    params['Z_STEP'] = 0.01
    params['MAIN_OUTPUT_FILE'] = '{0}.eazypy'.format(root)

    params['Z_MAX'] = 12
    params['MW_EBV'] = 0.
    params['PRIOR_ABZP'] = 23.9

    # Pick prior filter, starting from reddest
    for f in ['f435w', 'f606w', 'f814w', 'f105w', 'f110w', 'f125w', 'f140w', 'f160w'][::-1]:
        if f in filters:
            params['PRIOR_FILTER'] = trans[f]
            mag = 23.9-2.5*np.log10(cat['{0}_corr_{1}'.format(f, ix)])
            break
    #
    params['TEMPLATES_FILE'] = 'templates/fsps_full/tweak_fsps_QSF_12_v3.param'

    zpfile = None
    load_products = False

    eazy.symlink_eazy_inputs(path='/usr/local/share/python/eazy-py/eazy-photoz', path_is_env=False)

    self = eazy.photoz.PhotoZ(param_file=None, translate_file='zphot.translate', zeropoint_file=zpfile, params=params, load_prior=True, load_products=load_products)

    if object_only:
        return self

    idx = np.arange(self.NOBJ)

    #sample = (mag < 27) #& (self.cat['star_flag'] != 1)
    #sample |= (self.cat['z_spec'] > 0)
    sample = np.isfinite(mag)

    for iter in range(2):
        self.fit_parallel(idx[sample], n_proc=10)
        self.error_residuals()

    self.standard_output()
    zout = utils.read_catalog('{0}.eazypy.zout.fits'.format(root))

    np.save('{0}.eazypy.self.npy'.format(root), [self])

    return self, cat, zout

def show_from_ds9(ds9, self, zout, **kwargs):

    xy = np.cast[float](ds9.get('pan image').split())
    r = np.sqrt((self.cat['x_image']-xy[0])**2 + (self.cat['y_image']-xy[1])**2)

    ix = np.argmin(r)
    print('ID: {0}, r={1:.1f} pix'.format(self.cat['id'][ix], r[ix]))
    print('  z={0:.2f} logM={1:.2f}'.format(zout['z_phot'][ix], np.log10(zout['mass'][ix])))

    self.show_fit(self.cat['id'][ix], **kwargs)

class EazyPhot(object):
    def __init__(self, photoz, grizli_templates=None, zgrid=None):
        """
        photoz : `~eazypy.photoz.PhotoZ`
        """
        try:
            from .. import utils
        except:
            from grizli import utils

        not_obs_mask =  (photoz.fnu < -90) | (photoz.efnu < 0)

        self.zgrid = photoz.zgrid

        self.flam = photoz.fnu*photoz.to_flam*photoz.zp*photoz.ext_corr
        self.flam[not_obs_mask] = -99

        self.eflam = photoz.efnu*photoz.to_flam*photoz.zp**photoz.ext_corr
        self.eflam[not_obs_mask] = -99

        #self.rdcat = utils.GTable(photoz.cat['ra','dec'])
        self.ra_cat = photoz.cat['ra'].data
        self.dec_cat = photoz.cat['dec'].data

        self.filters = photoz.filters
        self.f_numbers = photoz.f_numbers
        self.param = photoz.param

        if grizli_templates is None:
            self.tempfilt = None
        else:
            self.tempfilt = self.initialize_templates(grizli_templates,
                                                      zgrid=zgrid)

    def initialize_templates(self, grizli_templates, zgrid=None):

        from eazy import templates as templates_module
        from eazy.photoz import TemplateGrid

        if zgrid is None:
            zgrid = self.zgrid

        template_list = [templates_module.Template(arrays=(grizli_templates[k].wave, grizli_templates[k].flux), name=k) for k in grizli_templates]

        tempfilt = TemplateGrid(zgrid, template_list, RES=self.param['FILTERS_RES'], f_numbers=self.f_numbers, add_igm=True, galactic_ebv=self.param['MW_EBV'], Eb=self.param['SCALE_2175_BUMP'])

        return tempfilt

    def get_phot_dict(self, ra, dec):
        """

        """
        from collections import OrderedDict
        try:
            from .. import utils
        except:
            from grizli import utils

        icat = utils.GTable()
        icat['ra'] = [ra]
        icat['dec'] = [dec]

        rdcat = utils.GTable()
        rdcat['ra'] = self.ra_cat
        rdcat['dec'] = self.dec_cat

        ix, dr = rdcat.match_to_catalog_sky(icat)

        phot = OrderedDict()
        phot['flam'] = self.flam[ix[0],:]*1.e-19
        phot['eflam'] = self.eflam[ix[0],:]*1.e-19
        phot['filters'] = self.filters
        phot['tempfilt'] = self.tempfilt
        return phot, ix[0], dr[0]

        <commit_msg>more filters for photoz<commit_after>def eazy_photoz(root, force=False, object_only=True):

    import os
    import eazy
    import numpy as np
    from grizli import utils

    if (os.path.exists('{0}.eazypy.self.npy'.format(root))) & (not force):
        self = np.load('{0}.eazypy.self.npy'.format(root))[0]
        zout = utils.read_catalog('{0}.eazypy.zout.fits'.format(root))
        cat = utils.read_catalog('{0}_phot_apcorr.fits'.format(root))
        return self, cat, zout

    trans = {'f105w':202, 'f110w':241, 'f125w':203, 'f140w':204, 'f160w':205, 'f435w':233, 'f606w':236, 'f814w':239, 'f702w':15, 'f555w':235}

    cat = utils.read_catalog('{0}_phot.fits'.format(root))
    filters = []
    for c in cat.meta:
        if c.endswith('_ZP'):
            filters.append(c.split('_ZP')[0].lower())

    # Total flux
    apcorr = {}
    for i in range(5):
        if 'flux_aper_{0}'.format(i) in cat.colnames:
            cat['apcorr_{0}'.format(i)] = cat['flux_auto']/cat['flux_aper_{0}'.format(i)]
            for f in filters:
                cat['{0}_corr_{1}'.format(f, i)] = cat['{0}_flux_aper_{1}'.format(f, i)]*cat['apcorr_{0}'.format(i)]
                cat['{0}_ecorr_{1}'.format(f, i)] = cat['{0}_fluxerr_aper_{1}'.format(f, i)]*cat['apcorr_{0}'.format(i)]

                bad = cat['{0}_mask_aper_{1}'.format(f, i)] > 0.2*np.percentile(cat['{0}_mask_aper_{1}'.format(f, i)], 95)
                cat['{0}_corr_{1}'.format(f, i)][bad] = -99
                cat['{0}_ecorr_{1}'.format(f, i)][bad] = -99

    cat.rename_column('number','id')
    cat['z_spec'] = cat['id']*0.-1
    cat.write('{0}_phot_apcorr.fits'.format(root), overwrite=True)

    # Translate
    ix = 0
    fp = open('zphot.translate','w')
    for f in filters:
        if f in trans:
            fp.write('{0}_corr_{1} F{2}\n'.format(f, ix, trans[f]))
            fp.write('{0}_ecorr_{1} E{2}\n'.format(f, ix, trans[f]))
    fp.close()

    params = {}
    params['CATALOG_FILE'] = '{0}_phot_apcorr.fits'.format(root)
    params['Z_STEP'] = 0.01
    params['MAIN_OUTPUT_FILE'] = '{0}.eazypy'.format(root)

    params['Z_MAX'] = 12
    params['MW_EBV'] = 0.
    params['PRIOR_ABZP'] = 23.9

    # Pick prior filter, starting from reddest
    for f in ['f435w', 'f606w', 'f814w', 'f105w', 'f110w', 'f125w', 'f140w', 'f160w'][::-1]:
        if f in filters:
            params['PRIOR_FILTER'] = trans[f]
            mag = 23.9-2.5*np.log10(cat['{0}_corr_{1}'.format(f, ix)])
            break
    #
    params['TEMPLATES_FILE'] = 'templates/fsps_full/tweak_fsps_QSF_12_v3.param'

    zpfile = None
    load_products = False

    eazy.symlink_eazy_inputs(path='/usr/local/share/python/eazy-py/eazy-photoz', path_is_env=False)

    self = eazy.photoz.PhotoZ(param_file=None, translate_file='zphot.translate', zeropoint_file=zpfile, params=params, load_prior=True, load_products=load_products)

    if object_only:
        return self

    idx = np.arange(self.NOBJ)

    #sample = (mag < 27) #& (self.cat['star_flag'] != 1)
    #sample |= (self.cat['z_spec'] > 0)
    sample = np.isfinite(mag)

    for iter in range(2):
        self.fit_parallel(idx[sample], n_proc=10)
        self.error_residuals()

    self.standard_output()
    zout = utils.read_catalog('{0}.eazypy.zout.fits'.format(root))

    np.save('{0}.eazypy.self.npy'.format(root), [self])

    return self, cat, zout

def show_from_ds9(ds9, self, zout, **kwargs):

    xy = np.cast[float](ds9.get('pan image').split())
    r = np.sqrt((self.cat['x_image']-xy[0])**2 + (self.cat['y_image']-xy[1])**2)

    ix = np.argmin(r)
    print('ID: {0}, r={1:.1f} pix'.format(self.cat['id'][ix], r[ix]))
    print('  z={0:.2f} logM={1:.2f}'.format(zout['z_phot'][ix], np.log10(zout['mass'][ix])))

    self.show_fit(self.cat['id'][ix], **kwargs)

class EazyPhot(object):
    def __init__(self, photoz, grizli_templates=None, zgrid=None):
        """
        photoz : `~eazypy.photoz.PhotoZ`
        """
        try:
            from .. import utils
        except:
            from grizli import utils

        not_obs_mask =  (photoz.fnu < -90) | (photoz.efnu < 0)

        self.zgrid = photoz.zgrid

        self.flam = photoz.fnu*photoz.to_flam*photoz.zp*photoz.ext_corr
        self.flam[not_obs_mask] = -99

        self.eflam = photoz.efnu*photoz.to_flam*photoz.zp**photoz.ext_corr
        self.eflam[not_obs_mask] = -99

        #self.rdcat = utils.GTable(photoz.cat['ra','dec'])
        self.ra_cat = photoz.cat['ra'].data
        self.dec_cat = photoz.cat['dec'].data

        self.filters = photoz.filters
        self.f_numbers = photoz.f_numbers
        self.param = photoz.param

        if grizli_templates is None:
            self.tempfilt = None
        else:
            self.tempfilt = self.initialize_templates(grizli_templates,
                                                      zgrid=zgrid)

    def initialize_templates(self, grizli_templates, zgrid=None):

        from eazy import templates as templates_module
        from eazy.photoz import TemplateGrid

        if zgrid is None:
            zgrid = self.zgrid

        template_list = [templates_module.Template(arrays=(grizli_templates[k].wave, grizli_templates[k].flux), name=k) for k in grizli_templates]

        tempfilt = TemplateGrid(zgrid, template_list, RES=self.param['FILTERS_RES'], f_numbers=self.f_numbers, add_igm=True, galactic_ebv=self.param['MW_EBV'], Eb=self.param['SCALE_2175_BUMP'])

        return tempfilt

    def get_phot_dict(self, ra, dec):
        """

        """
        from collections import OrderedDict
        try:
            from .. import utils
        except:
            from grizli import utils

        icat = utils.GTable()
        icat['ra'] = [ra]
        icat['dec'] = [dec]

        rdcat = utils.GTable()
        rdcat['ra'] = self.ra_cat
        rdcat['dec'] = self.dec_cat

        ix, dr = rdcat.match_to_catalog_sky(icat)

        phot = OrderedDict()
        phot['flam'] = self.flam[ix[0],:]*1.e-19
        phot['eflam'] = self.eflam[ix[0],:]*1.e-19
        phot['filters'] = self.filters
        phot['tempfilt'] = self.tempfilt
        return phot, ix[0], dr[0]

        <|endoftext|>


<commit_before># ----------------------------------------------------------------------
# Numenta Platform for Intelligent Computing (NuPIC)
# Copyright (C) 2015, Numenta, Inc.  Unless you have purchased from
# Numenta, Inc. a separate commercial license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero Public License version 3 as
# published by the Free Software Foundation.
#
# This program 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 Affero Public License for more details.
#
# You should have received a copy of the GNU Affero Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

import json
import numpy as np
import os
from pkg_resources import resource_stream

def getScalarMetricWithTimeOfDayAnomalyParams(metricData,
                                              tmImplementation,
                                              minVal=None,
                                              maxVal=None,
                                              minResolution=None):
  """
    Return a dict that can be used to create an anomaly model via OPF's
    ModelFactory.

    :param metricData: numpy array of metric data. Used to calculate minVal
      and maxVal if either is unspecified

    :param minVal: minimum value of metric. Used to set up encoders. If None
      will be derived from metricData.

    :param maxVal: maximum value of metric. Used to set up input encoders. If
      None will be derived from metricData

    :param minResolution: minimum resolution of metric. Used to set up
      encoders.  If None, will use default value of 0.001.

    :returns: a dict containing "modelConfig" and "inferenceArgs" top-level
      properties. The value of the "modelConfig" property is for passing to
      the OPF `ModelFactory.create()` method as the `modelConfig` parameter. The
      "inferenceArgs" property is for passing to the resulting model's
      `enableInference()` method as the inferenceArgs parameter. NOTE: the
      timestamp field corresponds to input "c0"; the predicted field corresponds
      to input "c1".
    :rtype: dict

    Example:
      from nupic.frameworks.opf.modelfactory import ModelFactory
      from nupic.frameworks.opf.common_models.cluster_params import (
        getScalarMetricWithTimeOfDayAnomalyParams)

      params = getScalarMetricWithTimeOfDayAnomalyParams(
        metricData=[0],
        minVal=0.0,
        maxVal=100.0)

      model = ModelFactory.create(modelConfig=params["modelConfig"])
      model.enableLearning()
      model.enableInference(params["inferenceArgs"])
  """
  # Default values
  if minResolution is None:
    minResolution = 0.001

  # Compute min and/or max from the data if not specified
  if minVal is None or maxVal is None:
    compMinVal, compMaxVal = _rangeGen(metricData)
    if minVal is None:
      minVal = compMinVal
    if maxVal is None:
      maxVal = compMaxVal

  # Handle the corner case where the incoming min and max are the same
  if minVal == maxVal:
    maxVal = minVal + 1

  # Load model parameters and update encoder params
  if (tmImplementation is "cpp"):
    paramFileRelativePath = os.path.join(
      "anomaly_params_random_encoder",
      "best_single_metric_anomaly_params_cpp.json")
  elif (tmImplementation is "tm_cpp"):
    paramFileRelativePath = os.path.join(
      "anomaly_params_random_encoder",
      "best_single_metric_anomaly_params_tm_cpp.json")
  with resource_stream(__name__, paramFileRelativePath) as infile:
    paramSet = json.load(infile)

  _fixupRandomEncoderParams(paramSet, minVal, maxVal, minResolution)

  return paramSet

def _rangeGen(data, std=1):
  """
  Return reasonable min/max values to use given the data.
  """
  dataStd = np.std(data)
  if dataStd == 0:
    dataStd = 1
  minval = np.min(data) -  std * dataStd
  maxval = np.max(data) +  std * dataStd
  return minval, maxval

def _fixupRandomEncoderParams(params, minVal, maxVal, minResolution):
  """
  Given model params, figure out the correct parameters for the
  RandomDistributed encoder. Modifies params in place.
  """
  encodersDict = (
    params["modelConfig"]["modelParams"]["sensorParams"]["encoders"]
  )

  for encoder in encodersDict.itervalues():
    if encoder is not None:
      if encoder["type"] == "RandomDistributedScalarEncoder":
        resolution = max(minResolution,
                         (maxVal - minVal) / encoder.pop("numBuckets")
                        )
        encodersDict["c1"]["resolution"] = resolution
<commit_msg>docstring and example added<commit_after># ----------------------------------------------------------------------
# Numenta Platform for Intelligent Computing (NuPIC)
# Copyright (C) 2015, Numenta, Inc.  Unless you have purchased from
# Numenta, Inc. a separate commercial license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero Public License version 3 as
# published by the Free Software Foundation.
#
# This program 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 Affero Public License for more details.
#
# You should have received a copy of the GNU Affero Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

import json
import numpy as np
import os
from pkg_resources import resource_stream

def getScalarMetricWithTimeOfDayAnomalyParams(metricData,
                                              tmImplementation,
                                              minVal=None,
                                              maxVal=None,
                                              minResolution=None):
  """
    Return a dict that can be used to create an anomaly model via OPF's
    ModelFactory.

    :param metricData: numpy array of metric data. Used to calculate minVal
      and maxVal if either is unspecified

    :param tmImplementation: string specifying type of temporal memory implementation.

    :param minVal: minimum value of metric. Used to set up encoders. If None
      will be derived from metricData.

    :param maxVal: maximum value of metric. Used to set up input encoders. If
      None will be derived from metricData

    :param minResolution: minimum resolution of metric. Used to set up
      encoders.  If None, will use default value of 0.001.

    :returns: a dict containing "modelConfig" and "inferenceArgs" top-level
      properties. The value of the "modelConfig" property is for passing to
      the OPF `ModelFactory.create()` method as the `modelConfig` parameter. The
      "inferenceArgs" property is for passing to the resulting model's
      `enableInference()` method as the inferenceArgs parameter. NOTE: the
      timestamp field corresponds to input "c0"; the predicted field corresponds
      to input "c1".
    :rtype: dict

    Example:
      from nupic.frameworks.opf.modelfactory import ModelFactory
      from nupic.frameworks.opf.common_models.cluster_params import (
        getScalarMetricWithTimeOfDayAnomalyParams)

      params = getScalarMetricWithTimeOfDayAnomalyParams(
        metricData=[0],
        tmImplementation="cpp",
        minVal=0.0,
        maxVal=100.0)

      model = ModelFactory.create(modelConfig=params["modelConfig"])
      model.enableLearning()
      model.enableInference(params["inferenceArgs"])
  """
  # Default values
  if minResolution is None:
    minResolution = 0.001

  # Compute min and/or max from the data if not specified
  if minVal is None or maxVal is None:
    compMinVal, compMaxVal = _rangeGen(metricData)
    if minVal is None:
      minVal = compMinVal
    if maxVal is None:
      maxVal = compMaxVal

  # Handle the corner case where the incoming min and max are the same
  if minVal == maxVal:
    maxVal = minVal + 1

  # Load model parameters and update encoder params
  if (tmImplementation is "cpp"):
    paramFileRelativePath = os.path.join(
      "anomaly_params_random_encoder",
      "best_single_metric_anomaly_params_cpp.json")
  elif (tmImplementation is "tm_cpp"):
    paramFileRelativePath = os.path.join(
      "anomaly_params_random_encoder",
      "best_single_metric_anomaly_params_tm_cpp.json")
  with resource_stream(__name__, paramFileRelativePath) as infile:
    paramSet = json.load(infile)

  _fixupRandomEncoderParams(paramSet, minVal, maxVal, minResolution)

  return paramSet

def _rangeGen(data, std=1):
  """
  Return reasonable min/max values to use given the data.
  """
  dataStd = np.std(data)
  if dataStd == 0:
    dataStd = 1
  minval = np.min(data) -  std * dataStd
  maxval = np.max(data) +  std * dataStd
  return minval, maxval

def _fixupRandomEncoderParams(params, minVal, maxVal, minResolution):
  """
  Given model params, figure out the correct parameters for the
  RandomDistributed encoder. Modifies params in place.
  """
  encodersDict = (
    params["modelConfig"]["modelParams"]["sensorParams"]["encoders"]
  )

  for encoder in encodersDict.itervalues():
    if encoder is not None:
      if encoder["type"] == "RandomDistributedScalarEncoder":
        resolution = max(minResolution,
                         (maxVal - minVal) / encoder.pop("numBuckets")
                        )
        encodersDict["c1"]["resolution"] = resolution
<|endoftext|>