Skip to content

Commit

Permalink
Merge branch 'master' into develop-merge
Browse files Browse the repository at this point in the history
  • Loading branch information
oleurud committed May 20, 2019
2 parents 448bdf5 + 5bd38b5 commit d676e80
Show file tree
Hide file tree
Showing 11 changed files with 406 additions and 410 deletions.
29 changes: 29 additions & 0 deletions CONTRIBUTING.md
Expand Up @@ -40,6 +40,35 @@ CARTOframes documentation is located inline in the functions, classes, and metho

**Tip:** A convenient, easy way of proposing changes in documentation is by using the GitHub editor directly on the web. You can easily create a branch with your changes and make a PR from there.

## Releases

To release a new version of cartoframes, create a new branch off of `master` called `vX.Y.Z_release`, where `X.Y.Z` should be replaced with the specific version to be released (e.g., 0.10.1). After this branch is created, update the following files:

1. ``cartoframes/__version__.py`` should have the newest version number in the ``__version__`` variable
2. NEWS.rst should be updated with all of the changes happening in this version. It should include the release number and the date of the release. Looking at merged pull requests sorted by last updated is a good way to ensure features are not missed.
3. The README.rst should be updated so that the mybinder tag at the top of the file is the release number/tag

Ensure that documentation is building correctly by building this branch in readthedocs. If not, this is a good time to fix documentation before publishing. You needed to be added as a contributor on readthedocs to be able to configure builds.

After the tests pass, merge into master. Next, we publish a release to [PyPi](https://pypi.org/project/cartoframes/) and [GitHub](https://github.com/CartoDB/cartoframes/releases).

### Documentation (readthedocs)

This step needs to be completed before any releases, but is here as a reminder that documentation should not be ignored. Docs are built with [ReadTheDocs](https://cartoframes.readthedocs.io/en/stable/) automatically from any tagged release and a few select branches. ``master`` is the docs build for ``latest``. Once docs are working from master from the previous step, ensure that the version shows up in the default docs page: https://cartoframes.readthedocs.io/en/stable/

### PyPi release

Run `make publish` in the base cartoframes directory. For a new release to be published on PyPi you need to be added as an author on the [PyPi's cartoframes project](https://pypi.org/project/cartoframes/). Also make sure that [`twine`](https://pypi.org/project/twine/) is installed.


### GitHub release

1. Make sure `master` is fresh from the `vX.Y.Z_release` merge
2. Title release `vX.Y.Z Release`
3. Add latest entry from NEWS.rst
4. Add the dist files from `make dist` (``cartoframes-X.Y.Z-py2-py3-none-any.whl`` and ``cartoframes-X.Y.Z.tar.gz``)
5. Select pre-release (for now)

## Submitting contributions

You will need to sign a Contributor License Agreement (CLA) before making a submission. [Learn more here](https://carto.com/contributions).
4 changes: 2 additions & 2 deletions README.rst
Expand Up @@ -6,8 +6,8 @@ CARTOframes
:target: https://travis-ci.org/CartoDB/cartoframes
.. image:: https://coveralls.io/repos/github/CartoDB/cartoframes/badge.svg?branch=master
:target: https://coveralls.io/github/CartoDB/cartoframes?branch=master
.. image:: https://mybinder.org/badge.svg
:target: https://mybinder.org/v2/gh/CartoDB/cartoframes/master?filepath=examples
.. image:: https://mybinder.org/badge_logo.svg
:target: https://mybinder.org/v2/gh/cartodb/cartoframes/v0.9.2?filepath=examples

A Python package for integrating `CARTO <https://carto.com/>`__ maps, analysis, and data services into data science workflows.

Expand Down
62 changes: 53 additions & 9 deletions cartoframes/columns.py
Expand Up @@ -7,6 +7,9 @@


class Column(object):
DATETIME_DTYPES = ['datetime64[D]', 'datetime64[ns]', 'datetime64[ns, UTC]']
SUPPORTED_GEOM_COL_NAMES = ['geom', 'the_geom', 'geometry']
RESERVED_COLUMN_NAMES = SUPPORTED_GEOM_COL_NAMES + ['the_geom_webmercator', 'cartodb_id']
MAX_LENGTH = 63
MAX_COLLISION_LENGTH = MAX_LENGTH - 4
RESERVED_WORDS = ('ALL', 'ANALYSE', 'ANALYZE', 'AND', 'ANY', 'ARRAY', 'AS', 'ASC', 'ASYMMETRIC', 'AUTHORIZATION',
Expand All @@ -21,20 +24,27 @@ class Column(object):
'TO', 'TRAILING', 'TRUE', 'UNION', 'UNIQUE', 'USER', 'USING', 'VERBOSE', 'WHEN', 'WHERE',
'XMIN', 'XMAX', 'FORMAT', 'CONTROLLER', 'ACTION', )

def __init__(self, name):
@staticmethod
def from_sql_api_fields(sql_api_fields):
return [Column(column, normalize=False, pgtype=sql_api_fields[column]['type']) for column in sql_api_fields]

def __init__(self, name, normalize=True, pgtype=None):
if not name:
raise ValueError('Column name cannot be null or empty')

self.name = str(name)
self.normalize()
self.pgtype = pgtype
self.dtype = pg2dtypes(pgtype)
if normalize:
self.normalize()

def normalize(self, forbidden_columns=None):
def normalize(self, forbidden_column_names=None):
self._sanitize()
self.name = self._truncate()

if forbidden_columns:
if forbidden_column_names:
i = 1
while self.name in forbidden_columns:
while self.name in forbidden_column_names:
self.name = '{}_{}'.format(self._truncate(length=Column.MAX_COLLISION_LENGTH), str(i))
i += 1

Expand Down Expand Up @@ -105,14 +115,48 @@ def normalize_names(column_names):
"""
result = []
for column_name in column_names:
column = Column(column_name).normalize(forbidden_columns=result)
column = Column(column_name).normalize(forbidden_column_names=result)
result.append(column.name)

return result


def normalize_name(name):
if name is None:
def normalize_name(column_name):
if column_name is None:
return None

return normalize_names([name])[0]
return normalize_names([column_name])[0]


def dtypes(columns, exclude_dates=False, exclude_the_geom=False):
return {x.name: x.dtype if not x.name == 'cartodb_id' else 'int64'
for x in columns if not (exclude_dates is True and x.dtype in Column.DATETIME_DTYPES)
and not(exclude_the_geom is True and x.name in Column.SUPPORTED_GEOM_COL_NAMES)}


def date_columns_names(columns):
return [x.name for x in columns if x.dtype in Column.DATETIME_DTYPES]


def pg2dtypes(pgtype):
"""Returns equivalent dtype for input `pgtype`."""
mapping = {
'bigint': 'float64',
'boolean': 'bool',
'date': 'datetime64[D]',
'double precision': 'float64',
'geometry': 'object',
'int': 'int64',
'integer': 'float64',
'number': 'float64',
'numeric': 'float64',
'real': 'float64',
'smallint': 'float64',
'string': 'object',
'timestamp': 'datetime64[ns]',
'timestampz': 'datetime64[ns]',
'timestamp with time zone': 'datetime64[ns]',
'timestamp without time zone': 'datetime64[ns]',
'USER-DEFINED': 'object',
}
return mapping.get(str(pgtype), 'object')

0 comments on commit d676e80

Please sign in to comment.