Compare commits

..
23 Commits
Author SHA1 Message Date
Marcel Nijenhof 948d3a4032 Versie als module & tests
continuous-integration/drone/push Build is passing
2021-08-01 06:20:27 +02:00
Marcel Nijenhof 3695baf322 Extra tijd starten als snelle oplossing
continuous-integration/drone/push Build encountered an error
2021-07-31 22:21:19 +02:00
Marcel Nijenhof 58bb303fa0 Drone: Flask toegevoegd aan docker test
continuous-integration/drone/push Build is failing
2021-07-30 14:46:03 +02:00
Marcel Nijenhof e4d9cef092 Issue 1: isoparse --> parse
continuous-integration/drone/push Build is failing
2021-07-30 14:40:18 +02:00
Marcel Nijenhof 3e2ee42678 Drone: naam Build aangepast
continuous-integration/drone/push Build is failing
2021-07-27 20:56:54 +02:00
Marcel Nijenhof 84bc01e454 Correctie .drone.yml 2021-07-27 20:50:34 +02:00
Marcel Nijenhof 2eea60eb92 precommithook: yamllint toegevoegd op drone.yml 2021-07-27 20:48:16 +02:00
Marcel Nijenhof 5ecc6eac1e Correctie dependecy 2021-07-27 20:47:29 +02:00
Marcel Nijenhof e3ea71bb21 .drone: Yamllint correcties 2021-07-27 20:47:00 +02:00
Marcel Nijenhof f4db4e28ac Openssl toegevoegd als dependecy 2021-07-27 19:52:45 +02:00
Marcel Nijenhof 818d5d0947 numpy toegevoegd als dependecy
continuous-integration/drone/push Build is failing
2021-07-27 19:49:46 +02:00
Marcel Nijenhof b5675651ac requests toegevoegd als dependecy
continuous-integration/drone/push Build is failing
2021-07-27 19:45:15 +02:00
Marcel Nijenhof 30c19106c8 Uitbreiding pipeline met test meerdere platformen
continuous-integration/drone/push Build is failing
2021-07-27 19:38:43 +02:00
Marcel Nijenhof 55b87f429d Meer testen locaties & quantities
continuous-integration/drone/push Build is passing
2021-06-19 11:16:03 +02:00
Marcel Nijenhof 9b9fb0acac Race conditie: geef test server meer tijd te starten 2021-06-19 11:12:43 +02:00
Marcel Nijenhof 55c4499099 Correctie twee keer wissen temp files 2021-06-19 10:57:16 +02:00
Marcel Nijenhof 56b9741e21 Eerste test op values
continuous-integration/drone/push Build is passing
2021-06-18 00:03:49 +02:00
Marcel Nijenhof 976d19e98f Comment en debug aanpassingen 2021-06-14 18:57:23 +02:00
Marcel Nijenhof 766dd8ff80 Test voor quantities
continuous-integration/drone/push Build is passing
2021-06-14 18:51:13 +02:00
Marcel Nijenhof 0f50cc2a46 Test webserver en locatie test
continuous-integration/drone/push Build is passing
2021-06-14 17:10:32 +02:00
Marcel Nijenhof 02666b5f7a Bug fix: checkssl doorgeven aan request 2021-06-14 16:49:19 +02:00
Marcel Nijenhof ef1b48a47b Merge branch 'master' of https://git.marceln.org/marceln/ddOperApi into test 2021-06-01 08:38:27 +02:00
Marcel Nijenhof 452f72505f ddOperApi.egg-info toegevoegd aan .gitignore 2021-06-01 08:17:37 +02:00
7 changed files with 477 additions and 29 deletions
+148 -5
View File
@@ -1,27 +1,170 @@
--- ---
kind: pipeline kind: pipeline
type: exec type: exec
name: default name: CentOS7_test
platform: platform:
os: linux os: linux
arch: amd64 arch: amd64
variant: CentOS7
steps: steps:
- name: Run unit test - name: Run unit test
environment: environment:
PYTHONPATH: . PYTHONPATH: .
commands: commands:
- python3 setup.py test
---
kind: pipeline
type: exec
name: CentOS8_test
platform:
os: linux
arch: amd64
variant: CentOS8
steps:
- name: Run unit test
environment:
PYTHONPATH: .
commands:
- python3 setup.py test
---
kind: pipeline
type: exec
name: Ubuntu1804_test
platform:
os: linux
arch: amd64
variant: Ubuntu1804
steps:
- name: Run unit test
environment:
PYTHONPATH: .
commands:
- python3 setup.py test
---
kind: pipeline
type: exec
name: Fedora_test
platform:
os: linux
arch: amd64
variant: Fedora
steps:
- name: Run unit test
environment:
PYTHONPATH: .
commands:
- python3 setup.py test
---
kind: pipeline
type: docker
name: docker_python:3.6
steps:
- name: Run unit test
image: python:3.6
environment:
PYTHONPATH: .
commands:
- pip install python-dateutil flask
- python setup.py test - python setup.py test
- name: Build package files
---
kind: pipeline
type: docker
name: docker_python:latest
steps:
- name: Run unit test
image: python:latest
environment:
PYTHONPATH: .
commands:
- pip install python-dateutil flask
- python setup.py test
---
kind: pipeline
type: exec
name: Build
platform:
os: linux
arch: amd64
vatiant: Fedora
steps:
- name: Build package files
commands: commands:
- python setup.py sdist - python setup.py sdist
- name: Versie toevoegen aan download - name: Versie toevoegen aan download
environment: environment:
DOWNLOADDIR: /usr/share/nginx/html/download/python/ddoperapi DOWNLOADDIR: /usr/share/nginx/html/download/python/ddoperapi
commands: commands:
- mkdir -p "$${DOWNLOADDIR}" - mkdir -p "$${DOWNLOADDIR}"
- cd dist - cd dist
- for f in *;do if [ -f "$${DOWNLOADDIR}/$${f}" ] ; then echo version error;exit 1;else cp "$${f}" "$${DOWNLOADDIR}";fi;done - |
for f in *
do
if [ -f "$${DOWNLOADDIR}/$${f}" ]
then
echo version error
exit 1
else
cp "$${f}" "$${DOWNLOADDIR}"
fi
done
- ls -l "$${DOWNLOADDIR}" - ls -l "$${DOWNLOADDIR}"
when: when:
branch: branch:
- master - master
depends_on:
- CentOS7_test
- CentOS8_test
- Ubuntu1804_test
- Fedora_test
- docker_python:latest
- docker_python:3.6
---
kind: pipeline
type: docker
name: installCheck
steps:
- name: install Check
image: python:3.6
# Make sure we run the pip installed version
commands:
- rm -rf ddOperApi
when:
branch:
- master
depends_on:
- Build
#
# todo: Uitzoeken
#
# - >
# pip install --extra-index-url
# https://marceln.org/download/python ddOperApi
# - python -c "import lmwsip"
# - python -c "import lmwsip; print(lmwsip.__version__)"
# - >
# [ $(python -c "import lmwsip; print(lmwsip.__version__)") =
# $(python setup.py --version) ]
# - python -m unittest -v lmwsip.tests
+5
View File
@@ -2,3 +2,8 @@
__pycache__ __pycache__
dd_oper_api.egg-info dd_oper_api.egg-info
dist dist
dependency_links.txt
PKG-INFO
requires.txt
SOURCES.txt
top_level.txt
+4 -4
View File
@@ -17,10 +17,10 @@ import logging
import requests import requests
import numpy as np import numpy as np
from datetime import datetime, timedelta from datetime import datetime, timedelta
from dateutil.parser import isoparse from dateutil.parser import parse
""" Version info changed by git hook """ """ Version info changed by git hook """
__version__ = 'XXXX' __version__ = '0.1.0'
class ddOperApi: class ddOperApi:
""" """
@@ -62,7 +62,7 @@ Note: Internal use!
""" """
logging.debug("ddOperApi.get(%s)" % url) logging.debug("ddOperApi.get(%s)" % url)
try: try:
req = requests.get(url, cert=(self.certfile, self.certkey)) req = requests.get(url, cert=(self.certfile, self.certkey), verify=self.checkssl)
except Exception as e: except Exception as e:
logging.exception("ddOperApi.get error: %s" % e) logging.exception("ddOperApi.get error: %s" % e)
raise(e) raise(e)
@@ -272,7 +272,7 @@ Note:
This may change! This may change!
""" """
for e in self.result()["events"]: for e in self.result()["events"]:
dt = isoparse(e["timeStamp"]) dt = parse(e["timeStamp"])
if "value" in e: if "value" in e:
p = e p = e
elif "aspects" in e: elif "aspects" in e:
+50 -3
View File
@@ -2,21 +2,68 @@
import unittest import unittest
import ddOperApi import ddOperApi
from time import sleep
from ddOperApi.tests.testServer import forkTestServer, stopTestServer
class ddOperApiTest(unittest.TestCase): class ddOperApiTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
global forkTestServerPid
forkTestServerPid = forkTestServer()
@classmethod
def tearDownClass(cls):
global forkTestServerPid
stopTestServer(forkTestServerPid)
def setUp(self): def setUp(self):
self.client = ddOperApi.ddOperApi(url="https://localhost:5000/dd-oper/2.0", checkssl=False)
def tearDown(self):
pass pass
def test_version(self): def test_version(self):
self.assertTrue(hasattr(ddOperApi, '__version__')) self.assertTrue(hasattr(ddOperApi, '__version__'))
self.assertEqual(type(ddOperApi.__version__), str) self.assertEqual(type(ddOperApi.__version__), str)
def setup(): def test_setup(self):
pass self.assertEqual(type(self.client), ddOperApi.ddOperApi)
def test_locations(self):
locations = self.client.locations()
self.assertEqual(type(locations), ddOperApi.ddOperLocation)
self.assertTrue(len(locations.data) > 0)
self.assertEqual(type(locations.data[0]["properties"]["locationName"]), str)
def test_locationNames(self):
names = self.client.locations().locationNames()
self.assertEqual(type(names), type({}.keys()))
self.assertTrue(len(names) > 0)
for n in names:
self.assertEqual(type(n), str)
def test_locationDetail(self):
locations = self.client.locations()
name = locations.data[0]["properties"]["locationName"]
detail = locations.locationDetail(name)
self.assertEqual(type(detail), dict)
self.assertEqual(detail["properties"]["locationName"], name)
def test_quantities(self):
quantities = self.client.quantities()
self.assertEqual(type(quantities), ddOperApi.ddOperQuantitie)
self.assertTrue(len(quantities.data) > 0)
self.assertEqual(type(quantities.data[0]), str)
for q in quantities.quantities():
self.assertEqual(type(q), str)
def test_values(self):
values = self.client.values("test1", "null")
self.assertEqual(type(values), ddOperApi.ddOperValues)
def main(): def main():
setup()
unittest.main() unittest.main()
if __name__ == '__main__': if __name__ == '__main__':
+249
View File
@@ -0,0 +1,249 @@
#!/usr/bin/python
import json
from os import fork, kill
from time import sleep
from random import getrandbits
from tempfile import NamedTemporaryFile
from datetime import datetime, timedelta
from dateutil.parser import parse
from dateutil.tz import gettz
from OpenSSL import crypto, SSL
from flask import Flask, request
#
# TODO: createCert verplaatsen zodat die ook beschikbaar is in de module.
# Dan kan deze als client certificaat gebruikt worden en kan er
# getest worden met client certificaat.
# De extra subclass kan dan ook weg!
#
class testServer(Flask):
def run(self):
self.createCert()
self.writeCert()
super().run(ssl_context=(self.certFile.name, self.keyFile.name))
def createCert(self):
CN="*"
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 2048)
serialnumber=getrandbits(64)
cert = crypto.X509()
cert.get_subject().C = "TS"
cert.get_subject().L = "Test"
cert.get_subject().ST = "Test"
cert.get_subject().O = "Test"
cert.get_subject().OU = "Test"
cert.get_subject().CN = CN
cert.set_serial_number(serialnumber)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(3600)
cert.set_issuer(cert.get_subject())
cert.add_extensions([crypto.X509Extension(b'subjectAltName', False, b'DNS:*')])
cert.set_pubkey(k)
cert.sign(k, 'sha512')
self.cert=crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
self.key=crypto.dump_privatekey(crypto.FILETYPE_PEM, k)
def writeCert(self):
self.certFile = NamedTemporaryFile()
self.certFile.file.write(self.cert)
self.certFile.file.flush()
self.keyFile = NamedTemporaryFile()
self.keyFile.file.write(self.key)
self.keyFile.flush()
app = testServer(__name__)
def timeStr(dt):
return("%sz" % dt.isoformat())
def provider(responseType):
return({
"name": "testServerDDOper",
"supportUrl": "https://git.marceln.org/marceln/ddOperApi",
"responseType": responseType,
"responseTimestamp": timeStr(datetime.now())
})
def geometryPoint(x, y):
return({
"type": "Point",
"coordinates": [ x, y ]
})
def roundTime(startTime, intervalSec):
t = startTime.timestamp()
m = intervalSec
if (t%m > 0):
s = int(t/m+1)*m
return(datetime.fromtimestamp(s, tz=gettz("UTC")))
else:
return(startTime)
def singleEvent(eventTime, intervalSec, location, quantity, aspectSet):
return({
"timeStamp": timeStr(eventTime),
"startTime": timeStr(eventTime-timedelta(seconds=intervalSec/2)),
"endTime": timeStr(eventTime+timedelta(seconds=intervalSec/2)),
"quality": 10,
"value": 0,
"additionalInfo": 0
})
def events(location, quantity, aspectSet, intervalLength, startTime, endTime):
ret = []
if intervalLength == "10min":
intervalSec=600
elif intervalLength == "1min":
intervalSec=60
eventTime = roundTime(startTime, intervalSec)
while (eventTime < endTime):
ret.append(singleEvent(eventTime, intervalSec, location, quantity, aspectSet))
eventTime = eventTime + timedelta(seconds=intervalSec)
return(ret)
def jsonReturn(data):
return(json.dumps(data))
@app.route("/")
def root():
return("Hello world!\n")
@app.route("/dd-oper/2.0/locations")
def locations():
ret = { "provider": provider("LocationListResponse"),
"results": [
{
"type": "Feature",
"geometry": geometryPoint(0, 0),
"properties": {
"locationName": "test1",
"locationNameSpace": "TS.DDOPER",
"displayNameGlobal": "Test location 1"
}
},
{
"type": "Feature",
"geometry": geometryPoint(1, 0),
"properties": {
"locationName": "test2",
"locationNameSpace": "TS.DDOPER",
"displayNameGlobal": "Test location 2"
}
},
{
"type": "Feature",
"geometry": geometryPoint(0, 1),
"properties": {
"locationName": "test3",
"locationNameSpace": "TS.DDOPER",
"displayNameGlobal": "Test location 3"
}
}
],
"_comment": "MKK-DL: Location catalogue"
}
return(jsonReturn(ret))
@app.route("/dd-oper/2.0/quantities")
def quantities():
ret = { "provider": provider("QuantityListResponse"),
"results": [
"time",
"null"
],
"_comment": "MKK-DL: Quantities catalogue"
}
return(jsonReturn(ret))
@app.route("/dd-oper/2.0/locations/<location>/quantities")
def locationsQuantities(location):
return("TODO: locations: %s\n" % location, 404)
@app.route("/dd-oper/2.0/quantities/<quantity>")
def quantitiesLocations(quantity):
return("TODO: quantities: %s\n" % quantity, 404)
@app.route("/dd-oper/2.0/locations/<location>/quantities/<quantity>/timeseries")
def values(location, quantity):
startTime = request.args.get('startTime', type = str)
endTime = request.args.get('endTime', type = str)
process = request.args.get('process', default='measurement', type = str)
intervalLength = request.args.get('intervalLength', default='10min', type = str)
aspectSet = request.args.get('aspectSet', default="minimum", type = str)
try:
startTime = parse(startTime)
endTime = parse(endTime)
except Exception as e:
return("parseerror time", 404)
ret = { "provider": provider("Timeseries"),
"results": [{
"source": {
"process": process,
"institution": {
"name": "testServerDDOper"
},
"location": {
"geometry": {
"type": "Point",
"coordinates": [ 0, 0]
}
},
"type": "Feature",
"properties": {
"locationName": location,
"locationNameSpace": "TS.DDOPER",
"crsName": "WGS84",
"displayNameGlobal": "test 1"
}
},
"startTime": timeStr(startTime),
"endTime": timeStr(endTime),
"events": events(location, quantity, aspectSet, intervalLength, startTime, endTime),
"observationType": {
"quantityName": quantity,
"aspectSet": {
"name": "minimum",
"aspects": [
{
"name": "Average",
"unit": ""
}
]
}
}
}]
}
return(jsonReturn(ret))
def forkTestServer():
pid = None
try:
pid = fork()
if (pid == 0):
app.run()
else:
sleep(1)
except Exception as e:
raise(e)
return(pid)
def stopTestServer(pid):
try:
kill(pid, 15)
sleep(0.01)
except Exception as e:
print("ERROR kill: %s" % e)
return()
def main():
app.run()
if __name__ == '__main__':
main()
+2 -1
View File
@@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh -e
VERSION=$(grep version setup.py | sed -e 's/.*="//' -e 's/",//') VERSION=$(grep version setup.py | sed -e 's/.*="//' -e 's/",//')
sed -i "s/^__version__ = .*/__version__ = '${VERSION}'/" ddOperApi/__init__.py sed -i "s/^__version__ = .*/__version__ = '${VERSION}'/" ddOperApi/__init__.py
git add ddOperApi/__init__.py git add ddOperApi/__init__.py
python setup.py test python setup.py test
yamllint .drone.yml
+4 -1
View File
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="ddOperApi", # Replace with your own username name="ddOperApi", # Replace with your own username
version="0.0.3", version="0.1.0",
author="Marcel Nijenhof", author="Marcel Nijenhof",
author_email="pip@pion.xs4all.nl", author_email="pip@pion.xs4all.nl",
description="Interface for dd-oper protocol", description="Interface for dd-oper protocol",
@@ -15,6 +15,9 @@ setuptools.setup(
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
install_requires=[ install_requires=[
'python-dateutil', 'python-dateutil',
'requests',
'numpy',
'pyopenssl'
], ],
classifiers=[ classifiers=[
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",