<commit_before># -*- coding: utf-8 -*-
from django import template
register = template.Library()
from greeking.placekittens import get_url
from greeking.lorem_ipsum import words, paragraphs
from greeking.jabberwocky import get_grafs, get_html as get_jabberwocky_html
from greeking.pangrams import get_pangram, get_html as get_pangram_html
from greeking import quotables

class PlaceKittenNode(template.Node):
    def __init__(self, width=200, height=200, color=True):
        self.width = width
        self.height = height
        self.color = color

    def render(self, context):
        return '' % get_url(self.width, self.height, color=self.color)

def placekitten(parser, token):
    """
    Creates an image of a random kitten at the provided width and height.

    Usage format:

        {% placekitten [width] [height] [gray] %}

    Example usage:

        Color image at 250 wide and 400 high
        {% placekitten 250 400 %}

        Grayscale image 100 wide and 100 high
        {% placekitten 100 100 gray %}

    """
    bits = list(token.split_contents())
    if len(bits) > 4 or len(bits) < 3:
        raise template.TemplateSyntaxError("Incorrect format")
    tagname, width, height = bits[:3]
    if len(bits) == 4:
        if bits[3] == 'gray':
            color = False
        else:
            raise template.TemplateSyntaxError("Incorrect format")
    else:
        color = True
    return PlaceKittenNode(width=width, height=height, color=color)
placekitten = register.tag(placekitten)

class CommentListNode(template.Node):
    def render(self, context):
        context['comment_list'] = quotables.get_comment_list()
        return ''

def greek_comment_list(parser, token):
    """
    Allows a template-level call of filler comments.

    Example usage:
    {% greek_comment_list as comment_list %}
    {% for comment in comment_list %}

{{ comment.comment }}

{{ comment.user_name }}

{{ comment.submit_date|date:"F j, Y" }}

{{ comment.user_email }}

{{ comment.user_url }}

    {% endfor %}
    """
    return CommentListNode()
greek_comment_list = register.tag(greek_comment_list)

