<commit_before>
import pprint as pp
from dateutil import parser
import StringIO
import csv,json,math
import numpy as np
from collections import defaultdict

from tastypie.serializers import Serializer
from tastypie.resources import ModelResource, ALL
from tastypie.authorization import Authorization
from tastypie.authentication import ApiKeyAuthentication
from tastypie import fields
from tastypie.bundle import Bundle
from tastypie.serializers import Serializer

from django.utils.decorators import method_decorator
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.http import HttpResponse
from django.db.models import Sum

from stronghold.decorators import public
import pandas as pd

from datapoints.models import *

class CustomSerializer(Serializer):
    formats = ['json', 'csv']
    content_types = {
        'json': 'application/json',
        'csv': 'text/csv',
    }

    def campaign_region_pivot(self,list_of_dicts):

        meta = None

        try:
            objects = list_of_dicts['objects']
        except KeyError as e:
            objects = []

        if not meta:
            try:
                meta = list_of_dicts['meta']
            except KeyError as e:
                pass

        df = pd.DataFrame(objects)

        try: # this block is used to pivot the parent region aggregate query
            pivoted = pd.pivot_table(df, values='the_sum', index=['parent_region', 'campaign'],
                     columns=['indicator'],aggfunc = lambda x: x)

            return pivoted, meta

        except KeyError as err:
            pass

        try: # this block is used to pivot basic api requests
            pivoted = pd.pivot_table(df, values='value', index=['region', 'campaign'],
                     columns=['indicator'],aggfunc = lambda x: x)
        except KeyError:
            pivoted = pd.DataFrame()

        return pivoted, meta

    def to_csv(self, data, options=None):

        options = options or {}
        data = self.to_simple(data, options)

        pivoted,meta = self.campaign_region_pivot(data)

        csv = StringIO.StringIO(str(pivoted.to_csv()))

        return csv

    def to_json(self, data, options=None):
        ## This needs to get Cleaned up
        ## also need a param that gives one obj per datapoint record
        response = {}
        response_objects = []

        options = options or {}
        data = self.to_simple(data, options)

        pivoted,meta = self.campaign_region_pivot(data)

        # replace NaN with None
        cleaned = pivoted.astype(object).replace(np.nan, 'None')
        # df1 = df.astype(object).replace(np.nan, 'None')

        for r_c in cleaned.iterrows():

            r_c_dict = {}

            rows = r_c[1] # zero = COLUMNS ; one = ROWS

            ix = rows.index # the index is the indicator

            indicator_list = []

            for i,(value) in enumerate(rows):
                ind_dict = {}

                ind_dict['indicator'] = ix[i]

                if value == 'None':
                    value = None

                ind_dict['value'] = value

                indicator_list.append(ind_dict)

            r_c_dict['indicators'] = indicator_list

            region,campaign = r_c[0][0],r_c[0][1]

            r_c_dict['region'] = region
            r_c_dict['campaign'] = campaign

            response_objects.append(r_c_dict)

        response['meta'] = meta

        response['objects'] = response_objects

        return json.dumps(response)
        # return json.dumps(data)

class SimpleApiResource(ModelResource):
    '''
    This is the top level class all other Resource Classes inherit from this.
    The API Key authentication is defined here and thus is required by all
    other resources.  This class enherits fro the Tastyppie "ModelResource"

    See Here: http://django-tastypie.readthedocs.org/en/latest/resources.html?highlight=modelresource
    '''

    class Meta():
        authentication = ApiKeyAuthentication()
        authorization = Authorization()
        always_return_data = True

class OfficeResource(SimpleApiResource):
    '''Office Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = Office.objects.all()
        resource_name = 'office'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
        }

class RegionResource(SimpleApiResource):
    '''Region Resource'''

    parent_region = fields.ForeignKey('datapoints.api.simple.RegionResource', 'parent_region', full=False, null=True)
    office = fields.ToOneField(OfficeResource, 'office')

    class Meta(SimpleApiResource.Meta):
        queryset = Region.objects.all()
        resource_name = 'region'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
            "office": ALL,
            "region_type": ALL,
        }

class IndicatorResource(SimpleApiResource):
    '''Indicator Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = Indicator.objects.all()
        resource_name = 'indicator'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
        }

class CampaignResource(SimpleApiResource):
    '''Campaign Resource'''

    office = fields.ToOneField(OfficeResource, 'office')

    class Meta(SimpleApiResource.Meta):
        queryset = Campaign.objects.all()
        resource_name = 'campaign'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
            "office": ALL,
        }

class UserResource(SimpleApiResource):
    '''User Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = User.objects.all()
        resource_name = 'user'
        excludes = ['password', 'username']
        allowed_methods = ['get']

class OfficeResource(SimpleApiResource):
    '''Office Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = Office.objects.all()
        resource_name = 'office'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
        }

    #############################################
    #############################################
    #############################################

class DataPointResource(SimpleApiResource):
    '''Datapoint Resource'''

    region = fields.ToOneField(RegionResource, 'region')
    indicator = fields.ToOneField(IndicatorResource, 'indicator')
    campaign = fields.ToOneField(CampaignResource, 'campaign')
    changed_by_id = fields.ToOneField(UserResource, 'changed_by')

    class Meta(SimpleApiResource.Meta):
        queryset = DataPoint.objects.all()
        resource_name = 'datapoint'
        excludes = ['note']
        filtering = {
            "value": ALL,
            "created_at":ALL,
            "indicator":ALL,
            "region": ALL ,
            "campaign": ALL,
        }
        allowed_methods = ['get']
        serializer = CustomSerializer()
        max_limit = None

    def filter_by_campaign(self,object_list,query_dict):
        ''' using the parse_campaign_st_end find the relevant campaign ids and
        return a result set where datpoitns are filtered by this list'''

        ## Find Start Date IDs ##
        campaigns_to_filter = Campaign.objects.all()

        try:
            c_st = query_dict['campaign_start']
            campaigns_to_filter = campaigns_to_filter.filter(start_date__gte=c_st)
        except KeyError:
             pass # campaigns_to_filter is all
        except ValidationError:
             campaigns_to_filter = []

        ## Find End Date IDs ##

        try:
            c_ed = query_dict['campaign_end']
            campaigns_to_filter = campaigns_to_filter.filter(end_date__lte=c_ed)
        except KeyError:
             pass
        except ValidationError:
             campaigns_to_filter = []

        campaign_ids = [c.id for c in campaigns_to_filter]

        return campaign_ids

    def parse_url_params(self,query_dict):

        try:
            the_limit = int(query_dict['the_limit'])
        except KeyError:
            the_limit = 10

        try:
            region_in = query_dict['region__in']
        except KeyError:
            region_in = []

        try:
            campaign_in = query_dict['campaign__in']
        except KeyError:
            campaign_in = []

        try:
            indicator_in = query_dict['indicator__in']
        except KeyError:
            indicator_in = []

        return region_in, campaign_in, indicator_in, the_limit

    def get_regions_and_campaigns_to_filter(self,query_dict):
        '''
        applying the limit to the region / campaign combo
        '''

        # get the params from the query dict

        regions, campaigns, indicators, the_limit = \
            self.parse_url_params(query_dict)

        if isinstance(indicators,list):
            # this means the indicator request is null
            # find all of the distinct regions / campaigns in the db
            indicator_list = indicators
            all_region_campaign_tuples = DataPoint.objects.values_list('region',\
                'campaign').distinct()

        else:
            # find the distinct campaing / region tuples for those indicators
            indicator_list = [int(ind) for ind in indicators.split(',')]

            all_region_campaign_tuples =DataPoint.objects.filter(indicator__in=\
                indicator_list).values_list('region','campaign').distinct()

        # if there was no region or campaign passed in just take the first
        # x elements in the list ( where x is the_limit ) and return that

        if len(regions) == 0 and len(campaigns) == 0:
            return all_region_campaign_tuples[:the_limit], indicator_list

        final_region_campaign_tuples = []

        # loop through all of the distinct campaigns/regions in the db and if
        # the request matches then add to the array that will be returned

        for r,c in all_region_campaign_tuples:

            if len(final_region_campaign_tuples) == the_limit:
                return final_region_campaign_tuples, indicator_list

            elif str(r) in regions and str(c) in campaigns:
                final_region_campaign_tuples.append((r,c))

            elif str(r) in regions and len(campaigns) == 0:
                final_region_campaign_tuples.append((r,c))

            elif len(regions) == 0 and str(c) in campaigns:
                final_region_campaign_tuples.append((r,c))

            else:
                pass

        return final_region_campaign_tuples, indicator_list

    def obj_get_list(self, bundle, **kwargs):
        '''
        Overriding this method because if i dont, the filters are applied
        to the aggregate and because of whcih i get no data
        '''
        # Filtering disabled for brevity...
        return self.get_object_list(bundle.request)

    def get_object_list(self, request):
        '''
        Evan needs ot be able to limit by region/campaign pairs so here
        i override the get object list with a method that finds the regions
        and campaigns that coorespond with the limit passed in conjunction
        with the campaign / region list
        '''
        query_dict = request.GET
        region_campaign_tuples, indicators = self.get_regions_and_campaigns_to_filter(query_dict)

        regions = list(set([rc[0] for rc in region_campaign_tuples]))
        campaigns = list(set([rc[1] for rc in region_campaign_tuples]))

        print regions
        print campaigns
        print indicators

        object_list = DataPoint.objects.filter(
            region__in = regions,
            campaign__in = campaigns,
            indicator__in = indicators
        )
        print 'THIS IS THE OBJECT LIST '
        print object_list

        return object_list

    def dehydrate(self, bundle):
        '''
        Depending on the <uri_display> parameter, return to the bundle
        the name, resurce_uri, slug or ID of the resource
        '''

        fk_columns = {'indicator':bundle.obj.indicator,\
            'campaign':bundle.obj.campaign,\
            'region':bundle.obj.region}

        try: # Default to showing the ID of the resource
            uri_display = bundle.request.GET['uri_display']
        except KeyError:
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj
            return bundle

        if uri_display == 'slug':
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj.slug

        elif uri_display == 'id':
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj.id

        elif uri_display == 'name':
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj.name

        else: # if there is any other uri_display, return the full uri
            pass

        return bundle
<commit_msg>if no indicators passed dont use as filter param<commit_after>
import pprint as pp
from dateutil import parser
import StringIO
import csv,json,math
import numpy as np
from collections import defaultdict

from tastypie.serializers import Serializer
from tastypie.resources import ModelResource, ALL
from tastypie.authorization import Authorization
from tastypie.authentication import ApiKeyAuthentication
from tastypie import fields
from tastypie.bundle import Bundle
from tastypie.serializers import Serializer

from django.utils.decorators import method_decorator
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.http import HttpResponse
from django.db.models import Sum

from stronghold.decorators import public
import pandas as pd

from datapoints.models import *

