Skip to content

Commit

Permalink
Completed tests & fixes for Schema
Browse files Browse the repository at this point in the history
  • Loading branch information
turukawa committed Feb 9, 2020
1 parent e1a044e commit 045f02a
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 35 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
49 changes: 49 additions & 0 deletions .gitignore
@@ -0,0 +1,49 @@
############
## Windows
############

# Windows image file caches
Thumbs.db

# Folder config file
Desktop.ini

#############
## Python
#############

*.py[co]
#.gitattributes
#.gitignore

# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg

# Installer logs
pip-log.txt

# Unit test / coverage reports
.coverage
.tox

#Translations
*.mo

###############################
# Whyqd
###############################
__pycache__/
*.komodoproject
.ipynb_checkpoints/
*.ipynb

92 changes: 92 additions & 0 deletions tests/test_schema.json
@@ -0,0 +1,92 @@
{
"name": "test_schema",
"title": "Test Schema",
"description": "A test Schema",
"fields": [
{
"name": "la_code",
"title": "Local authority code",
"type": "string",
"description": "Standard code for local authority."
},
{
"name": "ba_ref",
"title": "Billing reference",
"type": "string",
"description": "Unique code for a specific hereditament. May be multiple rows for history."
},
{
"name": "prop_ba_rates",
"title": "Property billing rates",
"type": "number",
"description": "Actual rates paid by a specific ratepayer."
},
{
"name": "occupant_name",
"title": "Occupier name",
"type": "string",
"description": "Name of the ratepayer."
},
{
"name": "postcode",
"title": "Postcode",
"type": "string",
"description": "Full address or postcode of ratepayer."
},
{
"name": "occupation_state",
"title": "Occupation state",
"type": "boolean",
"description": "Occupation status, void or occupied."
},
{
"name": "occupation_state_date",
"title": "Date of occupation state",
"type": "date",
"description": "Date of the start of status in occupation_state."
},
{
"name": "occupation_state_reliefs",
"title": "Occupation state reliefs",
"type": "array",
"description": "Array of the categories of reliefs / exemptions applied.",
"constraints": {
"category": [
{
"name": "small_business"
},
{
"name": "rural"
},
{
"name": "charity"
},
{
"name": "enterprise_zone"
},
{
"name": "vacancy"
},
{
"name": "hardship"
},
{
"name": "retail"
},
{
"name": "discretionary"
},
{
"name": "exempt"
},
{
"name": "transitional"
},
{
"name": "other"
}
]
}
}
]
}
26 changes: 25 additions & 1 deletion tests/test_schema.py
@@ -1,6 +1,12 @@
from pathlib import Path

import whyqd as _w
import whyqd.common as _c

filename = "/test_schema.json"
source = str(Path(__file__).resolve().parent) + filename
data = _c.load_json(source)

class TestSchema:

def test_create(self):
Expand Down Expand Up @@ -28,4 +34,22 @@ def test_create(self):
}
s.set_details(name="test_schema")
s.set_field(**field)
assert s.validates
assert s.validates

def test_load(self):
s = _w.Schema(source)
assert s.validates
assert s.settings == data

def test_build(self):
s = _w.Schema()
details = {
"name": data["name"],
"title": data["title"],
"description": data["description"]
}
s.set_details(**details)
for field in data["fields"]:
s.set_field(**field)
assert s.validates
assert s.settings == data
32 changes: 23 additions & 9 deletions whyqd/schema/field.py
Expand Up @@ -103,7 +103,10 @@ def settings(self):
-------
dict: settings
"""
return deepcopy(self.field_settings)
response = deepcopy(self.field_settings)
# Remove empty category fields if none used
if not response["constraints"]: del response["constraints"]
return response

@property
def validates(self):
Expand Down Expand Up @@ -158,15 +161,26 @@ def set_constraint(self, constraint, value):
# Each term is an object with `name`/`description`, where`name` defined by a `type`
# Test if terms are of the required type
terms = [t["name"] for t in value]
required_type = self.default_constraints["category"]["terms"]["type"]
if not all([_c.get_field_type(t) == required_type for t in terms]):
e = "`{}` are not all of type `{}`".format(terms, required_type)
raise ValueError(e)
# Not sure what my intention is here. I mean, names are going to be strings, right?
# Maybe need to specify the types in the array somewhere?
# leaving it for now...
#required_type = self.default_constraints["category"]["terms"]["type"]
#if not all([_c.get_field_type(t) == required_type for t in terms]):
# e = "`{}` are not all of type `{}`".format(terms, required_type)
# raise ValueError(e)
# Test if terms are unique
if self.default_constraints[constraint].get("uniqueItems"):
if len(terms) < len(set(terms)):
e = "`{}` has fewer values than required by `{}`".format(value, constraint)
raise ValueError(e)
if len(terms) < len(set(terms)):
e = "Category list `{}` has duplicate terms".format(value)
raise ValueError(e)
if constraint == "valueType":
# This is to set the `type` for the contents of an array
if self._type != "array":
import warnings
e = "`valueType` will be ignored for fields which are not type `array`."
warnings.warn(e)
if value not in self.default_constraints["valueType"]["type"]:
e = "valueType `{}` not one of `{}`".format(value, self.default_constraints["valueType"]["type"])
raise ValueError(e)
if constraint == "filters":
# This is a user-defined constraint which can be applied to any date-related field type
if not value.get("modifiers"):
Expand Down
35 changes: 10 additions & 25 deletions whyqd/settings/default_fields.json
Expand Up @@ -38,12 +38,8 @@
"description": "When `true`, each value for the property `MUST` be unique."
},
"category": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"terms": {
"type": "string"
}
"description": "A list of dictionary terms with `name` & (optional) `description`, and with minimum length `minItems`."
},
"minimum": {
"type": "integer",
Expand Down Expand Up @@ -78,12 +74,8 @@
"description": "When `true`, each value for the property `MUST` be unique."
},
"category": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"terms": {
"type": "number"
}
"description": "A list of dictionary terms with `name` & (optional) `description`, and with minimum length `minItems`."
},
"minimum": {
"type": "number",
Expand Down Expand Up @@ -118,12 +110,8 @@
"description": "When `true`, each value for the property `MUST` be unique."
},
"category": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"terms": {
"type": "integer"
}
"description": "A list of dictionary terms with `name` & (optional) `description`, and with minimum length `minItems`."
},
"minimum": {
"type": "integer",
Expand Down Expand Up @@ -157,12 +145,8 @@
"description": "Indicates whether a property must have a value for each instance."
},
"category": {
"type": "array",
"minItems": 2,
"uniqueItems": true,
"terms": {
"type": "boolean"
}
"description": "A list of dictionary terms with `name` & (optional) `description`, and with minimum length `minItems`."
}
}
},
Expand Down Expand Up @@ -213,12 +197,13 @@
"description": "When `true`, each value for the property `MUST` be unique."
},
"category": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"terms": {
"type": "integer"
}
"description": "A list of dictionary terms with `name` & (optional) `description`, and with minimum length `minItems`."
},
"valueType": {
"type": ["string", "number", "integer", "boolean", "date", "datetime", "year"],
"description": "Defines the `type` for contents of the array.",
"default": "string"
}
}
},
Expand Down

0 comments on commit 045f02a

Please sign in to comment.