<commit_before>"""
Define the calculators used for scoring in the redistricting app.

The classes in redistricting.calculators define the scoring
calculators used in the application. Each class relates to
one type of calculator that can be referenced within the config
file to define how to score a plan or district.

This file is part of The Public Mapping Project
http://sourceforge.net/projects/publicmapping/

License:
    Copyright 2010 <NAME>, <NAME>

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

Author: 
    <NAME>, <NAME>, <NAME>
"""

from math import sqrt, pi
from django.contrib.gis.geos import Point
from django.contrib.humanize.templatetags.humanize import intcomma
from django.utils import simplejson as json
from decimal import Decimal
import locale, sys, traceback

# This helps in formatting - by default, apache+wsgi uses the "C" locale
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')

class CalculatorBase:
    """
    A base class for all calculators that defines the result object,
    and defines a couple default rendering options for HTML, JSON, and 
    Boolean.
    """

    # This calculator's result
    result = None

    # This calculator's dictionary of arguments. This dictionary is keyed
    # by argument name, and the values are tuples of the type name and
    # the value.
    #
    # E.g.: arg_dict = { 'minimum':('literal','0.5',) }
    #
    arg_dict = {}

    def __init__(self):
        """
        Initialize the result and argument dictionary.
        """
        self.result = None
        self.arg_dict = {}

    def compute(self, **kwargs):
        """
        Compute the value for this calculator. The base class calculates
        nothing.
        """
        pass

    def sortkey(self):
        """
        How should this calculator be compared to others like it? The
        default sorting method sorts by the value of the result.
        """
        return self.result

    def html(self):
        """
        Return a basic HTML representation of the result.

        The base class generates an HTML span element, with the text
        content set to a string representation of the result. If the result
        is None, the string "n/a" is used.
        """
        if not self.result is None:
            return '<span>%s</span>' % self.result
        else:
            return '<span>n/a</span>'

    def json(self):
        """
        Return a basic JSON representation of the result.

        The base class generates an simple Javascript object that contains
        a single property, named "result".
        """
        return json.dumps( {'result':self.result}, use_decimal=True )

    def get_value(self, argument, district=None):
        """
        Get the value of an argument if it is a literal or a subject.

        This method is used anytime a calculator needs to get the value of
        a named argument. The type of the argument is determined from the 
        tuple in the argument dictionary, and either the literal value or
        the retrieved ComputedCharacteristic is returned. This only searches
        for the ComputedCharacteristic in the set attached to the district.

        If no district is provided, no subject argument value is ever 
        returned.
        """
        try:
            (argtype, argval) = self.arg_dict[argument]
        except:
            return None

        value = None
        if argtype == 'literal':
            value = argval
            try:
                # If our literal is a number, make it a decimal to match models
                value = Decimal(value)
            except:
                # No problem, it may be a string
                pass
        elif argtype == 'subject' and not district is None:
            # This method is more fault tolerant than _set.get, since it 
            # won't throw an exception if the item doesn't exist.
            cc = district.computedcharacteristic_set.filter(subject__name=argval)
            if cc.count() > 0:
                value = cc[0].number
        return value

