<commit_before>from datetime import date

import rules

from involvement.models import Application

# General Predicates
@rules.predicate
def is_admin(user):
    return user.has_perm('involvement.admin')

@rules.predicate
def is_official(user):
    return Application.objects.filter(
        applicant=user,
        status='appointed',
        position__term_from__lte=date.today(),
        position__term_to__gte=date.today(),
        position__function__official=True,
        position__function__team_id__isnull=False,
    ).exists()

# Team Permissions
@rules.predicate
def is_team_official(user, team):
    return Application.objects.filter(
        applicant=user,
        status='appointed',
        position__term_from__lte=date.today(),
        position__term_to__gte=date.today(),
        position__function__official=True,
        position__function__team=team,
    ).exists()

# Function Predicates
@rules.predicate
def is_function_official(user, function):
    return is_team_official(user, function.team)

# Position Predicates
@rules.predicate
def is_position_official(user, position):
    return is_team_official(user, position.function.team)

@rules.predicate
def before_commencement(user, position):
    return date.today() < position.commencement

# Application Predicates
@rules.predicate
def is_applicant(user, application):
    return application.applicant == user

@rules.predicate
def is_past_due(user, application):
    return application.position.is_past_due()

# Permissions
rules.add_perm('involvement', rules.always_allow)

rules.add_perm('involvement.list_team', is_admin | is_official)
rules.add_perm('involvement.add_team', is_admin)
rules.add_perm('involvement.change_team', is_admin | is_team_official)
rules.add_perm('involvement.delete_team', is_admin)

rules.add_perm('involvement.list_function', is_admin | is_official)
rules.add_perm('involvement.add_function', is_admin | is_official)
rules.add_perm('involvement.change_function', is_admin | is_function_official)
rules.add_perm('involvement.delete_function', is_admin)

rules.add_perm('involvement.list_position', is_admin | is_official)
rules.add_perm('involvement.add_position', is_admin | is_official)
rules.add_perm('involvement.change_position', is_admin | is_position_official)
rules.add_perm('involvement.delete_position', is_admin
               | (is_position_official & before_commencement))

rules.add_perm('involvement.list_application', is_admin)
rules.add_perm('involvement.add_application', is_admin | is_official)
rules.add_perm('involvement.change_application', is_admin | is_applicant)
rules.add_perm('involvement.delete_application', is_admin
               | (is_applicant & ~is_past_due))
<commit_msg>:green_heart: Fix flake8 empty line<commit_after>from datetime import date

import rules

from involvement.models import Application

# General Predicates
@rules.predicate
def is_admin(user):
    return user.has_perm('involvement.admin')

@rules.predicate
def is_official(user):
    return Application.objects.filter(
        applicant=user,
        status='appointed',
        position__term_from__lte=date.today(),
        position__term_to__gte=date.today(),
        position__function__official=True,
        position__function__team_id__isnull=False,
    ).exists()

# Team Permissions
@rules.predicate
def is_team_official(user, team):
    return Application.objects.filter(
        applicant=user,
        status='appointed',
        position__term_from__lte=date.today(),
        position__term_to__gte=date.today(),
        position__function__official=True,
        position__function__team=team,
    ).exists()

# Function Predicates
@rules.predicate
def is_function_official(user, function):
    return is_team_official(user, function.team)

# Position Predicates
@rules.predicate
def is_position_official(user, position):
    return is_team_official(user, position.function.team)

@rules.predicate
def before_commencement(user, position):
    return date.today() < position.commencement

# Application Predicates
@rules.predicate
def is_applicant(user, application):
    return application.applicant == user

@rules.predicate
def is_past_due(user, application):
    return application.position.is_past_due()

# Permissions
rules.add_perm('involvement', rules.always_allow)

rules.add_perm('involvement.list_team', is_admin | is_official)
rules.add_perm('involvement.add_team', is_admin)
rules.add_perm('involvement.change_team', is_admin | is_team_official)
rules.add_perm('involvement.delete_team', is_admin)

rules.add_perm('involvement.list_function', is_admin | is_official)
rules.add_perm('involvement.add_function', is_admin | is_official)
rules.add_perm('involvement.change_function', is_admin | is_function_official)
rules.add_perm('involvement.delete_function', is_admin)

rules.add_perm('involvement.list_position', is_admin | is_official)
rules.add_perm('involvement.add_position', is_admin | is_official)
rules.add_perm('involvement.change_position', is_admin | is_position_official)
rules.add_perm('involvement.delete_position', is_admin
               | (is_position_official & before_commencement))

rules.add_perm('involvement.list_application', is_admin)
rules.add_perm('involvement.add_application', is_admin | is_official)
rules.add_perm('involvement.change_application', is_admin | is_applicant)
rules.add_perm('involvement.delete_application', is_admin
               | (is_applicant & ~is_past_due))
<|endoftext|>


<commit_before>__import__('pkg_resources').declare_namespace(__name__)

VERSION = (0, 1, 1)

__version__ = '%d.%d.%d' % VERSION[:3]

<commit_msg>development version number changed to 0.1.2<commit_after>__import__('pkg_resources').declare_namespace(__name__)

VERSION = (0, 1, 2)

__version__ = '%d.%d.%d' % VERSION[:3]

<|endoftext|>


<commit_before>import itertools

import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.patches import Ellipse
import docopt

class ellipse:
    def __init__(self, x, y, Ma, ma, angle):
        self.x = x
        self.y = y
        self.Ma = max(Ma, ma)
        self.ma = min(Ma, ma)
        self.angle = angle

    def isCloseTo(self, other, err=1):
        ''' Returns True if ellipse is within 0.5 pixels of given ellipse
        '''
        if abs(self.x - other.x) < err:
            if abs(self.y - other.y) < err:
                return True
        return False

    def hasSameRotation(self, other, err=10):
        ''' Returns True if ellipse is has the same rotation as other
        '''

        if abs(self.angle - other.angle) < err:
            return True

        return False

    def isSmallerThan(self, other):
        ''' Return True if both axes are smaller than other ellipse's '''
        if self.Ma < other.Ma:
            if self.ma < other.ma:
                return True

        return False

    def isConcentricTo(self, other):
        ''' similar position, and rotation
        '''
        if self.isCloseTo(other):
            if self.hasSameRotation(other):
                return True

        return False

    def __str__(self):
        _str = "x: {x}\ny: {y}\nMa: {Ma}\nma: {ma}\nangle: {ang}".format(
            x=self.x, y=self.y, Ma=self.Ma, ma=self.ma, ang=self.angle)
        return _str

def get_contours(img):
    ''' Get contours in given image

    '''
    contours, hierarchy = cv2.findContours(thresh,
                                           cv2.RETR_TREE,
                                           cv2.CHAIN_APPROX_SIMPLE)

    # filter out short ones
    contours = [cnt for cnt in contours if len(cnt) > 10]

    return contours

def get_threshold(img):
    blur = cv2.bilateralFilter(img, 9, 75, 75)
    thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                   cv2.THRESH_BINARY_INV, 21, 2)
    return thresh

def filter_ellipses(ellipses):
    ''' Filter out unlikely candidates from a list of ellipses

        Return the list of ellipses without the false postives
    '''

    # TODO Filter out
    #  weed out some spurious ellipses

    MaMax = 50
    maMax = 50
    MaMin = 4
    maMin = 3
    Ma_ma = 5

    for i, ell in enumerate(ellipses):
        Ma, ma = ell[1]

        if (Ma > MaMax) or (Ma < MaMin):
            del ellipses[i]
            del hulls[i]
            continue

        if (ma > maMax) or (ma < maMin):
            del ellipses[i]
            del hulls[i]
            continue

        if float(Ma) / float(ma) > Ma_ma:
            del ellipses[i]
            del hulls[i]
            continue

    return ellipses

def find_rad_targets(ellipses):
    '''
    Given a list of ellipses, return a list of rad-targets, specified by their
    inner ellipse and outer ellipse i.e.

    [
        [small_ellipse, big_ellipse]
        .
        .
        .
    ]
    '''
    rad_targets = []

    # photomodeler rad targets have as specific small/large circle ratio
    # for each ellipse, find the closest in position which is smaller in both
    # axes and has a similar angle

    ellipse_pairs = itertools.combinations(ellipses, 2)

    for ell1, ell2 in ellipse_pairs:
        (x1, y1), (Ma1, ma1), ang1 = ell1
        (x2, y2), (Ma2, ma2), ang2 = ell2
        ellipse1 = ellipse(x1, y1, Ma1, ma1, ang1)
        ellipse2 = ellipse(x2, y2, Ma2, ma2, ang2)
        # now check if they are in fact 'concentric'
        if ellipse1.isCloseTo(ellipse2):
            if ellipse1.isSmallerThan(ellipse2):
                if 3.5 < (ellipse2.Ma / ellipse1.Ma) < 5:
                    rad_targets.append((ellipse1, ellipse2))
            else:
                if 3.5 < (ellipse1.Ma / ellipse2.Ma) < 5:
                    rad_targets.append((ellipse2, ellipse1))

    return rad_targets

def find_ellipses(contours):

    ellipses = []
    hulls = []
    # for each contour, fit an ellipse
    for i, cnt in enumerate(contours):
        # get convex hull of contour
        hull = cv2.convexHull(cnt, returnPoints=True)
        # defects = cv2.convexityDefects(cnt, hull)

        # if len(hull) > 5:
        #     newcnt = []
        #     for d in defects:
        #         if d[0][3] < 500:
        #             start = d[0][0]
        #             end = d[0][1]
        #             for i in range(start, end):
        #                 newcnt.append(cnt[i])
        if len(hull) > 5:
            # (x,y), (Ma, ma), angle = cv2.fitEllipse(hull)
            ellipse = cv2.fitEllipse(np.array(hull))
            ellipses.append(ellipse)
            hulls.append(hulls)

    return ellipses, hulls

def find_edges(img):
    ''' Find edges in the given image
    '''

    edges = cv2.Canny(img, 100, 255, 31)

    return edges

__doc__ = '''\
Usage: libtargets.py JPG... [--plot] [--save]
'''

if __name__ == "__main__":
    arguments = docopt.docopt(__doc__)
    for jpg in arguments['JPG']:
        print("Finding RAD targets in {f}".format(f=jpg))
        img = cv2.imread(jpg, 0)
        thresh = get_threshold(img)
        edges = find_edges(thresh)
        contours = get_contours(thresh)
        ellipses, hulls = find_ellipses(contours)
        radtargets = find_rad_targets(ellipses)
        print("    Found {N} rad-target candidates!".format(N=len(radtargets)))

        #
        # Make some plots
        #
        fig = plt.figure(figsize=(12, 12))
        ax1 = fig.add_subplot(121, aspect='equal')
        plt.imshow(img, cmap=cm.gray, interpolation='nearest')

        for rt in radtargets:
            ell1, ell2 = rt
            e1 = Ellipse((ell1.x, ell1.y), ell1.Ma, ell1.ma, ell1.angle+90,
                         facecolor='none', edgecolor='r')
            e2 = Ellipse((ell2.x, ell2.y), ell2.Ma, ell2.ma, ell2.angle+90,
                         facecolor='none', edgecolor='r')
            ax1.add_artist(e1)
            ax1.add_artist(e2)

        ax = fig.add_subplot(122, aspect='equal')
        plt.imshow(edges, cmap=cm.gray, interpolation='nearest',
                   vmin=0, vmax=55)

        for ell in ellipses:
            (x, y), (Ma, ma), angle = ell
            ell = Ellipse([x, y], Ma, ma, angle, facecolor='none',
                          edgecolor='r')
            ax.add_artist(ell)

        if arguments['--save']:
            plt.savefig('test.png'.format(
                f=jpg.replace('.jpg', '')), bbox_inches='tight')

        if arguments['--plot']:
            plt.show()
