sprint1: base func

main
Сергей Ванюшкин 2024-01-12 20:22:48 +03:00
parent 1354af0262
commit fe4ec551b5
13 changed files with 832 additions and 0 deletions

220
.gitignore vendored Normal file
View File

@ -0,0 +1,220 @@
pyproger/uploads/*
!pyproger/uploads/.gitkeep
config.ini
data/
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.env_*
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

6
main.py Normal file
View File

@ -0,0 +1,6 @@
from src.app import run_test_monitor
from src.config import test_config
if __name__ == "__main__":
run_test_monitor(config=test_config)

232
poetry.lock generated Normal file
View File

@ -0,0 +1,232 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "certifi"
version = "2023.11.17"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "exceptiongroup"
version = "1.2.0"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
]
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "influxdb-client"
version = "1.39.0"
description = "InfluxDB 2.0 Python client library"
optional = false
python-versions = ">=3.7"
files = [
{file = "influxdb_client-1.39.0-py3-none-any.whl", hash = "sha256:35c4d73713b6b7d30ca5ebaf74b7d53217fb6c58cdec63484e529b43d26f061e"},
{file = "influxdb_client-1.39.0.tar.gz", hash = "sha256:6a534913523bd262f1928e4ff80046bf95e313c1694ce13e45fd17eea90fe691"},
]
[package.dependencies]
certifi = ">=14.05.14"
python-dateutil = ">=2.5.3"
reactivex = ">=4.0.4"
setuptools = ">=21.0.0"
urllib3 = ">=1.26.0"
[package.extras]
async = ["aiocsv (>=1.2.2)", "aiohttp (>=3.8.1)"]
ciso = ["ciso8601 (>=2.1.1)"]
extra = ["numpy", "pandas (>=0.25.3)"]
test = ["aioresponses (>=0.7.3)", "coverage (>=4.0.3)", "flake8 (>=5.0.3)", "httpretty (==1.0.5)", "jinja2 (==3.1.2)", "nose (>=1.3.7)", "pluggy (>=0.3.1)", "psutil (>=5.6.3)", "py (>=1.4.31)", "pytest (>=5.0.0)", "pytest-cov (>=3.0.0)", "pytest-timeout (>=2.1.0)", "randomize (>=0.13)", "sphinx (==1.8.5)", "sphinx-rtd-theme"]
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "packaging"
version = "23.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
]
[[package]]
name = "pluggy"
version = "1.3.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
{file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pytest"
version = "7.4.4"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
[package.dependencies]
six = ">=1.5"
[[package]]
name = "python-dotenv"
version = "1.0.0"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
python-versions = ">=3.8"
files = [
{file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
{file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
]
[package.extras]
cli = ["click (>=5.0)"]
[[package]]
name = "reactivex"
version = "4.0.4"
description = "ReactiveX (Rx) for Python"
optional = false
python-versions = ">=3.7,<4.0"
files = [
{file = "reactivex-4.0.4-py3-none-any.whl", hash = "sha256:0004796c420bd9e68aad8e65627d85a8e13f293de76656165dffbcb3a0e3fb6a"},
{file = "reactivex-4.0.4.tar.gz", hash = "sha256:e912e6591022ab9176df8348a653fe8c8fa7a301f26f9931c9d8c78a650e04e8"},
]
[package.dependencies]
typing-extensions = ">=4.1.1,<5.0.0"
[[package]]
name = "setuptools"
version = "69.0.3"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"},
{file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
[[package]]
name = "typing-extensions"
version = "4.9.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
]
[[package]]
name = "urllib3"
version = "2.1.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
files = [
{file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"},
{file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "63e9ec089b621ebbb62f087b0c893cee4608b33e867a94543da384ea6520e3f6"

19
pyproject.toml Normal file
View File

@ -0,0 +1,19 @@
[tool.poetry]
name = "IoT_monitoring"
version = "0.0.1"
description = "Monitoring for Iot deveces and save data"
authors = ["pi3c <pi3c@yandex.ru>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "^1.0.0"
influxdb-client = "^1.39.0"
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.4"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

0
src/__init__.py Normal file
View File

19
src/app.py Normal file
View File

@ -0,0 +1,19 @@
from src.device import FakeInverter
from src.monitor import Monitor
def run_test_monitor(config: dict):
monitor = Monitor(session_config=config)
device_conf = {
"name": "myinv",
"model": "12345",
"location": "my_home",
"port": "COM",
}
device = FakeInverter(**device_conf)
monitor.init_device(device)
monitor.demo_get_device_data_from_file()
if __name__ == "__main__":
pass

5
src/config.py Normal file
View File

@ -0,0 +1,5 @@
from dotenv import dotenv_values
prod_config = dotenv_values("src/.env_prod")
test_config = dotenv_values("nts_ntu/.env_test")

110
src/device.py Normal file
View File

@ -0,0 +1,110 @@
from abc import ABC, abstractmethod
from collections import namedtuple
from datetime import datetime, timedelta
from typing import Any
class Device(ABC):
"""Абстрактный базовый класс прибора
Описывает необходимые к реализации атрибуты/методы, для подключения прибора
к системе мониторинга и сбора данных
Атрибуты обязятельные к определению в приборе
---------------------------------------------------------------------------
...
Методы обязательные к определению в приборе
---------------------------------------------------------------------------
def connect(self) -> None
реализация подключения к прибору
def get_data(self) -> dict
Получение текущих показаний прибора. Функция должна вернуть
словарь определенной структуры(см. соответствующий пункт документации)
"""
@abstractmethod
def connect(self):
"""Реализуется физическое подключение к прибору, для сбора данных"""
raise NotImplementedError
@abstractmethod
def get_data(self) -> dict:
"""Реализует получение данных от прибора возвращает словарь.
Считанные данные привести к dict описанной ниже структуры:
dict_structure = {
"measurement": "<name>",
"tags": {"<tag_name>": "<tag_value>"},
"fields": {"<field_name>": <field_value>},
"time": <measurement_time>
}
Допустимые типы данных в описанном словаре:
<name>: str
<tag_name>: str
<tag_value>: str
<field_name>: str
<field_value>: str | int | float | bool
<measurement_time>: datetime
Функция должна вернуть созданный словарь
"""
raise NotImplementedError
class Inverter(Device):
def __init__(self, **kwargs) -> None:
self.name: str = kwargs.get("name", "fakeinvertor")
self.model: str = kwargs.get("model", "fakemodel")
self.location: str = kwargs.get("name", "fakelocation")
self._raw_data: str
self.port: str = kwargs.get("name", "fakeCOMport")
self.__dict__.update(kwargs)
def connect(self):
pass
def get_data(self, current_time: datetime = datetime.now()) -> dict:
val_template = namedtuple(
"InvertorData",
[
"VVV", # O/P Voltage
"QQQ", # O/P load percent (Digital) 0, - 0%
"SS_S", # Battery voltage 12,24,48
"BBB", # Battery capacity (as O/P load percent)
"TT_T", # Heat Sink Temperature (0-99.9)
"MMM", # Utility Power Voltage (0-250VACоооо)
"RR_R", # Output Power Frequency (40.0-70.0) Hz
"DDD", # DC BUS Voltage (0V)
"PPP", # O/P load Percent (Analog) (0-100%)u
"command_bits",
],
)
raw = self._raw_data.strip().lstrip("(").rstrip(")").split()
print(raw)
values = val_template(*map(float, raw[:-1]), raw[-1])
answer: dict = {
"measurement": self.name,
"tags": {"model": self.model, "location": self.location},
"fields": values._asdict(),
"time": current_time,
}
return answer
class FakeInverter(Inverter):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.cur_datetime: datetime = datetime.now() - timedelta(hours=23)
self.delta: timedelta = timedelta(minutes=10)
def __getattribute__(self, __name: str) -> Any:
if __name == "cur_datetime":
self.__dict__["cur_datetime"] += self.delta
return super().__getattribute__(__name)
def set_fake_answer(self, line: str) -> None:
self._raw_data = line
def get_data(self, current_time: datetime = datetime.now()) -> dict:
return super().get_data(self.cur_datetime)

46
src/monitor.py Normal file
View File

@ -0,0 +1,46 @@
import time
from typing import List
from src.device import Device
from src.session import InfDBSession
class Monitor:
def __init__(
self,
session_config: dict,
pooling_delay: int | float = 0.1,
) -> None:
"""
description
"""
self.device_list: List[Device] = []
self.session_config: dict = session_config
self.pooling_delay: int | float = pooling_delay
def init_device(self, device: Device) -> None:
self.device_list.append(device)
def _get_device_data(self, device: Device) -> dict:
answer = device.get_data()
return answer
def __send_devices_data_to_db(self) -> None:
session = InfDBSession(config=self.session_config)
for device in self.device_list:
data: dict = self._get_device_data(device)
session.add(data)
session.commit()
def mainloop(self) -> None:
while True:
self.__send_devices_data_to_db()
time.sleep(self.pooling_delay)
def demo_get_device_data_from_file(self):
with open("telemetry.txt", mode="r") as f:
for line in f.readlines():
line = line.strip()
if line:
self.device_list[0].set_fake_answer(line)
self.__send_devices_data_to_db()

33
src/session.py Normal file
View File

@ -0,0 +1,33 @@
import influxdb_client as infdb
from influxdb_client import Point
from influxdb_client.client.write_api import SYNCHRONOUS
class InfDBSession:
def __init__(self, config: dict) -> None:
self.token: str
self.url: str
self.org: str
self.bucket: str
self.__dict__.update(config)
self.__points_queue: list = []
self.__client = infdb.InfluxDBClient(
url=self.url,
token=self.token,
org=self.org,
)
self.__write_api = self.__client.write_api(write_options=SYNCHRONOUS)
def add(self, point: dict) -> None:
print(point)
self.__points_queue.append(Point.from_dict(point))
print(len(self.__points_queue))
def commit(self) -> None:
while self.__points_queue:
self.__write_api.write(
org=self.org,
bucket=self.bucket,
record=self.__points_queue.pop(0),
)

135
telemetry.txt Normal file
View File

@ -0,0 +1,135 @@
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.3 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.8 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(230 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 20.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 30.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 40.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 50.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 60.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 70.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 80.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 99.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 70.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 40.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(250 050 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 050 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 075 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 100 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 100 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 100 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.2 100 00.0 000 50.0 000 007 1000000000000000000)
(229 025 27.1 100 00.0 000 50.0 000 007 1000000000000000000)

0
tests/__init__.py Normal file
View File

7
tests/test_interface.py Normal file
View File

@ -0,0 +1,7 @@
from nts_ntu.app import InfDBInterface
from nts_ntu.config import test_config
def test_intarface_creation():
interface = InfDBInterface(config=test_config)
assert interface.org == "MyOrg"