class Schwartzberg(CalculatorBase):
    """
    Calculator for the Schwartzberg measure of compactness.
        
    The Schwartzberg measure of compactness measures the perimeter of 
    the district to the circumference of the circle whose area is 
    equal to the area of the district.

    This calculator will calculate either the compactness score of a
    single district, or it will average the compactness scores of all 
    districts in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Schwartzberg measure of compactness.

        Keywords:
            district - A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            if district.geom.length == 0:
                continue
        
            r = sqrt(district.geom.area / pi)
            perimeter = 2 * pi * r
            compactness += perimeter / district.geom.length
            num += 1

        self.result = (compactness / num) if num > 0 else 0


    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class Roeck(CalculatorBase):
    """
    Calculator for the Roeck measure of compactness.

    The Roeck measure of compactness measures the area of the smallest
    enclosing circle around a district compared to the area of the district.

    This calculator will calculate either the compactness score of a single
    district, or it will average the compactness scores of all districts
    in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Roeck measure of compactness.

        Keywords:
            district -- A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            centroid = district.geom.centroid
            maxd = 0
            for linestring in district.geom.convex_hull:
                for coord in linestring:
                    maxd = max(maxd, centroid.distance(Point(coord)))

            cir_area = pi * maxd * maxd

            compactness += district.geom.area / cir_area
            num += 1

        self.result = compactness / num

    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class PolsbyPopper(CalculatorBase):
    """
    Calculator for the Polsby-Popper measure of compactness.

    The Polsby-Popper measure of campactness measures the area of a circle
    with the same perimeter as a district compared to area of the district.

    This calculator will calculate either the compactness score of a single
    district, or it will average the compactness scores of all districts
    in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Polsby-Popper measure of compactness.

        Keywords:
            district -- A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            perimeter = 0
            for poly in district.geom:
                for linestring in poly:
                    perimeter += linestring.length

            compactness += 4 * pi * district.geom.area / perimeter / perimeter
            num += 1

        self.result = compactness / num

    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class LengthWidthCompactness(CalculatorBase):
    """
    Calculator for the Length/Width measure of compactness.

    The Length/Width measure of campactness measures the length of the 
    district's bounding box, and divides it by the width of the district's
    bounding box.

    This calculator will calculate either the compactness score of a single
    district, or it will average the compactness scores of all districts
    in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Length/Width measure of compactness.

        Keywords:
            district -- A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            bbox = district.geom.extent
            compactness += (bbox[3] - bbox[1]) / (bbox[2] - bbox[0])
            num += 1

        self.result = compactness / num

    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class SumValues(CalculatorBase):
    """
    Sum up all values.

    For districts, this calculator will sum up a series of arguments.

    For plans, this calculator will sum up a series of arguments across
    all districts. If a literal value is included in a plan calculation,
    that literal value is combined with the subject value for each 
    district.

    For lists of numbers, this calculator will return the sum of the list.

    Each argument should be assigned the argument name "valueN", where N
    is a positive integer. The summation will add all arguments, starting
    at position 1, until an argument is not found.
    """
    def compute(self, **kwargs):
        """
        Calculate the sum of a series of values.
        """
        districts = []

        if 'district' in kwargs:
            districts = [kwargs['district']]
        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=False)
        elif 'list' in kwargs:
            lst = kwargs['list']
            self.result = reduce(lambda x,y: x + y, lst)
            return
        else:
            return
        
        self.result = 0

        for district in districts:
            argnum = 1
            while ('value%d'%argnum) in self.arg_dict:
                number = self.get_value('value%d'%argnum, district)
                if not number is None:
                    self.result += number

                argnum += 1

    def html(self):
        """
        Generate an HTML representation of the equivalence score. This
        is represented as an integer formatted with commas or "n/a"
        """
        if isinstance(self.result, Decimal):
            result = locale.format("%s", self.result, grouping=True)
            return '<span>%s</span>' % result
        else:
            return '<span>N/A</span>'