class CustomSerializer(Serializer):
    formats = ['json', 'csv']
    content_types = {
        'json': 'application/json',
        'csv': 'text/csv',
    }

    def campaign_region_pivot(self,list_of_dicts):

        meta = None

        try:
            objects = list_of_dicts['objects']
        except KeyError as e:
            objects = []

        if not meta:
            try:
                meta = list_of_dicts['meta']
            except KeyError as e:
                pass

        df = pd.DataFrame(objects)

        try: # this block is used to pivot the parent region aggregate query
            pivoted = pd.pivot_table(df, values='the_sum', index=['parent_region', 'campaign'],
                     columns=['indicator'],aggfunc = lambda x: x)

            return pivoted, meta

        except KeyError as err:
            pass

        try: # this block is used to pivot basic api requests
            pivoted = pd.pivot_table(df, values='value', index=['region', 'campaign'],
                     columns=['indicator'],aggfunc = lambda x: x)
        except KeyError:
            pivoted = pd.DataFrame()

        return pivoted, meta

    def to_csv(self, data, options=None):

        options = options or {}
        data = self.to_simple(data, options)

        pivoted,meta = self.campaign_region_pivot(data)

        csv = StringIO.StringIO(str(pivoted.to_csv()))

        return csv

    def to_json(self, data, options=None):
        ## This needs to get Cleaned up
        ## also need a param that gives one obj per datapoint record
        response = {}
        response_objects = []

        options = options or {}
        data = self.to_simple(data, options)

        pivoted,meta = self.campaign_region_pivot(data)

        # replace NaN with None
        cleaned = pivoted.astype(object).replace(np.nan, 'None')
        # df1 = df.astype(object).replace(np.nan, 'None')

        for r_c in cleaned.iterrows():

            r_c_dict = {}

            rows = r_c[1] # zero = COLUMNS ; one = ROWS

            ix = rows.index # the index is the indicator

            indicator_list = []

            for i,(value) in enumerate(rows):
                ind_dict = {}

                ind_dict['indicator'] = ix[i]

                if value == 'None':
                    value = None

                ind_dict['value'] = value

                indicator_list.append(ind_dict)

            r_c_dict['indicators'] = indicator_list

            region,campaign = r_c[0][0],r_c[0][1]

            r_c_dict['region'] = region
            r_c_dict['campaign'] = campaign

            response_objects.append(r_c_dict)

        response['meta'] = meta

        response['objects'] = response_objects

        return json.dumps(response)
        # return json.dumps(data)

class SimpleApiResource(ModelResource):
    '''
    This is the top level class all other Resource Classes inherit from this.
    The API Key authentication is defined here and thus is required by all
    other resources.  This class enherits fro the Tastyppie "ModelResource"

    See Here: http://django-tastypie.readthedocs.org/en/latest/resources.html?highlight=modelresource
    '''

    class Meta():
        authentication = ApiKeyAuthentication()
        authorization = Authorization()
        always_return_data = True

class OfficeResource(SimpleApiResource):
    '''Office Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = Office.objects.all()
        resource_name = 'office'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
        }

class RegionResource(SimpleApiResource):
    '''Region Resource'''

    parent_region = fields.ForeignKey('datapoints.api.simple.RegionResource', 'parent_region', full=False, null=True)
    office = fields.ToOneField(OfficeResource, 'office')

    class Meta(SimpleApiResource.Meta):
        queryset = Region.objects.all()
        resource_name = 'region'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
            "office": ALL,
            "region_type": ALL,
        }

class IndicatorResource(SimpleApiResource):
    '''Indicator Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = Indicator.objects.all()
        resource_name = 'indicator'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
        }

class CampaignResource(SimpleApiResource):
    '''Campaign Resource'''

    office = fields.ToOneField(OfficeResource, 'office')

    class Meta(SimpleApiResource.Meta):
        queryset = Campaign.objects.all()
        resource_name = 'campaign'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
            "office": ALL,
        }

class UserResource(SimpleApiResource):
    '''User Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = User.objects.all()
        resource_name = 'user'
        excludes = ['password', 'username']
        allowed_methods = ['get']

class OfficeResource(SimpleApiResource):
    '''Office Resource'''

    class Meta(SimpleApiResource.Meta):
        queryset = Office.objects.all()
        resource_name = 'office'
        filtering = {
            "slug": ('exact'),
            "id": ALL,
        }

    #############################################
    #############################################
    #############################################

class DataPointResource(SimpleApiResource):
    '''Datapoint Resource'''

    region = fields.ToOneField(RegionResource, 'region')
    indicator = fields.ToOneField(IndicatorResource, 'indicator')
    campaign = fields.ToOneField(CampaignResource, 'campaign')
    changed_by_id = fields.ToOneField(UserResource, 'changed_by')

    class Meta(SimpleApiResource.Meta):
        queryset = DataPoint.objects.all()
        resource_name = 'datapoint'
        excludes = ['note']
        filtering = {
            "value": ALL,
            "created_at":ALL,
            "indicator":ALL,
            "region": ALL ,
            "campaign": ALL,
        }
        allowed_methods = ['get']
        serializer = CustomSerializer()
        max_limit = None

    def filter_by_campaign(self,object_list,query_dict):
        ''' using the parse_campaign_st_end find the relevant campaign ids and
        return a result set where datpoitns are filtered by this list'''

        ## Find Start Date IDs ##
        campaigns_to_filter = Campaign.objects.all()

        try:
            c_st = query_dict['campaign_start']
            campaigns_to_filter = campaigns_to_filter.filter(start_date__gte=c_st)
        except KeyError:
             pass # campaigns_to_filter is all
        except ValidationError:
             campaigns_to_filter = []

        ## Find End Date IDs ##

        try:
            c_ed = query_dict['campaign_end']
            campaigns_to_filter = campaigns_to_filter.filter(end_date__lte=c_ed)
        except KeyError:
             pass
        except ValidationError:
             campaigns_to_filter = []

        campaign_ids = [c.id for c in campaigns_to_filter]

        return campaign_ids

    def parse_url_params(self,query_dict):

        try:
            the_limit = int(query_dict['the_limit'])
        except KeyError:
            the_limit = 10

        try:
            region_in = query_dict['region__in']
        except KeyError:
            region_in = []

        try:
            campaign_in = query_dict['campaign__in']
        except KeyError:
            campaign_in = []

        try:
            indicator_in = query_dict['indicator__in']
        except KeyError:
            indicator_in = []

        return region_in, campaign_in, indicator_in, the_limit

    def get_regions_and_campaigns_to_filter(self,query_dict):
        '''
        applying the limit to the region / campaign combo
        '''

        # get the params from the query dict

        regions, campaigns, indicators, the_limit = \
            self.parse_url_params(query_dict)

        if isinstance(indicators,list):
            # this means the indicator request is null
            # find all of the distinct regions / campaigns in the db
            indicator_list = indicators
            all_region_campaign_tuples = DataPoint.objects.values_list('region',\
                'campaign').distinct()

        else:
            # find the distinct campaing / region tuples for those indicators
            indicator_list = [int(ind) for ind in indicators.split(',')]

            all_region_campaign_tuples =DataPoint.objects.filter(indicator__in=\
                indicator_list).values_list('region','campaign').distinct()

        # if there was no region or campaign passed in just take the first
        # x elements in the list ( where x is the_limit ) and return that

        if len(regions) == 0 and len(campaigns) == 0:
            return all_region_campaign_tuples[:the_limit], indicator_list

        final_region_campaign_tuples = []

        # loop through all of the distinct campaigns/regions in the db and if
        # the request matches then add to the array that will be returned

        for r,c in all_region_campaign_tuples:

            if len(final_region_campaign_tuples) == the_limit:
                return final_region_campaign_tuples, indicator_list

            elif str(r) in regions and str(c) in campaigns:
                final_region_campaign_tuples.append((r,c))

            elif str(r) in regions and len(campaigns) == 0:
                final_region_campaign_tuples.append((r,c))

            elif len(regions) == 0 and str(c) in campaigns:
                final_region_campaign_tuples.append((r,c))

            else:
                pass

        return final_region_campaign_tuples, indicator_list

    def obj_get_list(self, bundle, **kwargs):
        '''
        Overriding this method because if i dont, the filters are applied
        to the aggregate and because of whcih i get no data
        '''
        # Filtering disabled for brevity...
        return self.get_object_list(bundle.request)

    def get_object_list(self, request):
        '''
        Evan needs ot be able to limit by region/campaign pairs so here
        i override the get object list with a method that finds the regions
        and campaigns that coorespond with the limit passed in conjunction
        with the campaign / region list
        '''
        query_dict = request.GET
        region_campaign_tuples, indicators = self.get_regions_and_campaigns_to_filter(query_dict)

        regions = list(set([rc[0] for rc in region_campaign_tuples]))
        campaigns = list(set([rc[1] for rc in region_campaign_tuples]))

        if len(indicators) > 0:

            object_list = DataPoint.objects.filter(
                region__in = regions,
                campaign__in = campaigns,
                indicator__in = indicators
            )

        else:
            object_list = DataPoint.objects.filter(
                region__in = regions,
                campaign__in = campaigns,
            )

        return object_list

    def dehydrate(self, bundle):
        '''
        Depending on the <uri_display> parameter, return to the bundle
        the name, resurce_uri, slug or ID of the resource
        '''

        fk_columns = {'indicator':bundle.obj.indicator,\
            'campaign':bundle.obj.campaign,\
            'region':bundle.obj.region}

        try: # Default to showing the ID of the resource
            uri_display = bundle.request.GET['uri_display']
        except KeyError:
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj
            return bundle

        if uri_display == 'slug':
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj.slug

        elif uri_display == 'id':
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj.id

        elif uri_display == 'name':
            for f_str,f_obj in fk_columns.iteritems():
                bundle.data[f_str] = f_obj.name

        else: # if there is any other uri_display, return the full uri
            pass

        return bundle
<|endoftext|>


<commit_before>#!/usr/bin/python2.6

import tornado

class CCCreator(tornado.web.RequestHandler):
    def get(self):
        try:
            content = "<license><license_url>" + self.get_argument("license_url") + "</license_url>"
            content = content +  "<license_name>" + self.get_argument("license_name") + "</license_name>"
            content = content +  "<license_button>" + self.get_argument("license_button") + "</license_button>"
            content = content +  "<deed_url>" + self.get_argument("deed_url") + "</deed_url></license>"

            self.write(  content  )

        except:
            self.write( "<error>Generic Error</error>" )

    #def post(self):

<commit_msg>modified CCcreator<commit_after>#!/usr/bin/python2.6

import tornado

html_content = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
   <head>
<style>

html {
	margin: 0;
	/* setting border: 0 hoses ie6 win window inner well border */
	padding: 0;
  }

body {
	font-family: Verdana, 'trebuchet ms', sans-serif;
	font-size: 13px;
	line-height : 140%;
	border-top: 10px solid #99CC66;
	margin : 0;
	padding : 0;
  }

a {
  color : blue;
  }

.pagebody {
  margin-left : 50px;
  width : 700px;
  margin-top : 50px;
  height : 1000px;
  }

.pagetitle {
  font-size : 20px;
  font-weight : normal;
  margin-top : 30px;
  }

/* this is stuff for our widget builder */

#customize {
  padding : 10px;
  padding-bottom : 30px;
  clear : both;
  }

#preview {
  padding-top : 20px;
  padding-left : 20px;
  padding-bottom : 40px;
  }

#step1, #step2 {
  clear : both;
  }

.instruction {
  padding : 10px;
  border-top : 1px solid silver;
  font-weight : bold;
  margin-top : 30px;
  }

.field {
  padding-top : 20px;
  clear : both;
  }

.button-style {
  font-size : 16px;
  padding : 5px;
  }

.field-label {
  float: left;
  display: inline;
  width: 150px;
  margin-right: 5px;
  padding-top: 4px;
  text-align: right;
  font-size : 12px;
  font-weight : bold;
  }

.field-input {
  float: left;
  display: inline;
  width: 325px;
  font-weight : normal;
  color : gray;
  font-size : 11px;
  }

/* this is stuff for the cc widget */

.cc_js_body {
  background-color: #fff;
  padding: 0;
  margin: 0;
}

#cc_js_header-main {
    width:  93%;
    min-width:  700px;
    margin: 0 3%;
    padding: 10px 0 2px 1%;
    text-align: left;
    font-size: 11px !important;
}

/* -- elements */

a.cs_js_a, a:link.cs_js_a {
  text-decoration: none;
  color: #00b;
}

a:hover.cs_js_a {
  text-decoration: underline;
}

/**
 * This was written by CC as a demonstration of how to interoperate
 * with the Creative Commons JsWidget.  No rights reserved.
 *
 * See README for a little more detail.
 */

#cc_js_license_selected
{
    border:         none;
    text-align:     left;
    margin-bottom: 2.7%;
		width: 100%;
    font-size : 13px;
    font-weight : normal;
}

#cc_js_license_selected img {
  display : block;
  }

#cc_js_jurisdiction_box
{
    /* border: 1px solid black; */
    padding: 0.5% 2% 1% 2%;
    margin-bottom: 1%;
}

#cc_js_lic-menu h2
{
    /* text-decoration: underline; */
    /* border-bottom: 1px solid black; */
    padding: 3% 0 1% 0;
    border: none;
}

#cc_js_lic-result
{
    padding: 0;
    margin: 0;
}

select#cc_js_jurisdiction
{
    margin-bottom: 2%;
}

textarea#cc_js_result
{
    width: 91%;
    border: 1px solid #ccc;
    color: gray;
    margin-top: 1%;
}

a.cc_js_a img
{
    border: none;
    text-decoration: none;
}

#cc_js_more_info
{
    border: 1px solid #eee;
    padding: 0.5% 2% 1% 2%;
    margin-bottom: 1%;
    margin-top: 1%;
		width: 87%;
}
#cc_js_more_info table
{
    width: 65%;
}

#cc_js_more_info .header
{
    width: 35%;
}

#cc_js_more_info input
{
    width: 100%;
    border: 1px solid #ccc;
}

#cc_js_required
{

    border: 1px solid #ccc;
    padding: 0.5% 2% 1% 2%;
    margin-bottom: 1%;
    margin-top: 1%;
    /* background: #efefef; */
    background: #eef6e6;
		width: 95%;
}

#cc_js_work_title
{
    font-style: italic;
}

#cc_js_optional
{
    border: 1px solid #eee;
    padding: 0.5% 2% 1% 2%;
    margin-bottom: 1%;
    margin-top: 1%;
		width: 87%;
}

.cc_js_cc-button
{
    padding-bottom: 1%;
}

.cc_js_info
{
    vertical-align: middle;
}

img.cc_js_info {
	float: right;
}

#cc_js_lic-menu p {
	width: 18em;
	padding: 3px 0 5px 0;
}

#cc_js_jurisdiction_box {
  float : left;
  }

.cc_js_tooltip {
    background:     white;
    border:         2px solid gray;
    padding:        3px 10px 3px 10px;
    width:          240px;
    text-align:     left;
    font-size: 11px;
    }

.cc_js_tooltip .cc_js_icon
{
    float:          left;
    padding-right:  4%;
    padding-bottom: 20%;
}

.cc_js_tooltipimage
{
    border:         2px solid gray;
}

.cc_js_infobox
{
    cursor:         help;
}

.cc_js_question
{
    cursor:         help;
    color:          #00b;
}

#cc_js_lic-menu p {
	margin: 0 0 5px 0;
}

</style>

     <!--<link rel="stylesheet" href="example-widget-style.css" />-->
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <!-- Written by Creative Commons in 2007.  No rights reserved; see COPYING for more details. -->
	 <!-- edited by Hasanat @ HIIT -->
   </head>
   <body>
     <div id="cc_js_widget_container">
       <script type="text/javascript" src="http://api.creativecommons.org/jswidget/tags/0.97/complete.js?locale=en_US&want_a_license=definitely"></script>
     </div>

     <script type="text/javascript">
		function showChosenLicenseURI() {
			return document.getElementById("cc_js_result_uri").value;
		}

		function showChosenLicenseName() {
			return document.getElementById("cc_js_result_name").value;
		}

		function showChosenLicenseImageURL() {
			return document.getElementById("cc_js_result_img").value;
	}</script>

	<button style="margin-left:30px;" onClick="window.location = '?lurl='+ showChosenLicenseURI() +'&lname='+ showChosenLicenseName() +'&liurl=' + showChosenLicenseImageURL() + ''";>Apply License</button>

   </body>
 </html>
 '''

