{ "info": { "author": "Azavea, Inc.", "author_email": "info@azavea.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 1.11", "Framework :: Django :: 1.8", "Framework :: Django :: 2.0", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Database", "Topic :: Database :: Database Engines/Servers", "Topic :: Database :: Front-Ends", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Internet :: WWW/HTTP :: WSGI :: Server", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Application Frameworks" ], "description": "# Grout \n\n[![Build Status](https://travis-ci.org/azavea/grout.svg?branch=develop)](https://travis-ci.org/azavea/grout)\n\nGrout is a flexible-schema framework for geospatial data, powered by Django and PostgreSQL. Think: NoSQL database server, but with schema validation and PostGIS support.\n\n## Contents\n\n- [**Introduction**](#introduction)\n- [**Getting started**](#getting-started)\n - [Django](#django)\n - [Requirements](#requirements)\n - [Installation](#installation)\n - [Configuration](#configuration)\n - [More examples](#more-examples)\n - [Non-Django applications](#non-django-applications)\n- [**Concepts**](#concepts)\n - [Data model](#data-model)\n - [Versioned schemas](#versioned-schemas)\n- [**API documentation**](#api-documentation)\n - [Request and response formats](#request-and-response-formats)\n - [Pagination](#pagination)\n - [Resources](#resources)\n - [RecordTypes](#recordtypes)\n - [RecordSchemas](#recordschemas)\n - [Records](#records)\n - [Boundaries](#boundaries)\n - [BoundaryPolygons](#boundarypolygons)\n- [**Developing**](#developing)\n - [Requirements](#requirements-1)\n - [Installation](#installation-1)\n - [Running tests](#running-tests)\n - [Cleaning up](#cleaning-up)\n - [Making migrations](#making-migrations)\n- [**Resources**](#resources)\n - [Grout suite](#grout-suite)\n - [Historical documents](#historical-documents)\n - [Roadmap](#roadmap)\n\n## Introduction\n\nGrout combines the flexibility of NoSQL databases with the geospatial muscle of\n[PostGIS](http://postgis.org/), allowing you to make migration-free edits to\nyour database schema while still having access to powerful geospatial queries.\n\nGrout will help you:\n\n- **Define, edit, and validate schemas** for records in your application\n- **Keep track of changes to schemas** using a built-in versioning system\n- Perform **fast filtering of user-defined fields**\n- Run **complex geospatial queries**, even on records stored with unstructured data\n\nGrout is the core library of the _Grout suite_, a toolkit for easily building\nflexible-schema apps on top of Grout. You can use Grout by installing it as an app\nin a Django project, or you can deploy it as a [standalone API\nserver](https://github.com/azavea/grout-server) with an optional [admin\nbackend](https://github.com/azavea/grout-schema-editor).\n\nReady for more? To get started using Grout with Django, see [Getting\nstarted](#getting-started). To get started using Grout with another\nstack, see [Non-Django applications](#non-django-applications). For more\nbackground on how Grout works, see [Concepts](#concepts).\n\n## Getting started\n\n### Django\n\nIf you're developing a Django project, you can install Grout as a Django app and\nuse it in your project.\n\n#### Requirements\n\nGrout supports the following versions of Python and Django:\n\n- **Python**: 2.7, 3.4, 3.5, 3.6, 3.7\n- **Django**: 1.11, 2.0\n\nCertain versions of Django only support certain versions of Python. To ensure\nthat your Python and Django versions work together, see the Django FAQ: [What\nPython version can I use with\nDjango?](https://docs.djangoproject.com/en/2.1/faq/install/#what-python-version-can-i-use-with-django)\n\n#### Installation\n\nInstall the Grout library from PyPi using `pip`.\n\n```bash\n$ pip install grout\n```\n\nTo use the development version of Grout, install it from GitHub.\n\n```bash\n$ git clone git@github.com:azavea/grout.git\n```\n\nMake sure Grout is included in `INSTALLED_APPS` in your project's `settings.py`.\n\n```python\n# settings.py\n\nINSTALLED_APPS = (\n ...\n 'grout',\n)\n```\n\nTo use Grout as an API server, you need to incorporate the API views into your\n`urls.py` file. The following example will include Grout views under the\n`/grout` endpoint. \n\n```python\n# urls.py\n\nurlpatterns = [\n url(r'^grout/', include('grout.urls'))\n]\n```\n\nNote that Grout automatically nests views under the `/api/` endpoint, meaning\nthat the setting above would create URLs like `hostname.com/grout/api/records`.\nIf you'd prefer Grout views to live under a top-level `/api/` endpoint (like\n`hostname.com/api/records`), you can import the Grout `urlpatterns` directly.\n\n```python\n# urls.py\n\nfrom grout import urlpatterns as grout_urlpatterns\n\nurlpatterns = grout_urlpatterns\n```\n\n#### Configuration\n\nGrout requires that the `GROUT` configuration variable be defined in your `settings.py` file\nin order to work properly. The `GROUT` variable is a dictionary of configuration\ndirectives for the app.\n\nCurrently, `'SRID'` is the only required key in the `GROUT` dictionary. `'SRID'` is an integer\ncorresponding to the [spatial reference\nidentifier](https://en.wikipedia.org/wiki/Spatial_reference_system#Identifier)\nthat Grout should use to store geometries. `4326` is the most common SRID, and\nis a good default for projects.\n\nHere's an example configuration for a development project:\n\n```python\n# settings.py\n\n# The projection for geometries stored in Grout.\nGROUT = { 'SRID': 4326 }\n```\n\nNote that Grout uses [Django REST Framework](http://www.django-rest-framework.org/)\nunder the hood to provide API endpoints. To configure DRF-specific settings like\nauthentication, see the [DRF docs](http://www.django-rest-framework.org/).\n\n#### More examples\n\n[Grout Server](https://github.com/azavea/grout-server) is a simple deployment\nof a Grout API server designed to be used as a standalone app. It also serves\nas a good example of how to incorporate Grout into a Django project, and\nincludes a preconfigured authentication module to boot. If you're\nhaving trouble installing or configuring Grout in your project, [Grout\nServer](https://github.com/azavea/grout-server)\nis a good resource for troubleshooting.\n\n### Non-Django applications\n\nIf you're not a Django developer, you can still use Grout as a standalone\nAPI server using the [Grout Server](https://github.com/azavea/grout-server)\nproject. See the [Grout Server docs](https://github.com/azavea/grout-server)\nfor details on how to install a Grout Server instance.\n\n## Concepts\n\n### Data model\n\n![The Grout data model centers around Records, each of which has an associated\nRecordSchema and RecordType.](./docs/images/grout-data-model.png).\n\nGrout is centered around _Records_, which are just **entities in your database**.\nA Record can be any type of thing or event in the world, although Grout is most\nuseful when your Records have some geospatial and temporal component.\n\nEvery Record contains a reference to a _RecordSchema_, which catalogs the\n**versioned schema** of the Record that points to it. This schema is stored as\n[JSONSchema](http://json-schema.org/), a specification for describing data models in JSON.\n\nFinally, each RecordSchema contains a reference to a _RecordType_, which is\na **simple container for organizing Records**. The RecordType exposes a way to\nreliably access a set of Records that represent the same type of thing, even if\nthey have different schemas. As we\u2019ll see shortly, RecordTypes are useful access\npoints to Records because RecordSchemas can change at any moment.\n\n### Versioned schemas\n\nIn Grout, RecordSchemas are append-only, meaning that they cannot be deleted.\nInstead, when you want to change the schema of a Record, you create a new\nRecordSchema and update the `version` attribute.\n\nFor a quick example, say that we have a RecordSchema describing data stored on\na `cat` RecordType. The RecordSchema might look something like this:\n\n```json\n{\n \"version\": 1,\n \"next_version\": null,\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Initial Schema\",\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"properties\": {\n \"catDetails\": {\n \"$ref\": \"#/definitions/driverPosterDetails\"\n },\n \"definitions\": {\n \"catDetails\": {\n \"type\": \"object\",\n \"title\": \"Cat Details\",\n \"properties\": {\n \"Name\": {\n \"type\": \"string\",\n \"fieldType\": \"text\",\n \"isSearchable\": true,\n \"propertyOrder\": 1\n },\n \"Age\": {\n \"type\": \"integer\",\n \"fieldType\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 100,\n \"isSearchable\": true,\n \"propertyOrder\": 2\n },\n \"Color\": {\n \"type\": \"string\",\n \"fieldType\": \"text\",\n \"isSearchable\": true,\n \"propertyOrder\": 3\n },\n \"Breed\": {\n \"type\": \"select\",\n \"fieldType\": \"selectlist\",\n \"enum\": [\n \"Tabby\",\n \"Bobtail\",\n \"Abyssinian\"\n ],\n \"isSearchable\": true,\n \"propertyOrder\": 4\n }\n }\n }\n }\n }\n}\n```\n\nA few things to note about this RecordSchema object:\n\n- This is the first version of the schema (its `version` is `1`)\n- There is no more recent version than this one (its `next_version` is `null`)\n- The schema definition itself is stored as a JSONSchema object on the `schema`\n attribute\n- All of the available fields are namespaced by the `catDetails` attribute, which\n we sometimes refer to as a _form_ or _related content_\n\nNow say we want to change the `Age` field to a `Date of Birth` field. Instead of\nchanging the schema directly, we'll create a new schema. Grout will automatically\nset `version: 2` and `next_version: null` for this updated schema:\n\n```json\n{\n \"version\": 2,\n \"next_version\": null,\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Initial Schema\",\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"properties\": {\n \"catDetails\": {\n \"$ref\": \"#/definitions/driverPosterDetails\"\n },\n \"definitions\": {\n \"catDetails\": {\n \"type\": \"object\",\n \"title\": \"Cat Details\",\n \"properties\": {\n \"Name\": {\n \"type\": \"string\",\n \"fieldType\": \"text\",\n \"isSearchable\": true,\n \"propertyOrder\": 1\n },\n \"Age\": {\n \"type\": \"integer\",\n \"fieldType\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 100,\n \"isSearchable\": true,\n \"propertyOrder\": 2\n },\n \"Date of Birth\": {\n \"type\": \"string\",\n \"format\": \"datetime\",\n \"fieldType\": \"text\",\n \"isSearchable\": true,\n \"propertyOrder\": 3\n },\n \"Color\": {\n \"type\": \"string\",\n \"fieldType\": \"text\",\n \"isSearchable\": true,\n \"propertyOrder\": 4\n },\n \"Breed\": {\n \"type\": \"select\",\n \"fieldType\": \"selectlist\",\n \"enum\": [\n \"Tabby\",\n \"Bobtail\",\n \"Abyssinian\"\n ],\n \"isSearchable\": true,\n \"propertyOrder\": 5\n }\n }\n }\n }\n }\n }\n}\n```\n\nIn addition, Grout will update the initial schema to set `next_version: 2`:\n\n```\n{\n \"version\": 1,\n \"next_version\": 2,\n \"schema\": {\n ...\n }\n}\n```\n\nNow, when a user searches for Records in the `cat` RecordType, Grout can find\nthe most recent schema by looking for the RecordSchema where `next_version: null`.\nThis preserves a full audit trail of the RecordSchema, allowing us to\ninspect how the schema has changed over time.\n\nFor a closer look at the Grout data model, see the [`models.py` file in the Grout\nlibrary](https://github.com/azavea/grout/blob/develop/grout/models.py).\n\n## API documentation\n\n### Request and response formats\n\nCommunication with the API generally follows the principles of RESTful API design.\nAPI paths correspond to resources, `GET` requests are used to retrieve objects, `POST`\nrequests are used to create new objects, and `PATCH` requests are used to update\nexisting objects. This pattern is followed in nearly all cases; any exceptions\nwill be noted in the documentation.\n\nResponses from the API are exclusively JSON.\n\nEndpoint behavior can be configured using query parameters for `GET` requests,\nwhile `POST` requests require a payload in JSON format.\n\n### Pagination\n\nAll API endpoints that return lists of resources are paginated. The pagination takes the following format:\n\n```\n{\n \"count\": 57624,\n \"next\": \"http://localhost:8000/api/records/?offset=20\",\n \"previous\": \"http://localhost:7000/api/records/\",\n \"results\": [\n ...\n ]\n}\n```\n\nIn a real response, the domain and port for the `next` and `previous` fields\nwill be that of the server responding to the request.\n\nThis format applies to the API endpoints below and will not be repeated in the\ndocumentation for each individual endpoint.\n\n### Resources\n\n#### RecordTypes\n\nBecause the RecordSchema for a set of Records can change at any time, the RecordType API\nendpoint provides a consistent access point for retrieving a set of Records.\nUse the RecordType endpoints to discover the most recent RecordSchema for the Records\nyou are interested in before performing further queries.\n\nPaths:\n\n* List: `/api/recordtypes/`\n* Detail: `/api/recordtypes/{uuid}/`\n\nQuery parameters:\n\n* `active`: Boolean\n * Filter for only RecordTypes with an `active` value of True.\n Generally, you will want to limit yourself to active RecordTypes.\n\nResults fields:\n\n| Field name | Type | Description |\n| ---------- | ---- | ----------- |\n| `uuid` | UUID | Unique identifier for this RecordType. |\n| `current_schema` | UUID | The most recent RecordSchema for this RecordType. |\n| `created` | Timestamp | The date and time when this RecordType was created. |\n| `modified` | Timestamp | The date and time when this RecordType was last modified. |\n| `label` | String | The name of this RecordType. |\n| `plural_label` | String | The plural version of the name of this RecordType. |\n| `description` | String | A short description of this RecordType. |\n| `active` | Boolean | Whether or not this RecordType is active. This field allows RecordTypes to be deactivated rather than deleted. |\n| `geometry_type` | String | The geometry type supported for Records of this RecordType. One of `point`, `polygon`, `multipolygon`, `linestring`, or `none`. | \n| `temporal` | Boolean | Whether or not Records of this RecordType should store datetime data in the `occurred_from` and `occurred_to` fields. |\n\n#### RecordSchemas\n\nThe RecordSchema API endpoint can help you discover the fields that\nshould be available on a given Record. This can be useful for automatically generating filters\nbased on a Record's fields, or for running custom validation on a Record's\nschema.\n\nPaths:\n\n* List: `/api/recordschemas/`\n* Detail: `/api/recordschemas/{uuid}/`\n\nResults fields:\n\n| Field name | Type | Description |\n| ---------- | ---- | ----------- |\n| `uuid` | UUID | Unique identifier for this RecordSchema. |\n| `created` | Timestamp | The date and time when this RecordSchema was created. |\n| `modified` | Timestamp | The date and time when this RecordSchema was last modified. |\n| `version` | Integer | A sequential number indicating what version of the RecordType's schema this is. Starts at 1. |\n| `next_version` | UUID | Unique identifier of the RecordSchema with the next-highest version number for this schema's RecordType. If this is the most recent version of the schema, this field will be `null`. |\n| `record_type` | UUID | Unique identifier of the RecordType that this RecordSchema refers to. |\n| `schema` | Object | A [JSONSchema](http://json-schema.org/) object that should validate Records that refer to this RecordSchema. |\n\n#### Records\n\nRecords are the heart of a Grout project: the entities in your database. The\nRecords API endpoint provides a way of retrieving these objects for analysis\nor display to an end user.\n\nPaths:\n\n* List: `/api/records/`\n* Detail: `/api/records/{uuid}/`\n\nQuery Parameters:\n\n* `archived`: Boolean\n * Records can be \"archived\" to denote that they are no longer current, as an\n alternative to deletion. Pass `True` (case-sensitive) to this parameter to return archived\n Records only, and pass `False` (case-sensitive) to return current Records only.\n Omitting this parameter returns both types.\n\n* `details_only`: Boolean\n * In the [Grout Schema Editor](https://github.com/azavea/grout-schema-editor),\n every Record is automatically generated with a `Details`\n form which is intended to contain a basic summary of information about the Record.\n Passing `True` (case-sensitive) to this parameter will omit any other\n forms which may exist on the Record. This is useful for limiting the size\n of the payload returned when only a summary view is needed.\n\n* `record_type`: UUID\n * Limit the response to Records matching the passed RecordType UUID.\n This is optional in theory, but for most applications it is a good idea\n to include this parameter by default. It is considered rare that it will\n be useful to return two different types of Records in a single request.\n It is usually a better idea to make a separate request for each RecordType.\n\n* `jsonb`: Object\n * Query the data fields of the object and filter on the result.\n * Keys in this object mimic the search paths to filter on a particular object\n field. However, in place of values, a filter rule definition is used. Example:\n`{ \"accidentDetails\": {\n \"Main+cause\": {\n \"_rule_type\": \"containment\",\n \"contains\": [\n \"Vehicle+defect\",\n \"Road+defect\",\n [\"Vehicle+defect\"],\n [\"Road+defect\"]\n ]\n },\n \"Num+driver+casualties\": {\n \"_rule_type\": \"intrange\",\n \"min\": 1,\n \"max\": 3\n }\n}}`. This query defines the following two filters:\n * `accidentDetails -> \"Main cause\" == \"Vehicle defect\" OR accidentDetails -> \"Main cause\" == \"Road defect\"`\n * `accidentDetails -> \"Num driver casualties\" >= 1 AND accidentDetails -> \"Num driver casualties\" <= 3`\n * There is a third filter rule type available: `containment_multiple`.\n This is used when searching a form of which there can be several on a single Record.\n Here's an example:\n`{\"person\":{\"Injury\":{\"_rule_type\":\"containment_multiple\",\"contains\":[\"Fatal\"]}}}`\n\n* `occurred_min`: Timestamp\n * Filter to Records occurring after this date.\n\n* `occurred_max`: Timestamp\n * Filter to Records occurring before this date.\n\n* `polygon_id`: UUID\n * Filter to Records which occurred within the Polygon identified by the\n UUID. The value must refer to a [Boundary](#boundaries) in the database.\n\n* `polygon`: GeoJSON\n * Filter to Records which occurred within the bounds of a valid GeoJSON\n object.\n\nResults fields:\n\n| Field name | Type | Description |\n| ---------- | ---- | ----------- |\n| `uuid` | UUID | Unique identifier for this Record. |\n| `created` | Timestamp | The date and time when this Record was created. |\n| `modified` | Timestamp | The date and time when this Record was last modified. |\n| `occurred_from` | Timestamp | The earliest time at which this Record might have occurred. |\n| `occurred_to` | Timestamp | The latest time at which this Record might have occurred. Note that this field is mandatory for temporal Records: if a Record only occurred at one moment in time, the `occurred_from` field and the `occurred_to` field will have the same value. |\n| `geom` | GeoJSON | Geometry representing the location associated with this Record. |\n| `location_text` | String | A description of the location where this Record occurred, typically an address. |\n| `archived` | Boolean | A way of hiding records without deleting them completely. `True` indicates the Record is archived. |\n| `schema` | UUID | References the RecordSchema which was used to create this Record. |\n| `data` | Object | A JSON object representing the flexible data fields associated with this Record. It is always true that the object stored in `data` conforms to the RecordSchema referenced by the `schema` UUID. |\n\n#### Boundaries\n\nBoundaries provide a quick way of storing Shapefile data in Grout without\nhaving to create separate RecordTypes. Using a Boundary, you can upload\nand retrieve Shapefile data for things like administrative borders and focus\nareas in your application.\n\nPaths:\n\n* List: `/api/boundaries/`\n* Detail: `/api/boundaries/{uuid}/`\n\nResults fields:\n\n| Field name | Type | Description |\n| ---------- | ---- | ----------- |\n| `uuid` | UUID | Unique identifier for this Boundary. |\n| `created` | Timestamp | The date and time when this Boundary was created. |\n| `modified` | Timestamp | The date and time when this Boundary was last modified. |\n| `label` | String | Label of this Boundary, for display. |\n| `color` | String | Color preference to use for rendering this Boundary. |\n| `display_field` | String | Which field of the imported Shapefile to use for display. |\n| `data_fields` | Array | List of the names of the fields contained in the imported Shapefile. |\n| `errors` | Array | A possible list of errors raised when importing the Shapefile. |\n| `status` | String | Import status of the Shapefile. |\n| `source_file` | String | URI of the Shapefile that was originally used to generate this Boundary. |\n\nNotes:\n\nCreating a new Boundary and its [BoundaryPolygon](#boundarypolygon) correctly is a two-step process.\n\n1. `POST` to `/api/boundaries/` with a zipped Shapefile attached; you will need\n to include the label as form data. You should receive a 201 response which\n contains a fully-fledged Boundary object, including a list of available\n data fields in `data_fields`.\n\n2. The response from the previous request will have a blank `display_field`.\n Select one of the fields in `data_fields` and make a `PATCH` request to\n `/api/boundaries/{uuid}/` with that value in `display_field`.\n You are now ready to use this Boundary and its associated BoundaryPolygon.\n\n#### BoundaryPolygons\n\nBoundaryPolygons store the Shapefile data associated with a [Boundary](#boundaries),\nincluding geometry and metadata.\n\nPaths:\n\n* List: `/api/boundarypolygons/`\n* Detail: `/api/boundarypolygons/{uuid}/`\n\nQuery Parameters:\n\n* `boundary`: UUID\n * Filter to Polygons associated with this parent Boundary.\n\n* `nogeom`: Boolean\n * When passed with any value, causes the geometry field to be replaced with\n a bbox field. This reduces the response size and is sufficient for many purposes.\n\nResults fields:\n\n| Field name | Type | Description |\n| ---------- | ---- | ----------- |\n| `uuid` | UUID | Unique identifier for this BoundaryPolygon. |\n| `created` | Timestamp | The date and time when this BoundaryPolygon was created. |\n| `modified` | Timestamp | The date and time when this BoundaryPolygon was last modified. |\n| `data` | Object | Each key in this Object will correspond to one of the `data_fields` in the parent Boundary, and will store the value for that field for this Polygon. |\n| `boundary` | UUID | Unique identifier of the parent Boundary for this BoundaryPolygon. |\n| `bbox` | Array | Minimum bounding box containing this Polygon's geometry, as an Array of lat/lon points. This field is optional -- see the `nogeom` parameter above for more details. |\n| `geometry` | GeoJSON | GeoJSON representation of this Polygon. This field is optional -- see the `nogeom` parameter above for more details. |\n\n## Developing\n\nThese instructions will help you set up a development version of Grout and\ncontribute changes back upstream.\n\n### Requirements\n\nThe Grout development environment is containerized with Docker to ensure similar\nenvironments across platforms. In order to develop with Docker, you need the\nfollowing dependencies:\n\n- [Docker CE Engine](https://docs.docker.com/install/) >= 1.13.0 (must be\n compatible with [Docker Compose file v3\n syntax](https://docs.docker.com/compose/compose-file/#compose-and-docker-compatibility-matrix))\n- [Docker Compose](https://docs.docker.com/compose/install/)\n\n### Installation\n\nClone the repo with git.\n\n```bash\n$ git clone git@github.com:azavea/grout.git\n$ cd grout\n```\n\nRun the `update` script to set up your development environment.\n\n```bash\n$ ./scripts/update\n```\n\n### Running tests\n\nOnce your environment is up to date, you can use the `scripts/test` script to\nrun the Grout unit test suite.\n\n```bash\n$ ./scripts/test\n```\n\nThis command will run a matrix of tests for **every supported version of Python and\nDjango** in the project. If you're developing locally and you just want to run\na subset of the tests, you can specify the version of Python\nthat you want to use to run tests: \n\n```bash\n# Only run tests for Python 2.7 (this will test Django 1.8).\n$ ./scripts/test app py27\n\n# Only run tests for Python 3.7 (this will test Django 2.0).\n$ ./scripts/test app py37\n```\n\nFor a list of available Python versions, see the `envlist` directive in the [`tox.ini`\nfile](./tox.ini). \n\n#### Cleaning up\n\nTox creates a new virtualenv for every combination of Python and Django versions\nused by the test suite. In order to clean up stopped containers and\nremove these virtualenvs, use the `clean` script:\n\n```bash\n$ ./scripts/clean\n```\n\nNote that `clean` will remove **all dangling images, stopped containers, and \nunused volumes** on your machine. If you don't want to remove these artifacts,\n[view the `clean` script](./scripts/clean) and run only the command that\ninterests you.\n\n### Making migrations\n\nIf you edit the data model in `grout/models.py`, you'll need to create a new\nmigration for the app. You can use the `django-admin` script in the `scripts`\ndirectory to automatically generate the migration:\n\n```bash\n$ ./scripts/django-admin makemigrations\n```\n\nMake sure to register the new migrations file with Git:\n\n```bash\n$ git add grout/migrations\n```\n\n## Resources \n\nThe following resources provide helpful tips for deploying and using Grout.\n\n### Grout suite\n\n- [Grout Server](https://github.com/azavea/grout-server): An easily-deployable\n standalone instance of a Grout API server.\n- [Grout Schema Editor](https://github.com/azavea/grout-schema-editor): A\n purely static app that can read and write flexible schemas from a Grout API.\n- [Demo app](https://github.com/jeancochrane/philly-fliers/): A demo project\n providing an example of incorporating the Grout suite into a Vue.js app.\n\n### Historical documents\n\n- [Concept map](./docs/concept-map.md): An early description of the Grout suite\n (formerly known as Ashlar) from an Open Source Fellow working on it during the summer\n of 2018. Describes the conceptual architecture of the suite, and summarizes\n ideas for future directions.\n\n- [Renaming the package to Grout](./docs/rename-package.md): An ADR documenting\n the decision to rename the package from \"Ashlar\" to \"Grout\".\n\n- [Evaluating Record-to-Record references](./docs/foreign-keys.md): An ADR\n documenting the reasons and requirements for implementing a Record-to-Record\n foreign key field. See also [the pull request\n thread](https://github.com/azavea/grout-2018-fellowship/pull/34) for further\n discussion.\n\n- [Evaluating alternate backends](./docs/nosql-backends.md): An ADR presenting\n research into possible NoSQL backends and service providers for Grout.\n\n- [Grout 2018 Fellowship](https://github.com/azavea/grout-2018-fellowship): A\n project management repo for working on Grout during Azavea's Summer 2018\n [Open Source Fellowship](https://fellowship.azavea.com). Useful for\n documentation around the motivation and trajectory of the project.\n\n### Roadmap\n\nWant to know where Grout is headed? See the [Roadmap](./docs/roadmap.md) to\nget a picture of future development.", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/azavea/grout", "keywords": "gis jsonschema", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "grout", "package_url": "https://pypi.org/project/grout/", "platform": "", "project_url": "https://pypi.org/project/grout/", "project_urls": { "Homepage": "https://github.com/azavea/grout" }, "release_url": "https://pypi.org/project/grout/2.0.1/", "requires_dist": null, "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "summary": "A flexible schema framework for geospatial data.", "version": "2.0.1" }, "last_serial": 4737167, "releases": { "0.0.0": [ { "comment_text": "", "digests": { "md5": "c1a787a11668900328234195f9790401", "sha256": "0eb5d5a973a0fa7753240f8aaffd5a36d6556be8ee63623af059583ca3c8ccd9" }, "downloads": -1, "filename": "grout-0.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "c1a787a11668900328234195f9790401", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 1996, "upload_time": "2018-07-10T18:04:11", "url": "https://files.pythonhosted.org/packages/0f/b2/865981c241567d4a78a3ad5d35f49ab9881d5d2b30eb559fb1e244d7af46/grout-0.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1bc11d169024fcce9ba91d41d0bdcd56", "sha256": "0ec05bc9a371919b69a293ab19bf994e5680d7d1cfb20a282d2db8889f2c1e9a" }, "downloads": -1, "filename": "grout-0.0.0.tar.gz", "has_sig": false, "md5_digest": "1bc11d169024fcce9ba91d41d0bdcd56", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 932, "upload_time": "2018-07-10T18:04:12", "url": "https://files.pythonhosted.org/packages/8e/e7/e49281e9f1450078cbef6bdaeee307ac425e6f5a98dfb78eb62e97dd5d71/grout-0.0.0.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "e590816f4eaac4517291f34ac7cf05f8", "sha256": "87a60aad55f6668034f84931eb4576397fcbb9e44e48ec0132ea5c6e2eb261dc" }, "downloads": -1, "filename": "grout-1.0.0.tar.gz", "has_sig": false, "md5_digest": "e590816f4eaac4517291f34ac7cf05f8", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 445048, "upload_time": "2018-07-13T15:24:47", "url": "https://files.pythonhosted.org/packages/45/f6/359746da209d2f9e318a8aa327fabcbdebec9f8ca6a4ddb258ac4b6bc827/grout-1.0.0.tar.gz" } ], "2.0.0": [ { "comment_text": "", "digests": { "md5": "c351ae1dbc8ac3dc9019809744d0c633", "sha256": "145186b1d76b47f316e8820ae82248d2974718404e041d06db9e3a9bd6b89dcf" }, "downloads": -1, "filename": "grout-2.0.0.tar.gz", "has_sig": false, "md5_digest": "c351ae1dbc8ac3dc9019809744d0c633", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 485615, "upload_time": "2018-08-28T18:55:25", "url": "https://files.pythonhosted.org/packages/3d/53/0f24578272c607c61734717470518564005c485f66dc19ab7c41410e3b55/grout-2.0.0.tar.gz" } ], "2.0.1": [ { "comment_text": "", "digests": { "md5": "c10cb4318684175b1ff8e01856c4846b", "sha256": "c27273ce305ff5df0782495218555965f059e337c360765ab0f17c8cade4eab3" }, "downloads": -1, "filename": "grout-2.0.1.tar.gz", "has_sig": false, "md5_digest": "c10cb4318684175b1ff8e01856c4846b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 486423, "upload_time": "2019-01-24T20:00:07", "url": "https://files.pythonhosted.org/packages/64/19/4248c2376d8800023a6b364b29e30ef0324e7115f445e4b4ad6a2b311750/grout-2.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c10cb4318684175b1ff8e01856c4846b", "sha256": "c27273ce305ff5df0782495218555965f059e337c360765ab0f17c8cade4eab3" }, "downloads": -1, "filename": "grout-2.0.1.tar.gz", "has_sig": false, "md5_digest": "c10cb4318684175b1ff8e01856c4846b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 486423, "upload_time": "2019-01-24T20:00:07", "url": "https://files.pythonhosted.org/packages/64/19/4248c2376d8800023a6b364b29e30ef0324e7115f445e4b4ad6a2b311750/grout-2.0.1.tar.gz" } ] }