# Path: ncdjango/fields.py
# class BoundingBoxField(models.TextField):
#     description = _('Bounding box with associated projection information')
# 
#     def from_db_value(self, value, expression, connection, context):
#         return self.to_python(value)
# 
#     def to_python(self, value):
#         if not value or isinstance(value, BBox):
#             return value
# 
#         try:
#             data = json.loads(value)
#             projection = pyproj.Proj(str(data.get('proj4'))) if data.get('proj4') else None
# 
#             return BBox(
#                 (data['xmin'], data['ymin'], data['xmax'], data['ymax']), projection=projection
#             )
#         except (ValueError, KeyError):
#             raise ValidationError("")
# 
#     def get_prep_value(self, value):
#         if not value:
#             return value
# 
#         return json.dumps({
#             'xmin': value.xmin,
#             'ymin': value.ymin,
#             'xmax': value.xmax,
#             'ymax': value.ymax,
#             'proj4': value.projection.srs if value.projection else None
#         })
# 
# class RasterRendererField(models.TextField):
#     description = _('A class to generate images from raster data')
# 
#     def from_db_value(self, value, expression, connection, context):
#         return self.to_python(value)
# 
#     def to_python(self, value):
#         if not value or isinstance(value, RasterRenderer):
#             return value
# 
#         try:
#             data = json.loads(value)
#             name = data['name']
#             params = data.get('params', {})
#             kwargs = {
#                 'colormap': [(c[0], Color(*c[1])) for c in data['colormap']],
#                 'fill_value': params.get('fill_value'),
#                 'background_color': Color(*params.get('background_color')) if params.get('background_color') else None
#             }
# 
#             if name == "stretched":
#                 cls = StretchedRenderer
#                 kwargs.update({
#                     'method': params.get('method', 'linear'),
#                     'colorspace': params.get('colorspace', 'hsv')
#                 })
#             elif name == "classified":
#                 cls = ClassifiedRenderer
#             elif name == "unique":
#                 cls = UniqueValuesRenderer
#                 kwargs.update({
#                     'labels': params.get('labels')
#                 })
#         except (ValueError, KeyError):
#             raise ValidationError("")
# 
#         return cls(**kwargs)
# 
#     def get_prep_value(self, value):
#         if not value:
#             return value
# 
#         params = {}
# 
#         if isinstance(value, StretchedRenderer):
#             name = "stretched"
#             params = {
#                 'method': value.method,
#                 'colorspace': value.colorspace
#             }
#         elif isinstance(value, ClassifiedRenderer):
#             name = "classified"
#         elif isinstance(value, UniqueValuesRenderer):
#             name = "unique"
#             params = {
#                 'labels': value.labels
#             }
#         else:
#             raise ValidationError("")
# 
#         if value.fill_value is not None:
#             params['fill_value'] = value.fill_value
#         if value.background_color:
#             params['background_color'] = value.background_color.to_tuple()
# 
#         return json.dumps({
#             'name': name,
#             'colormap': [(c[0], c[1].to_tuple()) for c in value.colormap],
#             'params': params
#         })
# 
# Path: ncdjango/utils.py
# def auto_memoize(func):
#     """
#     Based on django.util.functional.memoize. Automatically memoizes instance methods for the lifespan of an object.
#     Only works with methods taking non-keword arguments. Note that the args to the function must be usable as
#     dictionary keys. Also, the first argument MUST be self. This decorator will not work for functions or class
#     methods, only object methods.
#     """
# 
#     @wraps(func)
#     def wrapper(*args):
#         inst = args[0]
#         inst._memoized_values = getattr(inst, '_memoized_values', {})
#         key = (func, args[1:])
#         if key not in inst._memoized_values:
#             inst._memoized_values[key] = func(*args)
#         return inst._memoized_values[key]
#     return wrapper


import calendar
import datetime
import logging
import uuid
from celery.result import AsyncResult
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from .fields import BoundingBoxField, RasterRendererField
from .utils import auto_memoize
class Service(models.Model):
    """
    A service maps to a single NetCDF dataset. Services contain general metadata (name, description), and information
    about the data extend, projection, and support for time.
    """

    CALENDAR_CHOICES = (
        ('standard', 'Standard Gregorian'),
        ('noleap', 'Standard, no leap years'),
        ('360', '360-day years')
    )

    TIME_UNITS_CHOICES = (
        ('milliseconds', 'Milliseconds'),
        ('seconds', 'Seconds'),
        ('minutes', 'Minutes'),
        ('hours', 'Hours'),
        ('days', 'Days'),
        ('weeks', 'Weeks'),
        ('months', 'Months'),
        ('years', 'Years'),
        ('decades', 'Decades'),
        ('centuries', 'Centuries')
    )

    name = models.CharField(max_length=256, db_index=True, unique=True)
    description = models.TextField(null=True)
    data_path = models.FilePathField(path=SERVICE_DATA_ROOT, recursive=True, max_length=1024)
    projection = models.TextField()  # PROJ4 definition
    full_extent = BoundingBoxField()