class Percent(CalculatorBase):
    """
    Calculate a percentage for two values.

    A percentage calculator requires two arguments: "numerator", and 
    "denominator".

    When passed a district, the percentage calculator simply divides the
    numerator by the denominator.

    When passed a plan, the percentage calculator accumulates all the
    numerator values and denominator values for all districts in the plan.
    After the numerator and denominator values have been accumulated, 
    it computes the percentage of those totals.
    """
    def compute(self, **kwargs):
        """
        Calculate a percentage.
        """
        district = None

        if 'district' in kwargs:
            district = kwargs['district']

            num = self.get_value('numerator',district)
            den = self.get_value('denominator',district)

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=False)
            num = 0
            den = 0
            for district in districts:
                if district.district_id == 0:
                    continue

                tmpnum = self.get_value('numerator',district)
                tmpden = self.get_value('denominator',district)

                # If either the numerator or denominator don't exist,
                # we have to skip it.
                if tmpnum is None or tmpden is None:
                    continue

                den += tmpden
                num += tmpnum

        else:
            return

        if num is None or den is None or den == 0:
            return
    
        self.result = num / den

    def html(self):
        """
        Generate an HTML representation of the equivalence score. This
        is represented as an integer formatted with commas or "n/a"
        """
        if (type(self.result) == Decimal):
            return '<span>{0:.2%}</span>'.format(self.result)
        else:
            return '<span>N/A</span>'

class Threshold(CalculatorBase):
    """
    Determine a value, and indicate if it exceeds a threshold.

    This calculator accepts two arguments: "value", and "threshold". The
    result of this calculator is 1 or 0, to facilitate the combination of
    scores. One example may be where the number of districts that exceed a
    threshold are required.
    <commit_msg>Improved and corrected inline code comments for calculators.<commit_after>"""
Define the calculators used for scoring in the redistricting app.

The classes in redistricting.calculators define the scoring
calculators used in the application. Each class relates to
one type of calculator that can be referenced within the config
file to define how to score a plan or district.

This file is part of The Public Mapping Project
http://sourceforge.net/projects/publicmapping/

License:
    Copyright 2010 <NAME>, <NAME>

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

Author: 
    <NAME>, <NAME>, <NAME>