class CCCreator(tornado.web.RequestHandler):
    def get(self):
        try:
            content = "<license><license_url>" + self.get_argument("lurl") + "</license_url>"
            content = content +  "<license_name>" + self.get_argument("lname") + "</license_name>"
            content = content +  "<license_image>" + self.get_argument("liurl") + "</license_image></license>"

            self.write(  content  )
        except:
            self.write(html_content)

<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

version = (0, 1, 29)

__title__ = "python-simple-workflow"
__author__ = "Oleiade"
__license__ = "MIT"

__version__ = '.'.join(map(str, version))
<commit_msg>Bump version to 0.1.30<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

version = (0, 1, 30)

__title__ = "python-simple-workflow"
__author__ = "Oleiade"
__license__ = "MIT"

__version__ = '.'.join(map(str, version))
<|endoftext|>


<commit_before>class functions_dir(object):
	'''docstring for functions_dir'''
	def __init__(self):
		super(functions_dir, self).__init__()
		'''
			Las funciones son entradas en el diccionario functions.
			A cada funcion le corresponde de valor una tupla.
			Esta tupla contiene otro diccionario para varibales, y un return type
			Scope global del programa se inicia con diccionario de variables globales vacio.
			No se tiene un return type para el scope global.
			Scope es el function_id de cada funcion.
		'''
		self.functions = {}
		self.functions['global'] = [{}, None, 0]

		# 0 es el indice de la tupla de una funcion para accesar a su diccionario de variables
		self.variables_dict = 0

		# 1 es el indice de la tupla de una funcion para accesar su return type
		self.return_type = 1

		# 2 es el indide de la tupla de una funcion para indicar la cantidad de argumentos que espera
		self.expected_arguments = 2
		self.scope = 'global'

	# Add function to fuctions directory
	def add_function(self, function_id):
		# Verify if function already exists
		if self.functions.get(function_id, None) is not None:
			raise NameError('Function already declared!')
		else:
			self.functions[function_id] = [{}, None, 0]

	# Validate function exists
	def validate_function(self, function_id):
		if self.functions.get(function_id, None) is None:
			raise ValueError('Function not declared')

	# Incrementa cantidad de argumentos esperados por una funcion
	def increase_expected_arguments(self):
		self.functions[self.scope][self.expected_arguments] += 1

	def set_return_type(self, function_return_type):
		self.functions[self.scope][self.return_type] = function_return_type

	# Change the current scope from global to function scope
	def set_scope(self, scope):
		self.scope = scope

	# Reset scope to global
	def reset_scope(self):
		self.scope = 'global'

	# Add variable to current scope
	def add_var(self, variable_id, value = 0, var_type = 'mutable'):
		self.functions[self.scope][self.variables_dict][variable_id] = (value, var_type)

	# Validate variable exists
	def validate_variable(self, variable_id):
		# Look for variable in current scope
		if self.functions[self.scope][self.variables_dict].get(variable_id, None) is None:
			# Look for variable in global scope
			if self.functions['global'][self.variables_dict].get(variable_id, None) is None:
				raise ValueError('Variable not declared!')

	@property
	def current_scope(self):
		return self.scope<commit_msg>Actualiza documentacion<commit_after>class functions_dir(object):
	'''docstring for functions_dir'''
	def __init__(self):
		super(functions_dir, self).__init__()
		'''
			Las funciones son entradas en el diccionario functions.
			A cada funcion le corresponde de valor una lista.
			Esta lista contiene otro diccionario para varibales, y un return type, y cantidad de args
			Scope global del programa se inicia con diccionario de variables globales vacio.
			No se tiene un return type para el scope global.
			Scope es el function_id de cada funcion.
		'''
		self.functions = {}
		self.functions['global'] = [{}, None, 0]

		# 0 es el indice de la tupla de una funcion para accesar a su diccionario de variables
		self.variables_dict = 0

		# 1 es el indice de la tupla de una funcion para accesar su return type
		self.return_type = 1

		# 2 es el indide de la tupla de una funcion para indicar la cantidad de argumentos que espera
		self.expected_arguments = 2
		self.scope = 'global'

	# Add function to fuctions directory
	def add_function(self, function_id):
		# Verify if function already exists
		if self.functions.get(function_id, None) is not None:
			raise NameError('Function already declared!')
		else:
			self.functions[function_id] = [{}, None, 0]

	# Validate function exists
	def validate_function(self, function_id):
		if self.functions.get(function_id, None) is None:
			raise ValueError('Function not declared')

	# Incrementa cantidad de argumentos esperados por una funcion
	def increase_expected_arguments(self):
		self.functions[self.scope][self.expected_arguments] += 1

	def set_return_type(self, function_return_type):
		self.functions[self.scope][self.return_type] = function_return_type

	# Change the current scope from global to function scope
	def set_scope(self, scope):
		self.scope = scope

	# Reset scope to global
	def reset_scope(self):
		self.scope = 'global'

	# Add variable to current scope
	def add_var(self, variable_id, value = 0, var_type = 'mutable'):
		self.functions[self.scope][self.variables_dict][variable_id] = (value, var_type)

	# Validate variable exists
	def validate_variable(self, variable_id):
		# Look for variable in current scope
		if self.functions[self.scope][self.variables_dict].get(variable_id, None) is None:
			# Look for variable in global scope
			if self.functions['global'][self.variables_dict].get(variable_id, None) is None:
				raise ValueError('Variable not declared!')

	@property
	def current_scope(self):
		return self.scope<|endoftext|>


<commit_before>import copy
import logging
import os

from django import forms
from django.core.files import File
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import UploadedFile
from django.forms.models import BaseModelForm, ModelFormMetaclass
from django.utils import six
from django.utils.translation import ugettext_lazy as _

from pretix.base.i18n import I18nFormField
from pretix.base.models import Event

logger = logging.getLogger('pretix.plugins.ticketoutputpdf')