class LoremNode(template.Node):
    def __init__(self, count, method, common):
        self.count, self.method, self.common = count, method, common

    def render(self, context):
        try:
            count = int(self.count.resolve(context))
        except (ValueError, TypeError):
            count = 1
        if self.method == 'w':
            return words(count, common=self.common)
        else:
            paras = paragraphs(count, common=self.common)
        if self.method == 'p':
            paras = ['
%s
' % p for p in paras]
        return u'\n\n'.join(paras)

#@register.tag
def lorem(parser, token):
    """
    Creates random Latin text useful for providing test data in templates.

    Usage format::

        {% lorem [count] [method] [random] %}

    ``count`` is a number (or variable) containing the number of paragraphs or
    words to generate (default is 1).

    ``method`` is either ``w`` for words, ``p`` for HTML paragraphs, ``b`` for
    plain-text paragraph blocks (default is ``b``).

    ``random`` is the word ``random``, which if given, does not use the common
    paragraph (starting "Lorem ipsum dolor sit amet, consectetuer...").

    Examples:
        * ``{% lorem %}`` will output the common "lorem ipsum" paragraph
        * ``{% lorem 3 p %}`` will output the common "lorem ipsum" paragraph
          and two random paragraphs each wrapped in HTML ``
`` tags
        * ``{% lorem 2 w random %}`` will output two random latin words
    """
    bits = list(token.split_contents())
    tagname = bits[0]
    # Random bit
    common = bits[-1] != 'random'
    if not common:
        bits.pop()
    # Method bit
    if bits[-1] in ('w', 'p', 'b'):
        method = bits.pop()
    else:
        method = 'b'
    # Count bit
    if len(bits) > 1:
        count = bits.pop()
    else:
        count = '1'
    count = parser.compile_filter(count)
    if len(bits) != 1:
        raise template.TemplateSyntaxError("Incorrect format for %r tag" % tagname)
    return LoremNode(count, method, common)
lorem = register.tag(lorem)

class PangramNode(template.Node):
    def __init__(self, language):
        self.language = language

    def render(self, context):
        try:
            pangram = get_pangram(self.language)
        except KeyError:
            raise template.TemplateSyntaxError("Could not find a pangram for %r abbreviation" % self.language)
        return get_pangram_html(pangram)

#@register.tag
def pangram(parser, token):
    """
    Prints a pangram in the specified language.

    A pangram is a phrase that includes every letter of an alphabet.

    Default is English. For a full list of available languages, refer to pangrams.py

    Usage format::

        {% pangram [language] %}

    ``language`` is the two-letter abbreviation the desired language.

    Examples:
        * ``{% pangram %}`` will output the default English pangram.
        * ``{% pangram fr %}`` will output a French pangram.
    """
    bits = list(token.split_contents())
    tagname = bits[0]
    # Count bit
    if len(bits) > 1:
        language = bits[1]
    else:
        language = 'en'
    if len(bits) > 2:
        raise template.TemplateSyntaxError("Incorrect format for %r tag" % tagname)
    return PangramNode(language)
pangram = register.tag(pangram)

class JabberwockyNode(template.Node):
    def __init__(self, count):
        self.count = count

    def render(self, context):
        try:
            count = int(self.count)
        except (ValueError, TypeError):
            count = None
        return get_jabberwocky_html(get_grafs(count))

#@register.tag
def jabberwocky(parser, token):
    """
    Prints paragraphs from Lewis Caroll's poem Jabberwocky for greeking in templates.

    Usage format::

        {% jabberwocky [count] %}

    ``count`` is a number (or variable) containing the number of paragraphs or
    words to generate (default is 7, which prints the entire poem).

    Examples:
        * ``{% jabberwocky %}`` will output the common "lorem ipsum" paragraph
        * ``{% jabberwocky 3 %}`` will output three paragraphs from the poem
    """
    bits = list(token.split_contents())
    tagname = bits[0]
    # Count bit
    if len(bits) > 1:
        count = bits[1]
    else:
        count = None
    if len(bits) > 2:
        raise template.TemplateSyntaxError("Incorrect format for %r tag" % tagname)
    return JabberwockyNode(count)
jabberwocky = register.tag(jabberwocky)
<commit_msg>Cruft<commit_after># -*- coding: utf-8 -*-
from django import template
register = template.Library()
from greeking.placekittens import get_url
from greeking.lorem_ipsum import words, paragraphs
from greeking.jabberwocky import get_grafs, get_html as get_jabberwocky_html
from greeking.pangrams import get_pangram, get_html as get_pangram_html
from greeking import quotables

class PlaceKittenNode(template.Node):
    def __init__(self, width=200, height=200, color=True):
        self.width = width
        self.height = height
        self.color = color

    def render(self, context):
        return '' % get_url(self.width, self.height, color=self.color)

def placekitten(parser, token):
    """
    Creates an image of a random kitten at the provided width and height.

    Usage format:

        {% placekitten [width] [height] [gray] %}

    Example usage:

        Color image at 250 wide and 400 high
        {% placekitten 250 400 %}

        Grayscale image 100 wide and 100 high
        {% placekitten 100 100 gray %}

    """
    bits = list(token.split_contents())
    if len(bits) > 4 or len(bits) < 3:
        raise template.TemplateSyntaxError("Incorrect format")
    tagname, width, height = bits[:3]
    if len(bits) == 4:
        if bits[3] == 'gray':
            color = False
        else:
            raise template.TemplateSyntaxError("Incorrect format")
    else:
        color = True
    return PlaceKittenNode(width=width, height=height, color=color)
placekitten = register.tag(placekitten)

class CommentListNode(template.Node):
    def render(self, context):
        context['comment_list'] = quotables.get_comment_list()
        return ''

def greek_comment_list(parser, token):
    """
    Allows a template-level call of filler comments.

    Example usage:
    {% greek_comment_list as comment_list %}
    {% for comment in comment_list %}

{{ comment.comment }}

{{ comment.user_name }}

{{ comment.submit_date|date:"F j, Y" }}

{{ comment.user_email }}

{{ comment.user_url }}

    {% endfor %}
    """
    return CommentListNode()
greek_comment_list = register.tag(greek_comment_list)

class LoremNode(template.Node):
    def __init__(self, count, method, common):
        self.count, self.method, self.common = count, method, common

    def render(self, context):
        try:
            count = int(self.count.resolve(context))
        except (ValueError, TypeError):
            count = 1
        if self.method == 'w':
            return words(count, common=self.common)
        else:
            paras = paragraphs(count, common=self.common)
        if self.method == 'p':
            paras = ['
%s
' % p for p in paras]
        return u'\n\n'.join(paras)

def lorem(parser, token):
    """
    Creates random Latin text useful for providing test data in templates.

    Usage format::

        {% lorem [count] [method] [random] %}

    ``count`` is a number (or variable) containing the number of paragraphs or
    words to generate (default is 1).

    ``method`` is either ``w`` for words, ``p`` for HTML paragraphs, ``b`` for
    plain-text paragraph blocks (default is ``b``).

    ``random`` is the word ``random``, which if given, does not use the common
    paragraph (starting "Lorem ipsum dolor sit amet, consectetuer...").

    Examples:
        * ``{% lorem %}`` will output the common "lorem ipsum" paragraph
        * ``{% lorem 3 p %}`` will output the common "lorem ipsum" paragraph
          and two random paragraphs each wrapped in HTML ``
`` tags
        * ``{% lorem 2 w random %}`` will output two random latin words
    """
    bits = list(token.split_contents())
    tagname = bits[0]
    # Random bit
    common = bits[-1] != 'random'
    if not common:
        bits.pop()
    # Method bit
    if bits[-1] in ('w', 'p', 'b'):
        method = bits.pop()
    else:
        method = 'b'
    # Count bit
    if len(bits) > 1:
        count = bits.pop()
    else:
        count = '1'
    count = parser.compile_filter(count)
    if len(bits) != 1:
        raise template.TemplateSyntaxError("Incorrect format for %r tag" % tagname)
    return LoremNode(count, method, common)
lorem = register.tag(lorem)

class PangramNode(template.Node):
    def __init__(self, language):
        self.language = language

    def render(self, context):
        try:
            pangram = get_pangram(self.language)
        except KeyError:
            raise template.TemplateSyntaxError("Could not find a pangram for %r abbreviation" % self.language)
        return get_pangram_html(pangram)

#@register.tag
def pangram(parser, token):
    """
    Prints a pangram in the specified language.

    A pangram is a phrase that includes every letter of an alphabet.

    Default is English. For a full list of available languages, refer to pangrams.py

    Usage format::

        {% pangram [language] %}

    ``language`` is the two-letter abbreviation the desired language.

    Examples:
        * ``{% pangram %}`` will output the default English pangram.
        * ``{% pangram fr %}`` will output a French pangram.
    """
    bits = list(token.split_contents())
    tagname = bits[0]
    # Count bit
    if len(bits) > 1:
        language = bits[1]
    else:
        language = 'en'
    if len(bits) > 2:
        raise template.TemplateSyntaxError("Incorrect format for %r tag" % tagname)
    return PangramNode(language)
pangram = register.tag(pangram)

class JabberwockyNode(template.Node):
    def __init__(self, count):
        self.count = count

    def render(self, context):
        try:
            count = int(self.count)
        except (ValueError, TypeError):
            count = None
        return get_jabberwocky_html(get_grafs(count))

def jabberwocky(parser, token):
    """
    Prints paragraphs from Lewis Caroll's poem Jabberwocky for greeking in templates.

    Usage format::

        {% jabberwocky [count] %}

    ``count`` is a number (or variable) containing the number of paragraphs or
    words to generate (default is 7, which prints the entire poem).

    Examples:
        * ``{% jabberwocky %}`` will output the common "lorem ipsum" paragraph
        * ``{% jabberwocky 3 %}`` will output three paragraphs from the poem
    """
    bits = list(token.split_contents())
    tagname = bits[0]
    # Count bit
    if len(bits) > 1:
        count = bits[1]
    else:
        count = None
    if len(bits) > 2:
        raise template.TemplateSyntaxError("Incorrect format for %r tag" % tagname)
    return JabberwockyNode(count)
jabberwocky = register.tag(jabberwocky)
<|endoftext|>


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

__all__ = ['doi_to_wos', 'query', 'single']

from xml.etree import ElementTree as _ET
from xml.dom import minidom as _minidom
import re as _re

def single(wosclient, wos_query, xml_query=None, count=5, offset=1):
    """Perform a single Web of Science query and then XML query the results."""
    result = wosclient.search(wos_query, count, offset)
    xml = _re.sub(' xmlns="[^"]+"', '', result.records, count=1)
    if xml_query:
        xml = _ET.fromstring(xml)
        return [el.text for el in xml.findall(xml_query)]
    else:
        return _minidom.parseString(xml).toprettyxml()

def query(wosclient, wos_query, xml_query=None, count=5, offset=1, limit=100):
    """Query Web of Science and XML query results with multiple requests."""
    results = [single(wosclient, wos_query, xml_query, min(limit, count-x+1), x)
               for x in range(offset, count+1, limit)]
    if xml_query:
        return [el for res in results for el in res]
    else:
        pattern = _re.compile(r'.*?<records>|</records>.*', _re.DOTALL)
        return ('<?xml version="1.0" ?>\n<records>' +
                '\n'.join(pattern.sub('', res) for res in results) +
                '</records>')

def doi_to_wos(wosclient, doi):
    """Convert DOI to WOS identifier."""
    results = query(wosclient, 'DO=%s' % doi, './REC/UID', count=1)
    return results[0].lstrip('WOS:') if results else None
<commit_msg>encode xml result to utf-8 to accomodate more characters<commit_after>#!/usr/bin/env python

__all__ = ['doi_to_wos', 'query', 'single']

from xml.etree import ElementTree as _ET
from xml.dom import minidom as _minidom
import re as _re

def single(wosclient, wos_query, xml_query=None, count=5, offset=1):
    """Perform a single Web of Science query and then XML query the results."""
    result = wosclient.search(wos_query, count, offset)
    xml = _re.sub(' xmlns="[^"]+"', '', result.records, count=1).encode('utf-8')
    if xml_query:
        xml = _ET.fromstring(xml)
        return [el.text for el in xml.findall(xml_query)]
    else:
        return _minidom.parseString(xml).toprettyxml()

def query(wosclient, wos_query, xml_query=None, count=5, offset=1, limit=100):
    """Query Web of Science and XML query results with multiple requests."""
    results = [single(wosclient, wos_query, xml_query, min(limit, count-x+1), x)
               for x in range(offset, count+1, limit)]
    if xml_query:
        return [el for res in results for el in res]
    else:
        pattern = _re.compile(r'.*?<records>|</records>.*', _re.DOTALL)
        return ('<?xml version="1.0" ?>\n<records>' +
                '\n'.join(pattern.sub('', res) for res in results) +
                '</records>')

def doi_to_wos(wosclient, doi):
    """Convert DOI to WOS identifier."""
    results = query(wosclient, 'DO=%s' % doi, './REC/UID', count=1)
    return results[0].lstrip('WOS:') if results else None
<|endoftext|>


<commit_before># Copyright (c) 2011, Jimmy Cao
# All rights reserved.

# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from oyoyo.parse import parse_nick
import botconfig

def generate(fdict, **kwargs):
    """Generates a decorator generator.  Always use this"""
    def cmd(*s, raw_nick=False, admin_only=False, owner_only=False):
        def dec(f):
            def innerf(*args):
                largs = list(args)
                if largs[1]:
                    cloak = parse_nick(largs[1])[3]
                else:
                    cloak = ""
                if not raw_nick and largs[1]:
                    largs[1] = parse_nick(largs[1])[0]  # username
                    #if largs[1].startswith("#"):
                if owner_only:
                    if cloak and cloak == botconfig.OWNER:
                        return f(*largs)
                    elif cloak:
                        largs[0].notice(largs[1], "You are not the owner.")
                        return
                if admin_only:
                    if cloak and cloak in botconfig.ADMINS:
                        return f(*largs)
                    elif cloak:
                        largs[0].notice(largs[1], "You are not an admin.")
                        return
                return f(*largs)
            for x in s:
                if x not in fdict.keys():
                    fdict[x] = []
                else:
                    for fn in fdict[x]:
                        if (fn.owner_only != owner_only or
                            fn.admin_only != admin_only):
                            raise Exception("Command: "+x+" has non-matching protection levels!")
                fdict[x].append(innerf)
            innerf.owner_only = owner_only
            innerf.raw_nick = raw_nick
            innerf.admin_only = admin_only
            innerf.__doc__ = f.__doc__
            return innerf

        return dec

    return lambda *args, **kwarargs: cmd(*args, **kwarargs) if kwarargs else cmd(*args, **kwargs)<commit_msg>now the owner doesn't have to be added in the list of admins<commit_after># Copyright (c) 2011, Jimmy Cao
# All rights reserved.

# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from oyoyo.parse import parse_nick
import botconfig

def generate(fdict, **kwargs):
    """Generates a decorator generator.  Always use this"""
    def cmd(*s, raw_nick=False, admin_only=False, owner_only=False):
        def dec(f):
            def innerf(*args):
                largs = list(args)
                if largs[1]:
                    cloak = parse_nick(largs[1])[3]
                else:
                    cloak = ""
                if not raw_nick and largs[1]:
                    largs[1] = parse_nick(largs[1])[0]  # username
                    #if largs[1].startswith("#"):
                if owner_only:
                    if cloak and cloak == botconfig.OWNER:
                        return f(*largs)
                    elif cloak:
                        largs[0].notice(largs[1], "You are not the owner.")
                        return
                if admin_only:
                    if cloak and (cloak in botconfig.ADMINS or cloak == botconfig.OWNER):
                        return f(*largs)
                    elif cloak:
                        largs[0].notice(largs[1], "You are not an admin.")
                        return
                return f(*largs)
            for x in s:
                if x not in fdict.keys():
                    fdict[x] = []
                else:
                    for fn in fdict[x]:
                        if (fn.owner_only != owner_only or
                            fn.admin_only != admin_only):
                            raise Exception("Command: "+x+" has non-matching protection levels!")
                fdict[x].append(innerf)
            innerf.owner_only = owner_only
            innerf.raw_nick = raw_nick
            innerf.admin_only = admin_only
            innerf.__doc__ = f.__doc__
            return innerf

        return dec

    return lambda *args, **kwarargs: cmd(*args, **kwarargs) if kwarargs else cmd(*args, **kwargs)<|endoftext|>


<commit_before>import hashlib
import os
import shutil
import subprocess

import wand.image
import yaml

from datetime import datetime
from photoshell.image import Image
from photoshell.raw.cr2 import Cr2

raw_formats = ['.CR2']

class Library(object):

    def __init__(self, config):
        super(Library, self).__init__()

        self.library_path = config['library']
        self.import_path = os.path.join(self.library_path,
                                        config['import_path'])
        self.cache_path = os.path.join(self.library_path, '.cache')
        if not os.path.exists(self.library_path):
            os.makedirs(self.library_path)
        if not os.path.exists(self.cache_path):
            os.makedirs(self.cache_path)
        self.sidecars = []

        for root, _, files in os.walk(self.library_path):
            for file_name in files:
                if os.path.splitext(file_name)[1] == '.yaml':
                    file_path = os.path.join(root, file_name)
                    with open(file_path, 'r') as sidecar:
                        self.sidecars.append(yaml.load(sidecar))

    def all(self):
        return self.query(lambda image: True)

    def query(self, match):
        selection = Selection(self.library_path, match)
        for sidecar in self.sidecars:
            image = Image(sidecar['developed_path'])
            if match(image):
                selection.append(image)

        return selection

    def update(self, selection):
        current = selection.current()
        if current:
            image_path = current.image_path
        new_selection = self.query(selection.query)
        if current:
            new_selection.jump(image_path)
        return new_selection

    def import_photos(self, path, notify=None, imported=None,
                      copy_photos=True, delete_originals=False):
        file_list = []

        for root, _, files in os.walk(path):
            for file_name in files:
                if os.path.splitext(file_name)[1] in raw_formats:
                    file_path = os.path.join(root, file_name)
                    file_list.append(file_path)

        num_complete = 0

        for file_path in file_list:
            # TODO: skip if already imported

            if notify:
                notify(os.path.basename(file_path))

            file_hash = self.hash_file(file_path)
            file_ext = os.path.splitext(file_path)[1]

            if file_ext.lower() == ".cr2".lower():
                with Cr2(file_path) as i:
                    dt = i.ifd[0].find_entry('datetime').get_value()
            else:
                with wand.Image(filename=file_path) as i:
                    for key, value in i.metadata.items():
                        if key.startswith('exif:DateTime'):
                            dt = value
                            break

            dt = datetime.strptime(dt, "%Y:%m:%d %H:%M:%S")

            exists = False
            for sidecar in self.sidecars:
                if sidecar['hash'] == file_hash:
                    exists = True
                    break

            if not exists:
                if copy_photos:
                    original_filename = os.path.basename(
                        os.path.splitext(file_path)[0]
                    )
                    new_file_path = dt.strftime(self.import_path.format(
                        original_filename=original_filename,
                        file_hash=file_hash,
                    )) + file_ext
                    file_name = os.path.basename(new_file_path)
                    import_path = os.path.dirname(new_file_path)
                    if not os.path.exists(import_path):
                        os.makedirs(import_path)
                else:
                    new_file_path = file_path

                if file_path != new_file_path:
                    shutil.copyfile(file_path, new_file_path)
                    # TODO: Make sure the file copied w/o errors first?
                    if delete_originals:
                        os.unlink(file_path)

                # develop photo
                developed_name = '{file_hash}.{extension}'.format(
                    file_hash=file_hash,
                    extension='tiff',
                )
                developed_path = os.path.join(
                    self.cache_path,
                    developed_name,
                )

                if not os.path.isfile(developed_path):
                    # TODO: fail gracefully here (or even at startup)
                    blob = subprocess.check_output(
                        ['dcraw', '-c', '-e', new_file_path])

                    with wand.image.Image(blob=blob) as image:
                        with image.convert('jpeg') as developed:
                            developed.save(filename=developed_path)

                # create metadata
                meta_name = '{file_name}.{extension}'.format(
                    file_name=file_name,
                    extension='yaml',
                )

                meta_path = os.path.join(
                    import_path,
                    meta_name,
                )

                # TODO: rename these to be sidecar instead of meta
                if not os.path.isfile(meta_path):
                    metadata = {
                        "hash": file_hash,
                        "developed_path": developed_path,
                        "original_path": new_file_path,
                        "datetime": dt,
                    }

                    with open(meta_path, 'w+') as meta_file:
                        yaml.dump(
                            metadata, meta_file, default_flow_style=False)
                else:
                    with open(meta_path, 'r') as meta_file:
                        metadata = yaml.load(meta_file)

                self.sidecars.append(metadata)

            num_complete += 1

            if imported:
                imported(file_hash, num_complete / len(file_list))

    # TODO: this shouldn't live on self
    def hash_file(self, file_path):
        hash = hashlib.sha1()

        # TODO: probably block size or something, although if your machine
        # can't hold the whole file in memory you probably can't edit it
        # anyway.
        with open(file_path, 'rb') as f:
            data = f.read()

        hash.update(data)
        return hash.hexdigest()

class Selection(object):

    def __init__(self, library_path, query):
        super(Selection, self).__init__()

        self.library_path = library_path
        self.query = query
        self.images = []
        self.current_image = 0

    def append(self, image):
        self.images.append(image)

    def current(self):
        if len(self.images):
            return self.images[self.current_image]
        else:
            return None

    def next(self):
        l = len(self.images)
        if l > 1:
            self.current_image = (self.current_image + 1) % l
        return self.current()

    def prev(self):
        l = len(self.images)
        if l > 1:
            self.current_image = (self.current_image - 1) % l
        return self.current()

    def jump(self, image_path):
        # TODO: there is an idiomatic way to do this
        for i in range(len(self.images)):
            if self.images[i].image_path == image_path:
                self.current_image = i
                break

        return self.current()

    def each(self):
        for image in self.images:
            yield image
<commit_msg>Fix in-place import<commit_after>import hashlib
import os
import shutil
import subprocess

import wand.image
import yaml

from datetime import datetime
from photoshell.image import Image
from photoshell.raw.cr2 import Cr2

raw_formats = ['.CR2']

class Library(object):

    def __init__(self, config):
        super(Library, self).__init__()

        self.library_path = config['library']
        self.import_path = os.path.join(self.library_path,
                                        config['import_path'])
        self.cache_path = os.path.join(self.library_path, '.cache')
        if not os.path.exists(self.library_path):
            os.makedirs(self.library_path)
        if not os.path.exists(self.cache_path):
            os.makedirs(self.cache_path)
        self.sidecars = []

        for root, _, files in os.walk(self.library_path):
            for file_name in files:
                if os.path.splitext(file_name)[1] == '.yaml':
                    file_path = os.path.join(root, file_name)
                    with open(file_path, 'r') as sidecar:
                        self.sidecars.append(yaml.load(sidecar))

    def all(self):
        return self.query(lambda image: True)

    def query(self, match):
        selection = Selection(self.library_path, match)
        for sidecar in self.sidecars:
            image = Image(sidecar['developed_path'])
            if match(image):
                selection.append(image)

        return selection

    def update(self, selection):
        current = selection.current()
        if current:
            image_path = current.image_path
        new_selection = self.query(selection.query)
        if current:
            new_selection.jump(image_path)
        return new_selection

    def import_photos(self, path, notify=None, imported=None,
                      copy_photos=True, delete_originals=False):
        file_list = []

        for root, _, files in os.walk(path):
            for file_name in files:
                if os.path.splitext(file_name)[1] in raw_formats:
                    file_path = os.path.join(root, file_name)
                    file_list.append(file_path)

        num_complete = 0

        for file_path in file_list:
            # TODO: skip if already imported

            if notify:
                notify(os.path.basename(file_path))

            file_hash = self.hash_file(file_path)
            file_ext = os.path.splitext(file_path)[1]

            if file_ext.lower() == ".cr2".lower():
                with Cr2(file_path) as i:
                    dt = i.ifd[0].find_entry('datetime').get_value()
            else:
                with wand.Image(filename=file_path) as i:
                    for key, value in i.metadata.items():
                        if key.startswith('exif:DateTime'):
                            dt = value
                            break

            dt = datetime.strptime(dt, "%Y:%m:%d %H:%M:%S")

            exists = False
            for sidecar in self.sidecars:
                if sidecar['hash'] == file_hash:
                    exists = True
                    break

            if not exists:
                if copy_photos:
                    original_filename = os.path.basename(
                        os.path.splitext(file_path)[0]
                    )
                    new_file_path = dt.strftime(self.import_path.format(
                        original_filename=original_filename,
                        file_hash=file_hash,
                    )) + file_ext
                else:
                    new_file_path = file_path

                file_name = os.path.basename(new_file_path)
                import_path = os.path.dirname(new_file_path)
                if not os.path.exists(import_path):
                    os.makedirs(import_path)

                if file_path != new_file_path:
                    shutil.copyfile(file_path, new_file_path)
                    # TODO: Make sure the file copied w/o errors first?
                    if delete_originals:
                        os.unlink(file_path)

                # develop photo
                developed_name = '{file_hash}.{extension}'.format(
                    file_hash=file_hash,
                    extension='tiff',
                )
                developed_path = os.path.join(
                    self.cache_path,
                    developed_name,
                )

                if not os.path.isfile(developed_path):
                    # TODO: fail gracefully here (or even at startup)
                    blob = subprocess.check_output(
                        ['dcraw', '-c', '-e', new_file_path])

                    with wand.image.Image(blob=blob) as image:
                        with image.convert('jpeg') as developed:
                            developed.save(filename=developed_path)

                # create metadata
                meta_name = '{file_name}.{extension}'.format(
                    file_name=file_name,
                    extension='yaml',
                )

                meta_path = os.path.join(
                    import_path,
                    meta_name,
                )

                # TODO: rename these to be sidecar instead of meta
                if not os.path.isfile(meta_path):
                    metadata = {
                        "hash": file_hash,
                        "developed_path": developed_path,
                        "original_path": new_file_path,
                        "datetime": dt,
                    }

                    with open(meta_path, 'w+') as meta_file:
                        yaml.dump(
                            metadata, meta_file, default_flow_style=False)
                else:
                    with open(meta_path, 'r') as meta_file:
                        metadata = yaml.load(meta_file)

                self.sidecars.append(metadata)

            num_complete += 1

            if imported:
                imported(file_hash, num_complete / len(file_list))

    # TODO: this shouldn't live on self
    def hash_file(self, file_path):
        hash = hashlib.sha1()

        # TODO: probably block size or something, although if your machine
        # can't hold the whole file in memory you probably can't edit it
        # anyway.
        with open(file_path, 'rb') as f:
            data = f.read()

        hash.update(data)
        return hash.hexdigest()

class Selection(object):

    def __init__(self, library_path, query):
        super(Selection, self).__init__()

        self.library_path = library_path
        self.query = query
        self.images = []
        self.current_image = 0

    def append(self, image):
        self.images.append(image)

    def current(self):
        if len(self.images):
            return self.images[self.current_image]
        else:
            return None

    def next(self):
        l = len(self.images)
        if l > 1:
            self.current_image = (self.current_image + 1) % l
        return self.current()

    def prev(self):
        l = len(self.images)
        if l > 1:
            self.current_image = (self.current_image - 1) % l
        return self.current()

    def jump(self, image_path):
        # TODO: there is an idiomatic way to do this
        for i in range(len(self.images)):
            if self.images[i].image_path == image_path:
                self.current_image = i
                break

        return self.current()

    def each(self):
        for image in self.images:
            yield image
<|endoftext|>


<commit_before># Licensed under a 3-clause BSD style license - see LICENSE.rst

"""Functions for performing aperture photometry on 2-D arrays."""

import math

import numpy as np

__all__ = ["aperture_circular", "aperture_elliptical", "annulus_circular",
           "annulus_elliptical"]

def aperture_circular(arr, xc, yc, r, bkgerr=None, gain=1., mask=None,
                      maskmode='mirror', subpixels=5):
    """Return sum of array enclosed in circular aperture(s)."""
    pass

def aperture_elliptical(arr, xc, yc, a, b, theta, bkgerr=None, gain=1.,
                        mask=None, maskmode='mirror', subpixels=5):
    """Return sum of array enclosed in elliptical aperture(s)."""
    pass

def annulus_circular(arr, xc, yc, r_in, r_out, bkgerr=None, gain=1.,
                     mask=None, maskmode='mirror', subpixels=5):
    """Return sum of array enclosed in circular annuli."""
    pass

def annulus_elliptical(arr, xc, yc, a_in, a_out, b_out, theta,
                       bkgerr=None, gain=1., mask=None, maskmode='mirror',
                       subpixels=5):
    """Sum flux within elliptical annuli.

    .. warning::
        Some options not yet implemented.

    Parameters
    ----------
    arr : array_like
        The 2-d array on which to perform photometry.
    xc, yc : float or list_like
        The x and y coordinates of the aperture center(s) in pixels.
        If dimension is greater than 1, the array is flattened. The
        coordinates are zero-indexed, meaning that ``(x, y) = (0., 0.)``
        corresponds to the center of the lower-left array element.
        The value of arr[0, 0] is taken as the value over the range
        ``-0.5 < x <= 0.5``, ``-0.5 < y <= 0.5``. The array is thus
        defined over the range ``-0.5 < x <= arr.shape[1] - 0.5``,
        ``-0.5 < y <= arr.shape[0] - 0.5``.
    a_in, a_out, b_out, theta : array_like
        Parameters specifying the elliptical annuli: respectively the
        inner semimajor axis in pixels, the outer semimajor axis in
        pixels, the outer semiminor axis in pixels, and the
        position angle of the semimajor axis in radians. ``a_in``,
        ``a_out``, ``b_out``, ``theta`` must be broadcastable to the
        same shape. Different shapes are treated as follows:

        scalar
            The same parameters are applied to all objects in ``xc``, ``yc``
        1-d
            The parameters must be the same length as ``xc``, ``yc``
            and each object gets its own annulus parameters.
        2-d
            axis=1 corresponds to the object and must be either the same
            length as ``xc``, ``yc``, or 1. axis=0 specifies multiple
            annulus parameters for each object.
    bkgerr : float or array_like, optional
        Background (sky) error, interpreted as Gaussian 1-sigma uncertainty
        per pixel. If ``None`` fluxerr is not returned.
    gain : float or array_like, optional
        Ratio of Counts (or electrons) per flux (units of the array),
        for the purpose of calculating Poisson error.
        ARRAY_LIKE NOT YET IMPLEMENTED.
    mask : array_like (bool), optional
        Mask to apply to the data.  NOT YET IMPLEMENTED.
    maskmode : {'mirror', 'zero'}, optional
        What to do with masked pixels.  NOT YET IMPLEMENTED.
    subpixels : int, optional
        Resample pixels by this factor (in each dimension) when summing
        flux in apertures. That is, each pixel is divided into
        ``subpixels ** 2`` subpixels.

    Returns
    -------
    flux : float or `~numpy.ndarray`
        Enclosed flux in annuli(s).
    fluxerr : float or `~numpy.ndarray`
        Uncertainty in flux values.
    """

    # Check input array type and dimension.
    arr = np.asarray(arr)
    if np.iscomplexobj(arr):
        raise TypeError('Complex type not supported')
    if arr.ndim != 2:
        raise ValueError('{0}-d array not supported. '
                         'Only 2-d arrays supported.'.format(arr.ndim))

    # Check background error shape, convert to variance for internal use.
    scalar_bkgvar = False
    bkgvar = bkgerr
    if bkgvar is not None:
        if np.isscalar(bkgvar):
            scalar_bkgvar = True
            bkgvar = bkgerr ** 2
        else:
            bkgvar = np.asarray(bkgvar) ** 2
            if bkgvar.shape != arr.shape:
                raise ValueError('must match shape of input array')

    # Test dimension of object coordinate arrays so we know how to return.
    scalar_input = False
    if np.isscalar(xc) and np.isscalar(yc):
        scalar_input = True

    # Make xc and yc into 1-d arrays. For clarity to user, do not broadcast.
    xc = np.atleast_1d(xc)
    yc = np.atleast_1d(yc)
    if xc.ndim > 1 or yc.ndim > 1:
        raise ValueError('xc and yc can be at most 1-d.')
    if xc.shape != yc.shape:
        raise ValueError('Lengths of xc and yc must match')

    # Broadcast annulus parameters to same 2-d shape.
    a_in, a_out, b_out, theta = np.broadcast_arrays(a_in, a_out, b_out, theta)
    a_in, a_out, b_out, theta = np.atleast_2d(a_in, a_out, b_out, theta)

    # Check that trailing dimension is 1 or matches number of objects.
    if a_in.shape[1] not in [1, xc.shape[0]]:
        raise ValueError('Trailing dimension of annulus parameters'
                         ' must be 1 or match number of objects')

    # Expand trailing dimension of annulus parameters to match objects.
    a_in, a_out, b_out, theta, xc2d = \
        np.broadcast_arrays(a_in, a_out, b_out, theta, xc)

    # Check that a_out > a_in for all annuli.
    if not (a_out > a_in).all():
        raise ValueError('a_out must be greater than a_in.')

    # Check that 'subpixels' is an int and is 1 or greater.
    subpixels = int(subpixels)
    if subpixels < 1:
        raise ValueError('an integer greater than 0 is required')
    subpixelsize = 1. / subpixels  # Size of subpixels in original pixels.
    subpixelarea = subpixelsize * subpixelsize

    # Initialize arrays to return.
    flux = np.zeros(a_out.shape, dtype=np.float)
    if bkgvar is not None:
        fluxvar = np.zeros(a_out.shape, dtype=np.float)

    for i in range(xc.shape[0]):  # Loop over objects.
        # Maximum extent of all ellipses for this object.
        # We can't easily do a tight bounding box because there is no
        # guarantee that the multiple ellipses for each object are
        # concentric.
        r = max(a_out[:, i].max(), b_out[:, i].max())
        x_min = int(xc[i] - r + 0.5)
        x_max = int(xc[i] + r + 1.5)
        y_min = int(yc[i] - r + 0.5)
        y_max = int(yc[i] + r + 1.5)

        # Check that at least part of the sub-array is in the image.
        if (x_min >= arr.shape[1] or x_max <= 0 or
            y_min >= arr.shape[0] or y_max <= 0):
            # TODO: flag all the apertures for this object
            continue

        # Limit sub-array to be within the image.
        x_min = max(x_min, 0)
        x_max = min(x_max, arr.shape[1])
        y_min = max(y_min, 0)
        y_max = min(y_max, arr.shape[0])

        # Get the sub-array of the image.
        subarr = arr[y_min:y_max, x_min:x_max]
        if subpixels > 1:
            subarr = np.repeat(np.repeat(subarr, subpixels, axis=0),
                               subpixels, axis=1)

        # Get the subarray of background variance.
        if bkgvar is not None and not scalar_bkgvar:
            subbkgvar = bkgvar[y_min:y_max, x_min:x_max]
            if subpixels > 1:
                subbkgvar = np.repeat(np.repeat(subbkgvar, subpixels, axis=0),
                                      subpixels, axis=1)

        # Get the distance of each pixel in the sub-array from the object
        # center, in units of the original image pixels.
        x_dist = np.arange(x_min - 0.5 + subpixelsize / 2. - xc[i],
                           x_max - 0.5 + subpixelsize / 2. - xc[i],
                           subpixelsize)
        y_dist = np.arange(y_min - 0.5 + subpixelsize / 2. - yc[i],
                           y_max - 0.5 + subpixelsize / 2. - yc[i],
                           subpixelsize)
        xx, yy = np.meshgrid(x_dist, y_dist)

        # Loop over annuli for this object.
        for j in range(a_out.shape[0]):
            costheta = math.cos(theta[j, i])
            sintheta = math.sin(theta[j, i])
            b_in = (b_out[j, i] * a_in[j, i] /
                    a_out[j, i])
            idx_out = (((xx * costheta - yy * sintheta) /
                        a_out[j, i]) ** 2 +
                       ((yy * costheta + xx * sintheta) /
                        b_out[j, i]) ** 2) < 1.
            idx_in = (((xx * costheta - yy * sintheta) /
                        a_in[j, i]) ** 2 +
                       ((yy * costheta + xx * sintheta) / b_in) ** 2) > 1.
            idx = (idx_out & idx_in)

            # TODO: speed this up for apertures (non-annuli)??
            #       (don't compute idx_in; idx = idx_out)
            # TODO: speed up further for circles??
            #       (simpler formulae for idx)

            flux[j, i] = subarr[idx].sum() * subpixelarea
            if bkgvar is not None:
                if scalar_bkgvar:
                    fluxvar[j, i] = (
                        bkgvar * math.pi *
                        (a_out[j, i] * b_out[j, i] - a_in[j, i] * b_in) +
                        flux[j, i] / gain)
                else:
                    fluxvar[j, i] = (bkgvar[idx].sum() * subpixelarea +
                                     flux[j, i] / gain)

    if bkgvar is not None:
        fluxerr = np.sqrt(fluxvar)  # Convert back to error.

    # If the input xc, yc were scalars (single object), AND we only had
    # a single aperture, then and only then can we return a scalar:
    if scalar_input and flux.shape == (1, 1):
        if bkgvar is None:
            return flux[0, 0]
        else:
            return flux[0, 0], fluxerr[0, 0]

    # Otherwise, if we only had a single aperture for each object,
    # then we can return a 1-d array.
    elif flux.shape[0] == 1:
        if bkgvar is None:
            return np.reshape(flux, flux.shape[1])
        else:
            return (np.reshape(flux, flux.shape[1]),
                    np.reshape(fluxerr, flux.shape[1]))

    # Otherwise we have multiple apertures, so we need to return a 2-d array.
    else:
        if bkgvar is None:
            return flux
        else:
            return flux, fluxerr
<commit_msg>implement ApertureSet metaclass<commit_after># Licensed under a 3-clause BSD style license - see LICENSE.rst

"""Functions for performing aperture photometry on 2-D arrays."""

import math
import abc

import numpy as np

__all__ = ["aperture_circular", "aperture_elliptical", "annulus_circular",
           "annulus_elliptical"]

class ApertureSet(object):
    """An abstract base class for sets of arbitrary apertures.

    Sets of apertures allow for multiple objects and multiple
    apertures associated with each object.

    """

    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def encloses(i, j, xx, yy):
        """Return a bool array representing which elements of an array
        are in an aperture.

        Inputs
        ------
        i : int
            Index of object
        j : int
            Index of aperture
        xx : 2-d `~numpy.ndarray`
            x coordinates of array elements
        yy : 2-d `~numpy.ndarray`
            y coordinates of array elements
        """
        return

    @abc.abstractmethod
    def extent(i):
        """Return extent of all apertures for a single object."""
        return

    @abc.abstractproperty
    def nobjects():
        """Number of objects"""
        return

    @abc.abstractproperty
    def napertures():
        """Number of apertures per object."""
        return

    @abc.abstractproperty
    def shape():
        """(napertures, nobjects)"""
        return

    def area(i, j):
        """Return area of aperture or None if too difficult to calculate."""
        return None

class EllipticalAnnulusSet(Aperture):
    """A class for representing elliptical annuli"""

    def __init__(self, xc, yc, a_in, a_out, b_out, theta):

        # Make xc and yc into 1-d arrays. For clarity to user,
        # do not broadcast.
        self.xc = np.atleast_1d(xc)
        self.yc = np.atleast_1d(yc)
        if self.xc.ndim > 1 or self.yc.ndim > 1:
            raise ValueError('xc and yc can be at most 1-d.')
        if self.xc.shape != self.yc.shape:
            raise ValueError('Lengths of xc and yc must match')

        # Broadcast annulus parameters to same 2-d shape.
        a_in, a_out, b_out, theta = np.broadcast_arrays(a_in, a_out,
                                                        b_out, theta)
        a_in, a_out, b_out, theta = np.atleast_2d(a_in, a_out, b_out, theta)

        # Check that trailing dimension is 1 or matches number of objects.
        if a_in.shape[1] not in [1, xc.shape[0]]:
            raise ValueError('Trailing dimension of annulus parameters'
                             ' must be 1 or match number of objects')

        # Expand trailing dimension of annulus parameters to match objects.
        self.a_in, self.a_out, self.b_out, self.theta, xc2d = \
            np.broadcast_arrays(a_in, a_out, b_out, theta, xc)

        # Check that a_out > a_in for all annuli.
        if not (self.a_out > self.a_in).all():
            raise ValueError('a_out must be greater than a_in.')

    def extent(self, i):
        # We can't easily do a tight bounding box because there is no
        # guarantee that the multiple ellipses for each object are
        # concentric.
        r = max(self.a_out[:, i].max(), self.b_out[:, i].max())
        x_min = self.xc[i] - r
        x_max = self.xc[i] + r
        y_min = self.yc[i] - r
        y_max = self.yc[i] + r
        return x_min, x_max, y_min, y_max

    def encloses(self, i, j, xx, yy):
        costheta = math.cos(self.theta[j, i])
        sintheta = math.sin(self.theta[j, i])
        b_in = (self.b_out[j, i] * self.a_in[j, i] /
                self.a_out[j, i])
        xx_off = xx - self.xc[j, i]
        yy_off = yy - self.yc[j, i]
        numerator1 = (xx_off * costheta - yy_off * sintheta)
        numerator2 = (yy_off * costheta + xx_off * sintheta)
        idx_out = ((numerator1 / self.a_out[j, i]) ** 2 +
                   (numerator2 / self.b_out[j, i]) ** 2) < 1.
        idx_in = ((numerator1 / self.a_in[j, i]) ** 2 +
                  (numerator2 / b_in) ** 2) > 1.
        return (idx_out & idx_in)

    def nobjects():
        return self.xc.shape[0]

    def napertures():
        return self.a_in.shape[0]

    def shape():
        return self.a_in.shape

    def area(i, j):
        return math.pi * (self.a_out[j, i] * self.b_out[j, i] -
                          self.a_in[j, i] ** 2 * self.b_out[j, i] /
                          self.a_out[j, i])

def aperture_photometry(arr, apertureset, bkgerr=None, gain=1., mask=None,
                        subpixels=5)
    """Sum flux within aperture(s).

    .. warning::
        Some options not yet implemented.

    Parameters
    ----------
    arr : array_like
        The 2-d array on which to perform photometry.
    apertureset: an ApertureSet-derived object
        Contains the definition of the apertures.
    bkgerr : float or array_like, optional
        Background (sky) error, interpreted as Gaussian 1-sigma uncertainty
        per pixel. If ``None`` fluxerr is not returned.
    gain : float or array_like, optional
        Ratio of Counts (or electrons) per flux (units of the array),
        for the purpose of calculating Poisson error.
        ARRAY_LIKE NOT YET IMPLEMENTED.
    mask : array_like (bool), optional
        Mask to apply to the data.  NOT YET IMPLEMENTED.
    subpixels : int, optional
        Resample pixels by this factor (in each dimension) when summing
        flux in apertures. That is, each pixel is divided into
        ``subpixels ** 2`` subpixels.

    Returns
    -------
    flux : float or `~numpy.ndarray`
        Enclosed flux in annuli(s).
    fluxerr : float or `~numpy.ndarray`
        Uncertainty in flux values.
    """

    # Check input array type and dimension.
    arr = np.asarray(arr)
    if np.iscomplexobj(arr):
        raise TypeError('Complex type not supported')
    if arr.ndim != 2:
        raise ValueError('{0}-d array not supported. '
                         'Only 2-d arrays supported.'.format(arr.ndim))

    # Check background error shape, convert to variance for internal use.
    scalar_bkgvar = False
    bkgvar = bkgerr
    if bkgvar is not None:
        if np.isscalar(bkgvar):
            scalar_bkgvar = True
            bkgvar = bkgerr ** 2
        else:
            bkgvar = np.asarray(bkgvar) ** 2
            if bkgvar.shape != arr.shape:
                raise ValueError('bkgerr must match shape of input array')

    # Check that 'subpixels' is an int and is 1 or greater.
    subpixels = int(subpixels)
    if subpixels < 1:
        raise ValueError('an integer greater than 0 is required')
    subpixelsize = 1. / subpixels  # Size of subpixels in original pixels.
    subpixelarea = subpixelsize * subpixelsize

    # Test dimension of object coordinate arrays so we know how to return.
    scalar_input = False
    if np.isscalar(xc) and np.isscalar(yc):
        scalar_input = True

    # Initialize arrays to return.
    flux = np.zeros(apertureset.shape, dtype=np.float)
    if bkgvar is not None:
        fluxvar = np.zeros(apertureset.shape, dtype=np.float)

    for i in range(apertureset.nobjects):  # Loop over objects.
        # Maximum extent of all ellipses for this object.
        # We can't easily do a tight bounding box because there is no
        # guarantee that the multiple ellipses for each object are
        # concentric.
        x_min, x_max, y_min, y_max = apertureset.extent(i)
        x_min = int(x_min + 0.5)
        x_max = int(x_max + 1.5)
        y_min = int(y_min + 0.5)
        y_max = int(y_max + 1.5)

        # Check that at least part of the sub-array is in the image.
        if (x_min >= arr.shape[1] or x_max <= 0 or
            y_min >= arr.shape[0] or y_max <= 0):
            # TODO: flag all the apertures for this object
            continue

        # Limit sub-array to be within the image.
        x_min = max(x_min, 0)
        x_max = min(x_max, arr.shape[1])
        y_min = max(y_min, 0)
        y_max = min(y_max, arr.shape[0])

        # Get the sub-array of the image.
        subarr = arr[y_min:y_max, x_min:x_max]
        if subpixels > 1:
            subarr = np.repeat(np.repeat(subarr, subpixels, axis=0),
                               subpixels, axis=1)

        # Get the sub-array of background variance.
        if bkgvar is not None and not scalar_bkgvar:
            subbkgvar = bkgvar[y_min:y_max, x_min:x_max]
            if subpixels > 1:
                subbkgvar = np.repeat(np.repeat(subbkgvar, subpixels, axis=0),
                                      subpixels, axis=1)

        # Get the distance of each pixel in the sub-array from the object
        # center, in units of the original image pixels.
        x_vals = np.arange(x_min - 0.5 + subpixelsize / 2.,
                           x_max - 0.5 + subpixelsize / 2.,
                           subpixelsize)
        y_vals = np.arange(y_min - 0.5 + subpixelsize / 2.,
                           y_max - 0.5 + subpixelsize / 2.,
                           subpixelsize)
        xx, yy = np.meshgrid(x_vals, y_vals)

        # Loop over annuli for this object.
        for j in range(apertureset.napertures):
            idx = apertureset.encloses(i, j, xx, yy)
            flux[j, i] = subarr[idx].sum() * subpixelarea
            if bkgvar is not None:
                if scalar_bkgvar:
                    if apertureset.area(i, j) is not None:
                        fluxvar[j, i] = (bkgvar * apertureset.area(i, j) +
                                         flux[j, i] / gain)
                    else:
                        fluxvar[j, i] = (bkgvar * idx.sum() * subpixelarea +
                                         flux[j, i] / gain)
                else:
                    fluxvar[j, i] = (bkgvar[idx].sum() * subpixelarea +
                                     flux[j, i] / gain)

    if bkgvar is not None:
        fluxerr = np.sqrt(fluxvar)  # Convert back to error.

    # If the input xc, yc were scalars (single object), AND we only had
    # a single aperture, then and only then can we return a scalar:
    if scalar_input and flux.shape == (1, 1):
        if bkgvar is None:
            return flux[0, 0]
        else:
            return flux[0, 0], fluxerr[0, 0]

    # Otherwise, if we only had a single aperture for each object,
    # then we can return a 1-d array.
    elif flux.shape[0] == 1:
        if bkgvar is None:
            return np.reshape(flux, flux.shape[1])
        else:
            return (np.reshape(flux, flux.shape[1]),
                    np.reshape(fluxerr, flux.shape[1]))

    # Otherwise we have multiple apertures, so we need to return a 2-d array.
    else:
        if bkgvar is None:
            return flux
        else:
            return flux, fluxerr

def aperture_circular(arr, xc, yc, r, bkgerr=None, gain=1., mask=None,
                      maskmode='mirror', subpixels=5):
    """Return sum of array enclosed in circular aperture(s)."""
    pass

def aperture_elliptical(arr, xc, yc, a, b, theta, bkgerr=None, gain=1.,
                        mask=None, maskmode='mirror', subpixels=5):
    """Return sum of array enclosed in elliptical aperture(s)."""
    pass

def annulus_circular(arr, xc, yc, r_in, r_out, bkgerr=None, gain=1.,
                     mask=None, maskmode='mirror', subpixels=5):
    """Return sum of array enclosed in circular annuli."""
    pass

def annulus_elliptical(arr, xc, yc, a_in, a_out, b_out, theta,
                       bkgerr=None, gain=1., mask=None, maskmode='mirror',
                       subpixels=5):
    """Sum flux within elliptical annuli.

    .. warning::
        Some options not yet implemented.

    Parameters
    ----------
    arr : array_like
        The 2-d array on which to perform photometry.
    xc, yc : float or list_like
        The x and y coordinates of the aperture center(s) in pixels.
        If dimension is greater than 1, the array is flattened. The
        coordinates are zero-indexed, meaning that ``(x, y) = (0., 0.)``
        corresponds to the center of the lower-left array element.
        The value of arr[0, 0] is taken as the value over the range
        ``-0.5 < x <= 0.5``, ``-0.5 < y <= 0.5``. The array is thus
        defined over the range ``-0.5 < x <= arr.shape[1] - 0.5``,
        ``-0.5 < y <= arr.shape[0] - 0.5``.
    a_in, a_out, b_out, theta : array_like
        Parameters specifying the elliptical annuli: respectively the
        inner semimajor axis in pixels, the outer semimajor axis in
        pixels, the outer semiminor axis in pixels, and the
        position angle of the semimajor axis in radians. ``a_in``,
        ``a_out``, ``b_out``, ``theta`` must be broadcastable to the
        same shape. Different shapes are treated as follows:

        scalar
            The same parameters are applied to all objects in ``xc``, ``yc``
        1-d
            The parameters must be the same length as ``xc``, ``yc``
            and each object gets its own annulus parameters.
        2-d
            axis=1 corresponds to the object and must be either the same
            length as ``xc``, ``yc``, or 1. axis=0 specifies multiple
            annulus parameters for each object.
    bkgerr : float or array_like, optional
        Background (sky) error, interpreted as Gaussian 1-sigma uncertainty
        per pixel. If ``None`` fluxerr is not returned.
    gain : float or array_like, optional
        Ratio of Counts (or electrons) per flux (units of the array),
        for the purpose of calculating Poisson error.
        ARRAY_LIKE NOT YET IMPLEMENTED.
    mask : array_like (bool), optional
        Mask to apply to the data.  NOT YET IMPLEMENTED.
    maskmode : {'mirror', 'zero'}, optional
        What to do with masked pixels.  NOT YET IMPLEMENTED.
    subpixels : int, optional
        Resample pixels by this factor (in each dimension) when summing
        flux in apertures. That is, each pixel is divided into
        ``subpixels ** 2`` subpixels.

    Returns
    -------
    flux : float or `~numpy.ndarray`
        Enclosed flux in annuli(s).
    fluxerr : float or `~numpy.ndarray`
        Uncertainty in flux values.
    """
    pass
<|endoftext|>


<commit_before>import numpy as np

from astropy import units as u
from astropy.wcs import WCSSUB_CELESTIAL
from astropy.coordinates import SkyCoord, UnitSphericalRepresentation

def skycoord_to_pixel(coords, wcs):
    """
    Convert a set of SkyCoord coordinates into pixel coordinates.

    This function assumes that the coordinates are celestial.

    Parameters
    ----------
    coords : `~astropy.coordinates.SkyCoord`
        The coordinates to convert
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use

    Returns
    -------
    x, y : `~numpy.ndarray`
        The x and y pixel coordinates corresponding to the input coordinates
    """

    # TODO this should be simplified once wcs_world2pix() supports
    # SkyCoord objects as input

    # TODO: remove local wcs_to_celestial_frame once Astropy 1.0 is out
    try:
        from astropy.wcs.utils import wcs_to_celestial_frame
    except ImportError:  # Astropy < 1.0
        from .extern.wcs_utils import wcs_to_celestial_frame

    # Keep only the celestial part of the axes, also re-orders lon/lat
    wcs = wcs.sub([WCSSUB_CELESTIAL])

    if wcs.naxis != 2:
        raise ValueError("WCS should contain celestial component")

    # Check which frame the WCS uses
    frame = wcs_to_celestial_frame(wcs)

    # Check what unit the WCS needs
    xw_unit = u.Unit(wcs.wcs.cunit[0])
    yw_unit = u.Unit(wcs.wcs.cunit[1])

    # Convert positions to frame
    coords = coords.transform_to(frame)

    # Extract longitude and latitude
    lon = coords.spherical.lon.to(xw_unit)
    lat = coords.spherical.lat.to(yw_unit)

    # Convert to pixel coordinates
    xp, yp = wcs.wcs_world2pix(lon, lat, 0)

    return xp, yp

def skycoord_to_pixel_scale_angle(coords, wcs):
    """
    Convert a set of SkyCoord coordinates into pixel coordinates, pixel
    scales, and position angles.

    Parameters
    ----------
    coords : `~astropy.coordinates.SkyCoord`
        The coordinates to convert
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use

    Returns
    -------
    x, y : `~numpy.ndarray`
        The x and y pixel coordinates corresponding to the input coordinates
    scale : `~astropy.units.Quantity`
        The pixel scale at each location, in degrees/pixel
    angle : `~astropy.units.Quantity`
        The position angle of the celestial coordinate system in pixel space.
    """

    # Convert to pixel coordinates
    x, y = skycoord_to_pixel(coords, wcs)

    # We take a point directly 'above' (in latitude) the position requested
    # and convert it to pixel coordinates, then we use that to figure out the
    # scale and position angle of the coordinate system at the location of
    # the points.

    # Find the coordinates as a representation object
    r_old = coords.represent_as('unitspherical')

    # Add a a small perturbation in the latitude direction (since longitude
    # is more difficult because it is not directly an angle).
    dlat = 1 * u.arcsec
    r_new = UnitSphericalRepresentation(r_old.lon, r_old.lat + dlat)
    coords_offset = coords.realize_frame(r_new)

    # Find pixel coordinates of offset coordinates
    x_offset, y_offset = skycoord_to_pixel(coords_offset, wcs)

    # Find vector
    dx = x_offset - x
    dy = y_offset - y

    # Find the length of the vector
    scale = np.hypot(dx, dy) * u.pixel / dlat

    # Find the position angle
    angle = np.arctan2(dy, dx) * u.radian

    return x, y, scale, angle

def assert_angle_or_pixel(name, q):
    if isinstance(q, u.Quantity):
        if q.unit.physical_type == 'angle' or q.unit is u.pixel:
            pass
        else:
            raise ValueError("{0} should have angular or pixel units".format(name))
    else:
        raise TypeError("{0} should be a Quantity instance".format(name))

def assert_angle(name, q):
    if isinstance(q, u.Quantity):
        if q.unit.physical_type == 'angle':
            pass
        else:
            raise ValueError("{0} should have angular units".format(name))
    else:
        raise TypeError("{0} should be a Quantity instance".format(name))

def pixel_to_skycoord(xp, yp, wcs, origin=0, mode='all'):
    """
    Convert a set of pixel coordinates into a SkyCoord coordinate.

    Parameters
    ----------
    xp, yp : float or `numpy.ndarray`
        The coordinates to convert.
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use.
    origin : int
        Whether to return 0 or 1-based pixel coordinates.
    mode : 'all' or 'wcs'
        Whether to do the transformation including distortions (``'all'``) or
        only including only the core WCS transformation (``'wcs'``).

    Returns
    -------
    coords : `~astropy.coordinates.SkyCoord`
        The celestial coordinates
    """

    if wcs.has_distortion and wcs.naxis != 2:
        raise ValueError("Can only handle WCS with distortions for 2-dimensional WCS")

    # Keep only the celestial part of the axes, also re-orders lon/lat
    wcs = wcs.sub([WCSSUB_CELESTIAL])

    if wcs.naxis != 2:
        raise ValueError("WCS should contain celestial component")

    # TODO: remove local wcs_to_celestial_frame once Astropy 1.0 is out
    try:
        from astropy.wcs.utils import wcs_to_celestial_frame
    except ImportError:  # Astropy < 1.0
        from .extern.wcs_utils import wcs_to_celestial_frame

    # Check which frame the WCS uses
    frame = wcs_to_celestial_frame(wcs)

    # Check what unit the WCS gives
    lon_unit = u.Unit(wcs.wcs.cunit[0])
    lat_unit = u.Unit(wcs.wcs.cunit[1])

    # Convert pixel coordinates to celestial coordinates
    if mode == 'all':
        lon, lat = wcs.all_pix2world(xp, yp, origin)
    elif mode == 'wcs':
        lon, lat = wcs.wcs_pix2world(xp, yp, origin)
    else:
        raise ValueError("mode should be either 'all' or 'wcs'")

    # Add units to longitude/latitude
    lon = lon * lon_unit
    lat = lat * lat_unit

    # Create SkyCoord object
    data = UnitSphericalRepresentation(lon=lon, lat=lat)
    coords = SkyCoord(frame.realize_frame(data))

    return coords
<commit_msg>wcs.has_distortion workaround<commit_after>import numpy as np

from astropy import units as u
from astropy.wcs import WCSSUB_CELESTIAL
from astropy.coordinates import SkyCoord, UnitSphericalRepresentation

def skycoord_to_pixel(coords, wcs):
    """
    Convert a set of SkyCoord coordinates into pixel coordinates.

    This function assumes that the coordinates are celestial.

    Parameters
    ----------
    coords : `~astropy.coordinates.SkyCoord`
        The coordinates to convert
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use

    Returns
    -------
    x, y : `~numpy.ndarray`
        The x and y pixel coordinates corresponding to the input coordinates
    """

    # TODO this should be simplified once wcs_world2pix() supports
    # SkyCoord objects as input

    # TODO: remove local wcs_to_celestial_frame once Astropy 1.0 is out
    try:
        from astropy.wcs.utils import wcs_to_celestial_frame
    except ImportError:  # Astropy < 1.0
        from .extern.wcs_utils import wcs_to_celestial_frame

    # Keep only the celestial part of the axes, also re-orders lon/lat
    wcs = wcs.sub([WCSSUB_CELESTIAL])

    if wcs.naxis != 2:
        raise ValueError("WCS should contain celestial component")

    # Check which frame the WCS uses
    frame = wcs_to_celestial_frame(wcs)

    # Check what unit the WCS needs
    xw_unit = u.Unit(wcs.wcs.cunit[0])
    yw_unit = u.Unit(wcs.wcs.cunit[1])

    # Convert positions to frame
    coords = coords.transform_to(frame)

    # Extract longitude and latitude
    lon = coords.spherical.lon.to(xw_unit)
    lat = coords.spherical.lat.to(yw_unit)

    # Convert to pixel coordinates
    xp, yp = wcs.wcs_world2pix(lon, lat, 0)

    return xp, yp

def skycoord_to_pixel_scale_angle(coords, wcs):
    """
    Convert a set of SkyCoord coordinates into pixel coordinates, pixel
    scales, and position angles.

    Parameters
    ----------
    coords : `~astropy.coordinates.SkyCoord`
        The coordinates to convert
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use

    Returns
    -------
    x, y : `~numpy.ndarray`
        The x and y pixel coordinates corresponding to the input coordinates
    scale : `~astropy.units.Quantity`
        The pixel scale at each location, in degrees/pixel
    angle : `~astropy.units.Quantity`
        The position angle of the celestial coordinate system in pixel space.
    """

    # Convert to pixel coordinates
    x, y = skycoord_to_pixel(coords, wcs)

    # We take a point directly 'above' (in latitude) the position requested
    # and convert it to pixel coordinates, then we use that to figure out the
    # scale and position angle of the coordinate system at the location of
    # the points.

    # Find the coordinates as a representation object
    r_old = coords.represent_as('unitspherical')

    # Add a a small perturbation in the latitude direction (since longitude
    # is more difficult because it is not directly an angle).
    dlat = 1 * u.arcsec
    r_new = UnitSphericalRepresentation(r_old.lon, r_old.lat + dlat)
    coords_offset = coords.realize_frame(r_new)

    # Find pixel coordinates of offset coordinates
    x_offset, y_offset = skycoord_to_pixel(coords_offset, wcs)

    # Find vector
    dx = x_offset - x
    dy = y_offset - y

    # Find the length of the vector
    scale = np.hypot(dx, dy) * u.pixel / dlat

    # Find the position angle
    angle = np.arctan2(dy, dx) * u.radian

    return x, y, scale, angle

def assert_angle_or_pixel(name, q):
    if isinstance(q, u.Quantity):
        if q.unit.physical_type == 'angle' or q.unit is u.pixel:
            pass
        else:
            raise ValueError("{0} should have angular or pixel units".format(name))
    else:
        raise TypeError("{0} should be a Quantity instance".format(name))

def assert_angle(name, q):
    if isinstance(q, u.Quantity):
        if q.unit.physical_type == 'angle':
            pass
        else:
            raise ValueError("{0} should have angular units".format(name))
    else:
        raise TypeError("{0} should be a Quantity instance".format(name))

def pixel_to_skycoord(xp, yp, wcs, origin=0, mode='all'):
    """
    Convert a set of pixel coordinates into a SkyCoord coordinate.

    Parameters
    ----------
    xp, yp : float or `numpy.ndarray`
        The coordinates to convert.
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use.
    origin : int
        Whether to return 0 or 1-based pixel coordinates.
    mode : 'all' or 'wcs'
        Whether to do the transformation including distortions (``'all'``) or
        only including only the core WCS transformation (``'wcs'``).

    Returns
    -------
    coords : `~astropy.coordinates.SkyCoord`
        The celestial coordinates
    """

    # temporary workaround
    has_distortion = any(getattr(wcs, dist_attr) is not None
                         for dist_attr in ['cpdis1', 'cpdis2', 'det2im1', 'det2im2', 'sip'])

    if has_distortion and wcs.naxis != 2:
        raise ValueError("Can only handle WCS with distortions for 2-dimensional WCS")

    # Keep only the celestial part of the axes, also re-orders lon/lat
    wcs = wcs.sub([WCSSUB_CELESTIAL])

    if wcs.naxis != 2:
        raise ValueError("WCS should contain celestial component")

    # TODO: remove local wcs_to_celestial_frame once Astropy 1.0 is out
    try:
        from astropy.wcs.utils import wcs_to_celestial_frame
    except ImportError:  # Astropy < 1.0
        from .extern.wcs_utils import wcs_to_celestial_frame

    # Check which frame the WCS uses
    frame = wcs_to_celestial_frame(wcs)

    # Check what unit the WCS gives
    lon_unit = u.Unit(wcs.wcs.cunit[0])
    lat_unit = u.Unit(wcs.wcs.cunit[1])

    # Convert pixel coordinates to celestial coordinates
    if mode == 'all':
        lon, lat = wcs.all_pix2world(xp, yp, origin)
    elif mode == 'wcs':
        lon, lat = wcs.wcs_pix2world(xp, yp, origin)
    else:
        raise ValueError("mode should be either 'all' or 'wcs'")

    # Add units to longitude/latitude
    lon = lon * lon_unit
    lat = lat * lat_unit

    # Create SkyCoord object
    data = UnitSphericalRepresentation(lon=lon, lat=lat)
    coords = SkyCoord(frame.realize_frame(data))

    return coords
<|endoftext|>


<commit_before># Copyright (C) 2014 Linaro Limited
#
# Author: Milosz Wasilewski <milosz.wasilewski@linaro.org>
#
# This file is part of Testmanager.
#
# Testmanager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License version 3
# as published by the Free Software Foundation
#
# Testmanager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Testmanager.  If not, see <http://www.gnu.org/licenses/>.

import re
from django.db import models
from django.core.urlresolvers import reverse

class JenkinsService(models.Model):
    url = models.URLField()
    description = models.TextField(blank=True, null=True)

    def __unicode__(self):
        return str(self.url)

class JenkinsJob(models.Model):
    name = models.CharField(max_length=128)
    service = models.ForeignKey(JenkinsService)

    def __unicode__(self):
        return self.name

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

    def get_umbrella_builds(self):
        return self.builds.filter(is_umbrella=True).order_by("-timestamp")

    def get_last_build(self):
        last_umbrella_build = self.builds.filter(is_umbrella=True).order_by("-timestamp")
        if last_umbrella_build:
            return last_umbrella_build[0]
        last_build = self.builds.all().order_by("-timestamp")
        if last_build:
            return last_build[0]
        return None

    def is_last_test_result_green(self):
        last_build = self.get_last_build()
        if last_build:
            if not last_build.is_umbrella:
                for lavajob in last_build.lavajob_set.all():
                    if lavajob.has_results_missing():
                        return False
                    for lavajobresult in lavajob.lavajobresult_set.all():
                        result_count = lavajobresult.get_resultset_count_by_status()
                        if result_count and 'fail' in result_count.keys():
                            return False
                    #for key, value in lavajob.lavajobresult.get_resultset_count_by_status():
                    #    # todo: move status name to settings
                    #    if key == 'fail':
                    #        return False
            else:
                if not self.builds.filter(is_umbrella=False, number=last_build.number):
                    # Jenkins API returnet rubbish
                    return False
                for build in self.builds.filter(is_umbrella=False, number=last_build.number):
                    if not build.lavajob_set.all():
                        return False
                    for lavajob in build.lavajob_set.all():
                        if lavajob.has_results_missing():
                            return False
                        for lavajobresult in lavajob.lavajobresult_set.all():
                            result_count = lavajobresult.get_resultset_count_by_status()
                            if result_count and 'fail' in result_count.keys():
                                return False
        return True

class JenkinsBuildStatus(models.Model):
    name = models.CharField(max_length=16)

    def __unicode__(self):
        return self.name

class JenkinsBuild(models.Model):
    name = models.CharField(max_length=1024)
    job = models.ForeignKey(JenkinsJob, related_name="builds")
    umbrella_build = models.ForeignKey("self", blank=True, null=True)
    number = models.IntegerField()
    status = models.ForeignKey(JenkinsBuildStatus)
    is_umbrella = models.BooleanField(default=False)
    timestamp = models.DateTimeField(null=True, blank=True)

    def __unicode__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('jenkins_build_view', args=[str(self.job.name), str(self.number)])

    def get_hwpack_name(self):
        if self.is_umbrella:
            return None
        device_regexp = re.compile(ur'%s\s\xbb\s(?P<device_name>\w+)' % (self.job.name), re.UNICODE)
        hwpack_name_search = device_regexp.search(self.name)
        if hwpack_name_search:
            return hwpack_name_search.group('device_name')
        return None

class LavaJobStatus(models.Model):
    name = models.CharField(max_length=16)

    def __unicode__(self):
        return self.name

class LavaJob(models.Model):
    jenkins_build = models.ForeignKey(JenkinsBuild)
    number = models.IntegerField()
    status = models.ForeignKey(LavaJobStatus)
    test_definitions = models.ManyToManyField('testplanner.TestDefinition')
    device_type = models.ForeignKey('testplanner.Device')
    submit_time = models.DateTimeField(null=True, blank=True)
    start_time = models.DateTimeField(null=True, blank=True)

    def __unicode__(self):
        return "%s - LAVA %s" % (self.jenkins_build.job.name, self.number)

    def get_definitions_results(self):
        result_list = []
        for testdef in self.test_definitions.all():
            resultset = LavaJobResult.objects.filter(lava_job = self, test_definition = testdef)
            if resultset:
                result_list.append((testdef, resultset[0]))
            else:
                result_list.append((testdef, None))
        return result_list

    # TODO: move to helpers to be defined during import time, include field for failed tests
    def has_results_missing(self):
        for testdef in self.test_definitions.all():
            resultset = LavaJobResult.objects.filter(lava_job = self, test_definition = testdef)
            if not resultset:
                return True
        return False

    def get_absolute_url(self):
        return reverse('lava_job_view', args=[str(self.jenkins_build.job.name), str(self.jenkins_build.number), str(self.number)])

class LavaJobResultStatus(models.Model):
    name = models.CharField(max_length=8)

    def __unicode__(self):
        return self.name

class LavaJobResult(models.Model):
    lava_job = models.ForeignKey(LavaJob)
    test_definition = models.ForeignKey('testplanner.TestDefinition')
    test_revision = models.ForeignKey('testplanner.TestDefinitionRevision', blank=True, null=True)

    def __unicode__(self):
        return "%s result for %s" % (self.lava_job.__unicode__(), self.test_definition.__unicode__())

    def get_resultset_total(self):
        return self.lavajobtestresult_set.count()

    def get_resultset_count_by_status(self):
        status_count = {}
        # TODO: fix this, use aggregate to collect the numbers
        for testresult in self.lavajobtestresult_set.all():
            if testresult.status.name in status_count:
                status_count[testresult.status.name] += 1
            else:
                status_count[testresult.status.name] = 1
        #print status_count
        return status_count

class LavaJobTestResultUnit(models.Model):
    name = models.CharField(max_length=32)

    def __unicode__(self):
        return self.name

class LavaJobTestResult(models.Model):
    test_case_id = models.CharField(max_length=1024)
    lava_job_result = models.ForeignKey(LavaJobResult)
    status = models.ForeignKey(LavaJobResultStatus, blank=True, null=True) # there should be default of 'unknown'
    is_measurement = models.BooleanField(default=False)
    unit = models.ForeignKey(LavaJobTestResultUnit, blank=True, null=True)
    value = models.FloatField(blank=True, null=True)

    def __unicode__(self):
        return self.test_case_id
<commit_msg>whitespaces<commit_after># Copyright (C) 2014 Linaro Limited
#
# Author: Milosz Wasilewski <milosz.wasilewski@linaro.org>
#
# This file is part of Testmanager.
#
# Testmanager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License version 3
# as published by the Free Software Foundation
#
# Testmanager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Testmanager.  If not, see <http://www.gnu.org/licenses/>.

import re
from django.db import models
from django.core.urlresolvers import reverse

class JenkinsService(models.Model):
    url = models.URLField()
    description = models.TextField(blank=True, null=True)

    def __unicode__(self):
        return str(self.url)

class JenkinsJob(models.Model):
    name = models.CharField(max_length=128)
    service = models.ForeignKey(JenkinsService)

    def __unicode__(self):
        return self.name

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

    def get_umbrella_builds(self):
        return self.builds.filter(is_umbrella=True).order_by("-timestamp")

    def get_last_build(self):
        last_umbrella_build = self.builds.filter(is_umbrella=True).order_by("-timestamp")
        if last_umbrella_build:
            return last_umbrella_build[0]
        last_build = self.builds.all().order_by("-timestamp")
        if last_build:
            return last_build[0]
        return None

    def is_last_test_result_green(self):
        last_build = self.get_last_build()
        if last_build:
            if not last_build.is_umbrella:
                for lavajob in last_build.lavajob_set.all():
                    if lavajob.has_results_missing():
                        return False
                    for lavajobresult in lavajob.lavajobresult_set.all():
                        result_count = lavajobresult.get_resultset_count_by_status()
                        if result_count and 'fail' in result_count.keys():
                            return False
                    #for key, value in lavajob.lavajobresult.get_resultset_count_by_status():
                    #    # todo: move status name to settings
                    #    if key == 'fail':
                    #        return False
            else:
                if not self.builds.filter(is_umbrella=False, number=last_build.number):
                    # Jenkins API returnet rubbish
                    return False
                for build in self.builds.filter(is_umbrella=False, number=last_build.number):
                    if not build.lavajob_set.all():
                        return False
                    for lavajob in build.lavajob_set.all():
                        if lavajob.has_results_missing():
                            return False
                        for lavajobresult in lavajob.lavajobresult_set.all():
                            result_count = lavajobresult.get_resultset_count_by_status()
                            if result_count and 'fail' in result_count.keys():
                                return False
        return True

class JenkinsBuildStatus(models.Model):
    name = models.CharField(max_length=16)

    def __unicode__(self):
        return self.name

class JenkinsBuild(models.Model):
    name = models.CharField(max_length=1024)
    job = models.ForeignKey(JenkinsJob, related_name="builds")
    umbrella_build = models.ForeignKey("self", blank=True, null=True)
    number = models.IntegerField()
    status = models.ForeignKey(JenkinsBuildStatus)
    is_umbrella = models.BooleanField(default=False)
    timestamp = models.DateTimeField(null=True, blank=True)

    def __unicode__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('jenkins_build_view', args=[str(self.job.name), str(self.number)])

    def get_hwpack_name(self):
        if self.is_umbrella:
            return None
        device_regexp = re.compile(ur'%s\s\xbb\s(?P<device_name>\w+)' % (self.job.name), re.UNICODE)
        hwpack_name_search = device_regexp.search(self.name)
        if hwpack_name_search:
            return hwpack_name_search.group('device_name')
        return None

class LavaJobStatus(models.Model):
    name = models.CharField(max_length=16)

    def __unicode__(self):
        return self.name

class LavaJob(models.Model):
    jenkins_build = models.ForeignKey(JenkinsBuild)
    number = models.IntegerField()
    status = models.ForeignKey(LavaJobStatus)
    test_definitions = models.ManyToManyField('testplanner.TestDefinition')
    device_type = models.ForeignKey('testplanner.Device')
    submit_time = models.DateTimeField(null=True, blank=True)
    start_time = models.DateTimeField(null=True, blank=True)

    def __unicode__(self):
        return "%s - LAVA %s" % (self.jenkins_build.job.name, self.number)

    def get_definitions_results(self):
        result_list = []
        for testdef in self.test_definitions.all():
            resultset = LavaJobResult.objects.filter(lava_job = self, test_definition = testdef)
            if resultset:
                result_list.append((testdef, resultset[0]))
            else:
                result_list.append((testdef, None))
        return result_list

    # TODO: move to helpers to be defined during import time, include field for failed tests
    def has_results_missing(self):
        for testdef in self.test_definitions.all():
            resultset = LavaJobResult.objects.filter(lava_job = self, test_definition = testdef)
            if not resultset:
                return True
        return False

    def get_absolute_url(self):
        return reverse(
            'lava_job_view',
            args=[str(self.jenkins_build.job.name),
                  str(self.jenkins_build.number),
                  str(self.number)]
        )

class LavaJobResultStatus(models.Model):
    name = models.CharField(max_length=8)

    def __unicode__(self):
        return self.name

class LavaJobResult(models.Model):
    lava_job = models.ForeignKey(LavaJob)
    test_definition = models.ForeignKey('testplanner.TestDefinition')
    test_revision = models.ForeignKey('testplanner.TestDefinitionRevision', blank=True, null=True)

    def __unicode__(self):
        return "%s result for %s" % (self.lava_job.__unicode__(), self.test_definition.__unicode__())

    def get_resultset_total(self):
        return self.lavajobtestresult_set.count()

    def get_resultset_count_by_status(self):
        status_count = {}
        # TODO: fix this, use aggregate to collect the numbers
        for testresult in self.lavajobtestresult_set.all():
            if testresult.status.name in status_count:
                status_count[testresult.status.name] += 1
            else:
                status_count[testresult.status.name] = 1
        return status_count

class LavaJobTestResultUnit(models.Model):
    name = models.CharField(max_length=32)

    def __unicode__(self):
        return self.name

class LavaJobTestResult(models.Model):
    test_case_id = models.CharField(max_length=1024)
    lava_job_result = models.ForeignKey(LavaJobResult)
    status = models.ForeignKey(LavaJobResultStatus, blank=True, null=True) # there should be default of 'unknown'
    is_measurement = models.BooleanField(default=False)
    unit = models.ForeignKey(LavaJobTestResultUnit, blank=True, null=True)
    value = models.FloatField(blank=True, null=True)

    def __unicode__(self):
        return self.test_case_id
<|endoftext|>


<commit_before>import time
from openerp.osv import fields, osv

class hotel_book_order(osv.Model):

    def call_button(self, cr, uid, ids, context=None):
        for book_order in self.browse(cr, uid, ids):
            book_order.write({'state':'cout','check_out_date':time.strftime('%Y-%m-%d %H:%M:%S')})
        return True

    def _get_auto_room_id_view(self, cr, uid, ids, field_name, arg, context=None):
        all_orders=self.search(cr,uid,[],context=context)
        orders = self.browse(cr, uid, all_orders, context=context)
        res = {}
        for order in orders:
            name =""
            av_space = 0
            av_space = order.room_id.total_spaces-order.room_id.total_occupancy
            if order.state=="cin":
                name = order.room_id.name + " ( "+ str(av_space)+" ) "+ order.guest_type.name
                if order.guest_type.name == "Single":
                    if order.guest_id.gender:
                        if order.guest_id.gender == "m":
                            name+= " M"
                        else:
                            name+= " F"
            else:
                name = order.room_id.name + " ( "+ str(av_space)+" )"
            res[order.id] = name
        return res

    _name = "hotel.book.order"
    _description = "Hotel Book Order"
    _columns = {
        'guest_id': fields.many2one('hotel.guest.partner', 'Guest Name', required=True),
        'guest_type': fields.many2one('hotel.guest.type', 'Guest Type',required=True),
        'country_id': fields.many2one('res.country', 'Country',),
        'room_id': fields.many2one('hotel.room', 'Room Number', required=True, ondelete='cascade',),
        'room_id_view':fields.function(_get_auto_room_id_view, string='Room Number', type='char',store=True),
        'check_in_date': fields.datetime('Check - In', help="Check-in Time.", required=True,),
        'user_id': fields.many2one('res.users', 'User', readonly=True),
        'state': fields.selection([
            ('cin', 'Check IN'),
            ('cout', 'Check OUT'),
            ], 'Status'),
        'check_out_date': fields.datetime('Check - Out', help="Check-out Time.", select=True),
    }
    _defaults = {
        'state':'cin',
        'user_id': lambda obj, cr, uid, context: uid,
        'check_in_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
    }<commit_msg>Search Implementation<commit_after>import time
from openerp.osv import fields, osv

class hotel_book_order(osv.Model):

    def call_button(self, cr, uid, ids, context=None):
        for book_order in self.browse(cr, uid, ids):
            book_order.write({'state':'cout','check_out_date':time.strftime('%Y-%m-%d %H:%M:%S')})
        return True

    def _get_auto_room_id_view(self, cr, uid, ids, field_name, arg, context=None):
        all_orders=self.search(cr,uid,[],context=context)
        orders = self.browse(cr, uid, all_orders, context=context)
        res = {}
        for order in orders:
            name =""
            av_space = 0
            av_space = order.room_id.total_spaces-order.room_id.total_occupancy
            if order.state=="cin":
                name = order.room_id.name + " ( "+ str(av_space)+" ) "+ order.guest_type.name
                if order.guest_type.name == "Single":
                    if order.guest_id.gender:
                        if order.guest_id.gender == "m":
                            name+= " M"
                        else:
                            name+= " F"
            else:
                name = order.room_id.name + " ( "+ str(av_space)+" )"
            res[order.id] = name
        return res

    def _get_auto_name(self, cr, uid, ids, field_name, arg, context=None):
        """
        @return: Dictionary of values.
        """
        all_orders=self.search(cr,uid,[],context=context)
        orders = self.browse(cr, uid, all_orders, context=context)
        res = {}
        for order in orders:
            name =""
            name = order.guest_id.name + " " + order.guest_id.last_name +" ["+ order.guest_id.guest_ref +"]"
            res[order.id] = name
        return res

    _name = "hotel.book.order"
    _description = "Hotel Book Order"
    _order = "name"
    _columns = {
        'name':fields.function(_get_auto_name, string='Name', type='char',store=True),
        'first_name': fields.char('First Name', size=128, required=True, select=True, readonly=True, states={'cin': [('readonly', False)]}),
        'last_name': fields.char('Last Name', size=128, required=True, select=True, readonly=True, states={'cin': [('readonly', False)]}),
        'guest_id': fields.many2one('hotel.guest.partner', 'Guest Name', required=True),
        'guest_type': fields.many2one('hotel.guest.type', 'Guest Type',required=True),
        'country_id': fields.many2one('res.country', 'Country',),
        'room_id': fields.many2one('hotel.room', 'Room Number', required=True, ondelete='cascade',),
        'room_id_view':fields.function(_get_auto_room_id_view, string='Room Number', type='char',store=True),
        'check_in_date': fields.datetime('Check - In', help="Check-in Time.", required=True,),
        'user_id': fields.many2one('res.users', 'User', readonly=True),
        'state': fields.selection([
            ('cin', 'Check IN'),
            ('cout', 'Check OUT'),
            ], 'Status'),
        'check_out_date': fields.datetime('Check - Out', help="Check-out Time.", select=True),
    }
    _defaults = {
        'state':'cin',
        'user_id': lambda obj, cr, uid, context: uid,
        'check_in_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
    }<|endoftext|>


<commit_before>import os

import requests

from constants import *
from github_publisher import GitHubPublisher

class JIMSFetcher(object):
    @staticmethod
    def read():
        return requests.get(JIMS_1058_URL).text

    @staticmethod
    def save_to_file(date, directory=DATA_DIR):
        file_path = JIMSFetcher.build_file_path(date, directory)
        with open(file_path, 'w+') as f:
            f.write(JIMSFetcher.read())
        return file_path

    @staticmethod
    def save_to_github_commit(date):
        file_path = JIMSFetcher.build_file_path(date, DATA_DIR)
        # Do the import here so the rest of the methods work without defining settings
        import settings
        publisher = GitHubPublisher(settings.GITHUB_REPO_NAME, settings.GITHUB_API_TOKEN)
        # TODO verify the token has commit access to the repo
        return publisher.publish(file_path, settings.GITHUB_BRANCH, JIMSFetcher.read())

    @staticmethod
    def build_file_path(date, directory):
        fileName = "%s.accdb" % date.strftime('%Y-%m-%d')
        return os.path.join(directory, str(date.year), fileName)
<commit_msg>ensure the directory exists before saving<commit_after>import os

import requests

from constants import *
from github_publisher import GitHubPublisher

class JIMSFetcher(object):
    @staticmethod
    def read():
        return requests.get(JIMS_1058_URL).text

    @staticmethod
    def save_to_file(date, directory=DATA_DIR):
        file_path = JIMSFetcher.build_file_path(date, directory)
        dirname = os.path.dirname(file_path)
        if not os.path.exists(dirname):
            os.makedirs(dirname)
        with open(file_path, 'w+') as f:
            f.write(JIMSFetcher.read())
        return file_path

    @staticmethod
    def save_to_github_commit(date):
        file_path = JIMSFetcher.build_file_path(date, DATA_DIR)
        # Do the import here so the rest of the methods work without defining settings
        import settings
        publisher = GitHubPublisher(settings.GITHUB_REPO_NAME, settings.GITHUB_API_TOKEN)
        # TODO verify the token has commit access to the repo
        return publisher.publish(file_path, settings.GITHUB_BRANCH, JIMSFetcher.read())

    @staticmethod
    def build_file_path(date, directory):
        fileName = "%s.accdb" % date.strftime('%Y-%m-%d')
        return os.path.join(directory, str(date.year), fileName)
<|endoftext|>