"""

from math import sqrt, pi
from django.contrib.gis.geos import Point
from django.contrib.humanize.templatetags.humanize import intcomma
from django.utils import simplejson as json
from decimal import Decimal
import locale, sys, traceback

# This helps in formatting - by default, apache+wsgi uses the "C" locale
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')

class CalculatorBase:
    """
    The base class for all calculators. CalculatorBase defines the result 
    object and a couple default rendering options for HTML and JSON.
    """

    # This calculator's result
    result = None

    # This calculator's dictionary of arguments. This dictionary is keyed
    # by argument name, and the values are tuples of the type name and
    # the value.
    #
    # E.g.: arg_dict = { 'minimum':('literal','0.5',) }
    #
    arg_dict = {}

    def __init__(self):
        """
        Initialize the result and argument dictionary.
        """
        self.result = None
        self.arg_dict = {}

    def compute(self, **kwargs):
        """
        Compute the value for this calculator. The base class calculates
        nothing.
        """
        pass

    def sortkey(self):
        """
        Generate a key used to sort this calculator relative to all other
        calculators. The default sorting method sorts by the value of 
        the result.

        Returns:
            The value of the result.
        """
        return self.result

    def html(self):
        """
        Generate a basic HTML representation of the result.

        The base calculator generates an HTML span element, with the text
        content set to a string representation of the result. If the result
        is None, the string "n/a" is used.
        """
        if not self.result is None:
            return '<span>%s</span>' % self.result
        else:
            return '<span>n/a</span>'

    def json(self):
        """
        Generate a basic JSON representation of the result.

        The base calculator generates an simple Javascript object that 
        contains a single property, named "result".
        """
        return json.dumps( {'result':self.result}, use_decimal=True )

    def get_value(self, argument, district=None):
        """
        Get the value of an argument if it is a literal or a subject.

        This method is used anytime a calculator needs to get the value of
        a named argument. The type of the argument is determined from the 
        tuple in the argument dictionary, and either the literal value or
        the retrieved ComputedCharacteristic is returned. This only searches
        for the ComputedCharacteristic in the set attached to the district.

        If no district is provided, no subject argument value is ever 
        returned.

        Parameters:
            argument -- The name of the argument passed to the calculator.
            district -- An optional district, used to fetch related
              ComputedCharacteristics.

        Returns:
            The value of the subject or literal argument.
        """
        try:
            (argtype, argval) = self.arg_dict[argument]
        except:
            return None

        value = None
        if argtype == 'literal':
            value = argval
            try:
                # If our literal is a number, make it a decimal to match models
                value = Decimal(value)
            except:
                # No problem, it may be a string
                pass
        elif argtype == 'subject' and not district is None:
            # This method is more fault tolerant than _set.get, since it 
            # won't throw an exception if the item doesn't exist.
            cc = district.computedcharacteristic_set.filter(subject__name=argval)
            if cc.count() > 0:
                value = cc[0].number
        return value

class Schwartzberg(CalculatorBase):
    """
    Calculator for the Schwartzberg measure of compactness.
        
    The Schwartzberg measure of compactness measures the perimeter of 
    the district to the circumference of the circle whose area is 
    equal to the area of the district.

    This calculator will calculate either the compactness score of a
    single district, or it will average the compactness scores of all 
    districts in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Schwartzberg measure of compactness.

        Keywords:
            district - A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            if district.geom.length == 0:
                continue
        
            r = sqrt(district.geom.area / pi)
            perimeter = 2 * pi * r
            compactness += perimeter / district.geom.length
            num += 1

        self.result = (compactness / num) if num > 0 else 0


    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"

        Returns:
            A number formatted similar to "1.00%", or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class Roeck(CalculatorBase):
    """
    Calculator for the Roeck measure of compactness.

    The Roeck measure of compactness measures the area of the smallest
    enclosing circle around a district compared to the area of the district.

    This calculator will calculate either the compactness score of a single
    district, or it will average the compactness scores of all districts
    in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Roeck measure of compactness.

        Keywords:
            district -- A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            centroid = district.geom.centroid
            maxd = 0
            for linestring in district.geom.convex_hull:
                for coord in linestring:
                    maxd = max(maxd, centroid.distance(Point(coord)))

            cir_area = pi * maxd * maxd

            compactness += district.geom.area / cir_area
            num += 1

        self.result = compactness / num

    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"

        Returns:
            A number formatted similar to "1.00%", or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class PolsbyPopper(CalculatorBase):
    """
    Calculator for the Polsby-Popper measure of compactness.

    The Polsby-Popper measure of campactness measures the area of a circle
    with the same perimeter as a district compared to area of the district.

    This calculator will calculate either the compactness score of a single
    district, or it will average the compactness scores of all districts
    in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Polsby-Popper measure of compactness.

        Keywords:
            district -- A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            perimeter = 0
            for poly in district.geom:
                for linestring in poly:
                    perimeter += linestring.length

            compactness += 4 * pi * district.geom.area / perimeter / perimeter
            num += 1

        self.result = compactness / num

    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"

        Returns:
            A number formatted similar to "1.00%", or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class LengthWidthCompactness(CalculatorBase):
    """
    Calculator for the Length/Width measure of compactness.

    The Length/Width measure of campactness measures the length of the 
    district's bounding box, and divides it by the width of the district's
    bounding box.

    This calculator will calculate either the compactness score of a single
    district, or it will average the compactness scores of all districts
    in a plan.
    """
    def compute(self, **kwargs):
        """
        Calculate the Length/Width measure of compactness.

        Keywords:
            district -- A district whose compactness should be computed.
            plan -- A plan whose district compactnesses should be averaged.
        """
        districts = []
        if 'district' in kwargs:
            districts = [kwargs['district']]
            if districts[0].geom.empty:
                return

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=True)

        else:
            return

        num = 0
        compactness = 0
        for district in districts:
            if district.district_id == 0:
                continue

            if district.geom.empty:
                continue

            bbox = district.geom.extent
            compactness += (bbox[3] - bbox[1]) / (bbox[2] - bbox[0])
            num += 1

        self.result = compactness / num

    def html(self):
        """
        Generate an HTML representation of the compactness score. This
        is represented as a percentage or "n/a"

        Returns:
            A number formatted similar to "1.00%", or "n/a"
        """
        return ("%0.2f%%" % (self.result * 100)) if self.result else "n/a"