<commit_msg>Calculating encoding of targets almost done<commit_after>import itertools

import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.patches import Ellipse
import docopt

class ellipse:
    def __init__(self, x, y, Ma, ma, angle):
        self.x = x
        self.y = y
        self.Ma = max(Ma, ma)
        self.ma = min(Ma, ma)
        self.angle = angle

    def isCloseTo(self, other, err=1):
        ''' Returns True if ellipse is within 0.5 pixels of given ellipse
        '''
        if abs(self.x - other.x) < err:
            if abs(self.y - other.y) < err:
                return True
        return False

    def hasSameRotation(self, other, err=10):
        ''' Returns True if ellipse is has the same rotation as other
        '''

        if abs(self.angle - other.angle) < err:
            return True

        return False

    def isSmallerThan(self, other):
        ''' Return True if both axes are smaller than other ellipse's '''
        if self.Ma < other.Ma:
            if self.ma < other.ma:
                return True

        return False

    def isConcentricTo(self, other):
        ''' similar position, and rotation
        '''
        if self.isCloseTo(other):
            if self.hasSameRotation(other):
                return True

        return False

    def __str__(self):
        _str = "x: {x}\ny: {y}\nMa: {Ma}\nma: {ma}\nangle: {ang}".format(
            x=self.x, y=self.y, Ma=self.Ma, ma=self.ma, ang=self.angle)
        return _str

def get_contours(img):
    ''' Get contours in given image

    '''
    contours, hierarchy = cv2.findContours(thresh,
                                           cv2.RETR_TREE,
                                           cv2.CHAIN_APPROX_SIMPLE)

    # filter out short ones
    contours = [cnt for cnt in contours if len(cnt) > 10]

    return contours

def get_threshold(img):
    blur = cv2.bilateralFilter(img, 9, 75, 75)
    thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                   cv2.THRESH_BINARY_INV, 21, 2)
    return thresh

def filter_ellipses(ellipses):
    ''' Filter out unlikely candidates from a list of ellipses

        Return the list of ellipses without the false postives
    '''

    # TODO Filter out
    #  weed out some spurious ellipses

    MaMax = 50
    maMax = 50
    MaMin = 4
    maMin = 3
    Ma_ma = 5

    for i, ell in enumerate(ellipses):
        Ma, ma = ell[1]

        if (Ma > MaMax) or (Ma < MaMin):
            del ellipses[i]
            del hulls[i]
            continue

        if (ma > maMax) or (ma < maMin):
            del ellipses[i]
            del hulls[i]
            continue

        if float(Ma) / float(ma) > Ma_ma:
            del ellipses[i]
            del hulls[i]
            continue

    return ellipses

def find_rad_targets(ellipses):
    '''
    Given a list of ellipses, return a list of rad-targets, specified by their
    inner ellipse and outer ellipse i.e.

    [
        [small_ellipse, big_ellipse]
        .
        .
        .
    ]
    '''
    rad_targets = []

    # photomodeler rad targets have as specific small/large circle ratio
    # for each ellipse, find the closest in position which is smaller in both
    # axes and has a similar angle

    ellipse_pairs = itertools.combinations(ellipses, 2)

    for ell1, ell2 in ellipse_pairs:
        (x1, y1), (Ma1, ma1), ang1 = ell1
        (x2, y2), (Ma2, ma2), ang2 = ell2
        ellipse1 = ellipse(x1, y1, Ma1, ma1, ang1)
        ellipse2 = ellipse(x2, y2, Ma2, ma2, ang2)
        # now check if they are in fact 'concentric'
        if ellipse1.isCloseTo(ellipse2):
            if ellipse1.isSmallerThan(ellipse2):
                if 3.5 < (ellipse2.Ma / ellipse1.Ma) < 5:
                    rad_targets.append((ellipse1, ellipse2))
            else:
                if 3.5 < (ellipse1.Ma / ellipse2.Ma) < 5:
                    rad_targets.append((ellipse2, ellipse1))

    return rad_targets

def find_ellipses(contours):

    ellipses = []
    hulls = []
    # for each contour, fit an ellipse
    for i, cnt in enumerate(contours):
        # get convex hull of contour
        hull = cv2.convexHull(cnt, returnPoints=True)
        # defects = cv2.convexityDefects(cnt, hull)

        if len(hull) > 5:
            # (x,y), (Ma, ma), angle = cv2.fitEllipse(hull)
            ellipse = cv2.fitEllipse(np.array(hull))
            ellipses.append(ellipse)
            hulls.append(hulls)

    return ellipses, hulls

def find_edges(img):
    ''' Find edges in the given image
    '''

    edges = cv2.Canny(img, 100, 255, 31)

    return edges

def find_imval_at_ellipse_coordinates(img, ellipse, n=100):
    ''' Given rotated ellipse, return n coordinates along its perimeter '''
    # x=acos(theta) y=bsin(theta)
    theta = (ellipse.angle + 90)*np.pi/180.
    angles = np.linspace(0, 2*np.pi, n)
    # center of ellipse
    x0, y0 = ellipse.x, ellipse.y

    x = ellipse.Ma/2.0*np.cos(angles)
    y = ellipse.ma/2.0*np.sin(angles)

    xy = np.array([(x[i],y[i]) for i, xx in enumerate(x)]).T
    # rotation matrix
    rotMat = np.array([[np.sin(theta), -1.0*np.cos(theta)],
                       [np.cos(theta), np.sin(theta)]])

    rotatedXY = np.dot(rotMat, xy).T

    rotatedXY[:, 0] += y0
    rotatedXY[:, 1] += x0
    # round to ints
    rotatedXY = np.around(rotatedXY, 0)

    # find image values
    imval = []
    for row in rotatedXY:
        x, y = row[0], row[1]
        imval.append(img[x, y])
    imval = np.array(imval)
    # regions are either high or low. make high regions = 255 and low
    # regions == 1.0
    imval_max = imval.max()

    imval[imval > 0.25*imval_max] = 255
    imval[imval <= 0.25*imval_max] = 0
    print imval
    return rotatedXY, angles, np.array(imval)

def find_rad_encoding(img, radtarget):
    ''' given an image and a rad target ellipse pair, find the encoding used

    return as a string containing 1 and 0 i.e. encoding='101010101010'

    '''

    plot = True

    small = radtarget[0]
    big = radtarget[1]

    outer = ellipse(big.x, big.y, big.Ma*0.85, big.ma*0.85, big.angle)
    inner = ellipse(big.x, big.y, big.Ma*0.55, big.ma*0.55, big.angle)

    pouter, theta_outer, imval_outer = find_imval_at_ellipse_coordinates(img, outer, n=200)
    pinner, theta_inner, imval_inner = find_imval_at_ellipse_coordinates(img, inner, n=200)

    # some bug fixing plots
    if plot:
        fig = plt.figure(figsize=(12, 12))
        ax1 = fig.add_subplot(211, aspect='equal')
        x, y = int(big.x), int(big.y)
        intMa = int(big.Ma)
        plt.imshow(img, cmap=cm.gray, interpolation='nearest')
        ell1, ell2 = radtarget
        e1 = Ellipse((ell1.x, ell1.y), ell1.Ma, ell1.ma, ell1.angle+90,
                     facecolor='none', edgecolor='r')
        e2 = Ellipse((ell2.x, ell2.y), ell2.Ma, ell2.ma, ell2.angle+90,
                     facecolor='none', edgecolor='r')
        # make ellipse for the inner encoding ring
        e3 = Ellipse((ell2.x, ell2.y), ell2.Ma*(0.5), ell2.ma*(0.5),
                     ell2.angle+90, facecolor='none', edgecolor='b')
        # make ellipse for the outer encoding ring
        e4 = Ellipse((ell2.x, ell2.y), ell2.Ma*(0.9), ell2.ma*(0.9),
                     ell2.angle+90, facecolor='none', edgecolor='b')
        ax1.add_artist(e1)
        ax1.add_artist(e2)
        ax1.add_artist(e3)
        ax1.add_artist(e4)
        plt.xlim(x-intMa, x+intMa)
        plt.ylim(y-intMa, y+intMa)

        plt.subplot(212)
        plt.plot(theta_inner*180/np.pi, imval_inner)
        plt.plot(theta_outer*180/np.pi, imval_outer)

        # middle of zero
        theta_mid = np.average(theta_outer[imval_outer == 0]*180/np.pi)
        plt.vlines(theta_mid, 0, 255, colors='r')

        # TODO Roll the array so that the minimum of the outer ring is
        # at the start of the array, then calculate the values in the 12 bits

        plt.show()

__doc__ = '''\
Usage: libtargets.py JPG... [--plot] [--save]
'''

if __name__ == "__main__":
    arguments = docopt.docopt(__doc__)
    for jpg in arguments['JPG']:
        print("Finding RAD targets in {f}".format(f=jpg))
        img = cv2.imread(jpg, 0)
        thresh = get_threshold(img)
        edges = find_edges(thresh)
        contours = get_contours(thresh)
        ellipses, hulls = find_ellipses(contours)
        radtargets = find_rad_targets(ellipses)
        print("    Found {N} rad-target candidates!".format(N=len(radtargets)))
        for rt in radtargets:
            find_rad_encoding(img, rt)
        #
        # Make some plots
        #
        if arguments['--plot'] or arguments['--save']:
            fig = plt.figure(figsize=(12, 12))
            ax1 = fig.add_subplot(121, aspect='equal')
            plt.imshow(img, cmap=cm.gray, interpolation='nearest')

            for rt in radtargets:
                ell1, ell2 = rt
                e1 = Ellipse((ell1.x, ell1.y), ell1.Ma, ell1.ma, ell1.angle+90,
                             facecolor='none', edgecolor='r')
                e2 = Ellipse((ell2.x, ell2.y), ell2.Ma, ell2.ma, ell2.angle+90,
                             facecolor='none', edgecolor='r')
                ax1.add_artist(e1)
                ax1.add_artist(e2)

            ax = fig.add_subplot(122, aspect='equal')
            plt.imshow(edges, cmap=cm.gray, interpolation='nearest',
                       vmin=0, vmax=55)

            for ell in ellipses:
                (x, y), (Ma, ma), angle = ell
                ell = Ellipse([x, y], Ma, ma, angle, facecolor='none',
                              edgecolor='r')
                ax.add_artist(ell)

        if arguments['--save']:
            plt.savefig('test.png'.format(
                f=jpg.replace('.jpg', '')), bbox_inches='tight')

        if arguments['--plot']:
            plt.show()
