# Path: plenario/database.py
# def create_database(bind: Engine, database: str) -> None:
# def drop_database(bind: Engine, database: str) -> None:
# def create_extension(bind: Engine, extension: str) -> None:
# def psql(path: str) -> None:
# def postgres_session_context():
# def redshift_session_context():
# 
# Path: plenario/utils/helpers.py
# def get_size_in_degrees(meters, latitude):
#     earth_circumference = 40041000.0  # meters, average circumference
#     degrees_per_meter = 360.0 / earth_circumference
# 
#     degrees_at_equator = meters * degrees_per_meter
# 
#     latitude_correction = 1.0 / math.cos(latitude * (math.pi / 180.0))
# 
#     degrees_x = degrees_at_equator * latitude_correction
#     degrees_y = degrees_at_equator
# 
#     return degrees_x, degrees_y
# 
# def slugify(text: str, delimiter: str = '_') -> str:
#     return _slugify(text, separator=delimiter)


import json
import sqlalchemy as sa
from collections import namedtuple
from datetime import datetime
from hashlib import md5
from itertools import groupby
from operator import itemgetter
from flask_bcrypt import Bcrypt
from geoalchemy2 import Geometry
from shapely.geometry import shape
from sqlalchemy import Boolean, Column, Date, DateTime, String, Table, Text, func, select
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
from sqlalchemy.exc import ProgrammingError
from plenario.database import postgres_base, postgres_session
from plenario.utils.helpers import get_size_in_degrees, slugify
            return self._point_table
        except AttributeError:
            self._point_table = Table(self.dataset_name, postgres_base.metadata, autoload=True, extend_existing=True)
            return self._point_table

    @classmethod
    def attach_metadata(cls, rows):
        """Given a list of dicts that include a dataset_name, add metadata about the datasets to each dict.

        :param rows: List of dict-likes with a dataset_name attribute
        """
        dataset_names = [row['dataset_name'] for row in rows]

        # All the metadata attributes that we can pull in unaltered
        as_is_attr_names = ['dataset_name', 'human_name', 'date_added',
                            'obs_from', 'obs_to', 'last_update',
                            'attribution', 'description', 'update_freq',
                            'view_url', 'source_url',
                            'contributor_name', 'contributor_email',
                            'contributor_organization']
        as_is_attrs = [getattr(cls, name) for name in as_is_attr_names]

        # Bounding box is the exception. We need to process it a bit.
        bbox = func.ST_AsGeoJSON(cls.bbox)

        # Put our as-is and processed attributes together
        attr_names = as_is_attr_names + ['bbox']
        attrs = as_is_attrs + [bbox]

        # Make the DB call

# Path: plenario/database.py
# def create_database(bind: Engine, database: str) -> None:
# def drop_database(bind: Engine, database: str) -> None:
# def create_extension(bind: Engine, extension: str) -> None:
# def psql(path: str) -> None:
# def postgres_session_context():
# def redshift_session_context():
# 
# Path: plenario/utils/helpers.py
# def get_size_in_degrees(meters, latitude):
#     earth_circumference = 40041000.0  # meters, average circumference
#     degrees_per_meter = 360.0 / earth_circumference
# 
#     degrees_at_equator = meters * degrees_per_meter
# 
#     latitude_correction = 1.0 / math.cos(latitude * (math.pi / 180.0))
# 
#     degrees_x = degrees_at_equator * latitude_correction
#     degrees_y = degrees_at_equator
# 
#     return degrees_x, degrees_y
# 
# def slugify(text: str, delimiter: str = '_') -> str:
#     return _slugify(text, separator=delimiter)


# Path: plenario/models/MetaTable.py
import json
import sqlalchemy as sa
from collections import namedtuple
from datetime import datetime
from hashlib import md5
from itertools import groupby
from operator import itemgetter
from flask_bcrypt import Bcrypt
from geoalchemy2 import Geometry
from shapely.geometry import shape
from sqlalchemy import Boolean, Column, Date, DateTime, String, Table, Text, func, select
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
from sqlalchemy.exc import ProgrammingError
from plenario.database import postgres_base, postgres_session
from plenario.utils.helpers import get_size_in_degrees, slugify

            return self._point_table
        except AttributeError:
            self._point_table = Table(self.dataset_name, postgres_base.metadata, autoload=True, extend_existing=True)
            return self._point_table

    @classmethod
    def attach_metadata(cls, rows):
        """Given a list of dicts that include a dataset_name, add metadata about the datasets to each dict.

        :param rows: List of dict-likes with a dataset_name attribute
        """
        dataset_names = [row['dataset_name'] for row in rows]

        # All the metadata attributes that we can pull in unaltered
        as_is_attr_names = ['dataset_name', 'human_name', 'date_added',
                            'obs_from', 'obs_to', 'last_update',
                            'attribution', 'description', 'update_freq',
                            'view_url', 'source_url',
                            'contributor_name', 'contributor_email',
                            'contributor_organization']
        as_is_attrs = [getattr(cls, name) for name in as_is_attr_names]

        # Bounding box is the exception. We need to process it a bit.
        bbox = func.ST_AsGeoJSON(cls.bbox)

        # Put our as-is and processed attributes together
        attr_names = as_is_attr_names + ['bbox']
        attrs = as_is_attrs + [bbox]

        # Make the DB call

        result = postgres_session.query(*attrs). \