class SumValues(CalculatorBase):
    """
    Sum up all values.

    For districts, this calculator will sum up a series of arguments.

    For plans, this calculator will sum up a series of arguments across
    all districts. If a literal value is included in a plan calculation,
    that literal value is combined with the subject value for each 
    district.

    For lists of numbers, this calculator will return the sum of the list.

    Each argument should be assigned the argument name "valueN", where N
    is a positive integer. The summation will add all arguments, starting
    at position 1, until an argument is not found.
    """
    def compute(self, **kwargs):
        """
        Calculate the sum of a series of values.
        """
        districts = []

        if 'district' in kwargs:
            districts = [kwargs['district']]
        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=False)
        elif 'list' in kwargs:
            lst = kwargs['list']
            self.result = reduce(lambda x,y: x + y, lst)
            return
        else:
            return
        
        self.result = 0

        for district in districts:
            argnum = 1
            while ('value%d'%argnum) in self.arg_dict:
                number = self.get_value('value%d'%argnum, district)
                if not number is None:
                    self.result += number

                argnum += 1

    def html(self):
        """
        Generate an HTML representation of the equivalence score. This
        is represented as an integer formatted with commas or "n/a".

        Returns:
            The result wrapped in an HTML SPAN element: "<span>1</span>".
        """
        if isinstance(self.result, Decimal):
            result = locale.format("%s", self.result, grouping=True)
            return '<span>%s</span>' % result
        else:
            return '<span>N/A</span>'

class Percent(CalculatorBase):
    """
    Calculate a percentage for two values.

    A percentage calculator requires two arguments: "numerator", and 
    "denominator".

    When passed a district, the percentage calculator simply divides the
    numerator by the denominator.

    When passed a plan, the percentage calculator accumulates all the
    numerator values and denominator values for all districts in the plan.
    After the numerator and denominator values have been accumulated, 
    it computes the percentage of those totals.
    """
    def compute(self, **kwargs):
        """
        Calculate a percentage.
        """
        district = None

        if 'district' in kwargs:
            district = kwargs['district']

            num = self.get_value('numerator',district)
            den = self.get_value('denominator',district)

        elif 'plan' in kwargs:
            plan = kwargs['plan']
            version = kwargs['version'] if 'version' in kwargs else plan.version
            districts = plan.get_districts_at_version(version, include_geom=False)
            num = 0
            den = 0
            for district in districts:
                if district.district_id == 0:
                    continue

                tmpnum = self.get_value('numerator',district)
                tmpden = self.get_value('denominator',district)

                # If either the numerator or denominator don't exist,
                # we have to skip it.
                if tmpnum is None or tmpden is None:
                    continue

                den += tmpden
                num += tmpnum

        else:
            return

        if num is None or den is None or den == 0:
            return
    
        self.result = num / den

    def html(self):
        """
        Generate an HTML representation of the equivalence score. This
        is represented as an integer formatted with commas or "n/a"

        Returns:
            A number formatted similar to "1.00%", or "n/a"
        """
        if (type(self.result) == Decimal):
            return '<span>{0:.2%}</span>'.format(self.result)
        else:
            return '<span>N/A</span>'

class Threshold(CalculatorBase):
    """
    Determine a value, and indicate if it exceeds a threshold.

    This calculator accepts two arguments: "value", and "threshold". The
    result of this calculator is 1 or 0, to facilitate the combination of
    scores. One example may be where the number of districts that exceed a
    threshold are required.
    
{'fraction_non_alphanumeric': 0.07361800922796603, 'fraction_numerical': 0.004937255708701913, 'mean_word_length': 1.9369929224926636, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5893833', 'n_tokens_mistral': 8920, 'n_tokens_neox': 8374, 'n_words': 3472}