<|endoftext|>


<commit_before>from django.conf.urls.defaults import patterns, include, url
from armstrong.core.arm_wells.views import QuerySetBackedWellView
from armstrong.core.arm_sections.views import SimpleSectionView
from armstrong.apps.articles.models import Article
from django.views.generic.list_detail import object_detail
# ADMIN_BASE is the base URL for your Armstrong admin.  It is highly
# recommended that you change this to a different URL unless you enforce a
# strict password-strength policy for your users.
ADMIN_BASE = "admin"

# Comment the next two lines out to disnable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', '{{ project_name }}.views.home', name='home'),
    # url(r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),

    # Comment the admin/doc line below to disable admin documentation:
    url(r'^%s/doc/' % ADMIN_BASE, include('django.contrib.admindocs.urls')),

    # Comment the next line to disable the admin:
    url(r'^%s/' % ADMIN_BASE, include(admin.site.urls)),

    # Load the Armstrong "success" page by default
    # {% if demo %}
    url(r'^$', QuerySetBackedWellView.as_view(well_title='front_page',
                                              template_name="front_page.html",
                                              queryset=Article.published.all()),
            name='front_page'),
    # {% else %}
    # Below is an example well view that might be used to display a well named
    # 'front_page' allowing for placement of content on the home page. The view
    # will work after # running `armstrong loaddata fixtures/initial_data.json`
    #
    #url(r'^$', QuerySetBackedWellView.as_view(well_title='front_page',
    #                                          template_name="front_page.html",
    #                                          queryset=Article.published.all()),
    #        name='front_page'),
    url(r'^$', TemplateView.as_view(template_name="index.html")),
    # {% endif %}

    url(r'^section/(?P<full_slug>[-\w/]+)',
            SimpleSectionView.as_view(template_name='section.html'),
            name='section_view'),

    url(r'^article/(?P<slug>[-\w]+)/', object_detail, {
                        'queryset':Article.published.all().select_subclasses(),
                        'template_name':'article.html',
                        'slug_field':'slug',
                    },
            name='article_detail'),
)
<commit_msg>Fix a bug in standard urls.py<commit_after>from django.conf.urls.defaults import patterns, include, url
from armstrong.core.arm_wells.views import QuerySetBackedWellView
from armstrong.core.arm_sections.views import SimpleSectionView
from armstrong.apps.articles.models import Article
from django.views.generic.list_detail import object_detail
from django.views.generic import TemplateView
# ADMIN_BASE is the base URL for your Armstrong admin.  It is highly
# recommended that you change this to a different URL unless you enforce a
# strict password-strength policy for your users.
ADMIN_BASE = "admin"

# Comment the next two lines out to disnable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', '{{ project_name }}.views.home', name='home'),
    # url(r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),

    # Comment the admin/doc line below to disable admin documentation:
    url(r'^%s/doc/' % ADMIN_BASE, include('django.contrib.admindocs.urls')),

    # Comment the next line to disable the admin:
    url(r'^%s/' % ADMIN_BASE, include(admin.site.urls)),

    # Load the Armstrong "success" page by default
    # {% if demo %}
    url(r'^$', QuerySetBackedWellView.as_view(well_title='front_page',
                                              template_name="front_page.html",
                                              queryset=Article.published.all()),
            name='front_page'),
    # {% else %}
    # Below is an example well view that might be used to display a well named
    # 'front_page' allowing for placement of content on the home page. The view
    # will work after # running `armstrong loaddata fixtures/initial_data.json`
    #
    #url(r'^$', QuerySetBackedWellView.as_view(well_title='front_page',
    #                                          template_name="front_page.html",
    #                                          queryset=Article.published.all()),
    #        name='front_page'),
    url(r'^$', TemplateView.as_view(template_name="index.html")),
    # {% endif %}

    url(r'^section/(?P<full_slug>[-\w/]+)',
            SimpleSectionView.as_view(template_name='section.html'),
            name='section_view'),

    url(r'^article/(?P<slug>[-\w]+)/', object_detail, {
                        'queryset':Article.published.all().select_subclasses(),
                        'template_name':'article.html',
                        'slug_field':'slug',
                    },
            name='article_detail'),
)
<|endoftext|>


<commit_before>#!/usr/bin/python

from math import sqrt
import sys
import argparse
from collections import deque
from struct import pack, unpack

import gdal

gdal.UseExceptions()

class stlwriter():

	# facet_count: predicted number of facets
	# path: output binary stl file path (default: stdout)
	def __init__(self, facet_count, path=None):

		if path == None:
			self.f = sys.stdout
		else:
			self.f = open(path, 'w')

		# for future use: track number of facets actually written
		self.written = 0

		# write binary stl header with predicted facet count
		self.f.write('\0' * 80)
		self.f.write(pack('<I', facet_count))

	# n: normal vector to (a, b, c) facet
	# t: ((ax, ay, az), (bx, by, bz), (cx, cy, cz))
	def add_facet(self, n, t):
		self.f.write(pack('<3f', *n))
		for vertex in t:
			self.f.write(pack('<3f', *vertex))
		self.f.write('\0\0')
		self.written += 1

	def done(self):
		if self.f != sys.stdout:
			self.f.close()

	def __enter__(self):
		return self

	def __exit__(self, exc_type, exc_value, traceback):
		self.done()

def fail(msg):
	print >> sys.stderr, msg
	exit(1)

def log(msg):
	if args.verbose:
		print >> sys.stderr, msg

ap = argparse.ArgumentParser(description='Convert a GDAL raster (like a GeoTIFF heightmap) to an STL terrain surface.')
ap.add_argument('-x', action='store', default=0.0, type=float, help='Fit output x to extent (mm)')
ap.add_argument('-y', action='store', default=0.0, type=float, help='Fit output y to extent (mm)')
ap.add_argument('-z', action='store', default=1.0, type=float, help='Vertical scale factor')
ap.add_argument('-b', '--base', action='store', default=0.0, type=float, help='Base height')
ap.add_argument('-c', '--clip', action='store_true', default=False, help='Clip z to minimum elevation')
ap.add_argument('-v', '--verbose', action='store_true', default=False, help='Print log messages')
ap.add_argument('RASTER', help='Input heightmap image')
ap.add_argument('STL', nargs='?', default=None, help='Output STL path (stdout)')
args = ap.parse_args()

#
# XCoordinate
#
# Apply affine transformation to get output X coordinate from image coordinates.
#
# Parameters:
#  r, c: row, column image coordinates
#
# Accesses:
#  transform tuple
#
# Returns:
#  x value
#
def XCoordinate(r, c):
	return transform[0] + (c * transform[1]) + (r * transform[2])

#
# YCoordinate
#
# Apply affine transformation to get output Y coordinate from image coordinates.
#
# Parameters:
#  r, c: row, column image coordinates
#
# Accesses:
#  transform tuple
#
# Returns:
#  y value
#
def YCoordinate(r, c):
	return transform[3] + (c * transform[4]) + (r * transform[5])

#
# ZCoordinate
#
# Convert an elevation value to output Z units. Applies clipping to minimum
# elevation (zmin, if nonzero); z scaling (and exaggeration); and base offset.
#
# Parameters:
#  elevation
#
# Accesses:
#  zscale, zmin, and args.base
#
# Returns:
#  z value
#
def ZCoordinate(e):
	return (zscale * (float(e) - zmin)) + args.base

#
# NormalVector
#
# Calculate the normal vector of a triangle. (Unit vector perpendicular to
# triangle surface, pointing away from the "outer" face of the surface.)
#
# Parameters:
#  triangle vertices (nested x y z tuples)
#
# Returns:
#  normal vector (x y z tuple)
#
def NormalVector(t):

	(ax, ay, az) = t[0]
	(bx, by, bz) = t[1]
	(cx, cy, cz) = t[2]

	# first edge
	e1x = ax - bx
	e1y = ay - by
	e1z = az - bz

	# second edge
	e2x = bx - cx
	e2y = by - cy
	e2z = bz - cz

	# cross product
	cpx = e1y * e2z - e1z * e2y
	cpy = e1z * e2x - e1x * e2z
	cpz = e1x * e2y - e1y * e2x

	# return cross product vector normalized to unit length
	mag = sqrt((cpx * cpx) + (cpy * cpy) + (cpz * cpz))
	return (cpx/mag, cpy/mag, cpz/mag)

#
# AddTri
#
# a-c
# |/
# b
#
# Parameters:
#  m: the stl mesh to add the tri to
#  t: triangle vertices (a b c as nested x y z tuples)
#
# Results:
#  adds triangle facet to mesh
#
def AddTri(m, t):
	m.add_facet(NormalVector(t), t)

#
# AddQuad
#
# a-c
# |/|
# b-d
#
# Parameters:
#  m: the stl mesh to add the quad to
#  a, b, c, d: vertices (x y z tuples)
#
# Results:
#  adds two triangle facets to mesh
#
def AddQuad(m, a, b, c, d):
	AddTri(m, (a, b, c))
	AddTri(m, (d, c, b))

try:
	img = gdal.Open(args.RASTER)
except RuntimeError, e:
	fail(str(e).strip())

# input raster dimensions
w = img.RasterXSize
h = img.RasterYSize

# output mesh dimensions are one row and column less than raster
mw = w - 1
mh = h - 1

# get default transformation from image coordinates to world coordinates
transform = img.GetGeoTransform()

# save x pixel size if needed for scaling
xyres = abs(transform[1])

# initialize z scale to exaggeration factor, if any
zscale = args.z

# recalculate z scale and xy transform if different dimensions are requested
if args.x != 0.0 or args.y != 0.0:

	# recaculate xy scale based on requested x or y dimension
	# if both x and y dimension are set, select smaller scale
	if args.x != 0.0 and args.y != 0.0:
		pixel_scale = min(args.x / mw, args.y / mh)
	elif args.x != 0.0:
		pixel_scale = args.x / mw
	elif args.y != 0.0:
		pixel_scale = args.y / mh

	# adjust z scale to maintain proportions with new xy scale
	zscale *= pixel_scale / xyres

	# revise transformation matrix
	# image: 0,0 at top left corner of top left pixel (0.5,0.5 at pixel center)
	transform = (
			-pixel_scale * mw / 2.0, # 0 left edge of top left pixel
			 pixel_scale,            # 1 pixel width
			 0,                      # 2
			 pixel_scale * mh / 2.0, # 3 top edge of top left pixel
			 0,                      # 4
			-pixel_scale             # 5 pixel height
	)

log(transform)

band = img.GetRasterBand(1)

# map GDAL pixel data type to corresponding struct format character
typemap = {
	gdal.GDT_Byte:    'B',
	gdal.GDT_UInt16:  'H',
	gdal.GDT_Int16:   'h',
	gdal.GDT_UInt32:  'I',
	gdal.GDT_Int32:   'i',
	gdal.GDT_Float32: 'f',
	gdal.GDT_Float64: 'd'
}

if band.DataType not in typemap:
	fail('Unsupported data type: %s' % (gdal.GetDataTypeName(band.DataType)))

rowformat = typemap.get(band.DataType) * w

