diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..4358a081084b5e62fdd1ee09063c31206004cde5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,33 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,py}] +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Indentation override for all JS under lib directory +[lib/**.js] +indent_style = space +indent_size = 2 + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..dbca14d5a6eed50887eed86b3c76fb547c20d886 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "extends": "airbnb", + "plugins": [ + "jsx-a11y", + "import" + ], + "rules": { + }, + "env": { + "browser": true + } +} diff --git a/.gitignore b/.gitignore index 171d7787629356c3ebf8817d272bbf6961df57e6..4db2aa842187c3a007a4438a4de4a89579760a9a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ node_modules # misc .idea .vscode/ -static/ +/static +.coverage diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000000000000000000000000000000000000..10269cdd01363cb3eb0e374351d3250d46e8b6b8 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,407 @@ +[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_django + +# 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=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,import-star-module-level,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,long-suffix,old-ne-operator,old-octal-literal,suppressed-message,useless-suppression,locally-disabled + + +[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 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 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 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 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 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 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 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 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 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=120 + +# 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/blocnote/apps/budgetSimulator/__init__.py b/blocnote/apps/budgetSimulator/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/blocnote/apps/budgetSimulator/admin.py b/blocnote/apps/budgetSimulator/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/blocnote/apps/budgetSimulator/apps.py b/blocnote/apps/budgetSimulator/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..d13b03ba4873b5d79a3c1977b1e9c7ca039224ad --- /dev/null +++ b/blocnote/apps/budgetSimulator/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class BudgetsimulatorConfig(AppConfig): + name = 'budgetSimulator' diff --git a/blocnote/apps/budgetSimulator/fixtures/bills_overview_testdata.json b/blocnote/apps/budgetSimulator/fixtures/bills_overview_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..eb81c21360edd7b6c49847bafa131509a2cffb98 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/bills_overview_testdata.json @@ -0,0 +1,1346 @@ +[ + { + "model": "financialInputs.billsoverview", + "pk": 1, + "fields": { + "building_id": 100, + "year": "2014", + "electricity": "10000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": false, + "oil": null, + "oil_is_user_input": false, + "gas": null, + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2, + "fields": { + "building_id": 101, + "year": "2014", + "electricity": "10000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": false, + "oil": null, + "oil_is_user_input": false, + "gas": null, + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 3, + "fields": { + "building_id": 101, + "year": "2015", + "electricity": "12000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": false, + "oil": null, + "oil_is_user_input": false, + "gas": null, + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 4, + "fields": { + "building_id": 101, + "year": "2016", + "electricity": "15000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": false, + "oil": null, + "oil_is_user_input": false, + "gas": null, + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 5, + "fields": { + "building_id": 102, + "year": "2014", + "electricity": "10000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": false, + "oil": null, + "oil_is_user_input": false, + "gas": 5000, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 6, + "fields": { + "building_id": 103, + "year": "2014", + "electricity": "10000.00", + "electricity_is_user_input": true, + "water": 17000, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": false, + "gas": 5000, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 7, + "fields": { + "building_id": 104, + "year": "2014", + "electricity": "10000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": false, + "oil": null, + "oil_is_user_input": false, + "gas": null, + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 8, + "fields": { + "building_id": 104, + "year": "2015", + "electricity": "12000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": false, + "oil": null, + "oil_is_user_input": false, + "gas": 5000, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 9, + "fields": { + "building_id": 104, + "year": "2016", + "electricity": "14000.00", + "electricity_is_user_input": true, + "water": 17000, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": false, + "gas": 7000, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2410, + "fields": { + "building_id": 289, + "year": "2014", + "electricity": "82473.13", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "6686.16", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2411, + "fields": { + "building_id": 289, + "year": "2015", + "electricity": "83108.87", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": "18000.00", + "oil_is_user_input": true, + "gas": "6748.39", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2412, + "fields": { + "building_id": 289, + "year": "2016", + "electricity": "83839.67", + "electricity_is_user_input": false, + "water": "2000.00", + "water_is_user_input": true, + "oil": "20000.00", + "oil_is_user_input": true, + "gas": "6797.83", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2413, + "fields": { + "building_id": 289, + "year": "2017", + "electricity": "85516.56", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "6927.36", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2414, + "fields": { + "building_id": 289, + "year": "2018", + "electricity": "87650.14", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "7098.94", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2415, + "fields": { + "building_id": 289, + "year": "2019", + "electricity": "89797.71", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "7274.48", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2416, + "fields": { + "building_id": 289, + "year": "2020", + "electricity": "91900.16", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "7444.99", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2417, + "fields": { + "building_id": 289, + "year": "2021", + "electricity": "93851.39", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "7605.94", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2418, + "fields": { + "building_id": 289, + "year": "2022", + "electricity": "95627.40", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "7750.85", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2419, + "fields": { + "building_id": 289, + "year": "2023", + "electricity": "97371.86", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "7892.45", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2420, + "fields": { + "building_id": 289, + "year": "2024", + "electricity": "99155.59", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "8036.75", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2421, + "fields": { + "building_id": 289, + "year": "2025", + "electricity": "100999.33", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "8186.00", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2422, + "fields": { + "building_id": 289, + "year": "2026", + "electricity": "102987.98", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "8345.65", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2423, + "fields": { + "building_id": 289, + "year": "2027", + "electricity": "105128.49", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "8518.62", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2424, + "fields": { + "building_id": 289, + "year": "2028", + "electricity": "107321.71", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "8696.60", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2425, + "fields": { + "building_id": 289, + "year": "2029", + "electricity": "109516.91", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "8874.94", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2426, + "fields": { + "building_id": 289, + "year": "2030", + "electricity": "111712.78", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "9053.20", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2427, + "fields": { + "building_id": 289, + "year": "2031", + "electricity": "113915.17", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "9232.00", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2428, + "fields": { + "building_id": 289, + "year": "2032", + "electricity": "116143.70", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "9412.61", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2429, + "fields": { + "building_id": 289, + "year": "2033", + "electricity": "118423.26", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "9597.24", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2430, + "fields": { + "building_id": 289, + "year": "2034", + "electricity": "120763.97", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "9786.78", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2431, + "fields": { + "building_id": 289, + "year": "2035", + "electricity": "123168.44", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "9981.49", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2432, + "fields": { + "building_id": 289, + "year": "2036", + "electricity": "125636.99", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "10181.41", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2433, + "fields": { + "building_id": 289, + "year": "2037", + "electricity": "128159.45", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "10385.87", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2434, + "fields": { + "building_id": 289, + "year": "2038", + "electricity": "130723.50", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": "10593.76", + "gas_is_user_input": false + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2485, + "fields": { + "building_id": 2002, + "year": "2014", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2486, + "fields": { + "building_id": 2002, + "year": "2015", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2487, + "fields": { + "building_id": 2002, + "year": "2016", + "electricity": "1000.00", + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": "1100.00", + "oil_is_user_input": true, + "gas": "2000.00", + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2488, + "fields": { + "building_id": 2002, + "year": "2017", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2489, + "fields": { + "building_id": 2002, + "year": "2018", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2490, + "fields": { + "building_id": 2002, + "year": "2019", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2491, + "fields": { + "building_id": 2002, + "year": "2020", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2492, + "fields": { + "building_id": 2002, + "year": "2021", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2493, + "fields": { + "building_id": 2002, + "year": "2022", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2494, + "fields": { + "building_id": 2002, + "year": "2023", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2495, + "fields": { + "building_id": 2002, + "year": "2024", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2496, + "fields": { + "building_id": 2002, + "year": "2025", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2497, + "fields": { + "building_id": 2002, + "year": "2026", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2498, + "fields": { + "building_id": 2002, + "year": "2027", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2499, + "fields": { + "building_id": 2002, + "year": "2028", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2500, + "fields": { + "building_id": 2002, + "year": "2029", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2501, + "fields": { + "building_id": 2002, + "year": "2030", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2502, + "fields": { + "building_id": 2002, + "year": "2031", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2503, + "fields": { + "building_id": 2002, + "year": "2032", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2504, + "fields": { + "building_id": 2002, + "year": "2033", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2505, + "fields": { + "building_id": 2002, + "year": "2034", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2506, + "fields": { + "building_id": 2002, + "year": "2035", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2507, + "fields": { + "building_id": 2002, + "year": "2036", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2508, + "fields": { + "building_id": 2002, + "year": "2037", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2509, + "fields": { + "building_id": 2002, + "year": "2038", + "electricity": null, + "electricity_is_user_input": true, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2510, + "fields": { + "building_id": 2003, + "year": "2014", + "electricity": "82473.13", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2511, + "fields": { + "building_id": 2003, + "year": "2015", + "electricity": "83108.87", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2512, + "fields": { + "building_id": 2003, + "year": "2016", + "electricity": "83839.67", + "electricity_is_user_input": false, + "water": "20000.00", + "water_is_user_input": true, + "oil": "40000.00", + "oil_is_user_input": true, + "gas": "75000.00", + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2513, + "fields": { + "building_id": 2003, + "year": "2017", + "electricity": "85516.56", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2514, + "fields": { + "building_id": 2003, + "year": "2018", + "electricity": "87650.14", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2515, + "fields": { + "building_id": 2003, + "year": "2019", + "electricity": "89797.71", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2516, + "fields": { + "building_id": 2003, + "year": "2020", + "electricity": "91900.16", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2517, + "fields": { + "building_id": 2003, + "year": "2021", + "electricity": "93851.39", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2518, + "fields": { + "building_id": 2003, + "year": "2022", + "electricity": "95627.40", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2519, + "fields": { + "building_id": 2003, + "year": "2023", + "electricity": "97371.86", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2520, + "fields": { + "building_id": 2003, + "year": "2024", + "electricity": "99155.59", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2521, + "fields": { + "building_id": 2003, + "year": "2025", + "electricity": "100999.33", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2522, + "fields": { + "building_id": 2003, + "year": "2026", + "electricity": "102987.98", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2523, + "fields": { + "building_id": 2003, + "year": "2027", + "electricity": "105128.49", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2524, + "fields": { + "building_id": 2003, + "year": "2028", + "electricity": "107321.71", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2525, + "fields": { + "building_id": 2003, + "year": "2029", + "electricity": "109516.91", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2526, + "fields": { + "building_id": 2003, + "year": "2030", + "electricity": "111712.78", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2527, + "fields": { + "building_id": 2003, + "year": "2031", + "electricity": "113915.17", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2528, + "fields": { + "building_id": 2003, + "year": "2032", + "electricity": "116143.70", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2529, + "fields": { + "building_id": 2003, + "year": "2033", + "electricity": "118423.26", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2530, + "fields": { + "building_id": 2003, + "year": "2034", + "electricity": "120763.97", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2531, + "fields": { + "building_id": 2003, + "year": "2035", + "electricity": "123168.44", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2532, + "fields": { + "building_id": 2003, + "year": "2036", + "electricity": "125636.99", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2533, + "fields": { + "building_id": 2003, + "year": "2037", + "electricity": "128159.45", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + }, + { + "model": "financialInputs.billsoverview", + "pk": 2534, + "fields": { + "building_id": 2003, + "year": "2038", + "electricity": "130723.50", + "electricity_is_user_input": false, + "water": null, + "water_is_user_input": true, + "oil": null, + "oil_is_user_input": true, + "gas": null, + "gas_is_user_input": true + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/bills_testdata.json b/blocnote/apps/budgetSimulator/fixtures/bills_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..15de0257f3d29c58bd616e99522e6b8d456c93da --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/bills_testdata.json @@ -0,0 +1,914 @@ +[ + { + "model": "financialInputs.bills", + "pk": 1, + "fields": { + "building_id": 100, + "date_from": "2015-01-01", + "date_to": "2015-12-31", + "utility_type": "electricity", + "usage": "100.00", + "charge": "5000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 2, + "fields": { + "building_id": 102, + "date_from": "2015-01-01", + "date_to": "2015-12-31", + "utility_type": "electricity", + "usage": "100.00", + "charge": "5000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 3, + "fields": { + "building_id": 102, + "date_from": "2014-01-01", + "date_to": "2014-12-31", + "utility_type": "gas", + "usage": "200.00", + "charge": "7000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 4, + "fields": { + "building_id": 103, + "date_from": "2014-01-01", + "date_to": "2014-12-31", + "utility_type": "electricity", + "usage": "100.00", + "charge": "5000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 5, + "fields": { + "building_id": 103, + "date_from": "2015-01-01", + "date_to": "2015-12-31", + "utility_type": "electricity", + "usage": "200.00", + "charge": "10000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 6, + "fields": { + "building_id": 103, + "date_from": "2016-01-01", + "date_to": "2016-12-31", + "utility_type": "electricity", + "usage": "300.00", + "charge": "13000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 7, + "fields": { + "building_id": 103, + "date_from": "2015-01-01", + "date_to": "2015-12-31", + "utility_type": "gas", + "usage": "200.00", + "charge": "7000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 8, + "fields": { + "building_id": 103, + "date_from": "2016-01-01", + "date_to": "2016-12-31", + "utility_type": "gas", + "usage": "400.00", + "charge": "9500.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 9, + "fields": { + "building_id": 103, + "date_from": "2015-01-01", + "date_to": "2015-12-31", + "utility_type": "oil", + "usage": "100.00", + "charge": "5000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 10, + "fields": { + "building_id": 103, + "date_from": "2016-01-01", + "date_to": "2016-12-31", + "utility_type": "oil", + "usage": "250.00", + "charge": "6000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 11, + "fields": { + "building_id": 103, + "date_from": "2016-01-01", + "date_to": "2016-12-31", + "utility_type": "water", + "usage": "200.00", + "charge": "2000.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 532, + "fields": { + "building_id": 289, + "date_from": "2017-02-22", + "date_to": "2017-03-23", + "utility_type": "electricity", + "usage": "25800.00", + "charge": "4509.82" + } + }, + { + "model": "financialInputs.bills", + "pk": 533, + "fields": { + "building_id": 289, + "date_from": "2016-12-21", + "date_to": "2017-02-22", + "utility_type": "electricity", + "usage": "61800.00", + "charge": "10273.47" + } + }, + { + "model": "financialInputs.bills", + "pk": 534, + "fields": { + "building_id": 289, + "date_from": "2016-11-18", + "date_to": "2016-12-21", + "utility_type": "electricity", + "usage": "33600.00", + "charge": "5594.74" + } + }, + { + "model": "financialInputs.bills", + "pk": 535, + "fields": { + "building_id": 289, + "date_from": "2016-10-20", + "date_to": "2016-11-18", + "utility_type": "electricity", + "usage": "31800.00", + "charge": "5780.51" + } + }, + { + "model": "financialInputs.bills", + "pk": 536, + "fields": { + "building_id": 289, + "date_from": "2016-09-20", + "date_to": "2016-10-20", + "utility_type": "electricity", + "usage": "37800.00", + "charge": "6412.98" + } + }, + { + "model": "financialInputs.bills", + "pk": 537, + "fields": { + "building_id": 289, + "date_from": "2016-08-19", + "date_to": "2016-09-20", + "utility_type": "electricity", + "usage": "52200.00", + "charge": "8708.14" + } + }, + { + "model": "financialInputs.bills", + "pk": 538, + "fields": { + "building_id": 289, + "date_from": "2016-07-21", + "date_to": "2016-08-19", + "utility_type": "electricity", + "usage": "52200.00", + "charge": "8845.57" + } + }, + { + "model": "financialInputs.bills", + "pk": 539, + "fields": { + "building_id": 289, + "date_from": "2016-06-21", + "date_to": "2016-07-21", + "utility_type": "electricity", + "usage": "49800.00", + "charge": "8599.52" + } + }, + { + "model": "financialInputs.bills", + "pk": 540, + "fields": { + "building_id": 289, + "date_from": "2016-05-20", + "date_to": "2016-06-21", + "utility_type": "electricity", + "usage": "48600.00", + "charge": "8688.43" + } + }, + { + "model": "financialInputs.bills", + "pk": 541, + "fields": { + "building_id": 289, + "date_from": "2016-04-21", + "date_to": "2016-05-20", + "utility_type": "electricity", + "usage": "34800.00", + "charge": "5446.82" + } + }, + { + "model": "financialInputs.bills", + "pk": 542, + "fields": { + "building_id": 289, + "date_from": "2016-03-23", + "date_to": "2016-04-21", + "utility_type": "electricity", + "usage": "31800.00", + "charge": "5424.90" + } + }, + { + "model": "financialInputs.bills", + "pk": 543, + "fields": { + "building_id": 289, + "date_from": "2016-02-23", + "date_to": "2016-03-23", + "utility_type": "electricity", + "usage": "31200.00", + "charge": "5404.15" + } + }, + { + "model": "financialInputs.bills", + "pk": 544, + "fields": { + "building_id": 289, + "date_from": "2016-01-22", + "date_to": "2016-02-23", + "utility_type": "electricity", + "usage": "33600.00", + "charge": "5376.68" + } + }, + { + "model": "financialInputs.bills", + "pk": 545, + "fields": { + "building_id": 289, + "date_from": "2015-12-22", + "date_to": "2016-01-22", + "utility_type": "electricity", + "usage": "36600.00", + "charge": "6431.27" + } + }, + { + "model": "financialInputs.bills", + "pk": 546, + "fields": { + "building_id": 289, + "date_from": "2015-11-19", + "date_to": "2015-12-22", + "utility_type": "electricity", + "usage": "36000.00", + "charge": "5829.68" + } + }, + { + "model": "financialInputs.bills", + "pk": 547, + "fields": { + "building_id": 289, + "date_from": "2015-10-21", + "date_to": "2015-11-19", + "utility_type": "electricity", + "usage": "36000.00", + "charge": "6245.07" + } + }, + { + "model": "financialInputs.bills", + "pk": 548, + "fields": { + "building_id": 289, + "date_from": "2015-09-21", + "date_to": "2015-10-21", + "utility_type": "electricity", + "usage": "36600.00", + "charge": "6303.98" + } + }, + { + "model": "financialInputs.bills", + "pk": 549, + "fields": { + "building_id": 289, + "date_from": "2015-08-20", + "date_to": "2015-09-21", + "utility_type": "electricity", + "usage": "54000.00", + "charge": "9611.84" + } + }, + { + "model": "financialInputs.bills", + "pk": 550, + "fields": { + "building_id": 289, + "date_from": "2015-07-22", + "date_to": "2015-08-20", + "utility_type": "electricity", + "usage": "51000.00", + "charge": "9320.90" + } + }, + { + "model": "financialInputs.bills", + "pk": 551, + "fields": { + "building_id": 289, + "date_from": "2015-06-22", + "date_to": "2015-07-22", + "utility_type": "electricity", + "usage": "52800.00", + "charge": "10168.65" + } + }, + { + "model": "financialInputs.bills", + "pk": 552, + "fields": { + "building_id": 289, + "date_from": "2015-05-21", + "date_to": "2015-06-22", + "utility_type": "electricity", + "usage": "49200.00", + "charge": "9277.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 553, + "fields": { + "building_id": 289, + "date_from": "2015-04-22", + "date_to": "2015-05-21", + "utility_type": "electricity", + "usage": "40200.00", + "charge": "7404.47" + } + }, + { + "model": "financialInputs.bills", + "pk": 554, + "fields": { + "building_id": 289, + "date_from": "2015-03-24", + "date_to": "2015-04-22", + "utility_type": "electricity", + "usage": "36000.00", + "charge": "5751.96" + } + }, + { + "model": "financialInputs.bills", + "pk": 555, + "fields": { + "building_id": 289, + "date_from": "2017-02-15", + "date_to": "2017-03-16", + "utility_type": "gas", + "usage": "929.00", + "charge": "1059.41" + } + }, + { + "model": "financialInputs.bills", + "pk": 556, + "fields": { + "building_id": 289, + "date_from": "2017-01-17", + "date_to": "2017-02-15", + "utility_type": "gas", + "usage": "1083.00", + "charge": "1238.53" + } + }, + { + "model": "financialInputs.bills", + "pk": 557, + "fields": { + "building_id": 289, + "date_from": "2016-12-15", + "date_to": "2017-01-17", + "utility_type": "gas", + "usage": "1076.00", + "charge": "1092.90" + } + }, + { + "model": "financialInputs.bills", + "pk": 558, + "fields": { + "building_id": 289, + "date_from": "2016-11-15", + "date_to": "2016-12-15", + "utility_type": "gas", + "usage": "678.00", + "charge": "602.64" + } + }, + { + "model": "financialInputs.bills", + "pk": 559, + "fields": { + "building_id": 289, + "date_from": "2016-10-17", + "date_to": "2016-11-15", + "utility_type": "gas", + "usage": "552.00", + "charge": "441.53" + } + }, + { + "model": "financialInputs.bills", + "pk": 560, + "fields": { + "building_id": 289, + "date_from": "2016-09-16", + "date_to": "2016-10-17", + "utility_type": "gas", + "usage": "232.00", + "charge": "179.35" + } + }, + { + "model": "financialInputs.bills", + "pk": 561, + "fields": { + "building_id": 289, + "date_from": "2016-08-17", + "date_to": "2016-09-16", + "utility_type": "gas", + "usage": "103.00", + "charge": "86.60" + } + }, + { + "model": "financialInputs.bills", + "pk": 562, + "fields": { + "building_id": 289, + "date_from": "2016-07-19", + "date_to": "2016-08-17", + "utility_type": "gas", + "usage": "113.00", + "charge": "101.15" + } + }, + { + "model": "financialInputs.bills", + "pk": 563, + "fields": { + "building_id": 289, + "date_from": "2016-06-17", + "date_to": "2016-07-19", + "utility_type": "gas", + "usage": "224.00", + "charge": "204.29" + } + }, + { + "model": "financialInputs.bills", + "pk": 564, + "fields": { + "building_id": 289, + "date_from": "2016-05-20", + "date_to": "2016-06-17", + "utility_type": "gas", + "usage": "244.00", + "charge": "220.37" + } + }, + { + "model": "financialInputs.bills", + "pk": 565, + "fields": { + "building_id": 289, + "date_from": "2016-04-18", + "date_to": "2016-05-20", + "utility_type": "gas", + "usage": "674.00", + "charge": "552.46" + } + }, + { + "model": "financialInputs.bills", + "pk": 566, + "fields": { + "building_id": 289, + "date_from": "2016-03-16", + "date_to": "2016-04-18", + "utility_type": "gas", + "usage": "986.00", + "charge": "804.96" + } + }, + { + "model": "financialInputs.bills", + "pk": 567, + "fields": { + "building_id": 289, + "date_from": "2016-02-16", + "date_to": "2016-03-16", + "utility_type": "gas", + "usage": "1017.00", + "charge": "898.21" + } + }, + { + "model": "financialInputs.bills", + "pk": 568, + "fields": { + "building_id": 289, + "date_from": "2016-01-15", + "date_to": "2016-02-16", + "utility_type": "gas", + "usage": "1422.00", + "charge": "1243.22" + } + }, + { + "model": "financialInputs.bills", + "pk": 569, + "fields": { + "building_id": 289, + "date_from": "2015-12-15", + "date_to": "2016-01-15", + "utility_type": "gas", + "usage": "1096.00", + "charge": "1042.92" + } + }, + { + "model": "financialInputs.bills", + "pk": 570, + "fields": { + "building_id": 289, + "date_from": "2015-11-13", + "date_to": "2015-12-15", + "utility_type": "gas", + "usage": "934.00", + "charge": "865.51" + } + }, + { + "model": "financialInputs.bills", + "pk": 571, + "fields": { + "building_id": 289, + "date_from": "2015-10-15", + "date_to": "2015-11-13", + "utility_type": "gas", + "usage": "563.00", + "charge": "487.05" + } + }, + { + "model": "financialInputs.bills", + "pk": 572, + "fields": { + "building_id": 289, + "date_from": "2015-09-16", + "date_to": "2015-10-15", + "utility_type": "gas", + "usage": "329.00", + "charge": "275.92" + } + }, + { + "model": "financialInputs.bills", + "pk": 701, + "fields": { + "building_id": 2003, + "date_from": "2017-02-22", + "date_to": "2017-03-23", + "utility_type": "electricity", + "usage": "25800.00", + "charge": "4509.82" + } + }, + { + "model": "financialInputs.bills", + "pk": 702, + "fields": { + "building_id": 2003, + "date_from": "2016-12-21", + "date_to": "2017-02-22", + "utility_type": "electricity", + "usage": "61800.00", + "charge": "10273.47" + } + }, + { + "model": "financialInputs.bills", + "pk": 703, + "fields": { + "building_id": 2003, + "date_from": "2016-11-18", + "date_to": "2016-12-21", + "utility_type": "electricity", + "usage": "33600.00", + "charge": "5594.74" + } + }, + { + "model": "financialInputs.bills", + "pk": 704, + "fields": { + "building_id": 2003, + "date_from": "2016-10-20", + "date_to": "2016-11-18", + "utility_type": "electricity", + "usage": "31800.00", + "charge": "5780.51" + } + }, + { + "model": "financialInputs.bills", + "pk": 705, + "fields": { + "building_id": 2003, + "date_from": "2016-09-20", + "date_to": "2016-10-20", + "utility_type": "electricity", + "usage": "37800.00", + "charge": "6412.98" + } + }, + { + "model": "financialInputs.bills", + "pk": 706, + "fields": { + "building_id": 2003, + "date_from": "2016-08-19", + "date_to": "2016-09-20", + "utility_type": "electricity", + "usage": "52200.00", + "charge": "8708.14" + } + }, + { + "model": "financialInputs.bills", + "pk": 707, + "fields": { + "building_id": 2003, + "date_from": "2016-07-21", + "date_to": "2016-08-19", + "utility_type": "electricity", + "usage": "52200.00", + "charge": "8845.57" + } + }, + { + "model": "financialInputs.bills", + "pk": 708, + "fields": { + "building_id": 2003, + "date_from": "2016-06-21", + "date_to": "2016-07-21", + "utility_type": "electricity", + "usage": "49800.00", + "charge": "8599.52" + } + }, + { + "model": "financialInputs.bills", + "pk": 709, + "fields": { + "building_id": 2003, + "date_from": "2016-05-20", + "date_to": "2016-06-21", + "utility_type": "electricity", + "usage": "48600.00", + "charge": "8688.43" + } + }, + { + "model": "financialInputs.bills", + "pk": 710, + "fields": { + "building_id": 2003, + "date_from": "2016-04-21", + "date_to": "2016-05-20", + "utility_type": "electricity", + "usage": "34800.00", + "charge": "5446.82" + } + }, + { + "model": "financialInputs.bills", + "pk": 711, + "fields": { + "building_id": 2003, + "date_from": "2016-03-23", + "date_to": "2016-04-21", + "utility_type": "electricity", + "usage": "31800.00", + "charge": "5424.90" + } + }, + { + "model": "financialInputs.bills", + "pk": 712, + "fields": { + "building_id": 2003, + "date_from": "2016-02-23", + "date_to": "2016-03-23", + "utility_type": "electricity", + "usage": "31200.00", + "charge": "5404.15" + } + }, + { + "model": "financialInputs.bills", + "pk": 713, + "fields": { + "building_id": 2003, + "date_from": "2016-01-22", + "date_to": "2016-02-23", + "utility_type": "electricity", + "usage": "33600.00", + "charge": "5376.68" + } + }, + { + "model": "financialInputs.bills", + "pk": 714, + "fields": { + "building_id": 2003, + "date_from": "2015-12-22", + "date_to": "2016-01-22", + "utility_type": "electricity", + "usage": "36600.00", + "charge": "6431.27" + } + }, + { + "model": "financialInputs.bills", + "pk": 715, + "fields": { + "building_id": 2003, + "date_from": "2015-11-19", + "date_to": "2015-12-22", + "utility_type": "electricity", + "usage": "36000.00", + "charge": "5829.68" + } + }, + { + "model": "financialInputs.bills", + "pk": 716, + "fields": { + "building_id": 2003, + "date_from": "2015-10-21", + "date_to": "2015-11-19", + "utility_type": "electricity", + "usage": "36000.00", + "charge": "6245.07" + } + }, + { + "model": "financialInputs.bills", + "pk": 717, + "fields": { + "building_id": 2003, + "date_from": "2015-09-21", + "date_to": "2015-10-21", + "utility_type": "electricity", + "usage": "36600.00", + "charge": "6303.98" + } + }, + { + "model": "financialInputs.bills", + "pk": 718, + "fields": { + "building_id": 2003, + "date_from": "2015-08-20", + "date_to": "2015-09-21", + "utility_type": "electricity", + "usage": "54000.00", + "charge": "9611.84" + } + }, + { + "model": "financialInputs.bills", + "pk": 719, + "fields": { + "building_id": 2003, + "date_from": "2015-07-22", + "date_to": "2015-08-20", + "utility_type": "electricity", + "usage": "51000.00", + "charge": "9320.90" + } + }, + { + "model": "financialInputs.bills", + "pk": 720, + "fields": { + "building_id": 2003, + "date_from": "2015-06-22", + "date_to": "2015-07-22", + "utility_type": "electricity", + "usage": "52800.00", + "charge": "10168.65" + } + }, + { + "model": "financialInputs.bills", + "pk": 721, + "fields": { + "building_id": 2003, + "date_from": "2015-05-21", + "date_to": "2015-06-22", + "utility_type": "electricity", + "usage": "49200.00", + "charge": "9277.00" + } + }, + { + "model": "financialInputs.bills", + "pk": 722, + "fields": { + "building_id": 2003, + "date_from": "2015-04-22", + "date_to": "2015-05-21", + "utility_type": "electricity", + "usage": "40200.00", + "charge": "7404.47" + } + }, + { + "model": "financialInputs.bills", + "pk": 723, + "fields": { + "building_id": 2003, + "date_from": "2015-03-24", + "date_to": "2015-04-22", + "utility_type": "electricity", + "usage": "36000.00", + "charge": "5751.96" + } + }, + { + "model": "financialInputs.bills", + "pk": 724, + "fields": { + "building_id": 2004, + "date_from": "2015-03-24", + "date_to": "2015-04-22", + "utility_type": "electricity", + "usage": "36000.00", + "charge": "5751.96" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/cash_balance_testdata.json b/blocnote/apps/budgetSimulator/fixtures/cash_balance_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..d13a860d824e9f1a7388a5cd38b188db208078fa --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/cash_balance_testdata.json @@ -0,0 +1,72 @@ +[ + { + "model": "financialInputs.cashbalance", + "pk": 1, + "fields": { + "building_id": 100, + "statement_date": "2016-12-1", + "is_from_balance_sheet": true, + "balance_amount": "20000.00" + } + }, + { + "model": "financialInputs.cashbalance", + "pk": 2, + "fields": { + "building_id": 101, + "statement_date": "2016-12-1", + "is_from_balance_sheet": true, + "balance_amount": "20000.00" + } + }, + { + "model": "financialInputs.cashbalance", + "pk": 3, + "fields": { + "building_id": 101, + "statement_date": "2015-12-1", + "is_from_balance_sheet": false, + "balance_amount": "30000.00" + } + }, + { + "model": "financialInputs.cashbalance", + "pk": 73, + "fields": { + "building_id": 289, + "statement_date": "2016-12-21", + "is_from_balance_sheet": true, + "balance_amount": "50000.00" + } + }, + { + "model": "financialInputs.cashbalance", + "pk": 75, + "fields": { + "building_id": 2002, + "statement_date": "2016-12-01", + "is_from_balance_sheet": true, + "balance_amount": "25000.00" + } + }, + { + "model": "financialInputs.cashbalance", + "pk": 76, + "fields": { + "building_id": 2003, + "statement_date": "2015-12-10", + "is_from_balance_sheet": true, + "balance_amount": "25000.00" + } + }, + { + "model": "financialInputs.cashbalance", + "pk": 77, + "fields": { + "building_id": 2004, + "statement_date": "2015-12-10", + "is_from_balance_sheet": true, + "balance_amount": "25000.00" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/customer_preference_testdata.json b/blocnote/apps/budgetSimulator/fixtures/customer_preference_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..5a09c6a8ff899077f6b1ebff225407d7ec61bcf6 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/customer_preference_testdata.json @@ -0,0 +1,52 @@ +[ + { + "model": "financialInputs.customerpreference", + "pk": 1, + "fields": { + "building_id": 100, + "downpayment": "100.00", + "expected_payback": "10", + "expected_net_noi_dscr": "1.15" + } + }, + { + "model": "financialInputs.customerpreference", + "pk": 5, + "fields": { + "building_id": 289, + "downpayment": "50000.00", + "expected_payback": "12", + "expected_net_noi_dscr": "1.15" + } + }, + { + "model": "financialInputs.customerpreference", + "pk": 12, + "fields": { + "building_id": 2002, + "downpayment": "0.00", + "expected_payback": "120", + "expected_net_noi_dscr": "1.15" + } + }, + { + "model": "financialInputs.customerpreference", + "pk": 13, + "fields": { + "building_id": 2003, + "downpayment": "0.00", + "expected_payback": "120", + "expected_net_noi_dscr": "1.15" + } + }, + { + "model": "financialInputs.customerpreference", + "pk": 14, + "fields": { + "building_id": 2004, + "downpayment": "0.00", + "expected_payback": "120", + "expected_net_noi_dscr": "1.15" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/default_loan.json b/blocnote/apps/budgetSimulator/fixtures/default_loan.json new file mode 100644 index 0000000000000000000000000000000000000000..fe300d0b933e2ad38770f1b6f3e7420fb46dad36 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/default_loan.json @@ -0,0 +1,68 @@ +[ + { + "model": "financialInputs.defaultloan", + "pk": 1, + "fields": { + "lender": 1, + "fund": 1, + "interest_rate": "7.000", + "duration": "84", + "max_loan_amount": "5000000.00" + } + }, + { + "model": "financialInputs.defaultloan", + "pk": 2, + "fields": { + "lender": 2, + "fund": 1, + "interest_rate": "8.000", + "duration": "96", + "max_loan_amount": "7000000.00" + } + }, + { + "model": "financialInputs.defaultloan", + "pk": 3, + "fields": { + "lender": 3, + "fund": 1, + "interest_rate": "10.000", + "duration": "72", + "max_loan_amount": "10000000.00" + } + }, + { + "model": "financialInputs.defaultloan", + "pk": 4, + "fields": { + "lender": 4, + "fund": 2, + "interest_rate": "4.500", + "duration": "120", + "max_loan_amount": "8000000.00" + } + }, + { + "model": "financialInputs.defaultloan", + "pk": 5, + "fields": { + "lender": 5, + "fund": 2, + "interest_rate": "4.500", + "duration": "120", + "max_loan_amount": "2000000.00" + } + }, + { + "model": "financialInputs.defaultloan", + "pk": 6, + "fields": { + "lender": 2, + "fund": 2, + "interest_rate": "7.000", + "duration": "84", + "max_loan_amount": "2000000.00" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/estimation_algorithm_testdata.json b/blocnote/apps/budgetSimulator/fixtures/estimation_algorithm_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..193900993355b521b147f48844789dbc2028b439 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/estimation_algorithm_testdata.json @@ -0,0 +1,34 @@ +[ + { + "model": "financialInputs.estimationalgorithm", + "pk": 6, + "fields": { + "algorithm": "Rough Estimation", + "building_id": 289 + } + }, + { + "model": "financialInputs.estimationalgorithm", + "pk": 15, + "fields": { + "algorithm": "Rough Estimation", + "building_id": 2002 + } + }, + { + "model": "financialInputs.estimationalgorithm", + "pk": 16, + "fields": { + "algorithm": "Rough Estimation", + "building_id": 2003 + } + }, + { + "model": "financialInputs.estimationalgorithm", + "pk": 17, + "fields": { + "algorithm": "Rough Estimation", + "building_id": 2004 + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/financing_overview_testdata.json b/blocnote/apps/budgetSimulator/fixtures/financing_overview_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..f8ed3269f174fe732d5bab9bae5b9187e9f97674 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/financing_overview_testdata.json @@ -0,0 +1,98 @@ +[ + { + "model": "financialInputs.financingoverview", + "pk": 1, + "fields": { + "building_id": 100, + "fund": 1, + "required_noi_dscr": "0.00", + "requrired_cash_dscr": "0.00", + "pro_forma_start_date": "2014-01-01", + "pro_forma_duration": "25", + "analysis_date": "2017-05-18", + "anticipated_construction_start_date": "2017-07-01", + "anticipated_commissioning_date": "2017-08-01", + "anticipated_construction_period": "100" + } + }, + { + "model": "financialInputs.financingoverview", + "pk": 94, + "fields": { + "building_id": 289, + "fund": 1, + "required_noi_dscr": "0.00", + "requrired_cash_dscr": "0.00", + "pro_forma_start_date": "2014-01-01", + "pro_forma_duration": "25", + "analysis_date": "2017-05-18", + "anticipated_construction_start_date": "2017-05-03", + "anticipated_commissioning_date": "2017-07-07", + "anticipated_construction_period": "100" + } + }, + { + "model": "financialInputs.financingoverview", + "pk": 95, + "fields": { + "building_id": 2001, + "fund": 1, + "required_noi_dscr": "0.00", + "requrired_cash_dscr": "0.00", + "pro_forma_start_date": "2014-01-01", + "pro_forma_duration": "25", + "analysis_date": "2017-06-05", + "anticipated_construction_start_date": "2017-07-01", + "anticipated_commissioning_date": "2017-08-01", + "anticipated_construction_period": "4" + } + }, + { + "model": "financialInputs.financingoverview", + "pk": 96, + "fields": { + "building_id": 2002, + "fund": 1, + "required_noi_dscr": "1.15", + "requrired_cash_dscr": "1.15", + "pro_forma_start_date": "2014-01-01", + "pro_forma_duration": "25", + "analysis_date": "2017-06-05", + "anticipated_construction_start_date": "2017-07-01", + "anticipated_commissioning_date": "2017-08-01", + "anticipated_construction_period": "4" + } + }, + { + "model": "financialInputs.financingoverview", + "pk": 97, + "fields": { + "building_id": 2003, + "fund": 1, + "required_noi_dscr": "1.15", + "requrired_cash_dscr": "1.15", + "pro_forma_start_date": "2014-01-01", + "pro_forma_duration": "25", + "analysis_date": "2017-06-05", + "anticipated_construction_start_date": "2017-07-01", + "anticipated_commissioning_date": "2017-08-01", + "anticipated_construction_period": "4" + } + }, + { + "model": "financialInputs.financingoverview", + "pk": 98, + "fields": { + "building_id": 2004, + "fund": 1, + "required_noi_dscr": "1.15", + "requrired_cash_dscr": "1.15", + "pro_forma_start_date": "2014-01-01", + "pro_forma_duration": "25", + "analysis_date": "2017-06-05", + "anticipated_construction_start_date": "2017-07-01", + "anticipated_commissioning_date": "2017-08-01", + "anticipated_construction_period": "4" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/funds.json b/blocnote/apps/budgetSimulator/fixtures/funds.json new file mode 100644 index 0000000000000000000000000000000000000000..e3d3dd2ae26df062bfa57e9cbdaa8a658fa415f9 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/funds.json @@ -0,0 +1,16 @@ +[ + { + "model": "financialInputs.fund", + "pk": 1, + "fields": { + "Name": "Small Commercial Fund" + } + }, + { + "model": "financialInputs.fund", + "pk": 2, + "fields": { + "Name": "Bronx Healthy Buildings Fund" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/growth_rate_testdata.json b/blocnote/apps/budgetSimulator/fixtures/growth_rate_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..894d2ac52f2a095f835a7dcaf0f04082bbd49f2f --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/growth_rate_testdata.json @@ -0,0 +1,42 @@ +[ + { + "model": "financialInputs.growthrate", + "pk": 1, + "fields": { + "building_id": 100, + "growth_rate": "1" + } + }, + { + "model": "financialInputs.growthrate", + "pk": 170, + "fields": { + "building_id": 289, + "growth_rate": "0.10" + } + }, + { + "model": "financialInputs.growthrate", + "pk": 175, + "fields": { + "building_id": 2002, + "growth_rate": "-2.00" + } + }, + { + "model": "financialInputs.growthrate", + "pk": 176, + "fields": { + "building_id": 2003, + "growth_rate": "-2.00" + } + }, + { + "model": "financialInputs.growthrate", + "pk": 177, + "fields": { + "building_id": 2004, + "growth_rate": "-2.00" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/income_statement_testdata.json b/blocnote/apps/budgetSimulator/fixtures/income_statement_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..815c307e50ed41c02de341842caf7ac718a00735 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/income_statement_testdata.json @@ -0,0 +1,182 @@ +[ + { + "model": "financialInputs.incomestatement", + "pk": 1, + "fields": { + "building_id": 100, + "year": "2014", + "revenue": "200000.00", + "utility_expense": "90000.00", + "other_utility_expense": "80000.00", + "non_utility_operating_expense": "50000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 2, + "fields": { + "building_id": 100, + "year": "2015", + "revenue": "205000.00", + "utility_expense": "92000.00", + "other_utility_expense": "83000.00", + "non_utility_operating_expense": "55000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 3, + "fields": { + "building_id": 100, + "year": "2016", + "revenue": "210000.00", + "utility_expense": "94000.00", + "other_utility_expense": "86000.00", + "non_utility_operating_expense": "57000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 587, + "fields": { + "building_id": 289, + "year": "2014", + "revenue": "200000.00", + "utility_expense": "94000.00", + "other_utility_expense": "-14159.29", + "non_utility_operating_expense": "50000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 588, + "fields": { + "building_id": 289, + "year": "2015", + "revenue": "210000.00", + "utility_expense": "95000.00", + "other_utility_expense": "-12857.26", + "non_utility_operating_expense": "55000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 589, + "fields": { + "building_id": 289, + "year": "2016", + "revenue": "213000.00", + "utility_expense": "96000.00", + "other_utility_expense": "-14637.50", + "non_utility_operating_expense": "60000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 590, + "fields": { + "building_id": 2002, + "year": "2014", + "revenue": "200000.00", + "utility_expense": "100000.00", + "other_utility_expense": "93700.00", + "non_utility_operating_expense": "50000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 591, + "fields": { + "building_id": 2002, + "year": "2015", + "revenue": "201000.00", + "utility_expense": "105000.00", + "other_utility_expense": "98700.00", + "non_utility_operating_expense": "58000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 592, + "fields": { + "building_id": 2002, + "year": "2016", + "revenue": "202000.00", + "utility_expense": "110000.00", + "other_utility_expense": "103700.00", + "non_utility_operating_expense": "62000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 593, + "fields": { + "building_id": 2003, + "year": "2014", + "revenue": "210000.00", + "utility_expense": "90000.00", + "other_utility_expense": "-127473.13", + "non_utility_operating_expense": "40000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 594, + "fields": { + "building_id": 2003, + "year": "2015", + "revenue": "214000.00", + "utility_expense": "99000.00", + "other_utility_expense": "-119108.87", + "non_utility_operating_expense": "44000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 595, + "fields": { + "building_id": 2003, + "year": "2016", + "revenue": "219000.00", + "utility_expense": "108000.00", + "other_utility_expense": "-110839.67", + "non_utility_operating_expense": "50000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 596, + "fields": { + "building_id": 2004, + "year": "2014", + "revenue": "210000.00", + "utility_expense": "90000.00", + "other_utility_expense": "-127473.13", + "non_utility_operating_expense": "40000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 597, + "fields": { + "building_id": 2004, + "year": "2015", + "revenue": "214000.00", + "utility_expense": "99000.00", + "other_utility_expense": "-119108.87", + "non_utility_operating_expense": "44000.00" + } + }, + { + "model": "financialInputs.incomestatement", + "pk": 598, + "fields": { + "building_id": 2004, + "year": "2016", + "revenue": "219000.00", + "utility_expense": "108000.00", + "other_utility_expense": "-110839.67", + "non_utility_operating_expense": "50000.00" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/lender.json b/blocnote/apps/budgetSimulator/fixtures/lender.json new file mode 100644 index 0000000000000000000000000000000000000000..07d8973f21b7085363b137a1ef65fab6c69aaa21 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/lender.json @@ -0,0 +1,37 @@ +[ + { + "model": "financialInputs.lender", + "pk": 1, + "fields": { + "name": "NYCEEC" + } + }, + { + "model": "financialInputs.lender", + "pk": 2, + "fields": { + "name": "Green Bank" + } + }, + { + "model": "financialInputs.lender", + "pk": 3, + "fields": { + "name": "Deutsche Bank" + } + }, + { + "model": "financialInputs.lender", + "pk": 4, + "fields": { + "name": "Goldman Sachs" + } + }, + { + "model": "financialInputs.lender", + "pk": 5, + "fields": { + "name": "HPN" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/liabilities_testdata.json b/blocnote/apps/budgetSimulator/fixtures/liabilities_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..30624654788ae598826411bca13f5a20d87a6114 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/liabilities_testdata.json @@ -0,0 +1,46 @@ +[ + { + "model": "financialInputs.liabilities", + "pk": 1, + "fields": { + "building_id": 100, + "input_date": "2016-01-01", + "lender": "Adarsh", + "monthly_service": "300.00", + "remaining_term": "120" + } + }, + { + "model": "financialInputs.liabilities", + "pk": 2, + "fields": { + "building_id": 101, + "input_date": "2016-01-01", + "lender": "Adarsh", + "monthly_service": "300.00", + "remaining_term": "120" + } + }, + { + "model": "financialInputs.liabilities", + "pk": 3, + "fields": { + "building_id": 101, + "input_date": "2015-01-01", + "lender": "Tom", + "monthly_service": "250.00", + "remaining_term": "100" + } + }, + { + "model": "financialInputs.liabilities", + "pk": 54, + "fields": { + "building_id": 289, + "input_date": "2017-05-11", + "lender": "Adarsh Murthy", + "monthly_service": "120.00", + "remaining_term": "100" + } + } +] diff --git a/blocnote/apps/budgetSimulator/fixtures/loan_options_testdata.json b/blocnote/apps/budgetSimulator/fixtures/loan_options_testdata.json new file mode 100644 index 0000000000000000000000000000000000000000..8d44d2adcf98b6f99f0e56f73d2c6c07069e1d52 --- /dev/null +++ b/blocnote/apps/budgetSimulator/fixtures/loan_options_testdata.json @@ -0,0 +1,57 @@ +[ + { + "model": "financialInputs.loanoptions", + "pk": 1, + "fields": { + "building_id": 100, + "lender": 2, + "interest_rate": "8.000", + "duration": "100", + "max_loan_amount": "1000000.00" + } + }, + { + "model": "financialInputs.loanoptions", + "pk": 49, + "fields": { + "building_id": 289, + "lender": 2, + "interest_rate": "8.000", + "duration": "120", + "max_loan_amount": "7000000.00" + } + }, + { + "model": "financialInputs.loanoptions", + "pk": 50, + "fields": { + "building_id": 2002, + "lender": 1, + "interest_rate": "7.000", + "duration": "84", + "max_loan_amount": "5000000.00" + } + }, + { + "model": "financialInputs.loanoptions", + "pk": 51, + "fields": { + "building_id": 2003, + "lender": 1, + "interest_rate": "7.000", + "duration": "84", + "max_loan_amount": "5000000.00" + } + }, + { + "model": "financialInputs.loanoptions", + "pk": 54, + "fields": { + "building_id": 2004, + "lender": 1, + "interest_rate": "7.000", + "duration": "84", + "max_loan_amount": "5000000.00" + } + } +] diff --git a/blocnote/apps/budgetSimulator/migrations/__init__.py b/blocnote/apps/budgetSimulator/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/blocnote/apps/budgetSimulator/models.py b/blocnote/apps/budgetSimulator/models.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/blocnote/apps/budgetSimulator/static/budgetSimulator/scripts/app.js b/blocnote/apps/budgetSimulator/static/budgetSimulator/scripts/app.js new file mode 100644 index 0000000000000000000000000000000000000000..dc3fc764f44bc4ccc92aebb5edff04ba413325a5 --- /dev/null +++ b/blocnote/apps/budgetSimulator/static/budgetSimulator/scripts/app.js @@ -0,0 +1,122 @@ +function generateGraph(budgets, savingPotentialList) { + const config = { + type: 'line', + data: { + labels: savingPotentialList, + datasets: [{ + backgroundColor: 'transparent', + borderColor: '#F78511', + pointBackgroundColor: '#000000', + type: 'line', + label: 'Budget with Loan only', + data: budgets[0], + }, { + backgroundColor: 'transparent', + borderColor: '#66B2FF', + pointBackgroundColor: '#000000', + type: 'line', + label: 'Budget with Loan first', + data: budgets[1], + }, { + backgroundColor: 'transparent', + borderColor: '#000000', + pointBackgroundColor: '#000000', + type: 'line', + label: 'Budget with SF first', + data: budgets[2], + }, { + backgroundColor: 'transparent', + borderColor: '#99FF99', + pointBackgroundColor: '#000000', + type: 'line', + label: 'Budget with SF maximum', + data: budgets[3], + }], + }, + options: { + scales: { + yAxes: [{ + scaleLabel: { + display: true, + labelString: 'Budget Value in $', + }, + }], + xAxes: [{ + scaleLabel: { + display: true, + labelString: 'Savings', + }, + }], + }, + }, + }; + const ctx = document.getElementById('budget_graph').getContext('2d'); + new Chart(ctx, config); +} + +/** + * Take all the tables data from the backend and fill in the cells. + * + * @param {any} tables : List of all tables. Each table has list of rows. + * @param {any} savingsPotentialList : List of table headings. + */ +function fillTables(tables, savingsPotentialList) { + // Go over the keys in the tables dictionary. + Object.keys(tables).forEach((tableName) => { + // For each table, get its head and body. + const head = document.querySelector(`#${tableName} thead`); + const body = document.querySelector(`#${tableName} tbody`); + + // The following lines get the heading row to insert the headings received from backend. + const headingRowCount = head.rows.length; + const headingRow = head.insertRow(headingRowCount); + let cell = headingRow.insertCell(0); + cell.innerHTML = 'Savings'; + const savingsLength = savingsPotentialList.length; + for (let cellIndex = 1; cellIndex <= savingsLength; cellIndex += 1) { + cell = headingRow.insertCell(cellIndex); + cell.innerHTML = ` ${Number(savingsPotentialList[cellIndex - 1]) * 100} %`; + } + + // The following fills in the row data for each table. + const table = tables[tableName]; + table.forEach((rowData) => { + const rowCount = body.rows.length; + const row = body.insertRow(rowCount); + rowData.forEach((cellData, index) => { + cell = row.insertCell(index); + cell.innerHTML = cellData; + }); + }); + }); +} + +/** + * Make a GET request to the backend to get all the data needed to generate the graph and tables. + * + */ +function getBudgetData() { + request(`budget-data`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + }).then((res) => { + if (!res.err) { + const tables = res.payload.instance.tables; + const savingsPotentialList = res.payload.instance.saving_potential_list; + const budgets = Object.keys(tables).map(tableName => tables[tableName][0].slice(1,)); + generateGraph(budgets, savingsPotentialList); + fillTables(tables, savingsPotentialList); + } else { + res.err.responseBody.then((error) => { + document.querySelector('#budget_error_msg').innerHTML = ` + ${error.error} + `; + }); + } + }); +} + +getBudgetData(); diff --git a/blocnote/apps/budgetSimulator/templates/budgetSimulator/index.html b/blocnote/apps/budgetSimulator/templates/budgetSimulator/index.html new file mode 100644 index 0000000000000000000000000000000000000000..c20b9c867fc885a1b761e36cd83332a4da7727cc --- /dev/null +++ b/blocnote/apps/budgetSimulator/templates/budgetSimulator/index.html @@ -0,0 +1,86 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% block content %} +
+

Budget Simulator

+
+
+
+ +
+
+ +
+
+
+
+
+ Budget with Loan only + + + + + +
+
+
+
+
+ Budget with Loan first + + + + + +
+
+
+
+
+ Budget with SF first + + + + + +
+
+
+
+
+ Budget with SF maximum + + + + + +
+
+
+
+
+
+Go Back +{% endblock %} +{% block scripts %} + + + + +{% endblock %} diff --git a/blocnote/apps/budgetSimulator/tests.py b/blocnote/apps/budgetSimulator/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..73493e431638a2365e45fb4a4418bc977d46283b --- /dev/null +++ b/blocnote/apps/budgetSimulator/tests.py @@ -0,0 +1,558 @@ +"""Test all the function and views in budget endpoint.""" +from datetime import date +from django.test import TestCase + +from bpfin.back_end_call.back_end_budget import budget_simulation + +from .views import get_analysis_commissioning_dates, get_customer_preference, get_growth_rate, get_liability +from .views import get_cash_balance, get_loan_input, get_raw_income_statement, get_annual_bills, get_raw_bill + + +class GetAnalysisCommissioningDatesTest(TestCase): + """Test get_analysis_commissioning_dates function.""" + + fixtures = ['funds.json', 'financing_overview_testdata.json'] + + def test_analysis_commissioning_date_1(self): + """Fetch analysis date and commissioning date for a building with no data in database. + + Building with id 233 does not have financing overview records stored. This should raise an attribute error. + """ + self.assertRaises(AttributeError, get_analysis_commissioning_dates, 233) + + def test_analysis_commissioning_date_2(self): + """Fetch analysis date and commissioning date for a building with data.""" + analysis_date, commissioning_date = get_analysis_commissioning_dates(100) + expected_analysis_date = { + 'proforma_start': date(2014, 1, 1), + 'proforma_duration': 25, + } + expected_commissioning_date = date(2017, 8, 1) + self.assertEqual(analysis_date, expected_analysis_date) + self.assertEqual(commissioning_date, expected_commissioning_date) + + +class GetCustomerPreferenceTest(TestCase): + """Test get_customer_preference function.""" + + fixtures = ['customer_preference_testdata.json'] + + def test_customer_preference_1(self): + """Fetch customer preference for building with no data. + + No customer preference record exists for building id 233. AttributeError should be raised. + """ + self.assertRaises(AttributeError, get_customer_preference, 233) + + def test_customer_preference_2(self): + """Create customer preference record and test get_customer_preference function.""" + customer_preference = get_customer_preference(100) + expected_customer_preference = { + 'downpayment_max': 100.0, + 'expected_payback': 10, + 'cust_saving_dscr': 1.15, + } + self.assertEqual(customer_preference, expected_customer_preference) + + +class GetGrowthRateTest(TestCase): + """Test get_growth_rate function.""" + + fixtures = ['growth_rate_testdata.json'] + + def test_growth_rate_1(self): + """Fetch growth rate for building with no data in database. + + No growth is stored for building with id 233. This should raise an AttributeError. + """ + self.assertRaises(AttributeError, get_growth_rate, 233) + + def test_growth_rate_2(self): + """Create growth rate and test get_growth_rate function.""" + growth_rate = get_growth_rate(100) + expected_growth_rate = 1 + self.assertEqual(growth_rate, expected_growth_rate) + + +class GetLiabilitiesTest(TestCase): + """Test get_liability function.""" + + fixtures = ['liabilities_testdata.json'] + + def test_liabilities_1(self): + """Test get_liability for building with with no data. + + No liabilities records exists for this building hence empty dictionary should be returned. + """ + liabilities = get_liability(233) + self.assertEqual(liabilities, {}) + + def test_liabilities_2(self): + """Test get_liability when 1 record exists. + + Create a liability record for this building id test the reult. + """ + liability = get_liability(100) + expected_liability = { + 'debt1': (300, "Adarsh", 120, date(2016, 1, 1)) + } + self.assertEqual(liability, expected_liability) + + def test_liability_3(self): + """Test get_liability when 2 records exist. + + Create 2 liability records for building 233 and validate the output. + """ + liability = get_liability(101) + expected_liability = { + 'debt1': (300, "Adarsh", 120, date(2016, 1, 1)), + 'debt2': (250, "Tom", 100, date(2015, 1, 1)) + } + self.assertEqual(liability, expected_liability) + + +class GetCashBalanceTest(TestCase): + """Test get_cash_balance function.""" + + fixtures = ['cash_balance_testdata.json'] + + def test_cash_balance_1(self): + """Test get_cash_balance for building with no data. + + No cash balance record exist for this building so it should raise an AttributeError. + """ + self.assertRaises(AttributeError, get_cash_balance, 233) + + def test_cash_balance_2(self): + """Test get_cash_balance with 1 record. + + Create a cash balance record for building 233 and validate the result. + """ + cash_balance = get_cash_balance(100) + expected_cash_balance = { + date(2016, 12, 1): (20000, True), + } + self.assertEqual(cash_balance, expected_cash_balance) + + def test_cash_balance_3(self): + """Test get_cash_balance with 2 records. + + Create 2 cash balance records for building 233 and validate the result. + """ + cash_balance = get_cash_balance(101) + expected_cash_balance = { + date(2016, 12, 1): (20000, True), + date(2015, 12, 1): (30000, False), + } + self.assertEqual(cash_balance, expected_cash_balance) + + +class GetLoanInputTest(TestCase): + """Test get_loan_input function.""" + + fixtures = ['lender.json', 'loan_options_testdata.json'] + + def test_loan_input_1(self): + """Test get_cash_balance for building with no data in database. + + No loan options record exist for this building hence it should raise AttributeError. + """ + self.assertRaises(AttributeError, get_loan_input, 233) + + def test_loan_input_2(self): + """Test get_cash_balance with one record. + + Create a lender record. Create a loan options record and validate get_loan_input. + """ + loan_options = get_loan_input(100) + expected_loan_options = [ + { + 'institute': "Green Bank", + 'max_amount': 1000000, + 'interest': 0.08, + 'duration': 100, + } + ] + self.assertEqual(loan_options, expected_loan_options) + + +class GetRawIncomeStatementTest(TestCase): + """Test get_raw_income_statement function.""" + + fixtures = ['income_statement_testdata.json'] + + def test_income_statement_1(self): + """Test get_raw_income_statement for building with no data in database. + + No income statement exists for building 233. This should raise AttributeError. + """ + self.assertRaises(AttributeError, get_raw_income_statement, 233) + + def test_income_statement_2(self): + """Test get_raw_income_statement for a set of good values. + + Create income statement objects for the 3 years and test the function. + """ + raw_income_statement_input = get_raw_income_statement(100) + expected_raw_income_statement = { + 2014: { + 'revenue': 200000, + 'utility_expense': 90000, + 'non_utility_expense': 50000, + }, + 2015: { + 'revenue': 205000, + 'utility_expense': 92000, + 'non_utility_expense': 55000, + }, + 2016: { + 'revenue': 210000, + 'utility_expense': 94000, + 'non_utility_expense': 57000, + }, + } + self.assertEqual(raw_income_statement_input, expected_raw_income_statement) + + +class GetAnnualBillsTest(TestCase): + """Test get_annual_bills function.""" + + fixtures = ['bills_overview_testdata'] + + def test_annual_bills_1(self): + """Test get_annual_bills for building with no records in database. + + No records exist for this building hence this should return an empty dictionary. + """ + annual_bills = get_annual_bills(233) + expected_annual_bills = { + 'electricity': None, + 'gas': None, + 'oil': None, + 'water': None, + } + self.assertEqual(annual_bills, expected_annual_bills) + + def test_annual_bills_2(self): + """Test get_annual_bills for electricity records for a year. + + Create a bills overview record with only electricity data and test the function. + """ + annual_bills = get_annual_bills(100) + expected_annual_bills = { + 'electricity': { + 2014: 10000, + }, + 'gas': None, + 'oil': None, + 'water': None, + } + self.assertEqual(annual_bills, expected_annual_bills) + + def test_annual_bills_3(self): + """Test get_annual_bills with electricity values for 3 years. + + Create a bills overview records with only electricity data for 3 different years and test the function. + """ + annual_bills = get_annual_bills(101) + expected_annual_bills = { + 'electricity': { + 2014: 10000, + 2015: 12000, + 2016: 15000, + }, + 'gas': None, + 'oil': None, + 'water': None, + } + self.assertEqual(annual_bills, expected_annual_bills) + + def test_annual_bills_4(self): + """Test get_annual_bills for electricity and gas values for 1 year each. + + Create a bills overview record with only electricity and gas data and test the function. + """ + annual_bills = get_annual_bills(102) + expected_annual_bills = { + 'electricity': { + 2014: 10000, + }, + 'gas': { + 2014: 5000, + }, + 'oil': None, + 'water': None, + } + self.assertEqual(annual_bills, expected_annual_bills) + + def test_annual_bills_5(self): + """Test get_annual_bills for electricity, gas and water records for 1 year. + + Create a bills overview record with only electricity, gas and water data and test the function. + """ + annual_bills = get_annual_bills(103) + expected_annual_bills = { + 'electricity': { + 2014: 10000, + }, + 'gas': { + 2014: 5000, + }, + 'water': { + 2014: 17000, + }, + 'oil': None, + } + self.assertEqual(annual_bills, expected_annual_bills) + + def test_annual_bills_6(self): + """Test get_annual_bills with electricity 3 years, gas 2 and water 1 year. + + Create a bills overview record and test the function. + """ + annual_bills = get_annual_bills(104) + expected_annual_bills = { + 'electricity': { + 2014: 10000, + 2015: 12000, + 2016: 14000, + }, + 'gas': { + 2015: 5000, + 2016: 7000, + }, + 'water': { + 2016: 17000, + }, + 'oil': None, + } + self.assertEqual(annual_bills, expected_annual_bills) + + +class GetRawBillsTest(TestCase): + """Test get_raw_bill function.""" + + fixtures = ['bills_testdata.json'] + + def test_raw_bills_1(self): + """Test the function for building with no records. + + No bills exist for this building so it should return empty dictionary. + """ + raw_bills = get_raw_bill(233) + self.assertEqual(raw_bills, {}) + + def test_raw_bills_2(self): + """Test function with electricity bill. + + Create Bills record with electricity bill and validate the function. + """ + raw_bills = get_raw_bill(100) + expected_raw_bills = { + 'electricity': { + 'utility_type': 'electricity', + 'date_from': [date(2015, 1, 1)], + 'date_to': [date(2015, 12, 31)], + 'charge': [5000], + 'usage': [100], + }, + } + self.assertEqual(raw_bills, expected_raw_bills) + + def test_raw_bills_3(self): + """Test function with electricity and gas bill. + + Create Bills record with electricity and gas bills and validate the function. + """ + raw_bills = get_raw_bill(102) + expected_raw_bills = { + 'electricity': { + 'utility_type': 'electricity', + 'date_from': [date(2015, 1, 1)], + 'date_to': [date(2015, 12, 31)], + 'charge': [5000], + 'usage': [100], + }, + 'gas': { + 'utility_type': 'gas', + 'date_from': [date(2014, 1, 1)], + 'date_to': [date(2014, 12, 31)], + 'charge': [7000], + 'usage': [200], + } + } + self.assertEqual(raw_bills, expected_raw_bills) + + def test_raw_bills_4(self): + """Test function for all 4 utilities. + + Create Bills record with all bills and validate the function. + """ + raw_bills = get_raw_bill(103) + expected_raw_bills = { + 'electricity': { + 'utility_type': 'electricity', + 'date_from': [date(2014, 1, 1), date(2015, 1, 1), date(2016, 1, 1)], + 'date_to': [date(2014, 12, 31), date(2015, 12, 31), date(2016, 12, 31)], + 'charge': [5000, 10000, 13000], + 'usage': [100, 200, 300], + }, + 'gas': { + 'utility_type': 'gas', + 'date_from': [date(2015, 1, 1), date(2016, 1, 1)], + 'date_to': [date(2015, 12, 31), date(2016, 12, 31)], + 'charge': [7000, 9500], + 'usage': [200, 400], + }, + 'oil': { + 'utility_type': 'oil', + 'date_from': [date(2015, 1, 1), date(2016, 1, 1)], + 'date_to': [date(2015, 12, 31), date(2016, 12, 31)], + 'charge': [5000, 6000], + 'usage': [100, 250], + }, + 'water': { + 'utility_type': 'water', + 'date_from': [date(2016, 1, 1)], + 'date_to': [date(2016, 12, 31)], + 'charge': [2000], + 'usage': [200], + }, + } + self.assertEqual(raw_bills, expected_raw_bills) + + +class BudgetSimulatorIndexTest(TestCase): + """Test the index view of budget endpoint.""" + + def test_index_test_1(self): + """Test the get route which simply renders an html. + + Make sure the status is 200, and building_id is in the context of the response. + """ + response = self.client.get('/buildings/233/budget/') + self.assertEqual(response.status_code, 200) + self.assertTrue('building_id' in response.context) + self.assertEqual(response.context['building_id'], '233') + + +class BudgetSimulatorBudgetView(TestCase): + """Test the budget view of the budget endpoint.""" + + fixtures = [ + 'funds.json', + 'financing_overview_testdata.json', + 'bills_testdata.json', + 'estimation_algorithm_testdata.json', + 'bills_overview_testdata.json', + 'customer_preference_testdata', + 'liabilities_testdata.json', + 'cash_balance_testdata.json', + 'income_statement_testdata.json', + 'default_loan.json', + 'loan_options_testdata.json', + 'growth_rate_testdata.json', + 'lender.json', + ] + + def test_budget_view_1(self): + """Test budget view for building with id 2000 which has no inputs stored. + + building with id 2000 has no inputs stored. This should return a status code of 400. + """ + response = self.client.get('/buildings/2000/budget/budget-data/') + self.assertEqual(response.status_code, 400) + + def test_budget_view_2(self): + """Test budget view for building with id 289 which has all the right inputs stored. + + Building with id 289 has all inputs store. This should return a status 200 with all the right outputs + for budget simulation. + """ + response = self.client.get('/buildings/289/budget/budget-data/') + response_content = response.json() + req_dscr = { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10, + } + analysis_date, commissioning_date = get_analysis_commissioning_dates(289) + result = budget_simulation( + analysis_date, + commissioning_date, + get_customer_preference(289), + req_dscr, + get_raw_bill(289), + get_annual_bills(289), + get_raw_income_statement(289), + get_growth_rate(289), + get_liability(289), + get_cash_balance(289), + get_loan_input(289) + ) + self.assertEqual(response.status_code, 200) + self.assertTrue('instance' in response_content) + self.assertTrue('tables' in response_content['instance']) + self.assertTrue('saving_potential_list' in response_content['instance']) + self.assertEqual(len(result), 4) + + def test_budget_view_3(self): + """Test budget view for building with id 2001 with only the proforma inputs stored. + + Building with id 2001 has only the proforma inputs stored. This should return a status code of 200. + """ + response = self.client.get('/buildings/2001/budget/budget-data/') + self.assertEqual(response.status_code, 400) + + def test_budget_view_4(self): + """Test budget view for building with id 2002 with all inputs but water utility empty stored. + + Building with id 2002 has everything stored. The bills overview has water as empty. This should return + a status code of 200. + """ + response = self.client.get('/buildings/2002/budget/budget-data/') + response_content = response.json() + self.assertEqual(response.status_code, 200) + self.assertTrue('instance' in response_content) + self.assertTrue('tables' in response_content['instance']) + self.assertTrue('saving_potential_list' in response_content['instance']) + + def test_budget_view_5(self): + """Test budget for building with id 2003 all inputs but liability stored. + + Building with id 2003 has all inputs stored except Liabilities. This should still return a success status with + status code 200. + """ + response = self.client.get('/buildings/2003/budget/budget-data/') + response_content = response.json() + req_dscr = { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10, + } + analysis_date, commissioning_date = get_analysis_commissioning_dates(2003) + result = budget_simulation( + analysis_date, + commissioning_date, + get_customer_preference(2003), + req_dscr, + get_raw_bill(2003), + get_annual_bills(2003), + get_raw_income_statement(2003), + get_growth_rate(2003), + get_liability(2003), + get_cash_balance(2003), + get_loan_input(2003) + ) + self.assertEqual(response.status_code, 200) + self.assertTrue('instance' in response_content) + self.assertTrue('tables' in response_content['instance']) + self.assertTrue('saving_potential_list' in response_content['instance']) + self.assertEqual(len(result), 4) + + def test_budget_view_6(self): + """"Test budget view when electricity bill does not have 12 months data.""" + response = self.client.get('/buildings/2004/budget/budget-data/') + response_content = response.json() + self.assertEqual(response.status_code, 400) + self.assertTrue('error' in response_content) diff --git a/blocnote/apps/budgetSimulator/urls.py b/blocnote/apps/budgetSimulator/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..069f62a876380df28e02af104d6d3ad82e6a9e66 --- /dev/null +++ b/blocnote/apps/budgetSimulator/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import url + +from . import views + +app_name = 'budget_simulator' +urlpatterns = [ + url(r'^$', views.Index.as_view(), name='index'), + url(r'^budget-data/$', views.Budget.as_view(), name='budget'), +] + diff --git a/blocnote/apps/budgetSimulator/views.py b/blocnote/apps/budgetSimulator/views.py new file mode 100644 index 0000000000000000000000000000000000000000..8d1c432d4c9b1a348a7183a705653616140e71b7 --- /dev/null +++ b/blocnote/apps/budgetSimulator/views.py @@ -0,0 +1,304 @@ +"""Define the endpoints for budget simulator.""" +from django.shortcuts import render +from django.views import View +from django.http import JsonResponse + +from bpfin.back_end_call.back_end_budget import budget_simulation + +from blocnote.apps.financialInputs.models import FinancingOverview, Bills, BillsOverview, IncomeStatement, GrowthRate +from blocnote.apps.financialInputs.models import Liabilities, CashBalance, CustomerPreference, LoanOptions + + +def get_analysis_commissioning_dates(building_id): + """Fetch the proforma start date, proforma duration and commissioning date.""" + financing_overview_objects = FinancingOverview.objects.filter(building_id=building_id) + if financing_overview_objects: + analysis_date = { + 'proforma_start': financing_overview_objects[0].pro_forma_start_date, + 'proforma_duration': int(financing_overview_objects[0].pro_forma_duration), + } + return analysis_date, financing_overview_objects[0].anticipated_commissioning_date + else: + raise AttributeError("Could not find commissioning date for given building id.") + + +def get_customer_preference(building_id): + """Fetch the customer preference from the database for this building id.""" + customer_preference_objs = CustomerPreference.objects.filter(building_id=building_id) + if customer_preference_objs: + customer_preference_obj = customer_preference_objs[0] + customer_preference = { + 'downpayment_max': float(customer_preference_obj.downpayment), + 'expected_payback': int(customer_preference_obj.expected_payback), + 'cust_saving_dscr': float(customer_preference_obj.expected_net_noi_dscr) + } + return customer_preference + else: + raise AttributeError("Could not find customer preference data for this building id.") + + +def get_raw_bill(building_id): + """Fetch the utility bills for a given building id.""" + utilities = [ + 'electricity', + 'gas', + 'oil', + 'water', + ] + raw_bill_table = {} + for utility_type in utilities: + bills_object = Bills.objects.filter( + building_id=building_id, + utility_type=utility_type, + ) + + if bills_object: + raw_bill = {} + raw_bill['utility_type'] = utility_type + raw_bill['date_from'] = [] + raw_bill['date_to'] = [] + raw_bill['charge'] = [] + raw_bill['usage'] = [] + + for bill in bills_object: + raw_bill['date_from'].append(bill.date_from) + raw_bill['date_to'].append(bill.date_to) + raw_bill['usage'].append(float(bill.usage)) + raw_bill['charge'].append(float(bill.charge)) + raw_bill_table[utility_type] = raw_bill + return raw_bill_table + + +def get_annual_bills(building_id): + """Fetch manually input annual values from the database.""" + annual_bills = {} + utilities = ['electricity', 'gas', 'oil', 'water'] + bills_overview_objs = BillsOverview.objects.filter(building_id=building_id) + for obj in bills_overview_objs: + for utility in utilities: + if obj.__dict__[utility+'_is_user_input']: + if utility not in annual_bills: + annual_bills[utility] = {} + value = float(obj.__dict__[utility]) if obj.__dict__[utility] else None + annual_bills[utility][obj.__dict__['year']] = value + for utility in utilities: + if utility not in annual_bills: + annual_bills[utility] = None + return annual_bills + + +def get_raw_income_statement(building_id): + """Fetch income statement data from database. + + Args: + building_id: id of the building. + + Returns: + raw_income_statement: Dictionary with key as year and value a dictionary with revenue, utility expense and + non-utility expense data. + """ + income_statement_objs = IncomeStatement.objects.filter(building_id=building_id) + raw_income_statment = {} + if income_statement_objs: + for obj in income_statement_objs: + record = {} + record['revenue'] = float(obj.revenue) + record['utility_expense'] = float(obj.utility_expense) + record['non_utility_expense'] = float(obj.non_utility_operating_expense) + raw_income_statment[int(obj.year)] = record + return raw_income_statment + else: + raise AttributeError("Could not find income statement values for this building id.") + + +def get_growth_rate(building_id): + """Fetch growth rate from the database. + + Args: + building_id: id of the building. + + Returns: + growth_rate: Growth rate stored for this building. + """ + growth_rate_obj = GrowthRate.objects.filter(building_id=building_id) + if growth_rate_obj: + return float(growth_rate_obj[0].growth_rate) + else: + raise AttributeError("Could not find growth rate for this building id.") + + +def get_liability(building_id): + """Obtain liability information. + + Fetch the records containing the liability information from the database. + + Args: + building_id: id of the building. + analysis_date: Dictionary containing: + proforma_start with value Start of projection. + proforma_duration with value Total duration of projection. + + Returns: + liability_dictionary: Dictionary containing: + keys: + debt_id- id of the debt. + value- dictionary containing: + keys: + 1. lender with value as lender for the liability/mortgage. + 2. date with value when the mortgage/liabiity statment was + requested. + 3. remaining_term with value the number of months remaining + in the mortgage/liability. + 4. liability with value as monthly service for the mortgage. + """ + liability_objs = Liabilities.objects.filter(building_id=building_id) + liabilities_input = {} + if liability_objs: + for index, obj in enumerate(liability_objs, 1): + key = 'debt'+str(index) + liability = float(obj.monthly_service) + lender = obj.lender + remaining_term = int(obj.remaining_term) + input_date = obj.input_date + liabilities_input[key] = (liability, lender, remaining_term, input_date) + return liabilities_input + + +def get_cash_balance(building_id): + """Fetch Cash Balance data from Database. + + Args: + building_id: id of the building. + + Returns: + cash_balance: Dictionary that contains: + keys: + statement_date- date of the statement(Cash balance or bank statement). + value- Tuple containing balance amount and a boolean saying if it is + from balance sheet or not. + """ + cash_balance_objs = CashBalance.objects.filter(building_id=building_id) + if cash_balance_objs: + cash_balance_input = {} + for obj in cash_balance_objs: + cash_balance_input[obj.statement_date] = (float(obj.balance_amount), obj.is_from_balance_sheet) + return cash_balance_input + else: + raise AttributeError("Could not find cash balance for this building id.") + + +def get_loan_input(building_id): + """Fetch loan options from database. + + Args: + building_id: id of the building. + + Returns: + loan_options: List of records. Each record is a dicitonary containing instititue, max loan amount, rate of + interest and duration for loan. + """ + loan_options_objs = LoanOptions.objects.filter(building_id=building_id) + if loan_options_objs: + loan_options = [{ + 'institute': obj.lender.name, + 'max_amount': float(obj.max_loan_amount), + 'interest': float(obj.interest_rate)/100, + 'duration': int(obj.duration), + } for obj in loan_options_objs] + return loan_options + else: + raise AttributeError("Could not find Loan Options for this building id.") + + +class Index(View): + """Fetch the inputs, calculate the budget and display the graph and tables.""" + + def get(self, request, building_id): + """HTTP GET request.""" + context = { + 'building_id': building_id, + } + return render(request, 'budgetSimulator/index.html', context=context) + + +class Budget(View): + """Fetch the data required for budget simulation and perform simulation. Send results to the frontend.""" + + # pylint: disable=unused-argument + def get(self, request, building_id): + """HTTP GET request. + + Fetch data from database and make calls to bp functions to simulate budget and send result to frontend. + """ + # This is hard coded for the time being as finance team have not implemented logic for other values. + req_dscr = { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10 + } + + # Fetch values from the database needed to perform the budget simulation. + try: + analysis_date, commissioning_date = get_analysis_commissioning_dates(building_id) + customer_preference = get_customer_preference(building_id) + raw_bills = get_raw_bill(building_id) + annual_bills = get_annual_bills(building_id) + raw_income_statement = get_raw_income_statement(building_id) + growth_rate = get_growth_rate(building_id) + liabilities = get_liability(building_id) + cash_balance = get_cash_balance(building_id) + loan_options = get_loan_input(building_id) + except Exception as err: + error = err.args[0] + return JsonResponse({'error': error}, status=400) + try: + result = budget_simulation( + analysis_date, + commissioning_date, + customer_preference, + req_dscr, + raw_bills, + annual_bills, + raw_income_statement, + growth_rate, + liabilities, + cash_balance, + loan_options + ) + + # Go over all the tables in result. + for table in result: + # Go over each row in the table. + for row in table: + # Assign length of the row to a variable to prevent recalculating. + length = len(row) + # Go over the length of the row. + for index in range(length): + data = row[index] + # If the data in the row is float, format it to have 2 decimal places. + if isinstance(data, float): + row[index] = float("{0:.2f}".format(data)) + # Split the result into each individual tables without the savings potential list. + loan_only = result[0][1:] + loan_first = result[1][1:] + sf_first = result[2][1:] + sf_max = result[3][1:] + # Obtain the savings potential list. + saving_potential_list = [] + for savings in result[0][0]: + if isinstance(savings, float): + saving_potential_list.append(float("{0:.2f}".format(savings))) + tables = { + 'budget_loan_first': loan_first, + 'budget_loan_only': loan_only, + 'budget_sf_first': sf_first, + 'budget_sf_max': sf_max, + } + instance = { + 'tables': tables, + 'saving_potential_list': saving_potential_list, + } + return JsonResponse({'instance': instance}) + except Exception as err: + error = err.args[0] + return JsonResponse({'error': error}, status=400) diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/views.py index 35d0b4f588ce8f4b6112cfb924ee7eba71537b72..86193167b84cf64fd104c91ff32a7c5cd5b6284e 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/views.py @@ -7,7 +7,8 @@ from django.http import JsonResponse from django.db import connections from django.views import View -from bpfin.lib.back_end_call import monthly_bill, prior_income_statement_table +from bpfin.back_end_call.back_end_inputs import monthly_bill +from bpfin.back_end_call.back_end_inputs import form_prior_income_table as prior_income_statement_table from .models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference, EstimationAlgorithm @@ -488,7 +489,7 @@ class BillsOverviewView(View): # Fetch all bills from the database. raw_bill = get_raw_bill(building_id) # Project the charge for utilities whose atleast 12 months bills are available. - projected_bills = monthly_bill(raw_bill, analysis_date) + projected_bills, manual_input_dict, prior_month_dict = monthly_bill(raw_bill, analysis_date) for util in self.utility: # Check if the utility bill is present. if projected_bills[util]: @@ -888,21 +889,29 @@ class IncomeStatementTable(View): annual_bills['electricity'] = {} for obj in electricity_objs: annual_bills['electricity'][obj.year] = float(obj.electricity) if obj.electricity else None + else: + annual_bills['electricity'] = None if gas_objs: annual_bills['gas'] = {} for obj in gas_objs: annual_bills['gas'][obj.year] = float(obj.gas) if obj.gas else None + else: + annual_bills['gas'] = None if oil_objs: annual_bills['oil'] = {} for obj in oil_objs: annual_bills['oil'][obj.year] = float(obj.oil) if obj.oil else None + else: + annual_bills['oil'] = None if water_objs: annual_bills['water'] = {} for obj in water_objs: annual_bills['water'][obj.year] = float(obj.water) if obj.water else None + else: + annual_bills['water'] = None return annual_bills def put(self, request, building_id): diff --git a/blocnote/apps/landingPage/templates/landingPage/index.html b/blocnote/apps/landingPage/templates/landingPage/index.html index c1d74dac6fe04761467ce24a8cfd390c69e8bd8e..a08926827f1087c97882189b0e1394546d014634 100644 --- a/blocnote/apps/landingPage/templates/landingPage/index.html +++ b/blocnote/apps/landingPage/templates/landingPage/index.html @@ -27,6 +27,12 @@ OK + + + Budget Simulator + + OK + {% else %} @@ -52,6 +58,18 @@ + + + Budget Simulator + + + + + {% endif %} diff --git a/blocnote/apps/preliminaryFinance/views.py b/blocnote/apps/preliminaryFinance/views.py index 1531d439335858f2558162339e30897de3689d04..2c573976f5dc4726928e1a1647e2931a8b8c6651 100644 --- a/blocnote/apps/preliminaryFinance/views.py +++ b/blocnote/apps/preliminaryFinance/views.py @@ -1,22 +1,14 @@ +"""Define the views for the preliminary analysis endpoint.""" import json from django.shortcuts import render from django.views import View from django.http import JsonResponse from django.db import connections -import datetime -from bpfin.financials.financial_lib import organize_bill_overview -from bpfin.financials.borrower_schedule import packaging_data -from bpfin.financials.financial_lib import Income_Statement_Table -from bpfin.financials.cash_balance import cash_balance -from bpfin.financials.liability import final_liability_dict -from bpfin.financials.financial_lib import Balance_Sheet_Table -from bpfin.financials.scenario import Scenario as ScenarioClass - -from .models import Scenario, CostEstimation, SavingsEstimation -from blocnote.apps.financialInputs.models import GrowthRate, IncomeStatement, BillsOverview, FinancingOverview, CashBalance, Liabilities, LoanOptions, CustomerPreference, Bills -from bpfin.utilbills.bill_backend_call import prior_proj_rough_month +from blocnote.apps.financialInputs.models import GrowthRate, IncomeStatement, BillsOverview, FinancingOverview +from blocnote.apps.financialInputs.models import CashBalance, Liabilities, LoanOptions, CustomerPreference, Bills +from bpfin.back_end_call.back_end_prelim import prelim_scenario class Index(View): @@ -65,7 +57,7 @@ class Scenarios(View): to bpfin to calculate all the outputs to be displayed and pass the information to the frontend. """ - def get_liability(self, building_id, analysis_date): + def get_liability(self, building_id): """Obtain liability information. Fetch the records containing the liability information from the database. @@ -100,12 +92,7 @@ class Scenarios(View): remaining_term = int(obj.remaining_term) input_date = obj.input_date liabilities_input[key] = (liability, lender, remaining_term, input_date) - liability_dictionary = final_liability_dict( - analysis_date['proforma_start'], - liabilities_input, - analysis_date['proforma_duration'] - ) - return liability_dictionary + return liabilities_input def get_cash_balance(self, building_id): """Fetch Cash Balance data from Database. @@ -155,18 +142,19 @@ class Scenarios(View): bill_overview: Dictionary containing utility as key and value as a dictionary with key as year and value as annual charge for that utility. """ - objs = BillsOverview.objects.filter(building_id=building_id) + bills_overview_objs = BillsOverview.objects.filter(building_id=building_id) bill_overview = {} electricity = {} gas = {} oil = {} water = {} - if objs: - for obj in objs: - electricity[obj.year] = float(obj.electricity) - gas[obj.year] = float(obj.gas) - oil[obj.year] = float(obj.oil) - water[obj.year] = float(obj.water) + if bills_overview_objs: + for obj in bills_overview_objs: + electricity[obj.year] = float(obj.electricity) if obj.electricity else None + gas[obj.year] = float(obj.gas) if obj.gas else None + oil[obj.year] = float(obj.oil) if obj.oil else None + water[obj.year] = float(obj.water) if obj.water else None + obj = bills_overview_objs[0] bill_overview = { 'electricity': [electricity, not obj.electricity_is_user_input], 'gas': [gas, not obj.gas_is_user_input], @@ -273,7 +261,7 @@ class Scenarios(View): } return customer_preference - def get_prior_month_bill(self, building_id, analysis_date): + def get_prior_month_bill(self, building_id): """Get the prior bills on a monthly basis than annual. Args: @@ -289,7 +277,7 @@ class Scenarios(View): 'oil', 'water', ] - prior_month_bill = {} + raw_bill_table = {} for utility_type in utilities: bills_object = Bills.objects.filter( building_id=building_id, @@ -309,9 +297,53 @@ class Scenarios(View): raw_bill['date_to'].append(bill.date_to) raw_bill['usage'].append(float(bill.usage)) raw_bill['charge'].append(float(bill.charge)) - month_rough = prior_proj_rough_month(raw_bill, analysis_date) - prior_month_bill[utility_type] = month_rough - return prior_month_bill + raw_bill_table[utility_type] = raw_bill + return raw_bill_table + + def get_annual_bills(self, building_id): + """Fetch manually input annual values from the database.""" + annual_bills = {} + + electricity_objs = BillsOverview.objects.filter( + building_id=building_id, + electricity_is_user_input=True, + ) + + gas_objs = BillsOverview.objects.filter( + building_id=building_id, + gas_is_user_input=True, + ) + + oil_objs = BillsOverview.objects.filter( + building_id=building_id, + oil_is_user_input=True, + ) + + water_objs = BillsOverview.objects.filter( + building_id=building_id, + water_is_user_input=True, + ) + + if electricity_objs: + annual_bills['electricity'] = {} + for obj in electricity_objs: + annual_bills['electricity'][obj.year] = float(obj.electricity) if obj.electricity else None + + if gas_objs: + annual_bills['gas'] = {} + for obj in gas_objs: + annual_bills['gas'][obj.year] = float(obj.gas) if obj.gas else None + + if oil_objs: + annual_bills['oil'] = {} + for obj in oil_objs: + annual_bills['oil'][obj.year] = float(obj.oil) if obj.oil else None + + if water_objs: + annual_bills['water'] = {} + for obj in water_objs: + annual_bills['water'][obj.year] = float(obj.water) if obj.water else None + return annual_bills def put(self, request, building_id): """Handle HTTP PUT request. @@ -328,45 +360,24 @@ class Scenarios(View): growth_rate = self.get_growth_rate(building_id) raw_income_statement = self.get_raw_income_statement(building_id) analysis_date = self.get_analysis_date(building_id) - bill_overview = self.get_bill_overview(building_id) - bill_overview_organized = organize_bill_overview(bill_overview, analysis_date) cash_balance_input_dict = self.get_cash_balance(building_id) - cash_balance_input = cash_balance(analysis_date, cash_balance_input_dict) - - prior_income_statement_table = Income_Statement_Table( - raw_income_statement, - bill_overview_organized - ) - prior_income_statement_table.project( - growth_rate, - analysis_date, - bill_overview_organized - ) - - liability_dictionary = self.get_liability(building_id, analysis_date) - noi_dictionary = prior_income_statement_table.get_noi_dict() + liability_dictionary = self.get_liability(building_id) + loan_options = self.get_loan_input(building_id) + commission_date = self.get_commission_date(building_id) + customer_preference = self.get_customer_preference(building_id) + prior_month_bill = self.get_prior_month_bill(building_id) + annual_bills = self.get_annual_bills(building_id) - raw_balance_sheet = { - 'cash': cash_balance_input, - 'other_debt_service': liability_dictionary, - 'net_income': noi_dictionary, - } - prior_balance_sheet_table = Balance_Sheet_Table(raw_balance_sheet) - prior_balance_sheet_table.project_balance_sheet( - analysis_date, - liability_dictionary, - noi_dictionary, - ) + # Extract data from the PUT request body. + # Calculate the total cost of items. costs = [] cost_list = put['cost'] for record in cost_list: costs.append(float(record['cost'])) total_cost = sum(costs) - loan_options = self.get_loan_input(building_id) - commission_date = self.get_commission_date(building_id) savings_percent = self.get_savings_percent(put['savings']) - customer_preference = self.get_customer_preference(building_id) - prior_month_bill = self.get_prior_month_bill(building_id, analysis_date) + + # The following 2 variables are hard coded for now. They will be updated at a later point. full_saving_dict = { 'electricity': None, 'gas': None, @@ -378,31 +389,28 @@ class Scenarios(View): 'req_cash_dscr': 1.15, 'req_saving_dscr': 1.10 } - # The scenario class would store relevant data and perform preliminary analysis. - scenario = ScenarioClass( + + # prelim_scenario returns the project economics which are a set of values determining the financial health + # of the building and graph dictionary which contains total expense, loan repayment and total net savings. + # The graph_dict will be plotted as a stacked bar graph in the frontend to visually see if savings can cover + # loan repayment and if there will be any savings left are loan repayment. + graph_dict, project_economics_overview = prelim_scenario( + prior_month_bill, + annual_bills, + raw_income_statement, + growth_rate, + liability_dictionary, + cash_balance_input_dict, + loan_options, analysis_date, commission_date, total_cost, - bill_overview, - bill_overview_organized, - liability_dictionary, - prior_income_statement_table, - prior_balance_sheet_table, - loan_options, - ) - - # Perform preliminary analysis - scenario.prelim_anlaysis( - prior_month_bill, savings_percent, full_saving_dict, - growth_rate, req_dscr, - customer_preference + customer_preference, ) - # graph_dict contains the data for the stacked bar graph. - graph_dict = scenario.get_graph_dict() energy_expense = graph_dict['energy_expenses'] total_loan = graph_dict['total_loan'] net_savings = graph_dict['net_savings'] @@ -415,9 +423,6 @@ class Scenarios(View): energy_expense_list.append(energy_expense[year]) total_loan_list.append(total_loan[year]) net_savings_list.append(net_savings[year]) - - # economics_overview contains the information for the summary table on frontend. - project_economics_overview = scenario.get_economics() economics_overview = { 'Estimated Cost': float("{0:.2f}".format(project_economics_overview['estimated_cost'])), 'Overall Saving': float("{0:.2f}".format(project_economics_overview['overall_saving'])), diff --git a/blocnote/settings.py b/blocnote/settings.py index 44905a660b1c9a81e2f94be4884139bbbabff605..bed7ab25edab224514f93c8fd5b2fa280d51b3a0 100644 --- a/blocnote/settings.py +++ b/blocnote/settings.py @@ -43,7 +43,9 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'blocnote.apps.financialInputs', 'blocnote.apps.preliminaryFinance', + 'blocnote.apps.budgetSimulator', 'blocnote.apps.landingPage', + 'django_nose', ] MIDDLEWARE = [ @@ -74,6 +76,15 @@ TEMPLATES = [ }, ] +# Use nose to run all tests +TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' + +# Tell nose to measure coverage on the 'foo' and 'bar' apps +NOSE_ARGS = [ + '--with-coverage', + '--cover-package=blocnote.apps.budgetSimulator', +] + WSGI_APPLICATION = 'blocnote.wsgi.application' diff --git a/blocnote/urls.py b/blocnote/urls.py index 965a5959245db7cde783620aa24837056a858a8e..ed5ad6615052d624f771f5f25a088873c05b2a9f 100644 --- a/blocnote/urls.py +++ b/blocnote/urls.py @@ -20,6 +20,7 @@ urlpatterns = [ url('', admin.site.urls), url(r'^buildings/(?P[0-9]+)/preliminary-finance/', include('blocnote.apps.preliminaryFinance.urls')), url(r'^buildings/(?P[0-9]+)/financial-inputs/', include('blocnote.apps.financialInputs.urls')), + url(r'^buildings/(?P[0-9]+)/budget/', include('blocnote.apps.budgetSimulator.urls')), url(r'^buildings/(?P[0-9]+)/', include('blocnote.apps.landingPage.urls')), url(r'^admin/', admin.site.urls), ] diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..8b9f8eb49067988adb2297469577423a960a2eb6 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "blocnote", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Blocp/blocnote.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/Blocp/blocnote/issues" + }, + "homepage": "https://github.com/Blocp/blocnote#readme", + "dependencies": {}, + "devDependencies": { + "eslint": "^3.19.0", + "eslint-config-airbnb": "^15.0.1", + "eslint-plugin-import": "^2.3.0", + "eslint-plugin-jsx-a11y": "^5.0.3", + "eslint-plugin-react": "^7.0.1" + } +} diff --git a/requirements-dev.txt b/requirements-dev.txt index 8d2fff941f20cf49dd28e6bced3b6400822560d0..9fa50a173a9e70f8c012cd5361fec258c6f7e119 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,8 @@ -r requirements.txt -autopep8==1.3 -prospector==0.12.4 pycodestyle==2.0.0 pydocstyle==1.0.0 pylint>=1.5.6 pylint-django>=0.7.2 +yapf>=0.16.1 +django-nose +coverage diff --git a/requirements.txt b/requirements.txt index 511376951a0995d92f4a6d0beeea76d7d5390ac6..c7936fa20b82b7d3432c28458c000833b2b70403 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ Django==1.10.6 psycopg2==2.7 python-decouple==3.0 -git+ssh://git@github.com/Blocp/bpfin.git@v0.1.2 +git+ssh://git@github.com/Blocp/bpfin.git@v0.1.3