diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index 71e945081f9e359ddfd8b3fc8ceceee01a08f112..0000000000000000000000000000000000000000
--- a/.pylintrc
+++ /dev/null
@@ -1,407 +0,0 @@
-[MASTER]
-
-# Specify a configuration file.
-#rcfile=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS
-
-# Add files or directories matching the regex patterns to the blacklist. The
-# regex matches against base names, not paths.
-ignore-patterns=
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=pylint_flask
-
-# Use multiple processes to speed up Pylint.
-jobs=1
-
-# Allow loading of arbitrary C extensions. Extensions are imported into the
-# active Python interpreter and may run arbitrary code.
-unsafe-load-any-extension=no
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code
-extension-pkg-whitelist=
-
-# Allow optimization of some AST trees. This will activate a peephole AST
-# optimizer, which will apply various small optimizations. For instance, it can
-# be used to obtain the result of joining multiple strings with the addition
-# operator. Joining a lot of strings can lead to a maximum recursion error in
-# Pylint and this flag can prevent that. It has one side effect, the resulting
-# AST will be different than the one from reality. This option is deprecated
-# and it will be removed in Pylint 2.0.
-optimize-ast=no
-
-
-[MESSAGES CONTROL]
-
-# Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
-confidence=
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-disable=import-star-module-level,coerce-builtin,unicode-builtin,no-absolute-import,input-builtin,reduce-builtin,cmp-builtin,basestring-builtin,cmp-method,setslice-method,unichr-builtin,backtick,old-octal-literal,old-ne-operator,oct-method,map-builtin-not-iterating,raw_input-builtin,apply-builtin,range-builtin-not-iterating,useless-suppression,dict-view-method,filter-builtin-not-iterating,unpacking-in-except,getslice-method,intern-builtin,raising-string,standarderror-builtin,buffer-builtin,coerce-method,old-raise-syntax,file-builtin,dict-iter-method,metaclass-assignment,long-suffix,next-method-called,suppressed-message,long-builtin,delslice-method,execfile-builtin,old-division,nonzero-method,using-cmp-argument,xrange-builtin,hex-method,indexing-exception,print-statement,round-builtin,reload-builtin,parameter-unpacking,zip-builtin-not-iterating
-
-
-[REPORTS]
-
-# Set the output format. Available formats are text, parseable, colorized, msvs
-# (visual studio) and html. You can also give a reporter class, eg
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]". This option is deprecated
-# and it will be removed in Pylint 2.0.
-files-output=no
-
-# Tells whether to display a full report or only the messages
-reports=yes
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details
-#msg-template=
-
-
-[BASIC]
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=i,j,k,ex,Run,_
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=foo,bar,baz,toto,tutu,tata
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Include a hint for the correct naming format with invalid-name
-include-naming-hint=no
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-property-classes=abc.abstractproperty
-
-# Regular expression matching correct constant names
-const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Naming hint for constant names
-const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Regular expression matching correct attribute names
-attr-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for attribute names
-attr-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct argument names
-argument-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for argument names
-argument-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct method names
-method-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for method names
-method-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct variable names
-variable-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for variable names
-variable-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct function names
-function-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Naming hint for function names
-function-name-hint=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression matching correct class attribute names
-class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
-
-# Naming hint for class attribute names
-class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
-
-# Regular expression matching correct module names
-module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Naming hint for module names
-module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Regular expression matching correct class names
-class-rgx=[A-Z_][a-zA-Z0-9]+$
-
-# Naming hint for class names
-class-name-hint=[A-Z_][a-zA-Z0-9]+$
-
-# Regular expression matching correct inline iteration names
-inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
-
-# Naming hint for inline iteration names
-inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=^_
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=-1
-
-
-[ELIF]
-
-# Maximum number of nested blocks for function / method body
-max-nested-blocks=5
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=100
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )??$
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
-
-# List of optional constructs for which whitespace checking is disabled. `dict-
-# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
-# `trailing-comma` allows a space between comma and closing bracket: (a, ).
-# `empty-line` allows space-only lines.
-no-space-check=trailing-comma,dict-separator
-
-# Maximum number of lines in a module
-max-module-lines=1000
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=
-
-
-[LOGGING]
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format
-logging-modules=logging
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,XXX,TODO
-
-
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=no
-
-
-[SPELLING]
-
-# Spelling dictionary name. Available dictionaries: none. To make it working
-# install python-enchant package.
-spelling-dict=
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to indicated private dictionary in
-# --spelling-private-dict-file option instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[TYPECHECK]
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager
-
-
-[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the name of dummy variables (i.e. expectedly
-# not used).
-dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,_cb
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six.moves,future.builtins
-
-
-[CLASSES]
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=mcs
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,_fields,_replace,_source,_make
-
-
-[DESIGN]
-
-# Maximum number of arguments for function / method
-max-args=5
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore
-ignored-argument-names=_.*
-
-# Maximum number of locals for function / method body
-max-locals=15
-
-# Maximum number of return / yield for function / method body
-max-returns=6
-
-# Maximum number of branch for function / method body
-max-branches=12
-
-# Maximum number of statements in function / method body
-max-statements=50
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=7
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=2
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-# Maximum number of boolean expressions in a if statement
-max-bool-expr=5
-
-
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=optparse
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=Exception
diff --git a/Dockerfile b/Dockerfile
index 5611b7967ebb31336371530ba67d136bd60318cc..a80685cb102e9d5d8a318af37c2ba59f5c601035 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,10 +11,6 @@ ENV UWSGIREQ=/var/log/uwsgi.req.log
# Custom environment variables. These change from project to project.
ARG DOMAIN
ENV DOMAIN=${DOMAIN}
-ARG NUM_PROCESSES
-ENV NUM_PROCESSES=${NUM_PROCESSES}
-ARG NUM_THREADS
-ENV NUM_THREADS=${NUM_THREADS}
# Add the nginx reposoitory. We need 1.8 in order to support adding CORS headers to error responses.
RUN apt-get update
@@ -58,8 +54,6 @@ RUN \
echo "Copying environment variables from $1 to $2."; \
sed -e "s/\$DOMAIN/$DOMAIN/g" \
-e "s@\$CODEROOT@$CODEROOT@g" \
- -e "s@\$NUM_PROCESSES@$NUM_PROCESSES@g" \
- -e "s@\$NUM_THREADS@$NUM_THREADS@g" \
-e "s@\$NGINXERR@$NGINXERR@g" \
-e "s@\$NGINXREQ@$NGINXREQ@g" \
-e "s@\$UWSGIERR@$UWSGIERR@g" \
diff --git a/app/__init__.py b/app/__init__.py
index 990886ae845f4fe14207268e9767f6f53d9873fe..cbd49e1aa4393d1386a5194c114e38bee5aef288 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -24,4 +24,7 @@ def create_app(config):
from .lib import session
session.register(app)
+ with app.app_context():
+ database.db.create_all()
+
return app
diff --git a/app/config/development.default.py b/app/config/development.default.py
index 8d06722db7893b986851725d81d8eb08e21b89f1..5a20208c82fe48608f7fba419e724a09b1f5416e 100644
--- a/app/config/development.default.py
+++ b/app/config/development.default.py
@@ -1,23 +1,20 @@
-import os
-
-
SQLALCHEMY_TRACK_MODIFICATIONS = False
-SQLALCHEMY_DATABASE_URI = os.environ['DBURI']
+SQLALCHEMY_DATABASE_URI = 'postgres:///projectservice'
-REDIS_URI = os.environ['REDISURI']
+REDIS_URI = 'redis://127.0.0.1:6379/'
DEBUG = True
SERVICE_CONFIG = {
- 'app_key': os.environ['APP_KEY'],
- 'app_secret': os.environ['APP_SECRET'],
+ 'app_key': '$APP_KEY',
+ 'app_secret': '$APP_SECRET',
'headers': {
'app_key': 'x-blocpower-app-key',
'app_token': 'x-blocpower-app-token',
'app_secret': 'x-blocpower-app-secret'},
'urls': {
- 'app': 'http://dev.appservice.blocpower.io/',
- 'user': 'http://dev.userservice.blocpower.io/'}
+ 'app': 'http://127.0.0.1:5400',
+ 'user': 'http://127.0.0.1:5401'}
}
# AppService
diff --git a/app/config/local.default.py b/app/config/local.default.py
deleted file mode 100644
index 9c69a5fc0a9528f202fb2baef6b7160021522b80..0000000000000000000000000000000000000000
--- a/app/config/local.default.py
+++ /dev/null
@@ -1,29 +0,0 @@
-SQLALCHEMY_TRACK_MODIFICATIONS = False
-SQLALCHEMY_DATABASE_URI = 'postgres:///customer'
-
-REDIS_URI = 'redis://127.0.0.1:6379/'
-
-DEBUG = True
-
-SERVICE_CONFIG = {
- 'app_key': '$APP_KEY',
- 'app_secret': '$APP_SECRET',
- 'headers': {
- 'app_key': 'x-blocpower-app-key',
- 'app_token': 'x-blocpower-app-token',
- 'app_secret': 'x-blocpower-app-secret'},
- 'urls': {
- 'app': 'http://127.0.0.1:5400',
- 'user': 'http://127.0.0.1:5401'}
-}
-
-# AppService
-APP_CACHE_EXPIRY = 60 * 60 # One hour.
-
-# UserService
-GOOGLE_AUTH_HEADER = 'x-blocpower-google-token'
-USER_CACHE_EXPIRY = 60 * 60 # One Hour.
-
-# Blocpower auth information.
-HEADER_AUTH_KEY = 'x-blocpower-auth-key'
-HEADER_AUTH_TOKEN = 'x-blocpower-auth-token'
diff --git a/app/config/staging.default.py b/app/config/staging.default.py
index 9a06abb8252974d743d088a52ee30e93fb6dea1b..a4b9ead2775012af640da65b3d07e1e190fc0f64 100644
--- a/app/config/staging.default.py
+++ b/app/config/staging.default.py
@@ -6,7 +6,7 @@ SQLALCHEMY_DATABASE_URI = os.environ['DBURI']
REDIS_URI = os.environ['REDISURI']
-DEBUG = True
+DEBUG = False
SERVICE_CONFIG = {
'app_key': os.environ['APP_KEY'],
diff --git a/app/config/uwsgi.ini b/app/config/uwsgi.ini
index c996d6b66659ce79c29f7880619ca4a7ef23aff0..08d76933b88d4ca851eac0750135ca18f54a7d93 100644
--- a/app/config/uwsgi.ini
+++ b/app/config/uwsgi.ini
@@ -1,5 +1,5 @@
[uwsgi]
-# This is used if no configuration is specified.
+# This is used if no configuration is speicified.
ini = :base
socket = $CODEROOT/uwsgi.sock
@@ -7,10 +7,9 @@ socket = $CODEROOT/uwsgi.sock
chmod-socket = 666
master = true
-# Number of processes and workers should be determined at build time
-# Should be based on the type of instance that it is running on
-processes = $NUM_PROCESSES
-threads = $NUM_THREADS
+# This is a best guess. At some point in time, we should run some experiments
+# and get something more concrete.
+processes = 1
# Set loggers.
req-logger = file:$UWSGIREQ
diff --git a/app/controllers/base.py b/app/controllers/base.py
index a99b010074ff3e675381c33618d1cf080491cd45..9168871c498ca68863934b39fbbae8a83527b580 100644
--- a/app/controllers/base.py
+++ b/app/controllers/base.py
@@ -177,5 +177,5 @@ class SalesforceObjectController(RestController):
if model:
data.update({'id': model.id})
return self.put(model.id, data, filter_data)
-
+
return super(SalesforceObjectController, self).post(data, filter_data)
diff --git a/app/controllers/project.py b/app/controllers/project.py
index ed407fdca5bd8313aa1c34cd0faf81f4926d0f49..679b02cb5680060af18404fb41ffdb8573c019b7 100644
--- a/app/controllers/project.py
+++ b/app/controllers/project.py
@@ -26,9 +26,8 @@ class ProjectController(RestController):
filters = {
'q': lambda d: and_(*[
Project.name.ilike('%{}%'.format(term))
- for term in d['q'].split(' ')]),
- 'building_id': lambda d: Project.building_id == d['building_id'],
- }
+ for term in d['q'].split(' ')
+ ])}
def get_form(self, filter_data):
"""Return the project form."""
@@ -101,7 +100,7 @@ class ProjectController(RestController):
address = AddressController().put(address.id, address_data, {})
else:
address = AddressController().post(address_data, {})
-
+
place_data.update({'address_id': address.id})
if place:
@@ -109,7 +108,7 @@ class ProjectController(RestController):
place = PlaceController().put(place.id, place_data, {})
else:
place = PlaceController().post(place_data, {})
-
+
data.update({'place_id': place.id})
# Create/modify the project
diff --git a/app/forms/project.py b/app/forms/project.py
index a46bb06608c798200970d4d5d6883cb375000bf3..d69d9bf8789167e485632915787b59c2a6f94131 100644
--- a/app/forms/project.py
+++ b/app/forms/project.py
@@ -21,4 +21,3 @@ class ProjectForm(Form):
wtf.validators.Required(),
wtf.validators.AnyOf(Project.states)
])
- building_id = wtf.IntegerField()
diff --git a/app/lib/database.py b/app/lib/database.py
index 9bb84f0050838b745d8a1933afdec0c938caebd2..3de6fb01f9d757822f6327bc7214a086ead56bc8 100644
--- a/app/lib/database.py
+++ b/app/lib/database.py
@@ -21,7 +21,7 @@ def register(app):
def commit():
"""A wrapper for db.session.commit().
-
+
This rolls back the database session after a failed commit so that
the app can continue to request future commits on the same thread.
"""
@@ -30,68 +30,3 @@ def commit():
except Exception as e:
db.session.rollback()
raise e
-
-class ProcTable:
- """ProcTable represents a simplified class of SQLAlchemy db.model"""
-
- def __init__(self, name, *columns):
- self.name = name
- self.columns = [column for column in columns]
-
- def get_columns(self):
- """List all columns"""
- return [column.key for column in self.columns]
-
-class ProcColumn:
- """ProcColumn represents a column similar to SQLAlchemy column"""
-
- def __init__(self, key):
- self.key = key
-
-def proc(model, method, limit=None, offset=None, **kwargs):
- """
- Run stored procedure
-
- Args:
- model (class): The class of the db table
- method (str): Method name for stored procedure
- kwargs: Arguments for stored proc
- Returns:
- list: Results of the query
- """
-
- params = ""
- cols = ','.join(str(i) for i in model.__table__.get_columns())
- # By seperating the args like this sanatation will happen automatically
- input_args = {}
- for key, value in kwargs.items():
- if value is not None:
- params += "in_{} := :{}, ".format(key, key)
- input_args[key] = str(value)
- else:
- params += "in_{} := null, ".format(key)
- params = params[:-2] # remove last comma and space
-
- query = "select {} from {}.{}({})".format(
- cols,
- model.__table_args__['schema'],
- method,
- params
- )
- if limit:
- query += ' limit {}'.format(limit)
- if offset:
- query += ' offset {}'.format(offset)
-
- try:
- results = db.session.execute(query, input_args)
- db.session.commit()
-
- data = []
- for row in results:
- data.append(model(**dict(zip(row.keys(), row))))
-
- except Exception as err:
- raise err
-
- return data
diff --git a/app/models/base.py b/app/models/base.py
index 529e2b8a7a5d40f2100e88c7536baf923a92792a..8381060c7d044d4d4d8e2b77f321e16b2492f44d 100644
--- a/app/models/base.py
+++ b/app/models/base.py
@@ -13,8 +13,21 @@ from ..lib import geography
from . import columns
-class BaseModel:
+class Model(object):
"""A base mixin for all models."""
+ @declared_attr
+ def __tablename__(cls):
+ """Automatically set the database table name to the class name
+ lower-cased.
+ """
+ return cls.__name__.lower()
+
+ def __str__(self):
+ """Provide a sane default for model string representation."""
+ return '<{} (id={})>'.format(self.__class__.__name__, self.id)
+
+ id = db.Column(db.Integer, primary_key=True)
+
def get_dictionary(self):
"""Return a dictionary representation of the model.
@@ -41,23 +54,6 @@ class BaseModel:
return d
-class Model(BaseModel):
- """A base mixin for SQLAlchemy model."""
- @declared_attr
- def __tablename__(cls):
- """Automatically set the database table name to the class name
- lower-cased.
- """
- return cls.__name__.lower()
-
- __table_args__ = {"schema": "public"}
-
- def __str__(self):
- """Provide a sane default for model string representation."""
- return '<{} (id={})>'.format(self.__class__.__name__, self.id)
-
- id = db.Column(db.Integer, primary_key=True)
-
class Tracked(object):
"""A mixin to include tracking datetime fields."""
created = db.Column(columns.Arrow, default=func.now())
@@ -95,22 +91,3 @@ class External(object):
"""An external-facing model. These have a UUID key."""
key = db.Column(
columns.GUID(), index=True, nullable=False, default=uuid.uuid4)
-
-
-class ProjectSchema(BaseModel):
- """A mixin that changes the schema to project_service"""
-
- @declared_attr
- def __tablename__(cls):
- """Automatically set the database table name to the class name
- lower-cased.
- """
- return cls.__name__.lower()
-
- __table_args__ = {"schema": "project_service"}
-
- def __str__(self):
- """Provide a sane default for model string representation."""
- return '<{} (id={})>'.format(self.__class__.__name__, self.id)
-
- id = db.Column(db.Integer, primary_key=True)
diff --git a/app/models/client.py b/app/models/client.py
index 3a4ada2922a3b40bb758650575f26b280e48b51e..afa2543572c453303dd67511d7ee4016a4cb40b3 100644
--- a/app/models/client.py
+++ b/app/models/client.py
@@ -1,8 +1,8 @@
"""Models for tracking client information."""
from app.lib.database import db
-from app.models.base import Model, Tracked, SalesForce, ProjectSchema
+from app.models.base import Model, Tracked, SalesForce
-class Client(ProjectSchema, Tracked, SalesForce, db.Model):
+class Client(Model, Tracked, SalesForce, db.Model):
"""A client."""
name = db.Column(db.Unicode(255), nullable=False)
diff --git a/app/models/contact.py b/app/models/contact.py
index a16d788db4d868f93b3fc7efb78bf2e4814eeb06..0fefe9d3306d758dd8d13409363cffd4f8e95244 100644
--- a/app/models/contact.py
+++ b/app/models/contact.py
@@ -1,14 +1,14 @@
"""Models for dealing with contacts."""
from app.lib.database import db
-from app.models.base import Model, Tracked, SalesForce, ProjectSchema
+from app.models.base import Model, Tracked, SalesForce
-class Contact(ProjectSchema, Tracked, SalesForce, db.Model):
+class Contact(Model, Tracked, SalesForce, db.Model):
"""A contact (i.e an entity you can reach out to)."""
name = db.Column(db.Unicode(255), nullable=False)
-class ContactMethod(ProjectSchema, Tracked, db.Model):
+class ContactMethod(Model, Tracked, db.Model):
"""A contact method."""
# TODO SQLAlchemy < 1.1 does not support the use of a python enum in Enum
# fields, so for now we have to use a list of strings. When 1.1 leaves
@@ -24,12 +24,12 @@ class ContactMethod(ProjectSchema, Tracked, db.Model):
contact_id = db.Column(
db.Integer, db.ForeignKey('contact.id'), nullable=False)
method = db.Column(
- db.Enum(*methods, name='contact_methods', inherit_schema=True),
+ db.Enum(*methods, name='contact_methods'),
nullable=False)
value = db.Column(db.Unicode(255), nullable=False)
-class ProjectContact(ProjectSchema, Tracked, db.Model):
+class ProjectContact(Model, Tracked, db.Model):
"""A m2m relationship between project and contact."""
project_id = db.Column(
db.Integer, db.ForeignKey('project.id'), nullable=False)
diff --git a/app/models/document.py b/app/models/document.py
index b604dc424238fd80da262875afa3686dd5de17ab..f9fbb2d00d04b857d7bfef6d79f1ec7aed15821d 100644
--- a/app/models/document.py
+++ b/app/models/document.py
@@ -1,15 +1,15 @@
"""Models for handling associated documents."""
from app.lib.database import db
-from app.models.base import Model, Tracked, ProjectSchema
+from app.models.base import Model, Tracked
from app.models.columns import GUID
-class DocumentSlot(ProjectSchema, Tracked, db.Model):
+class DocumentSlot(Model, Tracked, db.Model):
"""A m2m relationship between the project model and documents (provided by
the document service).
"""
project_id = db.Column(
- db.Integer, db.ForeignKey('project_service.project.id'), nullable=False)
+ db.Integer, db.ForeignKey('project.id'), nullable=False)
document_key = db.Column(GUID(), nullable=False)
# How the document fits into the project.
diff --git a/app/models/note.py b/app/models/note.py
index 4385a8e4d6a5b16c5d04020ac528a19edf5c21fc..470c1dc24567b0b23f8639e9a56859e8d03ed67e 100644
--- a/app/models/note.py
+++ b/app/models/note.py
@@ -1,10 +1,10 @@
"""Models for making small notes on the project."""
from app.lib.database import db
-from app.models.base import Model, Tracked, SalesForce, ProjectSchema
+from app.models.base import Model, Tracked, SalesForce
from app.models import columns
-class Note(ProjectSchema, Tracked, SalesForce, db.Model):
+class Note(Model, Tracked, SalesForce, db.Model):
"""A note."""
project_id = db.Column(
db.Integer,
diff --git a/app/models/place.py b/app/models/place.py
index 625268a16f3fb065cd44d0b380e3ce5f9ff568e7..e06c9532fc8265c4f5a8c661ce87e3a855cd7fa7 100644
--- a/app/models/place.py
+++ b/app/models/place.py
@@ -2,27 +2,27 @@ from sqlalchemy.orm import relationship
from app.lib.database import db
from app.lib import geography
-from app.models.base import Tracked, SalesForce, ProjectSchema
+from app.models.base import Model, Tracked, SalesForce
-class Address(ProjectSchema, Tracked, db.Model):
+class Place(Model, Tracked, SalesForce, db.Model):
+ """The Place class"""
+ name = db.Column(db.Unicode(255), nullable=False)
+ address_id = db.Column(
+ db.Integer, db.ForeignKey('address.id'), nullable=False)
+
+ address = relationship('Address', uselist=False)
+
+
+class Address(Model, Tracked, db.Model):
"""The Address class"""
street_address = db.Column(db.Unicode(255), nullable=False)
city = db.Column(db.Unicode(255), nullable=False)
county = db.Column(db.Unicode(255))
state = db.Column(
- db.Enum(*geography.states, name='geo_states', inherit_schema=True),
+ db.Enum(*geography.states, name='geo_states'),
nullable=False)
country = db.Column(
- db.Enum(*geography.countries, name='geo_countries', inherit_schema=True),
+ db.Enum(*geography.countries, name='geo_countries'),
nullable=False)
postal_code = db.Column(db.Unicode(10))
-
-
-class Place(ProjectSchema, Tracked, SalesForce, db.Model):
- """The Place class"""
- name = db.Column(db.Unicode(255), nullable=False)
- address_id = db.Column(
- db.INTEGER, db.ForeignKey('project_service.address.id'), nullable=False)
-
- address = relationship('Address', uselist=False)
diff --git a/app/models/project.py b/app/models/project.py
index 7b71df11ae530b8dbf395831ad2db79a04117837..e1203c747e104fe8d1ab1528faa6f76f8ea03ba2 100644
--- a/app/models/project.py
+++ b/app/models/project.py
@@ -1,18 +1,17 @@
"""Models directly-relating to the top-level project."""
import re
from app.lib.database import db
-from app.models.base import Model, Tracked, SalesForce, ProjectSchema
+from app.models.base import Model, Tracked, SalesForce
-class Project(ProjectSchema, Tracked, SalesForce, db.Model):
+class Project(Model, Tracked, SalesForce, db.Model):
"""A project."""
client_id = db.Column(
- db.Integer, db.ForeignKey('project_service.client.id'), nullable=False)
+ db.Integer, db.ForeignKey('client.id'), nullable=False)
place_id = db.Column(
- db.Integer, db.ForeignKey('project_service.place.id'), nullable=False)
+ db.Integer, db.ForeignKey('place.id'), nullable=False)
name = db.Column(db.Unicode(255), nullable=False)
- building_id = db.Column(db.Integer, nullable=False)
@property
def computed_slug(self):
@@ -61,7 +60,7 @@ class Project(ProjectSchema, Tracked, SalesForce, db.Model):
'constructed', 'verified', 'paid'
]
state = db.Column(
- db.Enum(*states, name='project_states', inherit_schema=True),
+ db.Enum(*states, name='project_states'),
default='pending',
nullable=False)
@@ -72,11 +71,10 @@ class Project(ProjectSchema, Tracked, SalesForce, db.Model):
return d
-class ProjectStateChange(ProjectSchema, Tracked, db.Model):
+class ProjectStateChange(Model, Tracked, db.Model):
"""A state change of a project."""
- project_id = db.Column(
- db.Integer, db.ForeignKey('project_service.project.id'),
+ project_id = db.Column(db.Integer, db.ForeignKey('project.id'),
nullable=False)
state = db.Column(
- db.Enum(*Project.states, name='project_states', inherit_schema=True),
+ db.Enum(*Project.states, name='project_states'),
nullable=False)
diff --git a/app/views/base.py b/app/views/base.py
index b885c9c33cf93375d5c310d9804ec7c6d009afed..f37f3d12d92a2b050aba5724e327ca1b8a295f8d 100644
--- a/app/views/base.py
+++ b/app/views/base.py
@@ -70,7 +70,6 @@ class RestView(UnprotectedRestView):
"""
decorators = (standard_login_need,)
-
class SalesforceObjectView(UnprotectedRestView):
"""A view wrapper for Salesforce object that offers API protection"""
def get_controller(self):
diff --git a/docs/API/api.md b/docs/API/api.md
deleted file mode 100644
index 1b62768d25d1c33ecad4287c7057524f052ad8d7..0000000000000000000000000000000000000000
--- a/docs/API/api.md
+++ /dev/null
@@ -1,56 +0,0 @@
-**Title**
-----
- <_Additional information about your API call. Try to use verbs that match both request type (fetching vs modifying) and plurality (one vs multiple)._>
-
-* **URL**
-
- <_The URL Structure (path only, no root url)_>
-
-* **Method:**
-
- <_The request type_>
-
- `GET` | `POST` | `DELETE` | `PUT`
-
-* **URL Params**
-
- <_If URL params exist, specify them in accordance with name mentioned in URL section. Separate into optional and required. Document data constraints._>
-
- **Required:**
-
- `id=[integer]`
-
- **Optional:**
-
- `photo_id=[alphanumeric]`
-
-* **Data Params**
-
- <_If making a post request, what should the body payload look like? URL Params rules apply here too._>
-
-* **Success Response:**
-
- <_What should the status code be on success and is there any returned data? This is useful when people need to to know what their callbacks should expect!_>
-
- * **Code:** 200
- **Content:** `{ id : 12 }`
-
-* **Error Response:**
-
- <_Most endpoints will have many ways they can fail. From unauthorized access, to wrongful parameters etc. All of those should be liste d here. It might seem repetitive, but it helps prevent assumptions from being made where they should be._>
-
- * **Code:** 401 UNAUTHORIZED
- **Content:** `{ error : "Log in" }`
-
- OR
-
- * **Code:** 422 UNPROCESSABLE ENTRY
- **Content:** `{ error : "Email Invalid" }`
-
-* **Sample Call:**
-
- <_Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable._>
-
-* **Notes:**
-
- <_This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here._>
diff --git a/requirements-dev.txt b/requirements-dev.txt
deleted file mode 100644
index 5660f022283738e06640ee95df95506320277c59..0000000000000000000000000000000000000000
--- a/requirements-dev.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--r requirements.txt
-Flask-Testing==0.4.2
-nose==1.3.7
-pytest==2.9.1
-pylint>=1.6.5
-pylint-flask>=0.5
diff --git a/requirements.txt b/requirements.txt
index 39b9286885c59c1ef9aa02524e2e2c8fb467358f..dfb98eccb50d3c857782738574ae48a6f75ef34f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,6 +11,7 @@ docutils==0.12
Flask==0.10.1
Flask-Classy==0.6.10
Flask-SQLAlchemy==2.1
+Flask-Testing==0.4.2
GeoAlchemy2==0.3.0
httplib2==0.9.2
itsdangerous==0.24
@@ -18,6 +19,7 @@ Jinja2==2.8
jmespath==0.9.0
MarkupSafe==0.23
needs==1.0.9
+nose==1.3.7
oauth2client==2.0.1
parse==1.6.6
pathspec==0.3.3
@@ -27,6 +29,7 @@ py==1.4.31
pyasn1==0.1.9
pyasn1-modules==0.0.8
pycparser==2.14
+pytest==2.9.1
python-dateutil==2.4.2
PyYAML==3.11
redis==2.10.5
diff --git a/run.py b/run.py
index 9e5615095079b77d6db79e0b2c754298fa737e47..217555d390954c046072eaefd5063e810dfa4f1b 100644
--- a/run.py
+++ b/run.py
@@ -2,6 +2,6 @@ from app import create_app
from app.lib.database import db
-app = create_app('config/local.py')
+app = create_app('config/development.py')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5302)
diff --git a/wsgi.py b/wsgi.py
index 294e9eb1abfaa5e3fbedf6ca944c8b041490d65a..d147c76628e5663775b65db95241715368391318 100644
--- a/wsgi.py
+++ b/wsgi.py
@@ -9,3 +9,6 @@ env = os.environ['ENVIRONMENT']
# Correctly raise a file not found if the specified environment does not exist.
app = create_app('config/{}.py'.format(env))
+with app.app_context():
+ # Build the database models.
+ db.create_all()