# min, max, mean, sd; min used for z clipping
stats = band.GetStatistics(True, True)
log(stats)
if args.clip == True:
	zmin = stats[0]
else:
	zmin = 0

log('Initiating raster processing...')

# Space for two rows of image data is allocated. Extending the deque
# with a third (new) row of data automatically exposes the first (old).
pixels = deque(maxlen = (2 * w))

# not handling the data type flexibly here. should map raster datatype
# to an appropriate corresponding struct element specificier ('B', 'H', etc)
pixels.extend(unpack(rowformat, band.ReadRaster(0, 0, w, 1, w, 1, band.DataType)))

with stlwriter(mw * mh * 2, args.STL) as mesh:

	for y in range(mh):

		pixels.extend(unpack(rowformat, band.ReadRaster(0, y + 1, w, 1, w, 1, band.DataType)))

		for x in range(mw):

			ax = XCoordinate(y, x)
			ay = YCoordinate(y, x)
			az = ZCoordinate(pixels[x])

			bx = XCoordinate(y + 1, x)
			by = YCoordinate(y + 1, x)
			bz = ZCoordinate(pixels[w + x])

			cx = XCoordinate(y, x + 1)
			cy = YCoordinate(y, x + 1)
			cz = ZCoordinate(pixels[0 + x + 1])

			dx = XCoordinate(y + 1, x + 1)
			dy = YCoordinate(y + 1, x + 1)
			dz = ZCoordinate(pixels[w + x + 1])

			AddQuad(mesh, (ax, ay, az), (bx, by, bz), (cx, cy, cz), (dx, dy, dz))
<commit_msg>Remove extraneous AddTri function It was just acting as a wrapper for add_facet<commit_after>#!/usr/bin/python

from math import sqrt
import sys
import argparse
from collections import deque
from struct import pack, unpack

import gdal

gdal.UseExceptions()

class stlwriter():

	# facet_count: predicted number of facets
	# path: output binary stl file path (default: stdout)
	def __init__(self, facet_count, path=None):

		if path == None:
			self.f = sys.stdout
		else:
			self.f = open(path, 'w')

		# for future use: track number of facets actually written
		self.written = 0

		# write binary stl header with predicted facet count
		self.f.write('\0' * 80)
		self.f.write(pack('<I', facet_count))

	# n: normal vector to (a, b, c) facet
	# t: ((ax, ay, az), (bx, by, bz), (cx, cy, cz))
	def add_facet(self, n, t):
		self.f.write(pack('<3f', *n))
		for vertex in t:
			self.f.write(pack('<3f', *vertex))
		self.f.write('\0\0')
		self.written += 1

	def done(self):
		if self.f != sys.stdout:
			self.f.close()

	def __enter__(self):
		return self

	def __exit__(self, exc_type, exc_value, traceback):
		self.done()

def fail(msg):
	print >> sys.stderr, msg
	exit(1)

def log(msg):
	if args.verbose:
		print >> sys.stderr, msg

ap = argparse.ArgumentParser(description='Convert a GDAL raster (like a GeoTIFF heightmap) to an STL terrain surface.')
ap.add_argument('-x', action='store', default=0.0, type=float, help='Fit output x to extent (mm)')
ap.add_argument('-y', action='store', default=0.0, type=float, help='Fit output y to extent (mm)')
ap.add_argument('-z', action='store', default=1.0, type=float, help='Vertical scale factor')
ap.add_argument('-b', '--base', action='store', default=0.0, type=float, help='Base height')
ap.add_argument('-c', '--clip', action='store_true', default=False, help='Clip z to minimum elevation')
ap.add_argument('-v', '--verbose', action='store_true', default=False, help='Print log messages')
ap.add_argument('RASTER', help='Input heightmap image')
ap.add_argument('STL', nargs='?', default=None, help='Output STL path (stdout)')
args = ap.parse_args()

#
# XCoordinate
#
# Apply affine transformation to get output X coordinate from image coordinates.
#
# Parameters:
#  r, c: row, column image coordinates
#
# Accesses:
#  transform tuple
#
# Returns:
#  x value
#
def XCoordinate(r, c):
	return transform[0] + (c * transform[1]) + (r * transform[2])

#
# YCoordinate
#
# Apply affine transformation to get output Y coordinate from image coordinates.
#
# Parameters:
#  r, c: row, column image coordinates
#
# Accesses:
#  transform tuple
#
# Returns:
#  y value
#
def YCoordinate(r, c):
	return transform[3] + (c * transform[4]) + (r * transform[5])

#
# ZCoordinate
#
# Convert an elevation value to output Z units. Applies clipping to minimum
# elevation (zmin, if nonzero); z scaling (and exaggeration); and base offset.
#
# Parameters:
#  elevation
#
# Accesses:
#  zscale, zmin, and args.base
#
# Returns:
#  z value
#
def ZCoordinate(e):
	return (zscale * (float(e) - zmin)) + args.base

#
# NormalVector
#
# Calculate the normal vector of a triangle. (Unit vector perpendicular to
# triangle surface, pointing away from the "outer" face of the surface.)
# Could be bundled into stlwriter class.
#
# Parameters:
#  triangle vertices (nested x y z tuples)
#
# Returns:
#  normal vector (x y z tuple)
#
def NormalVector(t):

	(ax, ay, az) = t[0]
	(bx, by, bz) = t[1]
	(cx, cy, cz) = t[2]

	# first edge
	e1x = ax - bx
	e1y = ay - by
	e1z = az - bz

	# second edge
	e2x = bx - cx
	e2y = by - cy
	e2z = bz - cz

	# cross product
	cpx = e1y * e2z - e1z * e2y
	cpy = e1z * e2x - e1x * e2z
	cpz = e1x * e2y - e1y * e2x

	# return cross product vector normalized to unit length
	mag = sqrt((cpx * cpx) + (cpy * cpy) + (cpz * cpz))
	return (cpx/mag, cpy/mag, cpz/mag)

#
# AddQuad
#
# a-c
# |/|
# b-d
#
# Parameters:
#  m: the stl mesh to add the quad to
#  a, b, c, d: vertices (x y z tuples)
#
# Results:
#  adds two triangle facets to mesh
#
def AddQuad(m, a, b, c, d):
	m.add_facet(NormalVector((a, b, c)), (a, b, c))
	m.add_facet(NormalVector((d, c, b)), (d, c, b))

try:
	img = gdal.Open(args.RASTER)
except RuntimeError, e:
	fail(str(e).strip())

# input raster dimensions
w = img.RasterXSize
h = img.RasterYSize

# output mesh dimensions are one row and column less than raster
mw = w - 1
mh = h - 1

# get default transformation from image coordinates to world coordinates
transform = img.GetGeoTransform()

# save x pixel size if needed for scaling
xyres = abs(transform[1])

# initialize z scale to exaggeration factor, if any
zscale = args.z

# recalculate z scale and xy transform if different dimensions are requested
if args.x != 0.0 or args.y != 0.0:

	# recaculate xy scale based on requested x or y dimension
	# if both x and y dimension are set, select smaller scale
	if args.x != 0.0 and args.y != 0.0:
		pixel_scale = min(args.x / mw, args.y / mh)
	elif args.x != 0.0:
		pixel_scale = args.x / mw
	elif args.y != 0.0:
		pixel_scale = args.y / mh

	# adjust z scale to maintain proportions with new xy scale
	zscale *= pixel_scale / xyres

	# revise transformation matrix
	# image: 0,0 at top left corner of top left pixel (0.5,0.5 at pixel center)
	transform = (
			-pixel_scale * mw / 2.0, # 0 left edge of top left pixel
			 pixel_scale,            # 1 pixel width
			 0,                      # 2
			 pixel_scale * mh / 2.0, # 3 top edge of top left pixel
			 0,                      # 4
			-pixel_scale             # 5 pixel height
	)

log(transform)

band = img.GetRasterBand(1)

# map GDAL pixel data type to corresponding struct format character
typemap = {
	gdal.GDT_Byte:    'B',
	gdal.GDT_UInt16:  'H',
	gdal.GDT_Int16:   'h',
	gdal.GDT_UInt32:  'I',
	gdal.GDT_Int32:   'i',
	gdal.GDT_Float32: 'f',
	gdal.GDT_Float64: 'd'
}

if band.DataType not in typemap:
	fail('Unsupported data type: %s' % (gdal.GetDataTypeName(band.DataType)))

rowformat = typemap.get(band.DataType) * w

# min, max, mean, sd; min used for z clipping
stats = band.GetStatistics(True, True)
log(stats)
if args.clip == True:
	zmin = stats[0]
else:
	zmin = 0

log('Initiating raster processing...')

# Space for two rows of image data is allocated. Extending the deque
# with a third (new) row of data automatically exposes the first (old).
pixels = deque(maxlen = (2 * w))

# not handling the data type flexibly here. should map raster datatype
# to an appropriate corresponding struct element specificier ('B', 'H', etc)
pixels.extend(unpack(rowformat, band.ReadRaster(0, 0, w, 1, w, 1, band.DataType)))

with stlwriter(mw * mh * 2, args.STL) as mesh:

	for y in range(mh):

		pixels.extend(unpack(rowformat, band.ReadRaster(0, y + 1, w, 1, w, 1, band.DataType)))

		for x in range(mw):

			ax = XCoordinate(y, x)
			ay = YCoordinate(y, x)
			az = ZCoordinate(pixels[x])

			bx = XCoordinate(y + 1, x)
			by = YCoordinate(y + 1, x)
			bz = ZCoordinate(pixels[w + x])

			cx = XCoordinate(y, x + 1)
			cy = YCoordinate(y, x + 1)
			cz = ZCoordinate(pixels[0 + x + 1])

			dx = XCoordinate(y + 1, x + 1)
			dy = YCoordinate(y + 1, x + 1)
			dz = ZCoordinate(pixels[w + x + 1])

			AddQuad(mesh, (ax, ay, az), (bx, by, bz), (cx, cy, cz), (dx, dy, dz))
<|endoftext|>


<commit_before>#SpnTUI.py

"""
Asciimatics colours
BLACK
BLUE
CYAN
GREEN
MAGENTA
RED
WHITE
YELLOW
"""

import traceback
import better_exceptions
import asciimatics
from asciimatics.screen import Screen
from spoon_logging import D, L, W, E
import SpoonModes
SM = SpoonModes.SpoonSliceModes()
LM = SpoonModes.SpoonLoopModes()

#12345678901234567890123456789012345678901234567890
DESC_NAME = """
░█▀▀░█▀█░█▀█░█▀█░█▀█░░░█▀▀░▀█▀░█▀▀░█░█░▀█▀
░▀▀█░█▀▀░█░█░█░█░█░█░░░█▀▀░░█░░█░█░█▀█░░█░
░▀▀▀░▀░░░▀▀▀░▀▀▀░▀░▀░░░▀░░░▀▀▀░▀▀▀░▀░▀░░▀░

 """