class BaseI18nModelForm(BaseModelForm):
    """
    This is a helperclass to construct I18nModelForm
    """
    def __init__(self, *args, **kwargs):
        event = kwargs.pop('event', None)
        super().__init__(*args, **kwargs)
        if event:
            for k, field in self.fields.items():
                if isinstance(field, I18nFormField):
                    field.widget.enabled_langcodes = event.settings.get('locales')

class I18nModelForm(six.with_metaclass(ModelFormMetaclass, BaseI18nModelForm)):
    """
    This is a modified version of Django's ModelForm which differs from ModelForm in
    only one way: The constructor takes one additional optional argument ``event``
    which may be given an :pyclass:`pretix.base.models.Event` instance. If given, this
    instance is used to select the visible languages in all I18nFormFields of the form. If
    not given, all languages will be displayed.
    """
    pass

class SettingsForm(forms.Form):
    """
    This form is meant to be used for modifying Event- or OrganizerSettings
    """
    BOOL_CHOICES = (
        ('False', _('disabled')),
        ('True', _('enabled')),
    )

    def __init__(self, *args, **kwargs):
        self.obj = kwargs.pop('obj')
        kwargs['initial'] = self.obj.settings.freeze()
        super().__init__(*args, **kwargs)
        for k, field in self.fields.items():
            if isinstance(field, I18nFormField):
                field.widget.enabled_langcodes = self.obj.settings.get('locales')

    def save(self):
        for name, field in self.fields.items():
            value = self.cleaned_data[name]
            if isinstance(value, UploadedFile):
                if isinstance(self.obj, Event):
                    fname = '%s/%s/%s.%s' % (
                        self.obj.organizer.slug, self.obj.slug, name, value.name.split('.')[-1]
                    )
                else:
                    fname = '%s/%s.%s' % (self.obj.slug, name, value.name.split('.')[-1])
                if not os.path.exists(os.path.dirname(fname)):
                    os.makedirs(os.path.dirname(fname))
                with default_storage.open(fname, 'wb+') as destination:
                    for chunk in value.chunks():
                        destination.write(chunk)
                value._name = fname
            elif isinstance(value, File):
                # file is unchanged
                continue
            elif isinstance(field, forms.FileField):
                # file is deleted
                fname = self.obj.settings.get(name, as_type=File)
                if fname:
                    try:
                        default_storage.delete(fname.name)
                    except OSError:
                        logger.error('Deleting file %s failed.' % fname.name)

            if value is None:
                del self.obj.settings[name]
            elif self.obj.settings.get(name, as_type=type(value)) != value:
                self.obj.settings.set(name, value)
<commit_msg>Fixed bug in last commit<commit_after>import copy
import logging
import os

from django import forms
from django.core.files import File
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import UploadedFile
from django.forms.models import BaseModelForm, ModelFormMetaclass
from django.utils import six
from django.utils.translation import ugettext_lazy as _

from pretix.base.i18n import I18nFormField
from pretix.base.models import Event

logger = logging.getLogger('pretix.plugins.ticketoutputpdf')

class BaseI18nModelForm(BaseModelForm):
    """
    This is a helperclass to construct I18nModelForm
    """
    def __init__(self, *args, **kwargs):
        event = kwargs.pop('event', None)
        super().__init__(*args, **kwargs)
        if event:
            for k, field in self.fields.items():
                if isinstance(field, I18nFormField):
                    field.widget.enabled_langcodes = event.settings.get('locales')

class I18nModelForm(six.with_metaclass(ModelFormMetaclass, BaseI18nModelForm)):
    """
    This is a modified version of Django's ModelForm which differs from ModelForm in
    only one way: The constructor takes one additional optional argument ``event``
    which may be given an :pyclass:`pretix.base.models.Event` instance. If given, this
    instance is used to select the visible languages in all I18nFormFields of the form. If
    not given, all languages will be displayed.
    """
    pass

class SettingsForm(forms.Form):
    """
    This form is meant to be used for modifying Event- or OrganizerSettings
    """
    BOOL_CHOICES = (
        ('False', _('disabled')),
        ('True', _('enabled')),
    )

    def __init__(self, *args, **kwargs):
        self.obj = kwargs.pop('obj')
        kwargs['initial'] = self.obj.settings.freeze()
        super().__init__(*args, **kwargs)
        for k, field in self.fields.items():
            if isinstance(field, I18nFormField):
                field.widget.enabled_langcodes = self.obj.settings.get('locales')

    def save(self):
        for name, field in self.fields.items():
            value = self.cleaned_data[name]
            if isinstance(value, UploadedFile):
                if isinstance(self.obj, Event):
                    fname = '%s/%s/%s.%s' % (
                        self.obj.organizer.slug, self.obj.slug, name, value.name.split('.')[-1]
                    )
                else:
                    fname = '%s/%s.%s' % (self.obj.slug, name, value.name.split('.')[-1])
                default_storage.save(fname, value)
                value._name = fname
            elif isinstance(value, File):
                # file is unchanged
                continue
            elif isinstance(field, forms.FileField):
                # file is deleted
                fname = self.obj.settings.get(name, as_type=File)
                if fname:
                    try:
                        default_storage.delete(fname.name)
                    except OSError:
                        logger.error('Deleting file %s failed.' % fname.name)

            if value is None:
                del self.obj.settings[name]
            elif self.obj.settings.get(name, as_type=type(value)) != value:
                self.obj.settings.set(name, value)
<|endoftext|>


<commit_before>#!/usr/bin/env python
#
#  OpenSlide, a library for reading whole slide image files
#
#  Copyright (c) 2012 Carnegie Mellon University
#  All rights reserved.
#
#  OpenSlide is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation, version 2.1.
#
#  OpenSlide 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 Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with OpenSlide. If not, see
#  <http://www.gnu.org/licenses/>.
#

#
# Convert a slide file into a fake Hamamatsu VMU slide that's good enough
# to fool OpenSlide.
#

from __future__ import division
from ConfigParser import RawConfigParser
from fractions import gcd
from openslide import OpenSlide
import struct
import sys

BUF_HEIGHT = 512