# Path: ncdjango/fields.py
# class BoundingBoxField(models.TextField):
#     description = _('Bounding box with associated projection information')
# 
#     def from_db_value(self, value, expression, connection, context):
#         return self.to_python(value)
# 
#     def to_python(self, value):
#         if not value or isinstance(value, BBox):
#             return value
# 
#         try:
#             data = json.loads(value)
#             projection = pyproj.Proj(str(data.get('proj4'))) if data.get('proj4') else None
# 
#             return BBox(
#                 (data['xmin'], data['ymin'], data['xmax'], data['ymax']), projection=projection
#             )
#         except (ValueError, KeyError):
#             raise ValidationError("")
# 
#     def get_prep_value(self, value):
#         if not value:
#             return value
# 
#         return json.dumps({
#             'xmin': value.xmin,
#             'ymin': value.ymin,
#             'xmax': value.xmax,
#             'ymax': value.ymax,
#             'proj4': value.projection.srs if value.projection else None
#         })
# 
# class RasterRendererField(models.TextField):
#     description = _('A class to generate images from raster data')
# 
#     def from_db_value(self, value, expression, connection, context):
#         return self.to_python(value)
# 
#     def to_python(self, value):
#         if not value or isinstance(value, RasterRenderer):
#             return value
# 
#         try:
#             data = json.loads(value)
#             name = data['name']
#             params = data.get('params', {})
#             kwargs = {
#                 'colormap': [(c[0], Color(*c[1])) for c in data['colormap']],
#                 'fill_value': params.get('fill_value'),
#                 'background_color': Color(*params.get('background_color')) if params.get('background_color') else None
#             }
# 
#             if name == "stretched":
#                 cls = StretchedRenderer
#                 kwargs.update({
#                     'method': params.get('method', 'linear'),
#                     'colorspace': params.get('colorspace', 'hsv')
#                 })
#             elif name == "classified":
#                 cls = ClassifiedRenderer
#             elif name == "unique":
#                 cls = UniqueValuesRenderer
#                 kwargs.update({
#                     'labels': params.get('labels')
#                 })
#         except (ValueError, KeyError):
#             raise ValidationError("")
# 
#         return cls(**kwargs)
# 
#     def get_prep_value(self, value):
#         if not value:
#             return value
# 
#         params = {}
# 
#         if isinstance(value, StretchedRenderer):
#             name = "stretched"
#             params = {
#                 'method': value.method,
#                 'colorspace': value.colorspace
#             }
#         elif isinstance(value, ClassifiedRenderer):
#             name = "classified"
#         elif isinstance(value, UniqueValuesRenderer):
#             name = "unique"
#             params = {
#                 'labels': value.labels
#             }
#         else:
#             raise ValidationError("")
# 
#         if value.fill_value is not None:
#             params['fill_value'] = value.fill_value
#         if value.background_color:
#             params['background_color'] = value.background_color.to_tuple()
# 
#         return json.dumps({
#             'name': name,
#             'colormap': [(c[0], c[1].to_tuple()) for c in value.colormap],
#             'params': params
#         })
# 
# Path: ncdjango/utils.py
# def auto_memoize(func):
#     """
#     Based on django.util.functional.memoize. Automatically memoizes instance methods for the lifespan of an object.
#     Only works with methods taking non-keword arguments. Note that the args to the function must be usable as
#     dictionary keys. Also, the first argument MUST be self. This decorator will not work for functions or class
#     methods, only object methods.
#     """
# 
#     @wraps(func)
#     def wrapper(*args):
#         inst = args[0]
#         inst._memoized_values = getattr(inst, '_memoized_values', {})
#         key = (func, args[1:])
#         if key not in inst._memoized_values:
#             inst._memoized_values[key] = func(*args)
#         return inst._memoized_values[key]
#     return wrapper


# Path: ncdjango/models.py
import calendar
import datetime
import logging
import uuid
from celery.result import AsyncResult
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from .fields import BoundingBoxField, RasterRendererField
from .utils import auto_memoize

class Service(models.Model):
    """
    A service maps to a single NetCDF dataset. Services contain general metadata (name, description), and information
    about the data extend, projection, and support for time.
    """

    CALENDAR_CHOICES = (
        ('standard', 'Standard Gregorian'),
        ('noleap', 'Standard, no leap years'),
        ('360', '360-day years')
    )

    TIME_UNITS_CHOICES = (
        ('milliseconds', 'Milliseconds'),
        ('seconds', 'Seconds'),
        ('minutes', 'Minutes'),
        ('hours', 'Hours'),
        ('days', 'Days'),
        ('weeks', 'Weeks'),
        ('months', 'Months'),
        ('years', 'Years'),
        ('decades', 'Decades'),
        ('centuries', 'Centuries')
    )

    name = models.CharField(max_length=256, db_index=True, unique=True)
    description = models.TextField(null=True)
    data_path = models.FilePathField(path=SERVICE_DATA_ROOT, recursive=True, max_length=1024)
    projection = models.TextField()  # PROJ4 definition
    full_extent = BoundingBoxField()

    initial_extent = BoundingBoxField()