DESC_LINE1="""░█▀▀░█▀█░█▀█░█▀█░█▀█░░░█▀▀░▀█▀░█▀▀░█░█░▀█▀"""
DESC_LINE2="""░▀▀█░█▀▀░█░█░█░█░█░█░░░█▀▀░░█░░█░█░█▀█░░█░"""
DESC_LINE3="""░▀▀▀░▀░░░▀▀▀░▀▀▀░▀░▀░░░▀░░░▀▀▀░▀▀▀░▀░▀░░▀░"""

 # Logo width is 41??

#this sets

global_status = None
the_screen = None

first_time_draw = True

def fill_line(screen,line,char=' ', background=Screen.COLOUR_BLACK):
    for i in range(screen.width):
        screen.print_at(char,i,line,bg=background)
    return

LOOP_BLOCK_HEIGHT=2
LOOP_START_LINE=1

def get_loop_fg_colour(loop_mode):
    switcher={
        LM.PLAY : Screen.COLOUR_BLACK,  #bg is green
        LM.STOP : Screen.COLOUR_WHITE,
        LM.RANDOM : Screen.COLOUR_WHITE,
        LM.REVERSE : Screen.COLOUR_RED,
        LM.PLAY_SLAVE : Screen.COLOUR_WHITE,
        LM.PLAY_MASTER : Screen.COLOUR_WHITE
    }
    if loop_mode in switcher.keys():
        return switcher.get(loop_mode)
    else:
        return Screen.COLOUR_WHITE

def get_loop_bg_colour(loop_mode):
    switcher={
        LM.PLAY : Screen.COLOUR_GREEN,  #bg is green
        LM.STOP : Screen.COLOUR_BLACK,
        LM.RANDOM : Screen.COLOUR_BLUE,
        LM.REVERSE : Screen.COLOUR_CYAN,
        LM.PLAY_SLAVE : Screen.COLOUR_BLACK,
        LM.PLAY_MASTER : Screen.COLOUR_RED
    }
    if loop_mode in switcher.keys():
        return switcher.get(loop_mode)
    else:
        return Screen.COLOUR_WHITE

def get_slice_fg_colour(slice_mode):
    # returns forground colour of the slice
    switcher={
        SM.PLAY : Screen.COLOUR_BLACK,  #bg is green
        SM.MUTE : Screen.COLOUR_WHITE, #bg is red
        SM.RANDOM : Screen.COLOUR_WHITE, #bg is white
        SM.SKIP : Screen.COLOUR_WHITE, #bg is black
        SM.REVERSE : Screen.COLOUR_RED, #bg is yellow
        SM.DOUBLE : Screen.COLOUR_WHITE, #bg is magenta
        SM.HALF : Screen.COLOUR_WHITE    #bg is cyan
    }
    if slice_mode in switcher.keys():
        return switcher.get(slice_mode)
    # else return as default
    return Screen.COLOUR_WHITE

def get_slice_bg_colour(slice_mode):
    # returns forground colour of the slice
    switcher={
        SM.PLAY : Screen.COLOUR_GREEN,  #bg is green
        SM.MUTE : Screen.COLOUR_RED, #bg is red
        SM.RANDOM : Screen.COLOUR_BLUE, #bg is white
        SM.SKIP : Screen.COLOUR_BLACK, #bg is black
        SM.REVERSE : Screen.COLOUR_YELLOW, #bg is yellow
        SM.DOUBLE : Screen.COLOUR_MAGENTA, #bg is magenta
        SM.HALF : Screen.COLOUR_CYAN    #bg is cyan
    }
    if slice_mode in switcher.keys():
        return switcher.get(slice_mode)
    # else return as default
    return Screen.COLOUR_BLACK

def draw_loop_slices(screen, loop_to_display, x_start, y_start):
    #
    index = 0
    PRNT ="={}="

    for s in loop_to_display.audio_slices:
        x_loc = x_start + 5*index
        screen.print_at(PRNT.format(s.play_mode), x_loc, y_start, colour=get_slice_fg_colour(s.play_mode),bg=get_slice_bg_colour(s.play_mode))
        index+=1

    return

def draw_loop_box(screen,loop_to_display, loop_number):
    loop_y_offset = (LOOP_BLOCK_HEIGHT+1)*loop_number+LOOP_START_LINE
    fill_line(screen, loop_y_offset, background=Screen.COLOUR_WHITE)
    fill_line(screen, loop_y_offset+1, background=Screen.COLOUR_WHITE)
    screen.print_at(loop_to_display.name, 0, loop_y_offset, colour=Screen.COLOUR_BLUE, bg=Screen.COLOUR_WHITE)
    screen.print_at(":{}".format(loop_to_display.current_slice),7, loop_y_offset, colour=Screen.COLOUR_RED, bg=Screen.COLOUR_WHITE)
    screen.print_at(":[",9, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )
    screen.print_at("]",51, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )
    screen.print_at(loop_to_display.audio_file_name, 0, loop_y_offset+1, colour=Screen.COLOUR_BLACK,bg=Screen.COLOUR_WHITE )

    screen.print_at(":[",52, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )
    fgc = get_loop_fg_colour(loop_to_display.loop_mode)
    bgc = get_loop_bg_colour(loop_to_display.loop_mode)

    screen.print_at(loop_to_display.loop_mode, 54, loop_y_offset, colour=fgc, bg=bgc)
    screen.print_at("]",60, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )

    draw_loop_slices( screen, loop_to_display, 11, loop_y_offset)
    return

def display_messages(screen, the_status):
    y_offset = 17

    #clean the area
    for i in range(5):
        fill_line(screen, y_offset+i, background=Screen.COLOUR_BLACK)

    # lets display the last 5 messages
    if( len(the_status.event_history)>4 ):
        for mes_s in the_status.event_history[-5:]:
            screen.print_at(mes_s,1, y_offset, colour=Screen.COLOUR_WHITE, bg=Screen.COLOUR_BLACK )
            y_offset+=1
    else:
        for mes_s in the_status.event_history:
            screen.print_at(mes_s,1, y_offset, colour=Screen.COLOUR_WHITE, bg=Screen.COLOUR_BLACK )
            y_offset+=1

    return

def draw_the_tui(screen, the_status ):
    # draw top border
    #screen.move(0,0)
    #fill_line(screen,0)

    global first_time_draw

    if(first_time_draw):
        #draw LOGO
        screen.move(0,0)
        screen.centre(DESC_LINE1,0)
        screen.centre(DESC_LINE2,1)
        screen.centre(DESC_LINE3,2)
        #draw bottom border
        fill_line(screen,screen.height-1,background=Screen.COLOUR_YELLOW)
        # write the quite message
        screen.print_at(u'☠  press q to quit  ☠    press m to mute', 2, screen.height-1, attr=Screen.A_BOLD, colour=Screen.COLOUR_RED,bg=Screen.COLOUR_YELLOW)

        #draw seperator line
        fill_line(screen,3,char='=')
        #draw slice identifier names
        screen.print_at("0----1----2----3----4----5----6----7", 13,3,colour=Screen.COLOUR_WHITE, bg=Screen.COLOUR_BLACK)
        #draw seperator line
        fill_line(screen,15,char='=')

        first_time_draw = False

    draw_loop_box(screen, the_status.loops['/loop/1'] , 1)
    draw_loop_box(screen, the_status.loops['/loop/2'] , 2)
    draw_loop_box(screen, the_status.loops['/loop/3'] , 3)
    draw_loop_box(screen, the_status.loops['/loop/4'] , 4)
    display_messages(screen, the_status)
    #draw content
    return 1<commit_msg>add volume display<commit_after>#SpnTUI.py

"""
Asciimatics colours
BLACK
BLUE
CYAN
GREEN
MAGENTA
RED
WHITE
YELLOW
"""

import traceback
import better_exceptions
import asciimatics
from asciimatics.screen import Screen
from spoon_logging import D, L, W, E
import SpoonModes
SM = SpoonModes.SpoonSliceModes()
LM = SpoonModes.SpoonLoopModes()

#12345678901234567890123456789012345678901234567890
DESC_NAME = """
░█▀▀░█▀█░█▀█░█▀█░█▀█░░░█▀▀░▀█▀░█▀▀░█░█░▀█▀
░▀▀█░█▀▀░█░█░█░█░█░█░░░█▀▀░░█░░█░█░█▀█░░█░
░▀▀▀░▀░░░▀▀▀░▀▀▀░▀░▀░░░▀░░░▀▀▀░▀▀▀░▀░▀░░▀░

 """

DESC_LINE1="""░█▀▀░█▀█░█▀█░█▀█░█▀█░░░█▀▀░▀█▀░█▀▀░█░█░▀█▀"""
DESC_LINE2="""░▀▀█░█▀▀░█░█░█░█░█░█░░░█▀▀░░█░░█░█░█▀█░░█░"""
DESC_LINE3="""░▀▀▀░▀░░░▀▀▀░▀▀▀░▀░▀░░░▀░░░▀▀▀░▀▀▀░▀░▀░░▀░"""

 # Logo width is 41??

#this sets

global_status = None
the_screen = None

first_time_draw = True

def fill_line(screen,line,char=' ', background=Screen.COLOUR_BLACK):
    for i in range(screen.width):
        screen.print_at(char,i,line,bg=background)
    return

LOOP_BLOCK_HEIGHT=2
LOOP_START_LINE=1

def get_loop_fg_colour(loop_mode):
    switcher={
        LM.PLAY : Screen.COLOUR_BLACK,  #bg is green
        LM.STOP : Screen.COLOUR_WHITE,
        LM.RANDOM : Screen.COLOUR_WHITE,
        LM.REVERSE : Screen.COLOUR_RED,
        LM.PLAY_SLAVE : Screen.COLOUR_WHITE,
        LM.PLAY_MASTER : Screen.COLOUR_WHITE
    }
    if loop_mode in switcher.keys():
        return switcher.get(loop_mode)
    else:
        return Screen.COLOUR_WHITE

def get_loop_bg_colour(loop_mode):
    switcher={
        LM.PLAY : Screen.COLOUR_GREEN,  #bg is green
        LM.STOP : Screen.COLOUR_BLACK,
        LM.RANDOM : Screen.COLOUR_BLUE,
        LM.REVERSE : Screen.COLOUR_CYAN,
        LM.PLAY_SLAVE : Screen.COLOUR_BLACK,
        LM.PLAY_MASTER : Screen.COLOUR_RED
    }
    if loop_mode in switcher.keys():
        return switcher.get(loop_mode)
    else:
        return Screen.COLOUR_WHITE

def get_slice_fg_colour(slice_mode):
    # returns forground colour of the slice
    switcher={
        SM.PLAY : Screen.COLOUR_BLACK,  #bg is green
        SM.MUTE : Screen.COLOUR_WHITE, #bg is red
        SM.RANDOM : Screen.COLOUR_WHITE, #bg is white
        SM.SKIP : Screen.COLOUR_WHITE, #bg is black
        SM.REVERSE : Screen.COLOUR_RED, #bg is yellow
        SM.DOUBLE : Screen.COLOUR_WHITE, #bg is magenta
        SM.HALF : Screen.COLOUR_WHITE    #bg is cyan
    }
    if slice_mode in switcher.keys():
        return switcher.get(slice_mode)
    # else return as default
    return Screen.COLOUR_WHITE