class VmuLevel(object):
    def __init__(self, osr, level):
        self._osr = osr
        self.level = level
        self.width, self.height = osr.level_dimensions[level]
        self.column_width = gcd(self.width, 400)
        self.downsample = osr.level_downsamples[level]

    def save(self, path):
        with open(path, 'w') as fh:
            # Header
            fh.write(struct.pack('<2c2x3i8xi4x', 'G', 'N', self.width,
                    self.height, self.column_width, 32))

            # Body
            for col in xrange(self.width // self.column_width):
                # BUF_HEIGHT rows at a time
                for i in xrange((self.height + BUF_HEIGHT - 1) // BUF_HEIGHT):
                    rows = min(BUF_HEIGHT, self.height - i * BUF_HEIGHT)
                    img = self._osr.read_region(
                            (int(col * self.column_width * self.downsample),
                            int(i * BUF_HEIGHT * self.downsample)),
                            self.level,
                            (self.column_width, rows)).load()
                    for y in xrange(rows):
                        for x in xrange(self.column_width):
                            pix = [v << 4 for v in img[x, y]]
                            # FIXME: ignores alpha
                            fh.write(struct.pack('<3h', *pix[0:3]))

def make_vmu(in_path, out_base):
    path_conf = out_base + '.vmu'
    path_0 = out_base + '.l0'
    path_1 = out_base + '.l1'

    with OpenSlide(in_path) as osr:
        l0 = VmuLevel(osr, 0)
        l1 = VmuLevel(osr, osr.get_best_level_for_downsample(32))
        for i, l in enumerate([l0, l1]):
            print 'Level %d: %d pixels/column' % (i, l.column_width)
        l0.save(path_0)
        l1.save(path_1)

    section = 'Uncompressed Virtual Microscope Specimen'
    conf = {
        'NoLayers': '1',
        'ImageFile': path_0,
        'MapFile': path_1,
        'BitsPerPixel': '36',
        'PixelOrder': 'RGB',
    }

    c = RawConfigParser()
    c.optionxform = str
    c.add_section(section)
    for k, v in conf.iteritems():
        c.set(section, k, v)
    with open(path_conf, 'w') as fh:
        c.write(fh)

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print 'Usage: %s infile outbase' % sys.argv[0]
        sys.exit(1)
    make_vmu(sys.argv[1], sys.argv[2])
<commit_msg>misc/make-vmu: convert to Python 3 with 2to3<commit_after>#!/usr/bin/env python
#
#  OpenSlide, a library for reading whole slide image files
#
#  Copyright (c) 2012 Carnegie Mellon University
#  All rights reserved.
#
#  OpenSlide is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation, version 2.1.
#
#  OpenSlide 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 Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with OpenSlide. If not, see
#  <http://www.gnu.org/licenses/>.
#

#
# Convert a slide file into a fake Hamamatsu VMU slide that's good enough
# to fool OpenSlide.
#

from configparser import RawConfigParser
from fractions import gcd
from openslide import OpenSlide
import struct
import sys

BUF_HEIGHT = 512

class VmuLevel(object):
    def __init__(self, osr, level):
        self._osr = osr
        self.level = level
        self.width, self.height = osr.level_dimensions[level]
        self.column_width = gcd(self.width, 400)
        self.downsample = osr.level_downsamples[level]

    def save(self, path):
        with open(path, 'w') as fh:
            # Header
            fh.write(struct.pack('<2c2x3i8xi4x', 'G', 'N', self.width,
                    self.height, self.column_width, 32))

            # Body
            for col in range(self.width // self.column_width):
                # BUF_HEIGHT rows at a time
                for i in range((self.height + BUF_HEIGHT - 1) // BUF_HEIGHT):
                    rows = min(BUF_HEIGHT, self.height - i * BUF_HEIGHT)
                    img = self._osr.read_region(
                            (int(col * self.column_width * self.downsample),
                            int(i * BUF_HEIGHT * self.downsample)),
                            self.level,
                            (self.column_width, rows)).load()
                    for y in range(rows):
                        for x in range(self.column_width):
                            pix = [v << 4 for v in img[x, y]]
                            # FIXME: ignores alpha
                            fh.write(struct.pack('<3h', *pix[0:3]))

def make_vmu(in_path, out_base):
    path_conf = out_base + '.vmu'
    path_0 = out_base + '.l0'
    path_1 = out_base + '.l1'

    with OpenSlide(in_path) as osr:
        l0 = VmuLevel(osr, 0)
        l1 = VmuLevel(osr, osr.get_best_level_for_downsample(32))
        for i, l in enumerate([l0, l1]):
            print(('Level %d: %d pixels/column' % (i, l.column_width)))
        l0.save(path_0)
        l1.save(path_1)

    section = 'Uncompressed Virtual Microscope Specimen'
    conf = {
        'NoLayers': '1',
        'ImageFile': path_0,
        'MapFile': path_1,
        'BitsPerPixel': '36',
        'PixelOrder': 'RGB',
    }

    c = RawConfigParser()
    c.optionxform = str
    c.add_section(section)
    for k, v in list(conf.items()):
        c.set(section, k, v)
    with open(path_conf, 'w') as fh:
        c.write(fh)

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print(('Usage: %s infile outbase' % sys.argv[0]))
        sys.exit(1)
    make_vmu(sys.argv[1], sys.argv[2])
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""Layer effects API.

Class objects in this class corresponds to information in the layer effect
dialog in Photoshop.

Usage::

    for effect in psd.layers[0].effects:
        print(effect.name(), effect.dict())

"""
from __future__ import absolute_import
import inspect
import logging
from psd_tools.user_api.actions import translate
from psd_tools.constants import TaggedBlock, BlendMode2, ObjectBasedEffects

logger = logging.getLogger(__name__)

def get_effects(layer):
    """Return effects block from the layer."""
    effects = layer.get_tag([
        TaggedBlock.OBJECT_BASED_EFFECTS_LAYER_INFO,
        TaggedBlock.OBJECT_BASED_EFFECTS_LAYER_INFO_V0,
        TaggedBlock.OBJECT_BASED_EFFECTS_LAYER_INFO_V1,
        ])
    if not effects:
        return None
    descriptor = translate(effects.descriptor)
    return Effects(descriptor)

class _BaseEffect(object):
    """Base class for effect."""
    def __init__(self, descriptor):
        self._descriptor = descriptor

    @property
    def enabled(self):
        """Whether if the effect is enabled.

        :rtype: bool
        """
        return self.get(b'enab', True)

    @property
    def present(self):
        """Whether if the effect is present in UI.

        :rtype: bool
        """
        return self.get(b'present')

    @property
    def show_in_dialog(self):
        """Whether if the effect is shown in dialog.

        :rtype: bool
        """
        return self.get(b'showInDialog')

    @property
    def blend_mode(self):
        """Effect blending mode.

        :returns: blending mode
        :rtype: str

        Blending mode is one of the following.

          - normal
          - dissolve
          - darken
          - multiply
          - color burn
          - linear burn
          - darken
          - lighten
          - screen
          - color dodge
          - linear dodge
          - lighter color
          - overlay
          - soft light
          - hard light
          - vivid light
          - linear light
          - pin light
          - hard mix
          - difference
          - exclusion
          - subtract
          - divide
          - hue
          - saturation
          - color
          - luminosity
        """
        return BlendMode2.human_name_of(
            self.get(b'Md  ', BlendMode2.NORMAL), 'normal')

    @property
    def opacity(self):
        """Layer effect opacity in percentage.

        :rtype: float
        """
        return self.get(b'Opct', 100.0)

    @property
    def name(self):
        """Layer effect name.

        :rtype: str
        """
        return self.__class__.__name__

    def get(self, key, default=None):
        """Get attribute in the low-level structure.

        :param key: property key
        :type key: bytes
        :param default: default value to return
        """
        return self._descriptor.get(key, default)

    def properties(self):
        """Return a list of property names.

        :returns: list of properties.
        :rtype: list
        """
        return [k for (k, v) in inspect.getmembers(
            self.__class__, lambda x: isinstance(x, property))]

    def dict(self):
        """Convert to dict."""
        return {k: getattr(self, k) for k in self.properties()}

    def __repr__(self):
        return "<%s>" % (self.name.lower(),)

class _ColorMixin(object):
    @property
    def color(self):
        """Color.

        :returns: color tuple.
        :rtype: psd_tools.user_api.actions.Color
        """
        return self.get(b'Clr ')

class _ChokeNoiseMixin(_ColorMixin):
    @property
    def choke(self):
        """Choke level."""
        return self.get(b'Ckmt', 0.0)

    @property
    def size(self):
        """Size in pixels."""
        return self.get(b'blur', 41.0)

    @property
    def noise(self):
        """Noise level."""
        return self.get(b'Nose', 0.0)

    @property
    def anti_aliased(self):
        """Angi-aliased."""
        return self.get(b'AntA', False)

    @property
    def contour(self):
        """Contour configuration."""
        return self.get(b'TrnS')

class _ShadowEffect(_BaseEffect, _ChokeNoiseMixin):
    """Base class for shadow effect."""
    @property
    def use_global_light(self):
        """Using global light."""
        return self.get(b'uglg', True)

    @property
    def angle(self):
        """Angle."""
        return self.get(b'lagl', 90.0)

    @property
    def distance(self):
        """Distance."""
        return self.get(b'Dstn', 18.0)

class DropShadow(_ShadowEffect):
    """DropShadow effect."""
    @property
    def layer_knocks_out(self):
        """Layers are knocking out."""
        return self.get(b'layerConceals', True)

class InnerShadow(_ShadowEffect):
    """InnerShadow effect."""
    pass

class _GlowEffect(_BaseEffect, _ChokeNoiseMixin):
    """Base class for glow effect."""
    @property
    def glow_type(self):
        """ Elements technique, softer or precise """
        return {b'SfBL': 'softer'}.get(self.get(b'GlwT', b'SfBL'), 'precise')

    @property
    def quality_range(self):
        """Quality range."""
        return self.get(b'Inpr')

    @property
    def quality_jitter(self):
        """Quality jitter"""
        return self.get(b'ShdN')

class OuterGlow(_GlowEffect):
    """OuterGlow effect."""
    @property
    def spread(self):
        return self.get(b'ShdN')

class InnerGlow(_GlowEffect):
    """InnerGlow effect."""
    @property
    def glow_source(self):
        """ Elements source, center or edge """
        return {b'SrcE': 'edge'}.get(self.get(b'glwS', b'SrcE'), 'center')

class _OverlayEffect(_BaseEffect):
    pass

class _AlignScaleMixin(object):
    @property
    def scale(self):
        """Scale value."""
        return self.get(b'Scl ')

    @property
    def aligned(self):
        """Aligned."""
        return self.get(b'Algn')

class ColorOverlay(_OverlayEffect, _ColorMixin):
    """ColorOverlay effect."""
    pass

class GradientOverlay(_OverlayEffect, _AlignScaleMixin):
    """GradientOverlay effect."""
    @property
    def gradient(self):
        """Gradient configuration.

        :rtype: psd_tools.user_api.actions.Gradient
        """
        # TODO: Expose nested property.
        return self.get(b'Grad')

    @property
    def angle(self):
        """Angle value."""
        return self.get(b'Angl', 30.0)

    @property
    def type(self):
        """Gradient type, linear or radial."""
        # TODO: Rephrase bytes. b'Lnr ': 'linear'.
        return self.get(b'Type', b'Lnr ')

    @property
    def reversed(self):
        """Reverse flag."""
        return self.get(b'Rvrs', False)

    @property
    def dithered(self):
        """Dither flag."""
        return self.get(b'Dthr', False)

    @property
    def offset(self):
        """Offset value."""
        return self.get(b'Ofst')

class PatternOverlay(_OverlayEffect, _AlignScaleMixin):
    """PatternOverlay effect.

    Retrieving pattern data::

        if effect.name() == 'PatternOverlay':
            pattern = psd.patterns.get(effect.pattern.id)
    """
    @property
    def pattern(self):
        """Pattern config.

        :rtype: psd_tools.user_api.actions.Pattern
        """
        # TODO: Expose nested property.
        return self.get(b'Ptrn')

    @property
    def phase(self):
        """Phase value."""
        return self.get(b'phase')

class Stroke(_BaseEffect, _ColorMixin):
    """Stroke effect."""

    @property
    def position(self):
        """Position of the stroke."""
        # TODO: Rephrase bytes.
        return self.get(b'Styl', b'OutF')

    @property
    def fill_type(self):
        """Fill type, solid color, gradient, or pattern."""
        # TODO: Rephrase bytes. b'SClr': 'color'.
        return self.get(b'PntT', b'SClr')

    @property
    def size(self):
        """Size value."""
        return self.get(b'Sz ', 6.0)

    @property
    def overprint(self):
        """Overprint flag."""
        return self.get(b'overprint', False)

class BevelEmboss(_BaseEffect):
    """Bevel and Emboss effect."""
    @property
    def highlight_mode(self):
        """Highlight blending mode."""
        return BlendMode2.human_name_of(
            self.get(b'hglM', BlendMode2.NORMAL), 'normal')

    @property
    def highlight_color(self):
        """Highlight color value."""
        return self.get(b'hglC')

    @property
    def highlight_opacity(self):
        """Highlight opacity value."""
        return self.get(b'hglO')

    @property
    def shadow_mode(self):
        """Shadow blending mode."""
        return BlendMode2.human_name_of(
            self.get(b'sdwM', BlendMode2.NORMAL), 'normal')

    @property
    def shadow_color(self):
        """Shadow color value."""
        return self.get(b'sdwC')

    @property
    def shadow_opacity(self):
        """Shadow opacity value."""
        return self.get(b'sdwO')

    @property
    def bevel_type(self):
        """Bevel type."""
        # TODO: Rephrase bytes.
        return self.get(b'bvlT', b'SfBL')

    @property
    def bevel_style(self):
        """Bevel style, emboss or bevel."""
        # TODO: Rephrase bytes.
        return self.get(b'bvlS', b'Embs')

    @property
    def use_global_light(self):
        """Using global light."""
        return self.get(b'uglg', True)

    @property
    def angle(self):
        """Angle value."""
        return self.get(b'lagl', 90.0)

    @property
    def altitude(self):
        """Altitude value."""
        return self.get(b'Lald', 30.0)

    @property
    def depth(self):
        """Depth value."""
        return self.get(b'srgR', 100.0)

    @property
    def size(self):
        """Size value in pixel."""
        return self.get(b'blur', 41.0)

    @property
    def direction(self):
        """Direction."""
        # TODO: Rephrase bytes.
        return self.get(b'bvlD', b'In  ')

    @property
    def contour(self):
        """Contour configuration."""
        return self.get(b'TrnS')

    @property
    def anti_aliased(self):
        """Anti-aliased."""
        return self.get(b'antialiasGloss', False)

    @property
    def soften(self):
        """Soften value."""
        return self.get(b'Sftn', 0.0)

    @property
    def use_shape(self):
        """Using shape."""
        return self.get(b'useShape', False)

    @property
    def use_texture(self):
        """Using texture."""
        return self.get(b'useTexture', False)

class Satin(_BaseEffect, _ColorMixin):
    """ Satin effect """
    @property
    def anti_aliased(self):
        """Anti-aliased."""
        return self.get(b'AntA', True)

    @property
    def inverted(self):
        """Inverted."""
        return self.get(b'Invr', True)

    @property
    def angle(self):
        """Angle value."""
        return self.get(b'lagl', 90.0)

    @property
    def distance(self):
        """Distance value."""
        return self.get(b'Dstn', 250.0)

    @property
    def size(self):
        """Size value in pixel."""
        return self.get(b'blur', 250.0)

    @property
    def contour(self):
        """Contour configuration."""
        return self.get(b'MpgS')

class Effects(object):
    """Layer effects wrapper. Behaves like a list.

    Example::

        for effect in psd.layers[0].effects:
            print(effect.name())

        for effect in psd.layers[0].effects.find('coloroverlay'):
            print(effect.color)
    """
    _KEYS = {
        ObjectBasedEffects.DROP_SHADOW_MULTI: DropShadow,
        ObjectBasedEffects.DROP_SHADOW: DropShadow,
        ObjectBasedEffects.INNER_SHADOW_MULTI: InnerShadow,
        ObjectBasedEffects.INNER_SHADOW: InnerShadow,
        ObjectBasedEffects.OUTER_GLOW: OuterGlow,
        ObjectBasedEffects.COLOR_OVERLAY_MULTI: ColorOverlay,
        ObjectBasedEffects.COLOR_OVERLAY: ColorOverlay,
        ObjectBasedEffects.GRADIENT_OVERLAY_MULTI: GradientOverlay,
        ObjectBasedEffects.GRADIENT_OVERLAY: GradientOverlay,
        ObjectBasedEffects.PATTERN_OVERLAY: PatternOverlay,
        ObjectBasedEffects.STROKE_MULTI: Stroke,
        ObjectBasedEffects.STROKE: Stroke,
        ObjectBasedEffects.INNER_GLOW: InnerGlow,
        ObjectBasedEffects.BEVEL_EMBOSS: BevelEmboss,
        ObjectBasedEffects.SATIN: Satin,
        }

    def __init__(self, descriptor):
        self._descriptor = descriptor
        self.items = self._build_items()

    @property
    def scale(self):
        """Scale value."""
        return self.get(b'Scl ')

    @property
    def enabled(self):
        """Whether if all the effects are enabled.

        :rtype: bool
        """
        return self._descriptor.get(b'masterFXSwitch', True)

    def _build_items(self):
        items = []
        for key in self._descriptor:
            cls = self._KEYS.get(key, None)
            if not cls:
                continue
            if key.endswith(b'Multi'):
                for value in self._descriptor[key]:
                    items.append(cls(value))
            else:
                items.append(cls(self._descriptor[key]))
        return items

    def present_items(self):
        """List of effects present in Photoshop UI."""
        return [item for item in self.items if item.present]

    def enabled_items(self):
        """List of enabled effects."""
        if self.enabled:
            return [item for item in self.items
                    if getattr(item, 'enabled', False)]
        return []

    def has(self, kinds):
        if kinds == str or kinds == bytes:
            kinds = [kinds]
        kinds = {kind.lower() for kind in kinds}
        return any(item.name.lower() in kinds
                   for item in self.enabled_items())

    def find(self, kind):
        """Return a list of specified effects.

        Names can be one of the following:

        - DropShadow
        - InnerShadow
        - OuterGlow
        - ColorOverlay
        - GradientOverlay
        - PatternOverlay
        - Stroke
        - InnerGlow
        - BevelEmboss
        - Satin
        """
        return [item for item in self.enabled_items()
                if item.name.lower() == kind.lower()]

    def __getitem__(self, index):
        return self.enabled_items()[index]

    def __len__(self):
        return len(self.enabled_items())

    def __repr__(self):
        return "%s" % (self.enabled_items(),)
<commit_msg>Instantiate empty effects<commit_after># -*- coding: utf-8 -*-
"""Layer effects API.

Class objects in this class corresponds to information in the layer effect
dialog in Photoshop.

Usage::

    for effect in psd.layers[0].effects:
        print(effect.name(), effect.dict())

"""
from __future__ import absolute_import
import inspect
import logging
from psd_tools.user_api.actions import translate
from psd_tools.constants import TaggedBlock, BlendMode2, ObjectBasedEffects

logger = logging.getLogger(__name__)

def get_effects(layer):
    """Return effects block from the layer."""
    effects = layer.get_tag([
        TaggedBlock.OBJECT_BASED_EFFECTS_LAYER_INFO,
        TaggedBlock.OBJECT_BASED_EFFECTS_LAYER_INFO_V0,
        TaggedBlock.OBJECT_BASED_EFFECTS_LAYER_INFO_V1,
        ])
    if not effects:
        return Effects({})
    descriptor = translate(effects.descriptor)
    return Effects(descriptor)

class _BaseEffect(object):
    """Base class for effect."""
    def __init__(self, descriptor):
        self._descriptor = descriptor

    @property
    def enabled(self):
        """Whether if the effect is enabled.

        :rtype: bool
        """
        return self.get(b'enab', True)

    @property
    def present(self):
        """Whether if the effect is present in UI.

        :rtype: bool
        """
        return self.get(b'present')

    @property
    def show_in_dialog(self):
        """Whether if the effect is shown in dialog.

        :rtype: bool
        """
        return self.get(b'showInDialog')

    @property
    def blend_mode(self):
        """Effect blending mode.

        :returns: blending mode
        :rtype: str

        Blending mode is one of the following.

          - normal
          - dissolve
          - darken
          - multiply
          - color burn
          - linear burn
          - darken
          - lighten
          - screen
          - color dodge
          - linear dodge
          - lighter color
          - overlay
          - soft light
          - hard light
          - vivid light
          - linear light
          - pin light
          - hard mix
          - difference
          - exclusion
          - subtract
          - divide
          - hue
          - saturation
          - color
          - luminosity
        """
        return BlendMode2.human_name_of(
            self.get(b'Md  ', BlendMode2.NORMAL), 'normal')

    @property
    def opacity(self):
        """Layer effect opacity in percentage.

        :rtype: float
        """
        return self.get(b'Opct', 100.0)

    @property
    def name(self):
        """Layer effect name.

        :rtype: str
        """
        return self.__class__.__name__

    def get(self, key, default=None):
        """Get attribute in the low-level structure.

        :param key: property key
        :type key: bytes
        :param default: default value to return
        """
        return self._descriptor.get(key, default)

    def properties(self):
        """Return a list of property names.

        :returns: list of properties.
        :rtype: list
        """
        return [k for (k, v) in inspect.getmembers(
            self.__class__, lambda x: isinstance(x, property))]

    def dict(self):
        """Convert to dict."""
        return {k: getattr(self, k) for k in self.properties()}

    def __repr__(self):
        return "<%s>" % (self.name.lower(),)

class _ColorMixin(object):
    @property
    def color(self):
        """Color.

        :returns: color tuple.
        :rtype: psd_tools.user_api.actions.Color
        """
        return self.get(b'Clr ')

class _ChokeNoiseMixin(_ColorMixin):
    @property
    def choke(self):
        """Choke level."""
        return self.get(b'Ckmt', 0.0)

    @property
    def size(self):
        """Size in pixels."""
        return self.get(b'blur', 41.0)

    @property
    def noise(self):
        """Noise level."""
        return self.get(b'Nose', 0.0)

    @property
    def anti_aliased(self):
        """Angi-aliased."""
        return self.get(b'AntA', False)

    @property
    def contour(self):
        """Contour configuration."""
        return self.get(b'TrnS')

class _ShadowEffect(_BaseEffect, _ChokeNoiseMixin):
    """Base class for shadow effect."""
    @property
    def use_global_light(self):
        """Using global light."""
        return self.get(b'uglg', True)

    @property
    def angle(self):
        """Angle."""
        return self.get(b'lagl', 90.0)

    @property
    def distance(self):
        """Distance."""
        return self.get(b'Dstn', 18.0)

class DropShadow(_ShadowEffect):
    """DropShadow effect."""
    @property
    def layer_knocks_out(self):
        """Layers are knocking out."""
        return self.get(b'layerConceals', True)

class InnerShadow(_ShadowEffect):
    """InnerShadow effect."""
    pass

class _GlowEffect(_BaseEffect, _ChokeNoiseMixin):
    """Base class for glow effect."""
    @property
    def glow_type(self):
        """ Elements technique, softer or precise """
        return {b'SfBL': 'softer'}.get(self.get(b'GlwT', b'SfBL'), 'precise')

    @property
    def quality_range(self):
        """Quality range."""
        return self.get(b'Inpr')

    @property
    def quality_jitter(self):
        """Quality jitter"""
        return self.get(b'ShdN')

class OuterGlow(_GlowEffect):
    """OuterGlow effect."""
    @property
    def spread(self):
        return self.get(b'ShdN')

class InnerGlow(_GlowEffect):
    """InnerGlow effect."""
    @property
    def glow_source(self):
        """ Elements source, center or edge """
        return {b'SrcE': 'edge'}.get(self.get(b'glwS', b'SrcE'), 'center')

class _OverlayEffect(_BaseEffect):
    pass

class _AlignScaleMixin(object):
    @property
    def scale(self):
        """Scale value."""
        return self.get(b'Scl ')

    @property
    def aligned(self):
        """Aligned."""
        return self.get(b'Algn')

class ColorOverlay(_OverlayEffect, _ColorMixin):
    """ColorOverlay effect."""
    pass

class GradientOverlay(_OverlayEffect, _AlignScaleMixin):
    """GradientOverlay effect."""
    @property
    def gradient(self):
        """Gradient configuration.

        :rtype: psd_tools.user_api.actions.Gradient
        """
        # TODO: Expose nested property.
        return self.get(b'Grad')

    @property
    def angle(self):
        """Angle value."""
        return self.get(b'Angl', 30.0)

    @property
    def type(self):
        """Gradient type, linear or radial."""
        # TODO: Rephrase bytes. b'Lnr ': 'linear'.
        return self.get(b'Type', b'Lnr ')

    @property
    def reversed(self):
        """Reverse flag."""
        return self.get(b'Rvrs', False)

    @property
    def dithered(self):
        """Dither flag."""
        return self.get(b'Dthr', False)

    @property
    def offset(self):
        """Offset value."""
        return self.get(b'Ofst')

class PatternOverlay(_OverlayEffect, _AlignScaleMixin):
    """PatternOverlay effect.

    Retrieving pattern data::

        if effect.name() == 'PatternOverlay':
            pattern = psd.patterns.get(effect.pattern.id)
    """
    @property
    def pattern(self):
        """Pattern config.

        :rtype: psd_tools.user_api.actions.Pattern
        """
        # TODO: Expose nested property.
        return self.get(b'Ptrn')

    @property
    def phase(self):
        """Phase value."""
        return self.get(b'phase')

class Stroke(_BaseEffect, _ColorMixin):
    """Stroke effect."""

    @property
    def position(self):
        """Position of the stroke."""
        # TODO: Rephrase bytes.
        return self.get(b'Styl', b'OutF')

    @property
    def fill_type(self):
        """Fill type, solid color, gradient, or pattern."""
        # TODO: Rephrase bytes. b'SClr': 'color'.
        return self.get(b'PntT', b'SClr')

    @property
    def size(self):
        """Size value."""
        return self.get(b'Sz ', 6.0)

    @property
    def overprint(self):
        """Overprint flag."""
        return self.get(b'overprint', False)

class BevelEmboss(_BaseEffect):
    """Bevel and Emboss effect."""
    @property
    def highlight_mode(self):
        """Highlight blending mode."""
        return BlendMode2.human_name_of(
            self.get(b'hglM', BlendMode2.NORMAL), 'normal')

    @property
    def highlight_color(self):
        """Highlight color value."""
        return self.get(b'hglC')

    @property
    def highlight_opacity(self):
        """Highlight opacity value."""
        return self.get(b'hglO')

    @property
    def shadow_mode(self):
        """Shadow blending mode."""
        return BlendMode2.human_name_of(
            self.get(b'sdwM', BlendMode2.NORMAL), 'normal')

    @property
    def shadow_color(self):
        """Shadow color value."""
        return self.get(b'sdwC')

    @property
    def shadow_opacity(self):
        """Shadow opacity value."""
        return self.get(b'sdwO')

    @property
    def bevel_type(self):
        """Bevel type."""
        # TODO: Rephrase bytes.
        return self.get(b'bvlT', b'SfBL')

    @property
    def bevel_style(self):
        """Bevel style, emboss or bevel."""
        # TODO: Rephrase bytes.
        return self.get(b'bvlS', b'Embs')

    @property
    def use_global_light(self):
        """Using global light."""
        return self.get(b'uglg', True)

    @property
    def angle(self):
        """Angle value."""
        return self.get(b'lagl', 90.0)

    @property
    def altitude(self):
        """Altitude value."""
        return self.get(b'Lald', 30.0)

    @property
    def depth(self):
        """Depth value."""
        return self.get(b'srgR', 100.0)

    @property
    def size(self):
        """Size value in pixel."""
        return self.get(b'blur', 41.0)

    @property
    def direction(self):
        """Direction."""
        # TODO: Rephrase bytes.
        return self.get(b'bvlD', b'In  ')

    @property
    def contour(self):
        """Contour configuration."""
        return self.get(b'TrnS')

    @property
    def anti_aliased(self):
        """Anti-aliased."""
        return self.get(b'antialiasGloss', False)

    @property
    def soften(self):
        """Soften value."""
        return self.get(b'Sftn', 0.0)

    @property
    def use_shape(self):
        """Using shape."""
        return self.get(b'useShape', False)

    @property
    def use_texture(self):
        """Using texture."""
        return self.get(b'useTexture', False)

class Satin(_BaseEffect, _ColorMixin):
    """ Satin effect """
    @property
    def anti_aliased(self):
        """Anti-aliased."""
        return self.get(b'AntA', True)

    @property
    def inverted(self):
        """Inverted."""
        return self.get(b'Invr', True)

    @property
    def angle(self):
        """Angle value."""
        return self.get(b'lagl', 90.0)

    @property
    def distance(self):
        """Distance value."""
        return self.get(b'Dstn', 250.0)

    @property
    def size(self):
        """Size value in pixel."""
        return self.get(b'blur', 250.0)

    @property
    def contour(self):
        """Contour configuration."""
        return self.get(b'MpgS')

class Effects(object):
    """Layer effects wrapper. Behaves like a list.

    Example::

        for effect in psd.layers[0].effects:
            print(effect.name())

        for effect in psd.layers[0].effects.find('coloroverlay'):
            print(effect.color)
    """
    _KEYS = {
        ObjectBasedEffects.DROP_SHADOW_MULTI: DropShadow,
        ObjectBasedEffects.DROP_SHADOW: DropShadow,
        ObjectBasedEffects.INNER_SHADOW_MULTI: InnerShadow,
        ObjectBasedEffects.INNER_SHADOW: InnerShadow,
        ObjectBasedEffects.OUTER_GLOW: OuterGlow,
        ObjectBasedEffects.COLOR_OVERLAY_MULTI: ColorOverlay,
        ObjectBasedEffects.COLOR_OVERLAY: ColorOverlay,
        ObjectBasedEffects.GRADIENT_OVERLAY_MULTI: GradientOverlay,
        ObjectBasedEffects.GRADIENT_OVERLAY: GradientOverlay,
        ObjectBasedEffects.PATTERN_OVERLAY: PatternOverlay,
        ObjectBasedEffects.STROKE_MULTI: Stroke,
        ObjectBasedEffects.STROKE: Stroke,
        ObjectBasedEffects.INNER_GLOW: InnerGlow,
        ObjectBasedEffects.BEVEL_EMBOSS: BevelEmboss,
        ObjectBasedEffects.SATIN: Satin,
        }

    def __init__(self, descriptor):
        self._descriptor = descriptor
        self.items = self._build_items()

    @property
    def scale(self):
        """Scale value."""
        return self.get(b'Scl ')

    @property
    def enabled(self):
        """Whether if all the effects are enabled.

        :rtype: bool
        """
        return self._descriptor.get(b'masterFXSwitch', True)

    def _build_items(self):
        items = []
        for key in self._descriptor:
            cls = self._KEYS.get(key, None)
            if not cls:
                continue
            if key.endswith(b'Multi'):
                for value in self._descriptor[key]:
                    items.append(cls(value))
            else:
                items.append(cls(self._descriptor[key]))
        return items

    def present_items(self):
        """List of effects present in Photoshop UI."""
        return [item for item in self.items if item.present]

    def enabled_items(self):
        """List of enabled effects."""
        if self.enabled:
            return [item for item in self.items
                    if getattr(item, 'enabled', False)]
        return []

    def has(self, kinds):
        if kinds == str or kinds == bytes:
            kinds = [kinds]
        kinds = {kind.lower() for kind in kinds}
        return any(item.name.lower() in kinds
                   for item in self.enabled_items())

    def find(self, kind):
        """Return a list of specified effects.

        Names can be one of the following:

        - DropShadow
        - InnerShadow
        - OuterGlow
        - ColorOverlay
        - GradientOverlay
        - PatternOverlay
        - Stroke
        - InnerGlow
        - BevelEmboss
        - Satin
        """
        return [item for item in self.enabled_items()
                if item.name.lower() == kind.lower()]

    def __getitem__(self, index):
        return self.enabled_items()[index]

    def __len__(self):
        return len(self.enabled_items())

    def __repr__(self):
        return "%s" % (self.enabled_items(),)
<|endoftext|>


<commit_before># coding: utf-8
from __future__ import print_function

from datetime import datetime

from jinja2.exceptions import TemplateNotFound
import mkblogs

from mkblogs import nav, utils
from mkblogs import html as parser
from mkblogs.compat import urljoin, PY2
from mkblogs.relative_path_ext import RelativePathExtension, TitleExtension
import jinja2
import json
import markdown
import os
import logging
import operator

from mkblogs.build_pages import *

log = logging.getLogger('mkblogs')
omit_path = ['index.md', 'img']

BLANK_BLOG_CONTEXT = {
        'page_title': None,
        'page_description': None,

        'content': None,
        'toc': None,
        'meta': None,

        'canonical_url': None,

        'current_page': None, #maybe I should use home?
        'previous_page': None,
        'next_page': None
        }

def get_blog_context(config, html, toc, meta):
    """
    update a blogs' page context
    """
    return {

            #there is no next page and previous page for blo
            'content' : html,
            'toc' : toc,
            'meta' : meta
            }

class ScanContext:
    def __init__(self, config, dir_path, site_navigation):
        """same for all blogs"""
        self.site_navigation = site_navigation
        loader = jinja2.FileSystemLoader(config['theme_dir'])
        self.env = jinja2.Environment(loader=loader)
        """end: same for all blogs"""

        """
        since all files in the same dir all share the have same reference to
        media files, we just use a dummpy path to handle it.
        """
        dummpy = os.path.join(dir_path, 'dummpy')
        dummpy_page = nav.Page(None, url=utils.get_url_path(dummpy),
                path=dummpy, url_context=nav.URLContext())

        self.site_navigation.update_path(dummpy_page)
        self.global_context = get_global_context(self.site_navigation, config)

def _build_blog(path, config, scan_context):
    """
    convert a blog from @input_path to @output_path html, this func differ from
    _build_page where it use a page struct.
    """
    input_path = os.path.join(config['docs_dir'], path)
    try:
        input_content = open(input_path, 'r').read()
    except IOError:
        log.error('file not found: %s', input_path)
        return

    if PY2:
        input_content = input_content.decode('utf-8')

    # Process the markdown text

    #TODO: we need a different convert_markdown coz:
    #okay, there is a hack here, we pase a externsion struct in, when we
    #return, there will be md struct there. we can get what we want from md.
    extens = config['markdown_extensions']

    html_content, toc, meta = parser.convert_markdown(
        input_content, #without site_navigation
        extensions=extens, strict=config['strict'], wantmd=False
    )

    #if we want to parallize, we have to acquire a lock for global_context
    context = scan_context.global_context
    context.update(BLANK_BLOG_CONTEXT)
    context.update(get_blog_context(config, html_content, toc, meta))

    # Allow 'template:' override in md source files.
    if 'template' in meta:
        template = scan_context.env.get_template(meta['template'][0])
    else:
        template = scan_context.env.get_template('base.html')

    # Render the template.
    final_content =  template.render(context)
    #just write right in the directory
    output_path = os.path.splitext(input_path)[0] + '.html'
    with open(output_path, 'w') as f:
        f.write(final_content.encode('utf8'))
        f.close()

def read_ignore(ignored_file):
    ignored_list = []
    if not os.path.isfile(ignored_file):
        pass
    else:
        with open(ignored_file) as f:
            for line in f:
                ignored_files.append(line)
            f.close()
    return ignored_list

def add_top_n(newest_paths, to_add, n):
    newest_paths.extend(to_add)
    if not newest_paths:
        return []
    return sorted(newest_paths, key=operator.itemgetter(1), \
            reverse=True)[:n]

def recursive_scan(this_dir, config, n_new, cata_list, site_navigation, genindex=True):
    """
    @this_dir starts inside docs, so you will never see 'docs/' in it.
    @real_dir is @this_dir with a prefix, so we can list files done here
    @we use a @doc_path to indicate a logic log path, use @_doc_path to
    represent true path.

    also, we record the newest N blogs
    we use one single global_context to represent all files in the same dir
    """
    real_dir = os.path.join(config['docs_dir'], this_dir)
    scan_context = ScanContext(config, this_dir, site_navigation)

    global omit_path
    newest_paths = []
    local_paths = []
    dot_ignore = '.ignore'
    new_blogs = 0
    #globally ignore pages

    paths = os.listdir(real_dir)
    ignored_files = read_ignore(os.path.join(real_dir, dot_ignore))
    for f in paths:
        doc_path  = os.path.join(this_dir, f)
        _doc_path = os.path.join(real_dir, f)

        #locally ignored
        if f == dot_ignore:
            continue
        if f in ignored_files:
            continue
        if f in omit_path:
            continue
        #globally ingored
        if utils.is_page(doc_path, config['pages']):
            continue

#TODO: if any of documents changed, index should be changed as well. In other
#words, local index.md relies on local blogs.md. But solving title problem is
#hard.
        if utils.is_markdown_file(_doc_path):
            addtime = os.path.getatime(_doc_path)
            local_paths.append((doc_path, addtime))
            newest_paths.append((doc_path, addtime))
            if utils.is_newmd(_doc_path):
#XXX: build a blog when it is new
                _build_blog(doc_path, config, scan_context)
                new_blogs += 1

        elif os.path.isdir(_doc_path):
            sub_newest_paths = recursive_scan(doc_path, config, n_new,
                    cata_list, site_navigation)
#XXX: update top N pages
            newest_paths = add_top_n(newest_paths, sub_newest_paths, n_new)
        else:
            continue

    if genindex == True:
        index_title = parser.get_index_title(this_dir)
        cata_list.append((index_title, this_dir))

        if new_blogs > 0:
            index_path = os.path.join(this_dir, 'index.md')
            parser.write_index(index_path, local_paths, config, index_title)
            _build_blog(index_path, config, scan_context)

    #XXX: restore context
    return add_top_n(newest_paths, [], n_new)

def build_blogs(config):
    topn = config.get('n_blogs_to_show') or 5
    cata_list = []
    site_navigation = nav.SiteNavigation(config['pages'])

    n_newest_path = newest_blogs = recursive_scan('.', config,
            topn, cata_list, site_navigation, genindex=False)
    #write index.md and cata.md, since they are just [0] and [1] in the list, we
    #just need to do this
    parser.write_catalog(config, cata_list)
    parser.write_top_index(config, n_newest_path)

def build(config, live_server=False, clean_site_dir=False):
    """
    Perform a full site build.
    """
    if clean_site_dir:
        print("Cleaning site directory")
        utils.clean_directory(config['site_dir'])
    if not live_server:
        print("Building documentation to directory: %s" % config['site_dir'])
        if not clean_site_dir and site_directory_contains_stale_files(config['site_dir']):
            print("Directory %s contains stale files. Use --clean to remove them." % config['site_dir'])

    #switch the build sequence, so that we can build catalist and index along
    #with pages
    log.debug("Building blogs.")
    build_blogs(config)
    log.debug("Building markdown pages.")
    build_pages(config)

    # NOW move compiled blogs along with themes to site dir
    # Reversed as we want to take the media files from the builtin theme
    # and then from the custom theme_dir so the custom versions take take
    # precedence.
    for theme_dir in reversed(config['theme_dir']):
        log.debug("Copying static assets from theme: %s", theme_dir)
        utils.copy_media_files(theme_dir, config['site_dir'])

    log.debug("Copying static assets from the docs dir.")
    utils.copy_media_files(config['docs_dir'], config['site_dir'])

from mkblogs import config
if __name__ == "__main__":
    logger = logging.getLogger('mkblogs')
    logger.addHandler(logging.StreamHandler())
    logger.setLevel(logging.DEBUG)

    config = config.load_config('tests/test_conf.yml')
    #print(config['pages'])
    #so basically you need add a '/' to urls,
    build(config)
<commit_msg>reshape the blogger again<commit_after># coding: utf-8
from __future__ import print_function

from datetime import datetime

from jinja2.exceptions import TemplateNotFound
import mkblogs

from mkblogs import nav, utils
from mkblogs import html as parser
from mkblogs.compat import urljoin, PY2
from mkblogs.relative_path_ext import RelativePathExtension, TitleExtension
import jinja2
import json
import markdown
import os
import logging
import operator
import threading
from multiprocessing import cpu_count as num_of_thread

from mkblogs.build_pages import *

log = logging.getLogger('mkblogs')
omit_path = ['index.md', 'img']

BLANK_BLOG_CONTEXT = {
        'page_title': None,
        'page_description': None,

        'content': None,
        'toc': None,
        'meta': None,

        'canonical_url': None,

        'current_page': None, #maybe I should use home?
        'previous_page': None,
        'next_page': None
        }

def get_blog_context(config, html, toc, meta):
    """
    update a blogs' page context
    """
    return {

            #there is no next page and previous page for blo
            'content' : html,
            'toc' : toc,
            'meta' : meta
            }

class ScanContext:
    def __init__(self, config, dir_path, site_navigation):
        """same for all blogs"""
        self.site_navigation = site_navigation
        loader = jinja2.FileSystemLoader(config['theme_dir'])
        self.env = jinja2.Environment(loader=loader)
        """end: same for all blogs"""

        """
        since all files in the same dir all share the have same reference to
        media files, we just use a dummpy path to handle it.
        """
        dummpy = os.path.join(dir_path, 'dummpy')
        dummpy_page = nav.Page(None, url=utils.get_url_path(dummpy),
                path=dummpy, url_context=nav.URLContext())

        self.site_navigation.update_path(dummpy_page)
        self.global_context = get_global_context(self.site_navigation, config)

def read_ignore(ignored_file):
    ignored_list = []
    if not os.path.isfile(ignored_file):
        pass
    else:
        with open(ignored_file) as f:
            for line in f:
                ignored_files.append(line)
            f.close()
    return ignored_list

def add_top_n(newest_paths, to_add, n):
    newest_paths.extend(to_add)
    if not newest_paths:
        return []
    return sorted(newest_paths, key=operator.itemgetter(1), \
            reverse=True)[:n]

class BlogsGen(object):
    """
    BlogsGenerator provides all the context for compile every blog,
    it will also generate tags pages.
    """
    class UpdateList(object):
        def __init__(self, toupdate = None):
            self.lock = threading.Lock()
            self.updatelist = toupdate if toupdate != None else []

        def get_to_build(self):
            self.lock.acquire()
            output = self.updatelist.pop() if self.updatelist else None
            self.lock.release()
            return output

        def done_build(self, update):
            self.lock.acquire()
            self.updatelist.append(update)
            self.lock.release()

    class BlogBuilder(threading.Thread):
        """
        A very thing layer of Thread object in so we can build object
        simoultaneously
        """
        def __init__(self, Context):
            threading.Thread.__init__(self)
            self.context = Context

        def run(self):
            while True:
                output = self.Context.get_to_build()
                if not ouput:
                    break
                self.Context.build_blog(output)

    def __init__(self, config, tobuild):
        self.toupdate = UpdateList(tobuild)
        self.updated  = UpdateList()
        self.config = config
        #TODO: fix the line below
        self.scan_context = ScanContext(config['docs_dir'])

        try:
            nthread = num_of_thread()
        except NotImplementedError:
            nthread = 4
        self.workers = []
        for i in [0:nthread]:
            self.workers.append(BlogBuilder(self))

    def start(self):
        for t in self.workers:
            t.start()
        for t in self.workers:
            t.join()
        #build something else

    def get_to_build(self):
        return self.toupdate.get_to_build()
    def done_build(self):
        self.updated.done_build()

    def build_blog(self, path):
        self._build_blog(path, self.config, scan_context)

    def _build_blog(self, path, config, scan_context):
        """
        convert a blog from @input_path to @output_path html, this function
        differs from _build_page where it use a page struct.
        """
        input_path = os.path.join(config['docs_dir'], path)
        try:
            input_content = open(input_path, 'r').read()
        except IOError:
            log.error('file not found: %s', input_path)
            return

        if PY2:
            input_content = input_content.decode('utf-8')

        # Process the markdown text

        #TODO: we need a different convert_markdown coz:
        #okay, there is a hack here, we pase a externsion struct in, when we
        #return, there will be md struct there. we can get what we want from md.
        extens = config['markdown_extensions']

        html_content, toc, meta = parser.convert_markdown(
            input_content, #without site_navigation
            extensions=extens, strict=config['strict'], wantmd=False
        )

        #if we want to parallize, we have to acquire a lock for global_context
        context = scan_context.global_context
        context.update(BLANK_BLOG_CONTEXT)
        context.update(get_blog_context(config, html_content, toc, meta))

        # Allow 'template:' override in md source files.
        if 'template' in meta:
            template = scan_context.env.get_template(meta['template'][0])
        else:
            template = scan_context.env.get_template('base.html')

        # Render the template.
        final_content =  template.render(context)
        #just write right in the directory
        output_path = os.path.splitext(input_path)[0] + '.html'
        with open(output_path, 'w') as f:
            f.write(final_content.encode('utf8'))
            f.close()

def get_toupdate(directory):
    dot_ignore = '.ignore'
    ignored_files = read_ignore(os.path.join(directory,\
        dot_ignore)).append(dot_ignore)

    markdown_list = {}
    html_list = {}
    toupdate = []
    for f in os.listdir(directory):
        f_abs = os.path.join(directory, f)

        if f == dot_ignore:
            continue
        if f in ignored_files:
            continue
        if utils.is_page(config['pages']):
            continue
        if utils.is_markdown_file(f):
            markdown_list[f] = os.path.getmtime(f_abs)
        #XXX: this could be images, assets or something
        if utils.is_html_file(f):       #we dont care what the generated html extension is
            html_list[os.path.splitext(f)[0]] = os.path.getmtime(f_abs)
        if os.path.isdir(f_abs):
            continue

    for key in markdown_list.keys():
        name = os.path.splitext(key)[0]
        mtime = markdown_list[key]
        if not html_list.get(name):
            toupdate.append(key)
        elif html_list.get(name) < mtime:
            toupdate.append(key)
        else: #html_list.get(name) >= mtime
            continue
    #del markdown_list
    #del html_list
    return toupdate

def build_blogs(config):
    topn = config.get('n_blogs_to_show') or 5
    #if I have record for previous blogs, I will be so happy
    dot_record = config.get('dot_record') or '.record'
    blog_record = []
    if os.isfile(os.path.join(config['docs_dir'],dot_record)):
        f = open(os.path.join(config['docs_dir'],dot_record))
        blog_record = json.loads(f.read())
        f.close()
    toupdate = get_toupdate(config['docs_dir'])
    #what I need to do now is get a builder that are generic enough

    cata_list = []
    site_navigation = nav.SiteNavigation(config['pages'])

    n_newest_path = newest_blogs = recursive_scan('.', config,
            topn, cata_list, site_navigation, genindex=False)
    #write index.md and cata.md, since they are just [0] and [1] in the list, we
    #just need to do this
    parser.write_catalog(config, cata_list)
    parser.write_top_index(config, n_newest_path)

def build(config, live_server=False, clean_site_dir=False):
    """
    Perform a full site build.
    """
    if clean_site_dir:
        print("Cleaning site directory")
        utils.clean_directory(config['site_dir'])
    if not live_server:
        print("Building documentation to directory: %s" % config['site_dir'])
        if not clean_site_dir and site_directory_contains_stale_files(config['site_dir']):
            print("Directory %s contains stale files. Use --clean to remove them." % config['site_dir'])

    #switch the build sequence, so that we can build catalist and index along
    #with pages
    log.debug("Building blogs.")
    build_blogs(config)
    log.debug("Building markdown pages.")
    build_pages(config)

    # NOW move compiled blogs along with themes to site dir
    # Reversed as we want to take the media files from the builtin theme
    # and then from the custom theme_dir so the custom versions take take
    # precedence.
    for theme_dir in reversed(config['theme_dir']):
        log.debug("Copying static assets from theme: %s", theme_dir)
        utils.copy_media_files(theme_dir, config['site_dir'])

    log.debug("Copying static assets from the docs dir.")
    utils.copy_media_files(config['docs_dir'], config['site_dir'])

from mkblogs import config
if __name__ == "__main__":
    logger = logging.getLogger('mkblogs')
    logger.addHandler(logging.StreamHandler())
    logger.setLevel(logging.DEBUG)

    config = config.load_config('tests/test_conf.yml')
    #print(config['pages'])
    #so basically you need add a '/' to urls,
    build(config)
<|endoftext|>