def get_slice_bg_colour(slice_mode):
    # returns forground colour of the slice
    switcher={
        SM.PLAY : Screen.COLOUR_GREEN,  #bg is green
        SM.MUTE : Screen.COLOUR_RED, #bg is red
        SM.RANDOM : Screen.COLOUR_BLUE, #bg is white
        SM.SKIP : Screen.COLOUR_BLACK, #bg is black
        SM.REVERSE : Screen.COLOUR_YELLOW, #bg is yellow
        SM.DOUBLE : Screen.COLOUR_MAGENTA, #bg is magenta
        SM.HALF : Screen.COLOUR_CYAN    #bg is cyan
    }
    if slice_mode in switcher.keys():
        return switcher.get(slice_mode)
    # else return as default
    return Screen.COLOUR_BLACK

def draw_loop_slices(screen, loop_to_display, x_start, y_start):
    #
    index = 0
    PRNT ="={}="

    for s in loop_to_display.audio_slices:
        x_loc = x_start + 5*index
        screen.print_at(PRNT.format(s.play_mode), x_loc, y_start, colour=get_slice_fg_colour(s.play_mode),bg=get_slice_bg_colour(s.play_mode))
        index+=1

    return

def draw_loop_box(screen,loop_to_display, loop_number):
    loop_y_offset = (LOOP_BLOCK_HEIGHT+1)*loop_number+LOOP_START_LINE
    fill_line(screen, loop_y_offset, background=Screen.COLOUR_WHITE)
    fill_line(screen, loop_y_offset+1, background=Screen.COLOUR_WHITE)
    screen.print_at(loop_to_display.name, 0, loop_y_offset, colour=Screen.COLOUR_BLUE, bg=Screen.COLOUR_WHITE)
    screen.print_at(":{}".format(loop_to_display.current_slice),7, loop_y_offset, colour=Screen.COLOUR_RED, bg=Screen.COLOUR_WHITE)
    screen.print_at(":[",9, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )
    screen.print_at("]",51, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )
    screen.print_at(loop_to_display.audio_file_name, 0, loop_y_offset+1, colour=Screen.COLOUR_BLACK,bg=Screen.COLOUR_WHITE )

    screen.print_at(":[",52, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )
    fgc = get_loop_fg_colour(loop_to_display.loop_mode)
    bgc = get_loop_bg_colour(loop_to_display.loop_mode)

    screen.print_at(loop_to_display.loop_mode, 54, loop_y_offset, colour=fgc, bg=bgc)
    screen.print_at("]",60, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE )

    tmp_vol_s = "V:{0:.2f}".format(loop_to_display.volume_gain)
    screen.print_at(tmp_vol_s, 73, loop_y_offset, colour=Screen.COLOUR_BLACK, bg=Screen.COLOUR_WHITE)

    draw_loop_slices( screen, loop_to_display, 11, loop_y_offset)
    return

def display_messages(screen, the_status):
    y_offset = 17

    #clean the area
    for i in range(5):
        fill_line(screen, y_offset+i, background=Screen.COLOUR_BLACK)

    # lets display the last 5 messages
    if( len(the_status.event_history)>4 ):
        for mes_s in the_status.event_history[-5:]:
            screen.print_at(mes_s,1, y_offset, colour=Screen.COLOUR_WHITE, bg=Screen.COLOUR_BLACK )
            y_offset+=1
    else:
        for mes_s in the_status.event_history:
            screen.print_at(mes_s,1, y_offset, colour=Screen.COLOUR_WHITE, bg=Screen.COLOUR_BLACK )
            y_offset+=1

    return

def draw_the_tui(screen, the_status ):
    # draw top border
    #screen.move(0,0)
    #fill_line(screen,0)

    global first_time_draw

    if(first_time_draw):
        #draw LOGO
        screen.move(0,0)
        screen.centre(DESC_LINE1,0)
        screen.centre(DESC_LINE2,1)
        screen.centre(DESC_LINE3,2)
        #draw bottom border
        fill_line(screen,screen.height-1,background=Screen.COLOUR_YELLOW)
        # write the quite message
        screen.print_at(u'☠  press q to quit  ☠    press m to mute', 2, screen.height-1, attr=Screen.A_BOLD, colour=Screen.COLOUR_RED,bg=Screen.COLOUR_YELLOW)

        #draw seperator line
        fill_line(screen,3,char='=')
        #draw slice identifier names
        screen.print_at("0----1----2----3----4----5----6----7", 13,3,colour=Screen.COLOUR_WHITE, bg=Screen.COLOUR_BLACK)
        #draw seperator line
        fill_line(screen,15,char='=')

        first_time_draw = False

    draw_loop_box(screen, the_status.loops['/loop/1'] , 1)
    draw_loop_box(screen, the_status.loops['/loop/2'] , 2)
    draw_loop_box(screen, the_status.loops['/loop/3'] , 3)
    draw_loop_box(screen, the_status.loops['/loop/4'] , 4)
    display_messages(screen, the_status)
    #draw content
    return 1<|endoftext|>


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

"""
QC checks (linter) for recipes, returning a TSV of issues identified.

The strategy here is to use simple functions that do a single check on
a recipe. When run on a single recipe it can be used for linting new
contributions; when run on all recipes it helps highlight entire classes of
problems to be addressed.

See the `lint_functions` module for these.

After writing the function, register it in the global `registry` dict,
`lint_functions.registry`.

The output is a TSV where the "info" column contains the dicts returned by
each check function, and this column is expanded into multiple extra colums.
While this results in a lot of NaNs, it makes it easy to drop non-interesting
cases with pandas, e.g.,

   recipes_with_missing_tests = df.dropna(subset=['no_tests'])

or

    def not_in_bioconda(x):
        if not isinstance(x, set):
            return np.nan
        res = set(x).difference(['bioconda'])
        if len(res):
            return(res)
        return np.nan

    df['other'] = df.exists_in_channel.apply(not_in_bioconda)
    other_channels = df[['recipe', 'other']].dropna()

---------------------------------------------------------------------------
TODO:

- check version and build number against master branch. I think there's stuff
  in bioconductor updating to handle this sort of thing. Also bioconda_utils
  has utils for checking against master branch.

  - if version changed, ensure build number is 0
  - if version unchanged, ensure build number incremented

- currently we only check a single environment (see the `get_meta` function).
  This should probably be converted to a generator function.

- currently we don't pay attention to py27/py3. It would be nice to handle that.

- how to define valid licenses?
  (conda_build.metadata.ensure_valid_license_family is for family)

- gcc/llvm have their respective preprocessing selectors

- excessive comments (from skeletons?)
"""

usage = """
Perform various checks on recipes.
"""

import os
import sys
import glob
import re
import json
import argparse
from collections import defaultdict

import pandas as pd
import numpy as np
import ruamel_yaml as yaml
import jinja2
from conda_build import metadata

from . import utils
from . import lint_functions

import logging
logger = logging.getLogger(__name__)

def get_meta(recipe, config):
    """
    Given a package name, find the current meta.yaml file, parse it, and return
    the dict.
    """
    cfg = utils.load_config(config)
    env_matrix = cfg['env_matrix']

    # TODO: Currently just uses the first env. Should turn this into
    # a generator.

    env = dict(next(iter(utils.EnvMatrix(yaml.load(open(env_matrix))))))
    pth = os.path.join(recipe, 'meta.yaml')
    jinja_env = jinja2.Environment()
    content = jinja_env.from_string(open(pth, 'r', encoding='utf-8').read()).render(env)
    meta = yaml.load(content, yaml.RoundTripLoader)
    return meta

def channel_dataframe(cache=None, channels=['bioconda', 'conda-forge', 'defaults', 'r']):
    if cache is not None and os.path.exists(cache):
        df = pd.read_table(cache)
    else:
        # Get the channel data into a big dataframe
        dfs = []
        for platform in ['linux', 'osx']:
            for channel in channels:
                repo, noarch = utils.get_channel_repodata(channel, platform)
                x = pd.DataFrame(repo)
                x = x.drop([
                    'arch',
                    'default_numpy_version',
                    'default_python_version',
                    'platform',
                    'subdir'])
                for k in ['build', 'build_number', 'name', 'version', 'license', 'platform']:
                    x[k] = x['packages'].apply(lambda y: y.get(k, np.nan))

                x['channel'] = channel
                dfs.append(x)

        df = pd.concat(dfs).drop(['info', 'packages'], axis=1)

        if cache is not None:
            df.to_csv(cache, sep='\t')
    return df

def lint(packages, config, df, registry=None):
    """
    Parameters
    ----------

    packages : list
        List of packages to lint

    config : str, dict
        Used to pass any necessary environment variables (CONDA_BOOST, etc) to
        meta.yaml files. If str, path to config file. If dict, parsed version
        of the config file. If `quick=True`, then must be path to config file.

    df : pandas.DataFrame
        Dataframe containing channel data, typically as output from
        `channel_dataframe()`

    registry : list or tuple
        List of functions to apply to each recipe. If None, defaults to
        `lint_functions.registry`.
    """

    if registry is None:
        registry = lint_functions.registry

    skip_dict = defaultdict(list)

    txt = ""
    if 'LINT_SKIP' in os.environ:
        txt = os.environ['LINT_SKIP']
    elif os.environ.get('TRAVIS') == 'true':
        if os.environ.get('TRAVIS_COMMIT'):
            # In pull requests, TRAVIS_COMMIT_MESSAGE doesn't seem to be the latest
            # commit message but rather a "merge $hash1 into $hash2" message.
            # So we extract the actual commit message here.
            p = utils.run(['git', 'log', '--format=%B', '-n', '1', os.environ['TRAVIS_COMMIT']])
            txt = p.stdout

    # For example the following text in the commit message will skip
    # lint_functions.uses_setuptools:
    #
    # [ lint skip uses_setuptools ]
    print('txt:', txt)
    print('commit:', os.environ['TRAVIS_COMMIT'])

    skip_re = re.compile(r'\[\s*lint skip (?P<func>\w+) for (?P<recipe>.*?)\s*\]')
    to_skip = skip_re.findall(txt)
    for func, recipe in to_skip:
        skip_dict[recipe].append(func)

    hits = []
    for recipe in packages:
        try:
            meta = get_meta(recipe, config)
        except (yaml.scanner.ScannerError, yaml.constructor.ConstructorError) as e:
            result = {'parse_error': str(e)}
            hits.append(
                {'recipe': recipe,
                 'check': 'parse_error',
                 'severity': 'ERROR',
                 'info': result})
            continue
        skip_for_this_recipe = skip_dict[recipe]
        for func in registry:
            if func.__name__ in skip_for_this_recipe:
                logger.info('Commit message defines skip lint test %s for recipe %s' % (func.__name__, recipe))
                continue
            result = func(recipe, meta, df)
            if result:
                hits.append(
                    {'recipe': recipe,
                     'check': func.__name__,
                     'info': result})

    if hits:
        report = pd.DataFrame(hits)[['recipe', 'check', 'info']]

        # expand out the info into more columns
        info = pd.DataFrame(list(report['info'].values))
        report = pd.concat((report, info), axis=1)
        return report
    else:
        return
<commit_msg>debugging<commit_after>#!/usr/bin/env python

"""
QC checks (linter) for recipes, returning a TSV of issues identified.

The strategy here is to use simple functions that do a single check on
a recipe. When run on a single recipe it can be used for linting new
contributions; when run on all recipes it helps highlight entire classes of
problems to be addressed.

See the `lint_functions` module for these.

After writing the function, register it in the global `registry` dict,
`lint_functions.registry`.

The output is a TSV where the "info" column contains the dicts returned by
each check function, and this column is expanded into multiple extra colums.
While this results in a lot of NaNs, it makes it easy to drop non-interesting
cases with pandas, e.g.,

   recipes_with_missing_tests = df.dropna(subset=['no_tests'])

or

    def not_in_bioconda(x):
        if not isinstance(x, set):
            return np.nan
        res = set(x).difference(['bioconda'])
        if len(res):
            return(res)
        return np.nan

    df['other'] = df.exists_in_channel.apply(not_in_bioconda)
    other_channels = df[['recipe', 'other']].dropna()

---------------------------------------------------------------------------
TODO:

- check version and build number against master branch. I think there's stuff
  in bioconductor updating to handle this sort of thing. Also bioconda_utils
  has utils for checking against master branch.

  - if version changed, ensure build number is 0
  - if version unchanged, ensure build number incremented

- currently we only check a single environment (see the `get_meta` function).
  This should probably be converted to a generator function.

- currently we don't pay attention to py27/py3. It would be nice to handle that.

- how to define valid licenses?
  (conda_build.metadata.ensure_valid_license_family is for family)

- gcc/llvm have their respective preprocessing selectors

- excessive comments (from skeletons?)
"""

usage = """
Perform various checks on recipes.
"""

import os
import sys
import glob
import re
import json
import argparse
from collections import defaultdict

import pandas as pd
import numpy as np
import ruamel_yaml as yaml
import jinja2
from conda_build import metadata

from . import utils
from . import lint_functions

import logging
logger = logging.getLogger(__name__)

def get_meta(recipe, config):
    """
    Given a package name, find the current meta.yaml file, parse it, and return
    the dict.
    """
    cfg = utils.load_config(config)
    env_matrix = cfg['env_matrix']

    # TODO: Currently just uses the first env. Should turn this into
    # a generator.

    env = dict(next(iter(utils.EnvMatrix(yaml.load(open(env_matrix))))))
    pth = os.path.join(recipe, 'meta.yaml')
    jinja_env = jinja2.Environment()
    content = jinja_env.from_string(open(pth, 'r', encoding='utf-8').read()).render(env)
    meta = yaml.load(content, yaml.RoundTripLoader)
    return meta

def channel_dataframe(cache=None, channels=['bioconda', 'conda-forge', 'defaults', 'r']):
    if cache is not None and os.path.exists(cache):
        df = pd.read_table(cache)
    else:
        # Get the channel data into a big dataframe
        dfs = []
        for platform in ['linux', 'osx']:
            for channel in channels:
                repo, noarch = utils.get_channel_repodata(channel, platform)
                x = pd.DataFrame(repo)
                x = x.drop([
                    'arch',
                    'default_numpy_version',
                    'default_python_version',
                    'platform',
                    'subdir'])
                for k in ['build', 'build_number', 'name', 'version', 'license', 'platform']:
                    x[k] = x['packages'].apply(lambda y: y.get(k, np.nan))

                x['channel'] = channel
                dfs.append(x)

        df = pd.concat(dfs).drop(['info', 'packages'], axis=1)

        if cache is not None:
            df.to_csv(cache, sep='\t')
    return df

def lint(packages, config, df, registry=None):
    """
    Parameters
    ----------

    packages : list
        List of packages to lint

    config : str, dict
        Used to pass any necessary environment variables (CONDA_BOOST, etc) to
        meta.yaml files. If str, path to config file. If dict, parsed version
        of the config file. If `quick=True`, then must be path to config file.

    df : pandas.DataFrame
        Dataframe containing channel data, typically as output from
        `channel_dataframe()`

    registry : list or tuple
        List of functions to apply to each recipe. If None, defaults to
        `lint_functions.registry`.
    """

    if registry is None:
        registry = lint_functions.registry

    skip_dict = defaultdict(list)

    txt = ""
    if 'LINT_SKIP' in os.environ:
        txt = os.environ['LINT_SKIP']
    elif os.environ.get('TRAVIS') == 'true':
        if os.environ.get('TRAVIS_COMMIT'):
            # In pull requests, TRAVIS_COMMIT_MESSAGE doesn't seem to be the latest
            # commit message but rather a "merge $hash1 into $hash2" message.
            # So we extract the actual commit message here.
            p = utils.run(['git', 'log', '--format=%B', '-n', '1', 'HEAD'])
            txt = p.stdout

    # For example the following text in the commit message will skip
    # lint_functions.uses_setuptools:
    #
    # [ lint skip uses_setuptools ]
    print('txt:', txt)
    print('commit:', os.environ['TRAVIS_COMMIT'])

    skip_re = re.compile(r'\[\s*lint skip (?P<func>\w+) for (?P<recipe>.*?)\s*\]')
    to_skip = skip_re.findall(txt)
    for func, recipe in to_skip:
        skip_dict[recipe].append(func)

    hits = []
    for recipe in packages:
        try:
            meta = get_meta(recipe, config)
        except (yaml.scanner.ScannerError, yaml.constructor.ConstructorError) as e:
            result = {'parse_error': str(e)}
            hits.append(
                {'recipe': recipe,
                 'check': 'parse_error',
                 'severity': 'ERROR',
                 'info': result})
            continue
        skip_for_this_recipe = skip_dict[recipe]
        for func in registry:
            if func.__name__ in skip_for_this_recipe:
                logger.info('Commit message defines skip lint test %s for recipe %s' % (func.__name__, recipe))
                continue
            result = func(recipe, meta, df)
            if result:
                hits.append(
                    {'recipe': recipe,
                     'check': func.__name__,
                     'info': result})

    if hits:
        report = pd.DataFrame(hits)[['recipe', 'check', 'info']]

        # expand out the info into more columns
        info = pd.DataFrame(list(report['info'].values))
        report = pd.concat((report, info), axis=1)
        return report
    else:
        return
<|endoftext|>


<commit_before>import html.parser
import logging
import os

import sublime
import sublime_plugin

from ..lib import get_setting
from ..lib.sublime_lib.constants import style_flags_from_list
from ..lib.weakmethod import WeakMethodProxy

from .region_math import (VALUE_SCOPE, KEY_SCOPE, KEY_COMPLETIONS_SCOPE,
                          get_key_region_at, get_last_key_region)
from .known_settings import KnownSettings

__all__ = (
    'SettingsListener',
    'GlobalSettingsListener',
)

POPUP_TEMPLATE = """

{0}

"""

PHANTOM_TEMPLATE = """

{0}

"""

# user package pattern
USER_PATH = "{0}Packages{0}User{0}".format(os.sep)

# logging
l = logging.getLogger(__name__)

class SettingsListener(sublime_plugin.ViewEventListener):

    is_completing_key = False

    @classmethod
    def is_applicable(cls, settings):
        """Enable the listener for Sublime Settings syntax only."""
        syntax = settings.get('syntax') or ""
        return (syntax.endswith("/Sublime Text Settings.sublime-syntax")
                or syntax.endswith("/Sublime Text Project.sublime-syntax"))

    def __init__(self, view):
        """Initialize view event listener object."""
        # Need this "hack" to allow reloading of the module,
        # because `super` tries to use the old reference (I think?).
        # See also https://lists.gt.net/python/python/139992
        sublime_plugin.ViewEventListener.__init__(self, view)

        filepath = view.file_name()
        l.debug("initializing SettingsListener for %r", view.file_name())

        if filepath and filepath.endswith(".sublime-settings"):
            filename = os.path.basename(filepath)
            self.known_settings = KnownSettings(filename)
            self.known_settings.add_on_loaded(self.do_linting)
        elif filepath and filepath.endswith(".sublime-project"):
            self.known_settings = KnownSettings("Preferences.sublime-settings")
            self.known_settings.add_on_loaded(self.do_linting)
        else:
            self.known_settings = None
            l.error("Not a Sublime Text Settings or Project file: %r", filepath)

        self.phantom_set = sublime.PhantomSet(self.view, "sublime-settings-edit")
        if self._is_base_settings_view():
            self.build_phantoms()

    def __del__(self):
        l.debug("deleting SettingsListener instance for %r", self.view.file_name())
        self.view.erase_regions('unknown_settings_keys')
        self.phantom_set.update([])

    def on_modified_async(self):
        """Sublime Text modified event handler to update linting."""
        self.do_linting()
        if self._is_base_settings_view():
            # This may only occur for unpacked packages
            self.build_phantoms()

    def on_query_completions(self, prefix, locations):
        """Sublime Text query completions event handler.

        Create a list with completions for all known settings or values.

        Arguments:
            prefix (string):
                the line content before cursor
            locations (list of int):
                the text positions of all characters in prefix

        Returns:
            tuple ([ [trigger, content], [trigger, content] ], flags):
                the tuple with content ST needs to display completions
        """
        if self.known_settings and len(locations) == 1:
            point = locations[0]
            self.is_completing_key = False
            if self.view.match_selector(point, VALUE_SCOPE):
                completions_aggregator = self.known_settings.value_completions
            elif self.view.match_selector(point, KEY_COMPLETIONS_SCOPE):
                completions_aggregator = self.known_settings.key_completions
                self.is_completing_key = True
            else:
                return None
            return completions_aggregator(self.view, prefix, point)

    def on_hover(self, point, hover_zone):
        """Sublime Text hover event handler to show tooltip if needed."""
        # not a settings file or not hovering text
        if not self.known_settings or hover_zone != sublime.HOVER_TEXT:
            return
        # settings key name under cursor
        key_region = get_key_region_at(self.view, point)
        if not key_region:
            return

        self.show_popup_for(key_region)

    def show_popup_for(self, key_region):
        key = self.view.substr(key_region)

        body = self.known_settings.build_tooltip(self.view, key)
        window_width = min(1000, int(self.view.viewport_extent()[0]) - 64)
        # offset
 padding, if possible
        key_start = key_region.begin()
        location = max(key_start - 1, self.view.line(key_start).begin())

        self.view.show_popup(
            content=POPUP_TEMPLATE.format(body),
            location=location,
            max_width=window_width,
            flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY | sublime.COOPERATE_WITH_AUTO_COMPLETE
        )

    def on_navigate(self, href):
        """Popup navigation event handler."""
        command, _, argument = href.partition(":")
        argument = html.parser.HTMLParser().unescape(argument)

        if command == 'edit':
            view_id = self.view.settings().get('edit_settings_other_view_id')
            user_view = sublime.View(view_id)
            if not user_view.is_valid():
                return
            result = user_view.find('"{}"'.format(argument), 0)
            self.view.hide_popup()
            if self.view.window():
                self.view.window().focus_view(user_view)
            if result.a == -1:
                self.known_settings.insert_snippet(user_view, argument)
            else:
                user_view.sel().clear()
                user_view.show_at_center(result.end())
                user_view.sel().add(result.end() + 2)

    def do_linting(self):
        """Highlight all unknown settings keys."""
        unknown_regions = None
        file_name = self.view.file_name() or ""
        if (
            self.known_settings
            and (USER_PATH in file_name
                 or file_name.endswith(".sublime-project"))
            and get_setting('settings.linting')
        ):
            unknown_regions = [
                region for region in self.view.find_by_selector(KEY_SCOPE)
                if self.view.substr(region) not in self.known_settings
            ]

        if unknown_regions:
            styles = get_setting(
                'settings.highlight_styles',
                ['DRAW_SOLID_UNDERLINE', 'DRAW_NO_FILL', 'DRAW_NO_OUTLINE']
            )
            self.view.add_regions(
                'unknown_settings_keys',
                unknown_regions,
                scope=get_setting('settings.highlight_scope', "text"),
                icon='dot',
                flags=style_flags_from_list(styles)
            )
        else:
            self.view.erase_regions('unknown_settings_keys')

    def build_phantoms(self):
        """Add links to side-by-side base file for editing this setting in the user file."""
        l.debug("Building phantom set for view %r", self.view.file_name())
        key_regions = self.view.find_by_selector(KEY_SCOPE)
        phantoms = []
        for region in key_regions:
            key_name = self.view.substr(region)
            phantom_region = sublime.Region(region.end() + 1)  # before colon
            content = "✏".format(html.escape(key_name))
            phantoms.append(sublime.Phantom(
                region=phantom_region,
                content=PHANTOM_TEMPLATE.format(content),
                layout=sublime.LAYOUT_INLINE,
                # use weak reference for callback
                # to allow for phantoms to be cleaned up in __del__
                on_navigate=WeakMethodProxy(self.on_navigate),
            ))

        self.phantom_set.update(phantoms)

    def _is_base_settings_view(self):
        return self.view.settings().get('edit_settings_view') == 'base'

# Some hooks are not available to ViewEventListeners,
# which is why we need an EventListener as well.
class GlobalSettingsListener(sublime_plugin.EventListener):

    def on_post_text_command(self, view, command_name, args):
        if command_name == 'hide_auto_complete':
            listener = sublime_plugin.find_view_event_listener(view, SettingsListener)
            if listener:
                listener.is_completing_key = False
        elif command_name in ('commit_completion', 'insert_best_completion'):
            listener = sublime_plugin.find_view_event_listener(view, SettingsListener)
            if not (listener and listener.is_completing_key):
                return

            listener.is_completing_key = False
            sel = view.sel()
            if len(sel) != 1:
                # unclear what to do, so just do nothing
                return
            point = sel[0].begin()
            key_region = get_last_key_region(view, point)
            if key_region:
                key = view.substr(key_region)
                l.debug("showing popup after inserting key completion for %r", key)
                listener.show_popup_for(key_region)

    def on_post_save(self, view):
        listener = sublime_plugin.find_view_event_listener(view, SettingsListener)
        if listener and listener.known_settings:
            listener.known_settings.trigger_settings_reload()
<commit_msg>Use transparent background for settings phantoms (#193)<commit_after>import html.parser
import logging
import os

import sublime
import sublime_plugin

from ..lib import get_setting
from ..lib.sublime_lib.constants import style_flags_from_list
from ..lib.weakmethod import WeakMethodProxy

from .region_math import (VALUE_SCOPE, KEY_SCOPE, KEY_COMPLETIONS_SCOPE,
                          get_key_region_at, get_last_key_region)
from .known_settings import KnownSettings

__all__ = (
    'SettingsListener',
    'GlobalSettingsListener',
)

POPUP_TEMPLATE = """

{0}

"""

PHANTOM_TEMPLATE = """

{0}

"""

# user package pattern
USER_PATH = "{0}Packages{0}User{0}".format(os.sep)

# logging
l = logging.getLogger(__name__)

class SettingsListener(sublime_plugin.ViewEventListener):

    is_completing_key = False

    @classmethod
    def is_applicable(cls, settings):
        """Enable the listener for Sublime Settings syntax only."""
        syntax = settings.get('syntax') or ""
        return (syntax.endswith("/Sublime Text Settings.sublime-syntax")
                or syntax.endswith("/Sublime Text Project.sublime-syntax"))

    def __init__(self, view):
        """Initialize view event listener object."""
        # Need this "hack" to allow reloading of the module,
        # because `super` tries to use the old reference (I think?).
        # See also https://lists.gt.net/python/python/139992
        sublime_plugin.ViewEventListener.__init__(self, view)

        filepath = view.file_name()
        l.debug("initializing SettingsListener for %r", view.file_name())

        if filepath and filepath.endswith(".sublime-settings"):
            filename = os.path.basename(filepath)
            self.known_settings = KnownSettings(filename)
            self.known_settings.add_on_loaded(self.do_linting)
        elif filepath and filepath.endswith(".sublime-project"):
            self.known_settings = KnownSettings("Preferences.sublime-settings")
            self.known_settings.add_on_loaded(self.do_linting)
        else:
            self.known_settings = None
            l.error("Not a Sublime Text Settings or Project file: %r", filepath)

        self.phantom_set = sublime.PhantomSet(self.view, "sublime-settings-edit")
        if self._is_base_settings_view():
            self.build_phantoms()

    def __del__(self):
        l.debug("deleting SettingsListener instance for %r", self.view.file_name())
        self.view.erase_regions('unknown_settings_keys')
        self.phantom_set.update([])

    def on_modified_async(self):
        """Sublime Text modified event handler to update linting."""
        self.do_linting()
        if self._is_base_settings_view():
            # This may only occur for unpacked packages
            self.build_phantoms()

    def on_query_completions(self, prefix, locations):
        """Sublime Text query completions event handler.

        Create a list with completions for all known settings or values.

        Arguments:
            prefix (string):
                the line content before cursor
            locations (list of int):
                the text positions of all characters in prefix

        Returns:
            tuple ([ [trigger, content], [trigger, content] ], flags):
                the tuple with content ST needs to display completions
        """
        if self.known_settings and len(locations) == 1:
            point = locations[0]
            self.is_completing_key = False
            if self.view.match_selector(point, VALUE_SCOPE):
                completions_aggregator = self.known_settings.value_completions
            elif self.view.match_selector(point, KEY_COMPLETIONS_SCOPE):
                completions_aggregator = self.known_settings.key_completions
                self.is_completing_key = True
            else:
                return None
            return completions_aggregator(self.view, prefix, point)

    def on_hover(self, point, hover_zone):
        """Sublime Text hover event handler to show tooltip if needed."""
        # not a settings file or not hovering text
        if not self.known_settings or hover_zone != sublime.HOVER_TEXT:
            return
        # settings key name under cursor
        key_region = get_key_region_at(self.view, point)
        if not key_region:
            return

        self.show_popup_for(key_region)

    def show_popup_for(self, key_region):
        key = self.view.substr(key_region)

        body = self.known_settings.build_tooltip(self.view, key)
        window_width = min(1000, int(self.view.viewport_extent()[0]) - 64)
        # offset
 padding, if possible
        key_start = key_region.begin()
        location = max(key_start - 1, self.view.line(key_start).begin())

        self.view.show_popup(
            content=POPUP_TEMPLATE.format(body),
            location=location,
            max_width=window_width,
            flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY | sublime.COOPERATE_WITH_AUTO_COMPLETE
        )

    def on_navigate(self, href):
        """Popup navigation event handler."""
        command, _, argument = href.partition(":")
        argument = html.parser.HTMLParser().unescape(argument)

        if command == 'edit':
            view_id = self.view.settings().get('edit_settings_other_view_id')
            user_view = sublime.View(view_id)
            if not user_view.is_valid():
                return
            result = user_view.find('"{}"'.format(argument), 0)
            self.view.hide_popup()
            if self.view.window():
                self.view.window().focus_view(user_view)
            if result.a == -1:
                self.known_settings.insert_snippet(user_view, argument)
            else:
                user_view.sel().clear()
                user_view.show_at_center(result.end())
                user_view.sel().add(result.end() + 2)

    def do_linting(self):
        """Highlight all unknown settings keys."""
        unknown_regions = None
        file_name = self.view.file_name() or ""
        if (
            self.known_settings
            and (USER_PATH in file_name
                 or file_name.endswith(".sublime-project"))
            and get_setting('settings.linting')
        ):
            unknown_regions = [
                region for region in self.view.find_by_selector(KEY_SCOPE)
                if self.view.substr(region) not in self.known_settings
            ]

        if unknown_regions:
            styles = get_setting(
                'settings.highlight_styles',
                ['DRAW_SOLID_UNDERLINE', 'DRAW_NO_FILL', 'DRAW_NO_OUTLINE']
            )
            self.view.add_regions(
                'unknown_settings_keys',
                unknown_regions,
                scope=get_setting('settings.highlight_scope', "text"),
                icon='dot',
                flags=style_flags_from_list(styles)
            )
        else:
            self.view.erase_regions('unknown_settings_keys')

    def build_phantoms(self):
        """Add links to side-by-side base file for editing this setting in the user file."""
        l.debug("Building phantom set for view %r", self.view.file_name())
        key_regions = self.view.find_by_selector(KEY_SCOPE)
        phantoms = []
        for region in key_regions:
            key_name = self.view.substr(region)
            phantom_region = sublime.Region(region.end() + 1)  # before colon
            content = "✏".format(html.escape(key_name))
            phantoms.append(sublime.Phantom(
                region=phantom_region,
                content=PHANTOM_TEMPLATE.format(content),
                layout=sublime.LAYOUT_INLINE,
                # use weak reference for callback
                # to allow for phantoms to be cleaned up in __del__
                on_navigate=WeakMethodProxy(self.on_navigate),
            ))

        self.phantom_set.update(phantoms)

    def _is_base_settings_view(self):
        return self.view.settings().get('edit_settings_view') == 'base'

# Some hooks are not available to ViewEventListeners,
# which is why we need an EventListener as well.
class GlobalSettingsListener(sublime_plugin.EventListener):

    def on_post_text_command(self, view, command_name, args):
        if command_name == 'hide_auto_complete':
            listener = sublime_plugin.find_view_event_listener(view, SettingsListener)
            if listener:
                listener.is_completing_key = False
        elif command_name in ('commit_completion', 'insert_best_completion'):
            listener = sublime_plugin.find_view_event_listener(view, SettingsListener)
            if not (listener and listener.is_completing_key):
                return

            listener.is_completing_key = False
            sel = view.sel()
            if len(sel) != 1:
                # unclear what to do, so just do nothing
                return
            point = sel[0].begin()
            key_region = get_last_key_region(view, point)
            if key_region:
                key = view.substr(key_region)
                l.debug("showing popup after inserting key completion for %r", key)
                listener.show_popup_for(key_region)

    def on_post_save(self, view):
        listener = sublime_plugin.find_view_event_listener(view, SettingsListener)
        if listener and listener.known_settings:
            listener.known_settings.trigger_settings_reload()
<|endoftext|>