diff --git a/docs/api.rst b/docs/api.rst index e3381884a..f5ed3e2c4 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,13 +1,13 @@ API === -The application has a simple, one-command API for remote dataloggers. It allows you to run the datalogger and application separately. +The application has an API allowing you to insert/create readings and retrieve statistics. .. contents:: -Configuration -------------- +Configuration & authentication +------------------------------ Enable API ^^^^^^^^^^ @@ -20,11 +20,11 @@ Example :target: _static/screenshots/admin_api_settings.png :alt: API admin settings -Authentication +Authenticating ^^^^^^^^^^^^^^ -Besides allowing the API to listen for requests, you will also need use the generated API Auth Key. -It can be found on the same page as in the screenshot above. The configuration page will also display it, but only partly. -Feel free to alter the API Auth Key when required. The application initially generates one randomly for you. +Besides allowing the API to listen for requests, you will also need send your API key with each request. +The API key can be found on the same page as in the screenshot above. +The application generates one for you initially, but feel free to alter the API key when required. You should pass it in the header of every API call. The header should be defined as ``X-AUTHKEY``. See below for an example. @@ -35,37 +35,45 @@ Using ``cURL``:: curl http://YOUR-DSMR-URL/api/v1/datalogger/dsmrreading \ -d 'telegram=xxxxx' \ - -H 'X-AUTHKEY: YOUR-DSMR-API-AUTHKEY' + -H 'X-AUTHKEY: YOUR-API-KEY' Using ``requests``:: requests.post( 'http://YOUR-DSMR-URL/api/v1/datalogger/dsmrreading', - headers={'X-AUTHKEY': 'YOUR-DSMR-API-AUTHKEY'}, + headers={'X-AUTHKEY': 'YOUR-API-KEY'}, data={'telegram': 'xxxxx'}, ) -API calls ---------- +[API v1] Remote datalogger +-------------------------- -POST ``/api/v1`` ``/datalogger/dsmrreading`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``POST`` - ``datalogger/dsmrreading`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This allows you to insert a raw telegram, into the application as if it was read locally using the serial cable. + +.. note:: + + Since ``DSMR-reader v1.6`` this call now returns ``HTTP 201`` instead of ``HTTP 200`` when successful. + + +URI +~~~ +Full path: ``/api/v1/datalogger/dsmrreading`` -Description -~~~~~~~~~~~ -This allows you to insert a raw telegram, read from your meter remotely, into the application as if it was read locally using the serial cable. Parameters ~~~~~~~~~~ -.. note:: +- ``telegram`` (*string*) - The raw telegram string containing all linefeeds ``\n``, and carriage returns ``\r``, as well! - Since ``v1.6`` this call now returns ``HTTP 201`` instead of ``HTTP 200`` when successful. -- Method: ``POST`` -- Data: ``telegram`` (as raw string containing all linefeeds ``\n``, and carriage returns ``\r``, as well!) -- Status code returned: ``HTTP 201`` on success, any other on failure. +Response +~~~~~~~~ +``HTTP 201`` on success, with empty body. Any other status code on failure. + Example ~~~~~~~ @@ -74,7 +82,6 @@ Example import requests # Tested with requests==2.9.1 - # Fake buffer. telegram_string = ''.join([ "/KFM5KAIFA-METER\r\n", "\r\n", @@ -104,7 +111,7 @@ Example # Register telegram by simply sending it to the application with a POST request. response = requests.post( 'http://YOUR-DSMR-URL/api/v1/datalogger/dsmrreading', - headers={'X-AUTHKEY': 'YOUR-DSMR-API-AUTHKEY'}, + headers={'X-AUTHKEY': 'YOUR-API-KEY'}, data={'telegram': telegram_string}, ) @@ -115,7 +122,7 @@ Example Script ------- +~~~~~~ Below is a more detailed script you can use to run via Supervisor. It will send telegrams to one or multiple instances of DSMR-reader. @@ -261,3 +268,442 @@ Supervisor config in ``/etc/supervisor/conf.d/dsmr-client.conf``:: sudo supervisorctl reread sudo supervisorctl update + + +---- + + +[API v2] RESTful API +-------------------- + +.. note:: + + These API calls are available since ``v1.7``. + + +``POST`` - ``datalogger/dsmrreading`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a reading from direct values, omitting the need for the telegram. + +.. note:: + + **Please note**: Readings are processed simultaneously. Inserting readings **retroactively** might result in undesired results due to the data processing, which is always reading ahead. + + Therefor inserting historic data might require you to delete all aggregated data using the ``./manage.py dsmr_backend_delete_aggregated_data`` command. + + This will process all readings again, from the very first start, and aggregate them (and **will** take a long time depending on your reading count). + + +URI +~~~ +Full path: ``/api/v2/datalogger/dsmrreading`` + + +Parameters +~~~~~~~~~~ +**[R]** = Required field + +- **[R]** ``timestamp`` (*datetime*) - Timestamp indicating when the reading was taken, according to the smart meter +- **[R]** ``electricity_currently_delivered`` (*float*) - Current electricity delivered in kW +- **[R]** ``electricity_currently_returned`` (*float*) - Current electricity returned in kW +- **[R]** ``electricity_delivered_1`` (*float*) - Meter position stating electricity delivered (low tariff) in kWh +- **[R]** ``electricity_delivered_2`` (*float*) - Meter position stating electricity delivered (normal tariff) in kWh +- **[R]** ``electricity_returned_1`` (*float*) - Meter position stating electricity returned (low tariff) in kWh +- **[R]** ``electricity_returned_2`` (*float*) - Meter position stating electricity returned (normal tariff) in kWh +- ``phase_currently_delivered_l1`` (*float*) - Current electricity used by phase L1 (in kW) +- ``phase_currently_delivered_l2`` (*float*) - Current electricity used by phase L2 (in kW) +- ``phase_currently_delivered_l3`` (*float*) - Current electricity used by phase L3 (in kW) +- ``extra_device_timestamp`` (*datetime*) - Last timestamp read from the extra device connected (gas meter) +- ``extra_device_delivered`` (*float*) - Last value read from the extra device connected (gas meter) + +.. note:: + + **datetime format** = ``YYYY-MM-DDThh:mm[:ss][+HH:MM|-HH:MM|Z]``, i.e.: ``2017-01-01T12:00:00+01`` (CET), ``2017-04-15T12:00:00+02`` (CEST) or ``2017-04-15T100:00:00Z`` (UTC). + +Response +~~~~~~~~ +``HTTP 201`` on success. Body contains the reading created in JSON format. Any other status code on failure. + + +Example +~~~~~~~ +**Data** to insert:: + + electricity_currently_delivered: 1.500 + electricity_currently_returned: 0.025 + electricity_delivered_1: 2000 + electricity_delivered_2: 3000 + electricity_returned_1: 0 + electricity_returned_2: 0 + timestamp: 2017-04-15T00:00:00+02 + + +Using **cURL** (commandline):: + + # Please note that the plus symbol "+" has been replaced by "%2B" here, to make it work for cURL. + curl http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading \ + -d 'timestamp=2017-04-15T00:00:00%2B02&electricity_currently_delivered=1.5&electricity_currently_returned=0.025&electricity_delivered_1=2000&electricity_delivered_2=3000&electricity_returned_1=0&electricity_returned_2=0' \ + -H 'X-AUTHKEY: YOUR-API-KEY' | python -m json.tool + + +Using **requests** (Python):: + + import requests + import json + + response = requests.post( + 'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading', + headers={'X-AUTHKEY': 'YOUR-API-KEY'}, + data={ + 'electricity_currently_delivered': 1.500, + 'electricity_currently_returned': 0.025, + 'electricity_delivered_1': 2000, + 'electricity_delivered_2': 3000, + 'electricity_returned_1': 0, + 'electricity_returned_2': 0, + 'timestamp': '2017-04-15T00:00:00+02', + } + ) + + if response.status_code != 201: + print('Error: {}'.format(response.text)) + else: + print('Created: {}'.format(json.loads(response.text))) + + +**Result**:: + + { + "id": 4343119, + "timestamp": "2017-04-15T00:00:00+02:00", + "electricity_delivered_1": "2000.000", + "electricity_returned_1": "0.000", + "electricity_delivered_2": "3000.000", + "electricity_returned_2": "0.000", + "electricity_currently_delivered": "1.500", + "electricity_currently_returned": "0.025", + "phase_currently_delivered_l1": null, + "phase_currently_delivered_l2": null, + "phase_currently_delivered_l3": null, + "extra_device_timestamp": null, + "extra_device_delivered": null + } + + +---- + + +``GET`` - ``datalogger/dsmrreading`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Retrieves any readings stored. The readings are either constructed from incoming telegrams or were created using this API. + + +URI +~~~ +Full path: ``/api/v2/datalogger/dsmrreading`` + + +Parameters +~~~~~~~~~~ +All parameters are optional. + +- ``timestamp__gte`` (*datetime*) - Limits the result to any readings having a timestamp **higher or equal** to this parameter. +- ``timestamp__lte`` (*datetime*) - Limits the result to any readings having a timestamp **lower or equal** to this parameter. +- ``ordering`` (*string*) - Use ``-timestamp`` to sort **descending**. Omit or use ``timestamp`` to sort **ascending** (default). +- ``offset`` (*integer*) - When iterating large resultsets, the offset determines the starting point. +- ``limit`` (*integer*) - Limits the resultset size returned. Omit for maintaining the default limit (**25**). + + +Response +~~~~~~~~ +``HTTP 200`` on success. Body contains the result(s) in JSON format. Any other status code on failure. + + +.. _generic-examples-anchor: + +Example 1 - Fetch all readings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This demonstrates how to fetch all readings stored, without using any of the parameters. + + +Using **cURL** (commandline):: + + curl 'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading' \ + -H 'X-AUTHKEY: YOUR-API-KEY' | python -m json.tool + + +Using **requests** (Python):: + + import requests + import json + + response = requests.get( + 'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading', + headers={'X-AUTHKEY': 'YOUR-API-KEY'}, + ) + + if response.status_code != 200: + print('Error: {}'.format(response.text)) + else: + print('Response: {}'.format(json.loads(response.text))) + + +**Result**:: + + # Please note that by default only 25 results are returned. The actual number of results + # is available in the 'count' field. You can iterate these using the offset-parameter. + { + "count": 4343060, + "next": "http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?limit=25&offset=25", + "previous": null, + "results": [ + { + "id": 1, + "timestamp": "2015-12-11T21:25:05Z", + "electricity_delivered_1": "594.560", + "electricity_returned_1": "0.000", + "electricity_delivered_2": "593.006", + "electricity_returned_2": "0.000", + "electricity_currently_delivered": "0.183", + "electricity_currently_returned": "0.000", + "phase_currently_delivered_l1": null, + "phase_currently_delivered_l2": null, + "phase_currently_delivered_l3": null, + "extra_device_timestamp": "2015-12-11T21:00:00Z", + "extra_device_delivered": "956.212" + }, + ... ... + ] + } + + +Example 2 - Fetch latest reading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This demonstrates how to fetch the latest reading stored. Therefor we request all readings, sort them descending by timestamp and limit the result to only one. + + +Using **cURL** (commandline):: + + curl 'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?ordering=-timestamp&limit=1' \ + -H 'X-AUTHKEY: YOUR-API-KEY' | python -m json.tool + + +Using **requests** (Python):: + + import requests + import json + + response = requests.get( + 'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?ordering=-timestamp&limit=1', + headers={'X-AUTHKEY': 'YOUR-API-KEY'}, + ) + + if response.status_code != 200: + print('Error: {}'.format(response.text)) + else: + print('Response: {}'.format(json.loads(response.text))) + + +**Result**:: + + # This should present you the latest reading (determined by the timestamp field) + { + "count": 4343060, + "next": "http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?limit=1&offset=1&ordering=-timestamp", + "previous": null, + "results": [ + { + "id": 4343116, + "timestamp": "2017-04-29T03:59:25Z", + "electricity_delivered_1": "1871.589", + "electricity_returned_1": "0.000", + "electricity_delivered_2": "1756.704", + "electricity_returned_2": "0.000", + "electricity_currently_delivered": "0.078", + "electricity_currently_returned": "0.000", + "phase_currently_delivered_l1": "0.024", + "phase_currently_delivered_l2": "0.054", + "phase_currently_delivered_l3": "0.000", + "extra_device_timestamp": "2017-04-29T03:00:00Z", + "extra_device_delivered": "1971.929" + } + ] + } + + +Example 3 - Fetch readings by month +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This demonstrates how to fetch all readings within a month. We limit the search by specifying the month start and end. + + +Using **cURL** (commandline):: + + # Note that the whitespace in the timestamps has been converted to '%20' for cURL. + curl 'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?timestamp__gte=2017-02-01%2000:00:00×tamp__lte=2017-03-01%2000:00:00' \ + -H 'X-AUTHKEY: YOUR-API-KEY' | python -m json.tool + + +Using **requests** (Python):: + + import requests + import json + + response = requests.get( + 'http://YOUR-DSMR-URL/api/v2/statistics/day?read_at__gte=2017-02-01 00:00:00&read_at__lte=2017-03-01 00:00:00', + headers={'X-AUTHKEY': 'YOUR-API-KEY'}, + ) + + if response.status_code != 200: + print('Error: {}'.format(response.text)) + else: + print('Response: {}'.format(json.loads(response.text))) + + +**Result**:: + + # This should present you a set of all readings in the month we selected. + { + "count": 240968, + "next": "http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?limit=25&offset=25×tamp__gte=2017-02-01+00%3A00%3A00×tamp__lte=2017-03-01+00%3A00%3A00", + "previous": null, + "results": [ + { + "id": 3593621, + "timestamp": "2017-01-31T23:00:03Z", + "electricity_delivered_1": "1596.234", + "electricity_returned_1": "0.000", + "electricity_delivered_2": "1484.761", + "electricity_returned_2": "0.000", + "electricity_currently_delivered": "0.075", + "electricity_currently_returned": "0.000", + "phase_currently_delivered_l1": "0.017", + "phase_currently_delivered_l2": "0.058", + "phase_currently_delivered_l3": "0.000", + "extra_device_timestamp": "2017-01-31T22:00:00Z", + "extra_device_delivered": "1835.904" + }, + ... ... + ] + } + + +.. warning:: + + Please note that all timestamps **returned** are in **UTC (CET -1 / CEST -2)**. This is indicated as well by the timestamps ending with a 'Z' (Zulu timezone). + + +---- + + +``GET`` - ``statistics/day`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Retrieves any **aggregated day statistics**. Please note that these are generated a few hours **after midnight**. + + +URI +~~~ +Full path: ``/api/v2/statistics/day`` + + +Parameters +~~~~~~~~~~ +All parameters are optional. + +- ``day__gte`` (*date*) - Limits the result to any statistics having their date **higher or equal** to this parameter. +- ``day__lte`` (*date*) - Limits the result to any statistics having their date **lower or equal** to this parameter. +- ``ordering`` (*string*) - Use ``-day`` to sort **descending**. Omit or use ``day`` to sort **ascending** (default). +- ``offset`` (*integer*) - When iterating large resultsets, the offset determines the starting point. +- ``limit`` (*integer*) - Limits the resultset size returned. Omit for maintaining the default limit (**25**). + + +Response +~~~~~~~~ +``HTTP 200`` on success. Body contains the result(s) in JSON format. Any other status code on failure. + + +Example +~~~~~~~ +All the :ref:`generic DSMRREADING examples ` apply here as well, since only the ``timestamp`` field differs. + +**Data structure returned**:: + + { + "count": 29, + "next": "http://YOUR-DSMR-URL/api/v2/statistics/day?day__gte=2017-02-01&day__lte=2017-03-01&limit=25&offset=25", + "previous": null, + "results": [ + { + "id": 709, + "day": "2017-02-25", + "total_cost": "3.14", + "electricity1": "7.289", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "1.30", + "electricity2_cost": "0.00", + "gas": "3.047", + "gas_cost": "1.84", + "lowest_temperature": "0.6", + "highest_temperature": "7.9", + "average_temperature": "4.3" + } + ] + } + + +---- + + +``GET`` - ``statistics/hour`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Retrieves any **aggregated hourly statistics**. Please note that these are generated a few hours **after midnight**. + + +URI +~~~ +Full path: ``/api/v2/statistics/hour`` + + +Parameters +~~~~~~~~~~ +All parameters are optional. + +- ``hour_start__gte`` (*datetime*) - Limits the result to any statistics having their datetime (hour start) **higher or equal** to this parameter. +- ``hour_start__lte`` (*datetime*) - Limits the result to any statistics having their datetime (hour start) **lower or equal** to this parameter. +- ``ordering`` (*string*) - Use ``-hour_start`` to sort **descending**. Omit or use ``hour_start`` to sort **ascending** (default). +- ``offset`` (*integer*) - When iterating large resultsets, the offset determines the starting point. +- ``limit`` (*integer*) - Limits the resultset size returned. Omit for maintaining the default limit (**25**). + + +Response +~~~~~~~~ +``HTTP 200`` on success. Body contains the result(s) in JSON format. Any other status code on failure. + + +Example +~~~~~~~ +All the :ref:`generic DSMRREADING examples ` apply here as well, since only the ``timestamp`` field differs. + +**Data structure returned**:: + + { + "count": 673, + "next": "http://YOUR-DSMR-URL/api/v2/statistics/hour?hour_start__gte=2017-02-01+00%3A00%3A00&hour_start__lte=2017-03-01+00%3A00%3A00&limit=25&offset=25", + "previous": null, + "results": [ + { + "id": 12917, + "hour_start": "2017-02-01T23:00:00Z", + "electricity1": "0.209", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.886" + } + ] + } + + diff --git a/docs/changelog.rst b/docs/changelog.rst index f8e294662..ccedad8a7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -17,6 +17,28 @@ Please make sure you have a fresh **database backup** before upgrading! Upgradin - `About upgrading `_. + +v1.7.0 - 2017-05-04 +^^^^^^^^^^^^^^^^^^^ + +.. warning:: + + Please note that the ``dsmr_datalogger.0007_dsmrreading_timestamp_index`` migration **will take quite some time**, as it adds an index on one of the largest database tables! + + It takes **around two minutes** on a RaspberryPi 2 & 3 with ``> 4.3 million`` readings on PostgreSQL. Results may differ on **slower RaspberryPi's** or **with MySQL**. + + +.. note:: + + The API-docs for the new v2 API `can be found here `_. + + +**Tickets resolved in this release:** + +- [`#230 `_] Support for exporting data via API + + + v1.6.2 - 2017-04-23 ^^^^^^^^^^^^^^^^^^^ diff --git a/docs/credits.rst b/docs/credits.rst index 6998b1dbe..f692cb5b6 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -30,42 +30,26 @@ Software used Please note and respect their licences as well, if any. Credits to the following software and projects: - `Raspbian `_ - - `Django Project `_ - - `Django Solo `_ - - `Django Colorfield `_ - - `Supervisor `_ - - `MySQL `_ - - `MariaDB `_ - - `PostgreSQL `_ - - `Director Responsive Admin `_ - - Favicon made by `Freepik `_ from `flaticon.com `_ - - `Real Favicon Generator `_ - - `Bootstrap-datepicker `_ - - `Github `_ - - `TravisCI `_ - - `Codecov `_ - - `Read The Docs `_ - - `MW `_ - - `Full Page Screen Capture `_ - - `Buienradar `_ +- `Django REST Framework `_ +- `Swagger UI Themes `_ Misc diff --git a/docs/installation.rst b/docs/installation.rst index d3478520b..24d7df801 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -204,7 +204,7 @@ You now should see something similar to ``Connected.`` and a wall of text and nu *We will now prepare the webserver, Nginx. It will serve all application's static files directly and proxy any application requests to the backend, Gunicorn controlled by Supervisor, which we will configure later on.* -- Make sure you are still acting here as ``root`` or ``sudo`` user. +- Make sure you are acting here as ``root`` or ``sudo`` user. If not, press CTRL + D to log out of the ``dsmr`` user. Django will later copy all static files to the directory below, used by Nginx to serve statics. Therefor it requires (write) access to it:: @@ -347,7 +347,7 @@ You've almost completed the installation now. It's possible to have other applications use Nginx as well, but that requires you to remove the wildcard in the ``dsmr-webinterface`` vhost, which you will copy below. -Go back to ``root`` / ``sudo`` user to configure the webserver (press ``CTRL + D`` once). +- Make sure you are acting here as ``root`` or ``sudo`` user. If not, press CTRL + D to log out of the ``dsmr`` user. Remove the default Nginx vhost (**only when you do not use it yourself, see the note above**):: diff --git a/docs/locale/nl/LC_MESSAGES/api.mo b/docs/locale/nl/LC_MESSAGES/api.mo index 4365021c7..96cc8c015 100644 Binary files a/docs/locale/nl/LC_MESSAGES/api.mo and b/docs/locale/nl/LC_MESSAGES/api.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/api.po b/docs/locale/nl/LC_MESSAGES/api.po index c5f09e584..ea26a50dc 100644 --- a/docs/locale/nl/LC_MESSAGES/api.po +++ b/docs/locale/nl/LC_MESSAGES/api.po @@ -8,14 +8,16 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader v1.x\n" "Report-Msgid-Bugs-To: Dennis Siemensma \n" -"POT-Creation-Date: 2017-02-16 15:58+0100\n" -"PO-Revision-Date: 2017-01-21 14:17+0100\n" +"POT-Creation-Date: 2017-05-02 21:26+0200\n" +"PO-Revision-Date: 2017-05-02 22:37+0200\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" +"Language: nl\n" +"X-Generator: Poedit 1.8.7.1\n" #: ../../api.rst:2 msgid "API" @@ -23,16 +25,15 @@ msgstr "API" #: ../../api.rst:3 msgid "" -"The application has a simple, one-command API for remote dataloggers. It " -"allows you to run the datalogger and application separately." +"The application has an API allowing you to insert/create readings and retrieve " +"statistics." msgstr "" -"De applicatie heeft een eenvoudige, enkele API-call voor dataloggers op " -"afstand. Hiermee kun je de datalogger en applicatie gescheiden van elkaar" -" draaien." +"De applicatie heeft een API waarmee metingen kunt aanmaken/opvragen en diverse " +"statistieken uitlezen." #: ../../api.rst:10 -msgid "Configuration" -msgstr "Configuratie" +msgid "Configuration & authentication" +msgstr "Configuratie & authenticatie" #: ../../api.rst:13 msgid "Enable API" @@ -40,42 +41,40 @@ msgstr "API inschakelen" #: ../../api.rst:15 msgid "" -"The API is disabled by default in the application. You may enable it in " -"your configuration or admin settings." +"The API is disabled by default in the application. You may enable it in your " +"configuration or admin settings." msgstr "" -"Standaard is de API in de applicatie uitgeschakeld. Je kunt deze " -"inschakelen in het configuratiescherm of beheerderpaneel." +"Standaard is de API in de applicatie uitgeschakeld. Je kunt deze inschakelen " +"in het configuratiescherm of beheerderpaneel." -#: ../../api.rst:18 ../../api.rst:71 +#: ../../api.rst:18 ../../api.rst:79 ../../api.rst:330 ../../api.rst:627 +#: ../../api.rst:687 msgid "Example" msgstr "Voorbeeld" #: ../../api.rst:24 -msgid "Authentication" -msgstr "Autorisatie" +msgid "Authenticating" +msgstr "Authenticatie" #: ../../api.rst:25 msgid "" -"Besides allowing the API to listen for requests, you will also need use " -"the generated API Auth Key. It can be found on the same page as in the " -"screenshot above. The configuration page will also display it, but only " -"partly. Feel free to alter the API Auth Key when required. The " -"application initially generates one randomly for you." +"Besides allowing the API to listen for requests, you will also need send your " +"API key with each request. The API key can be found on the same page as in the " +"screenshot above. The application generates one for you initially, but feel " +"free to alter the API key when required." msgstr "" -"Naast het inschakelen van de API zul je ook een (automatisch) " -"gegenereerde API autorisatiesleutel moeten gebruiken. Deze kun je " -"terugvinden op dezelfde pagina als in bovenstaand screenshot. Op de " -"configuratiepagina staat de sleutel ook, maar slechts ten delete. Pas " -"overigens gerust de API autorisatiesleutel naar wens aan. De applicatie " -"genereert initieel (eenmalig) een voor je." +"Naast het inschakelen van de API, zul je bij elke request de API-key moeten " +"meesturen. De API-key kun je inzien op dezelfde pagina's als die in de " +"screenshot hierboven. De applicatie maakt standaard een API-key voor je aan. " +"Deze kun je eventueel zelf wijzigen wanneer daar de behoefte voor is." #: ../../api.rst:29 msgid "" "You should pass it in the header of every API call. The header should be " "defined as ``X-AUTHKEY``. See below for an example." msgstr "" -"Je moet deze gebruiken voor elke API call die je uitvoert. De header heet" -" ``X-AUTHKEY``. Zie hieronder voor een voorbeeld." +"Je moet deze gebruiken voor elke API call die je uitvoert. De header heet ``X-" +"AUTHKEY``. Zie hieronder voor een voorbeeld." #: ../../api.rst:32 msgid "Examples" @@ -90,109 +89,496 @@ msgid "Using ``requests``::" msgstr "Met ``requests``::" #: ../../api.rst:50 -msgid "API calls" -msgstr "API calls" +msgid "[API v1] Remote datalogger" +msgstr "[API v1] Remote datalogger" -#: ../../api.rst:53 -msgid "POST ``/api/v1`` ``/datalogger/dsmrreading``" -msgstr "POST ``/api/v1`` ``/datalogger/dsmrreading``" +#: ../../api.rst:53 ../../api.rst:285 +msgid "``POST`` - ``datalogger/dsmrreading``" +msgstr "``POST`` - ``datalogger/dsmrreading``" -#: ../../api.rst:56 -msgid "Description" -msgstr "Beschrijving" +#: ../../api.rst:55 +msgid "" +"This allows you to insert a raw telegram, into the application as if it was " +"read locally using the serial cable." +msgstr "" +"Dit staat je toe om een ruwe telegram aan de applicatie door te geven, wanneer " +"je deze op afstand uitleest." -#: ../../api.rst:57 +#: ../../api.rst:59 msgid "" -"This allows you to insert a raw telegram, read from your meter remotely, " -"into the application as if it was read locally using the serial cable." +"Since ``DSMR-reader v1.6`` this call now returns ``HTTP 201`` instead of " +"``HTTP 200`` when successful." msgstr "" -"Dit staat je toe om een ruwe telegram aan de applicatie door te geven, " -"wanneer je deze op afstand uitleest." +"Sinds ``DSMR-reader v1.6`` geeft deze call ``HTTP 201`` terug in plaats van " +"``HTTP 200``, wanneer succesvol." + +#: ../../api.rst:63 ../../api.rst:299 ../../api.rst:404 ../../api.rst:606 +#: ../../api.rst:666 +msgid "URI" +msgstr "URI" + +#: ../../api.rst:64 +msgid "Full path: ``/api/v1/datalogger/dsmrreading``" +msgstr "Volledig pad: ``/api/v1/datalogger/dsmrreading``" -#: ../../api.rst:60 +#: ../../api.rst:68 ../../api.rst:304 ../../api.rst:409 ../../api.rst:611 +#: ../../api.rst:671 msgid "Parameters" msgstr "Parameters" -#: ../../api.rst:64 +#: ../../api.rst:70 msgid "" -"Since ``v1.6`` this call now returns ``HTTP 201`` instead of ``HTTP 200``" -" when successful." +"``telegram`` (*string*) - The raw telegram string containing all linefeeds ``" +"\\n``, and carriage returns ``\\r``, as well!" msgstr "" -"Sinds ``v1.6`` geeft deze call ``HTTP 201`` terug in plaats van ``HTTP " -"200``, wanneer succesvol." +"``telegram`` (*string*) - De ruwe telegram tekenreeks inclusief alle " +"regeleindes ``\\n`` en 'carriage returns' ``\\r``." -#: ../../api.rst:66 -msgid "Method: ``POST``" -msgstr "Methode: ``POST``" +#: ../../api.rst:74 ../../api.rst:325 ../../api.rst:420 ../../api.rst:622 +#: ../../api.rst:682 +msgid "Response" +msgstr "Respons (van de server)" -#: ../../api.rst:67 +#: ../../api.rst:75 msgid "" -"Data: ``telegram`` (as raw string containing all linefeeds ``\\n``, and " -"carriage returns ``\\r``, as well!)" -msgstr "" -"Data: ``telegram`` (als een ruwe tekenreeks inclusief zowel alle " -"regeleindes ``\\n`` als 'carriage returns' ``\\r``)" - -#: ../../api.rst:68 -msgid "Status code returned: ``HTTP 201`` on success, any other on failure." +"``HTTP 201`` on success, with empty body. Any other status code on failure." msgstr "" -"Status code resultaat: ``HTTP 201`` wanneer succesvol, elke andere code " +"``HTTP 201`` wanneer succesvol, met een lege respons. Elke andere status code " "bij falen." -#: ../../api.rst:73 +#: ../../api.rst:81 msgid "(using the ``requests`` library available on PIP)::" msgstr "(met de ``requests`` tool beschikbaar in PIP)::" -#: ../../api.rst:118 +#: ../../api.rst:125 msgid "Script" msgstr "Script" -#: ../../api.rst:119 +#: ../../api.rst:126 msgid "" -"Below is a more detailed script you can use to run via Supervisor. It " -"will send telegrams to one or multiple instances of DSMR-reader." +"Below is a more detailed script you can use to run via Supervisor. It will " +"send telegrams to one or multiple instances of DSMR-reader." msgstr "" -"Hieronder staat een uitgebreider script die je via Supervisor kan " -"draaien. Dit script stuurt telegrammen door naar één of meerdere " -"instanties van DSMR-reader." +"Hieronder staat een uitgebreider script die je via Supervisor kan draaien. Dit " +"script stuurt telegrammen door naar één of meerdere instanties van DSMR-reader." -#: ../../api.rst:124 +#: ../../api.rst:131 msgid "" -"You will still require the ``dsmr`` user and VirtualEnv, :doc:`as " -"discussed in the install guide` in **chapters 3 and 6**!" +"You will still require the ``dsmr`` user and VirtualEnv, :doc:`as discussed in " +"the install guide` in **chapters 3 and 6**!" msgstr "" -"Je hebt nog steeds de ``dsmr`` gebruiker en VirtualEnv nodig, :doc:`zoals" -" besproken in de installatiehandleiding` in **hoofdstukken " -"3 en 6**!" +"Je hebt nog steeds de ``dsmr`` gebruiker en VirtualEnv nodig, :doc:`zoals " +"besproken in de installatiehandleiding` in **hoofdstukken 3 en " +"6**!" -#: ../../api.rst:126 +#: ../../api.rst:133 msgid "**VirtualEnv**::" msgstr "**VirtualEnv**::" -#: ../../api.rst:135 +#: ../../api.rst:142 msgid "The serial connection in this example is based on ``DSMR v4``." msgstr "" -"De seriële verbinding in het voorbeeld hieronder is gebaseerd op ``DSMR " -"v4``." +"De seriële verbinding in het voorbeeld hieronder is gebaseerd op ``DSMR v4``." -#: ../../api.rst:139 +#: ../../api.rst:146 msgid "Don't forget to insert your own configuration below in ``API_SERVERS``." msgstr "" "Vergeet niet om je eigen configuratie hieronder in te stellen in " "``API_SERVERS``." -#: ../../api.rst:141 +#: ../../api.rst:148 msgid "Client file in ``/home/dsmr/dsmr_datalogger_api_client.py``::" msgstr "Client bestand in ``/home/dsmr/dsmr_datalogger_api_client.py``::" -#: ../../api.rst:243 +#: ../../api.rst:250 msgid "Supervisor config in ``/etc/supervisor/conf.d/dsmr-client.conf``::" msgstr "Supervisor configuratie in ``/etc/supervisor/conf.d/dsmr-client.conf``::" -#: ../../api.rst:260 +#: ../../api.rst:267 msgid "**Supervisor**::" msgstr "**Supervisor**::" -#~ msgid "Contents" -#~ msgstr "Inhoud" +#: ../../api.rst:277 +msgid "[API v2] RESTful API" +msgstr "[API v2] RESTful API" + +#: ../../api.rst:281 +msgid "These API calls are available since ``v1.7``." +msgstr "Deze API-calls zijn beschikbaar sinds ``v1.7``." + +#: ../../api.rst:287 +msgid "Creates a reading from direct values, omitting the need for the telegram." +msgstr "" +"Maakt een meting aan zonder het telegram op te sturen, op basis van de " +"meegegeven waarden." + +#: ../../api.rst:291 +msgid "" +"**Please note**: Readings are processed simultaneously. Inserting readings " +"**retroactively** might result in undesired results due to the data " +"processing, which is always reading ahead." +msgstr "" +"**Let op**: Metingen worden gelijktijdig verwerkt. Wanneer je metingen met " +"**terugwerkende kracht** toevoegt, kan dit leiden tot ongewenste resultaten in " +"de dataverwerking, die altijd vooruit werkt." + +#: ../../api.rst:293 +msgid "" +"Therefor inserting historic data might require you to delete all aggregated " +"data using the ``./manage.py dsmr_backend_delete_aggregated_data`` command." +msgstr "" +"Daarom kan het nodig zijn dat je alle geaggregeerde gegevens verwijdert met " +"het ``./manage.py dsmr_backend_delete_aggregated_data`` commando." + +#: ../../api.rst:295 +msgid "" +"This will process all readings again, from the very first start, and aggregate " +"them (and **will** take a long time depending on your reading count)." +msgstr "" +"Dit zorgt ervoor dat de applicatie alle metingen opnieuw inleest, helemaal " +"vanaf het begin, en ze opnieuw aggregeert. Dit **zal** lang duren, afhankelijk " +"van hoeveel metingen er opgeslagen zijn." + +#: ../../api.rst:300 ../../api.rst:405 +msgid "Full path: ``/api/v2/datalogger/dsmrreading``" +msgstr "Volledig pad: ``/api/v2/datalogger/dsmrreading``" + +#: ../../api.rst:305 +msgid "**[R]** = Required field" +msgstr "**[R]** = Verplicht veld" + +#: ../../api.rst:307 +msgid "" +"**[R]** ``timestamp`` (*datetime*) - Timestamp indicating when the reading was " +"taken, according to the smart meter" +msgstr "" +"**[R]** ``timestamp`` (*datetime*) - Moment waarop de meting is gedaan, " +"volgens de meter" + +#: ../../api.rst:308 +msgid "" +"**[R]** ``electricity_currently_delivered`` (*float*) - Current electricity " +"delivered in kW" +msgstr "" +"**[R]** ``electricity_currently_delivered`` (*float*) - Huidig " +"elektriciteitsverbruik in kW" + +#: ../../api.rst:309 +msgid "" +"**[R]** ``electricity_currently_returned`` (*float*) - Current electricity " +"returned in kW" +msgstr "" +"**[R]** ``electricity_currently_returned`` (*float*) - Huidige teruglevering " +"elektriciteit in kW" + +#: ../../api.rst:310 +msgid "" +"**[R]** ``electricity_delivered_1`` (*float*) - Meter position stating " +"electricity delivered (low tariff) in kWh" +msgstr "" +"**[R]** ``electricity_delivered_1`` (*float*) - Meterstand van verbruikte " +"elektriciteit (laagtarief) in kWh" + +#: ../../api.rst:311 +msgid "" +"**[R]** ``electricity_delivered_2`` (*float*) - Meter position stating " +"electricity delivered (normal tariff) in kWh" +msgstr "" +"**[R]** ``electricity_delivered_2`` (*float*) - Meterstand van verbruikte " +"elektriciteit (piektarief) in kWh" + +#: ../../api.rst:312 +msgid "" +"**[R]** ``electricity_returned_1`` (*float*) - Meter position stating " +"electricity returned (low tariff) in kWh" +msgstr "" +"**[R]** ``electricity_returned_1`` (*float*) - Meterstand van teruggeleverde " +"elektriciteit (laagtarief) in kWh" + +#: ../../api.rst:313 +msgid "" +"**[R]** ``electricity_returned_2`` (*float*) - Meter position stating " +"electricity returned (normal tariff) in kWh" +msgstr "" +"**[R]** ``electricity_returned_2`` (*float*) -Meterstand van teruggeleverde " +"elektriciteit (piektarief) in kWh" + +#: ../../api.rst:314 +msgid "" +"``phase_currently_delivered_l1`` (*float*) - Current electricity used by phase " +"L1 (in kW)" +msgstr "" +"``phase_currently_delivered_l1`` (*float*) - Huidig elektriciteitsverbruik in " +"fase L1 (in kW)" + +#: ../../api.rst:315 +msgid "" +"``phase_currently_delivered_l2`` (*float*) - Current electricity used by phase " +"L2 (in kW)" +msgstr "" +"``phase_currently_delivered_l2`` (*float*) - Huidig elektriciteitsverbruik in " +"fase L2 (in kW)" + +#: ../../api.rst:316 +msgid "" +"``phase_currently_delivered_l3`` (*float*) - Current electricity used by phase " +"L3 (in kW)" +msgstr "" +"``phase_currently_delivered_l3`` (*float*) - Huidig elektriciteitsverbruik in " +"fase L3 (in kW)" + +#: ../../api.rst:317 +msgid "" +"``extra_device_timestamp`` (*datetime*) - Last timestamp read from the extra " +"device connected (gas meter)" +msgstr "" +"``extra_device_timestamp`` (*datetime*) - Tijdstip van meting door de externe " +"(gas)meter" + +#: ../../api.rst:318 +msgid "" +"``extra_device_delivered`` (*float*) - Last value read from the extra device " +"connected (gas meter)" +msgstr "" +"``extra_device_delivered`` (*float*) - Meterstand van de externe (gas)meter" + +#: ../../api.rst:322 +msgid "" +"**datetime format** = ``YYYY-MM-DDThh:mm[:ss][+HH:MM|-HH:MM|Z]``, i.e.: " +"``2017-01-01T12:00:00+01`` (CET), ``2017-04-15T12:00:00+02`` (CEST) or " +"``2017-04-15T100:00:00Z`` (UTC)." +msgstr "" +"**datetime formaat** = ``YYYY-MM-DDThh:mm[:ss][+HH:MM|-HH:MM|Z]``, " +"bijvoorbeeld: ``2017-01-01T12:00:00+01`` (CET), ``2017-04-15T12:00:00+02`` " +"(CEST) of ``2017-04-15T100:00:00Z`` (UTC)." + +#: ../../api.rst:326 +msgid "" +"``HTTP 201`` on success. Body contains the reading created in JSON format. Any " +"other status code on failure." +msgstr "" +"``HTTP 201`` wanneer succesvol. Elke andere status code bij falen. De respons " +"bevat de aangemaakte meting in JSON-formaat." + +#: ../../api.rst:331 +msgid "**Data** to insert::" +msgstr "**Data** te verwerken::" + +#: ../../api.rst:342 ../../api.rst:431 ../../api.rst:487 ../../api.rst:541 +msgid "Using **cURL** (commandline)::" +msgstr "Met **cURL** (commandline)::" + +#: ../../api.rst:350 ../../api.rst:437 ../../api.rst:493 ../../api.rst:548 +msgid "Using **requests** (Python)::" +msgstr "Met ``requests`` (Python)::" + +#: ../../api.rst:375 ../../api.rst:453 ../../api.rst:509 ../../api.rst:564 +msgid "**Result**::" +msgstr "**Resultaat**::" + +#: ../../api.rst:398 +msgid "``GET`` - ``datalogger/dsmrreading``" +msgstr "``GET`` - ``datalogger/dsmrreading``" + +#: ../../api.rst:400 +msgid "" +"Retrieves any readings stored. The readings are either constructed from " +"incoming telegrams or were created using this API." +msgstr "" +"Haalt opgeslagen metingen op. De metingen komen ofwel voort uit telegrammen of " +"zijn handmatig aangemaakt via deze API." + +#: ../../api.rst:410 ../../api.rst:612 ../../api.rst:672 +msgid "All parameters are optional." +msgstr "Alle parameters zijn optioneel." + +#: ../../api.rst:412 +msgid "" +"``timestamp__gte`` (*datetime*) - Limits the result to any readings having a " +"timestamp **higher or equal** to this parameter." +msgstr "" +"``timestamp__gte`` (*datetime*) - Beperkt het resultaat tot alle " +"(datum)tijdstippen die **hoger of gelijk zijn aan** deze parameter." + +#: ../../api.rst:413 +msgid "" +"``timestamp__lte`` (*datetime*) - Limits the result to any readings having a " +"timestamp **lower or equal** to this parameter." +msgstr "" +"``timestamp__lte`` (*datetime*) - Beperkt het resultaat tot alle " +"(datum)tijdstippen die **lager of gelijk zijn aan** deze parameter." + +#: ../../api.rst:414 +msgid "" +"``ordering`` (*string*) - Use ``-timestamp`` to sort **descending**. Omit or " +"use ``timestamp`` to sort **ascending** (default)." +msgstr "" +"``ordering`` (*string*) - Gebruik ``-timestamp`` om **aflopend** te sorteren. " +"Laat de parameter weg of gebruik ``timestamp`` om **oplopend** te sorteren " +"(standaard)." + +#: ../../api.rst:415 ../../api.rst:617 ../../api.rst:677 +msgid "" +"``offset`` (*integer*) - When iterating large resultsets, the offset " +"determines the starting point." +msgstr "" +"``offset`` (*integer*) - Bij het doorlopen van vele resultaten kun je hiermee " +"het startpunt bepalen." + +#: ../../api.rst:416 ../../api.rst:618 ../../api.rst:678 +msgid "" +"``limit`` (*integer*) - Limits the resultset size returned. Omit for " +"maintaining the default limit (**25**)." +msgstr "" +"``limit`` (*integer*) - Beperkt het aantal resultaten dat teruggegeven wordt. " +"Laat deze parameter weg voor de standaardwaarde (**25**)." + +#: ../../api.rst:421 ../../api.rst:623 ../../api.rst:683 +msgid "" +"``HTTP 200`` on success. Body contains the result(s) in JSON format. Any other " +"status code on failure." +msgstr "" +"``HTTP 200`` wanneer succesvol. Het antwoord bevat de resultaten in JSON-" +"formaat. Geeft elke andere status code terug bij falen." + +#: ../../api.rst:427 +msgid "Example 1 - Fetch all readings" +msgstr "Voorbeeld 1 - Haal alle metingen op" + +#: ../../api.rst:428 +msgid "" +"This demonstrates how to fetch all readings stored, without using any of the " +"parameters." +msgstr "" +"Dit toont hoe je alle opgeslagen metingen kunt ophalen, zonder gebruik te " +"maken van de parameters." + +#: ../../api.rst:483 +msgid "Example 2 - Fetch latest reading" +msgstr "Voorbeeld 2 - Haal de laatste meting op" + +#: ../../api.rst:484 +msgid "" +"This demonstrates how to fetch the latest reading stored. Therefor we request " +"all readings, sort them descending by timestamp and limit the result to only " +"one." +msgstr "" +"Dit laat zien hoe je de laatst opgeslagen meting ophaalt. Hiervoor vragen we " +"alle metingen op, sorteren deze aflopend op tijdstip en beperken het resultaat " +"tot één." + +#: ../../api.rst:537 +msgid "Example 3 - Fetch readings by month" +msgstr "Voorbeeld 3 - Haal alle metingen van een specifieke maand op" + +#: ../../api.rst:538 +msgid "" +"This demonstrates how to fetch all readings within a month. We limit the " +"search by specifying the month start and end." +msgstr "" +"Dit toont hoe je alle metingen van één maand kan ophalen. Hiervoor beperken we " +"de zoekopdracht tot het begin en einde van de betreffende maand." + +#: ../../api.rst:594 +msgid "" +"Please note that all timestamps **returned** are in **UTC (CET -1 / CEST " +"-2)**. This is indicated as well by the timestamps ending with a 'Z' (Zulu " +"timezone)." +msgstr "" +"Let op dat alle **teruggegeven** (datum)tijdstippen in **UTC (CET -1 / CEST " +"-2)**-formaat zijn. Dit is ook te zien aan elke instantie die eindigt met " +"'Z' (Zulu tijdzone)." + +#: ../../api.rst:601 +msgid "``GET`` - ``statistics/day``" +msgstr "``GET`` - ``statistics/day``" + +#: ../../api.rst:602 +msgid "" +"Retrieves any **aggregated day statistics**. Please note that these are " +"generated a few hours **after midnight**." +msgstr "" +"Haalt **geaggregeerde dagstatistieken** op. Deze worden elke dag een paar uur " +"**ná middernacht** gegenereerd." + +#: ../../api.rst:607 +msgid "Full path: ``/api/v2/statistics/day``" +msgstr "Volledig pad: ``/api/v2/statistics/day``" + +#: ../../api.rst:614 +msgid "" +"``day__gte`` (*date*) - Limits the result to any statistics having their date " +"**higher or equal** to this parameter." +msgstr "" +"``day__gte`` (*date*) - Beperkt het resultaat tot alle datums die **hoger of " +"gelijk zijn aan** deze parameter." + +#: ../../api.rst:615 +msgid "" +"``day__lte`` (*date*) - Limits the result to any statistics having their date " +"**lower or equal** to this parameter." +msgstr "" +"``day__lte`` (*date*) - Beperkt het resultaat tot alle datums die **lager of " +"gelijk zijn aan** deze parameter." + +#: ../../api.rst:616 +msgid "" +"``ordering`` (*string*) - Use ``-day`` to sort **descending**. Omit or use " +"``day`` to sort **ascending** (default)." +msgstr "" +"``ordering`` (*string*) - Gebruik ``-day`` om **aflopend** te sorteren. Laat " +"de parameter weg of gebruik ``day`` om **oplopend** te sorteren (standaard)." + +#: ../../api.rst:628 ../../api.rst:688 +msgid "" +"All the :ref:`generic DSMRREADING examples ` apply " +"here as well, since only the ``timestamp`` field differs." +msgstr "" +"Alle :ref:`generieke DSMRREADING voorbeelden ` zijn " +"hier tevens van toepassing, gezien alleen het ``timestamp`` field afwijkt." + +#: ../../api.rst:630 ../../api.rst:690 +msgid "**Data structure returned**::" +msgstr "**Datastructuur**::" + +#: ../../api.rst:661 +msgid "``GET`` - ``statistics/hour``" +msgstr "``GET`` - ``statistics/hour``" + +#: ../../api.rst:662 +msgid "" +"Retrieves any **aggregated hourly statistics**. Please note that these are " +"generated a few hours **after midnight**." +msgstr "" +"Haalt **geaggregeerde uurstatistieken** op. Deze worden elke dag een paar uur " +"**ná middernacht** gegenereerd." + +#: ../../api.rst:667 +msgid "Full path: ``/api/v2/statistics/hour``" +msgstr "Volledig pad: ``/api/v2/statistics/hour``" + +#: ../../api.rst:674 +msgid "" +"``hour_start__gte`` (*datetime*) - Limits the result to any statistics having " +"their datetime (hour start) **higher or equal** to this parameter." +msgstr "" +"``hour_start__gte`` (*datetime*) - Beperkt het resultaat tot alle " +"(datum)tijdstippen die **hoger of gelijk zijn aan** deze parameter." + +#: ../../api.rst:675 +msgid "" +"``hour_start__lte`` (*datetime*) - Limits the result to any statistics having " +"their datetime (hour start) **lower or equal** to this parameter." +msgstr "" +"``hour_start__lte`` (*datetime*) - Beperkt het resultaat tot alle " +"(datum)tijdstippen die **lager of gelijk zijn aan** deze parameter." + +#: ../../api.rst:676 +msgid "" +"``ordering`` (*string*) - Use ``-hour_start`` to sort **descending**. Omit or " +"use ``hour_start`` to sort **ascending** (default)." +msgstr "" +"``ordering`` (*string*) - Gebruik ``-hour_start`` om **aflopend** te sorteren. " +"Laat de parameter weg of gebruik ``hour_start`` om **oplopend** te sorteren " +"(standaard)." diff --git a/docs/locale/nl/LC_MESSAGES/application.mo b/docs/locale/nl/LC_MESSAGES/application.mo index d5e3cafc8..9e68efb27 100644 Binary files a/docs/locale/nl/LC_MESSAGES/application.mo and b/docs/locale/nl/LC_MESSAGES/application.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/application.po b/docs/locale/nl/LC_MESSAGES/application.po index 7ed57bfa7..6bf85fb52 100644 --- a/docs/locale/nl/LC_MESSAGES/application.po +++ b/docs/locale/nl/LC_MESSAGES/application.po @@ -1,8 +1,9 @@ + msgid "" msgstr "" "Project-Id-Version: DSMR Reader v1.x\n" "Report-Msgid-Bugs-To: Dennis Siemensma \n" -"POT-Creation-Date: 2017-01-03 20:39+0100\n" +"POT-Creation-Date: 2017-05-02 21:26+0200\n" "PO-Revision-Date: 2017-01-03 20:41+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" @@ -10,36 +11,31 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" -"Language: nl\n" -"X-Generator: Poedit 1.8.7.1\n" #: ../../application.rst:2 msgid "Using the application" msgstr "De applicatie gebruiken" -#: ../../application.rst -msgid "Contents" -msgstr "Inhoudsopgave" - #: ../../application.rst:10 msgid "DSMR 2.x (legacy)" msgstr "DSMR 2.x (oude meters)" #: ../../application.rst:14 msgid "" -"Note: The application's default DSMR version used is 4.x. This version is also " -"the **default** for any recent smart meters placed at your home." +"Note: The application's default DSMR version used is 4.x. This version is" +" also the **default** for any recent smart meters placed at your home." msgstr "" -"N.B.: De standaard DSMR-versie die de applicatie gebruikt is DSMR 4.x. Dit is " -"namelijk de **standaardversie** voor nieuwe/recent geplaatste slimme meters." +"N.B.: De standaard DSMR-versie die de applicatie gebruikt is DSMR 4.x. " +"Dit is namelijk de **standaardversie** voor nieuwe/recent geplaatste " +"slimme meters." #: ../../application.rst:18 msgid "" -"Make sure to alter this setting in the backend's configuration page to DSMR 2.x " -"when required!" +"Make sure to alter this setting in the backend's configuration page to " +"DSMR 2.x when required!" msgstr "" -"Wanneer je een meter met DSMR 2.x hebt, zul je deze in het beheerderpaneel " -"onder ``dataloggerconfiguratie`` moet instellen." +"Wanneer je een meter met DSMR 2.x hebt, zul je deze in het " +"beheerderpaneel onder ``dataloggerconfiguratie`` moet instellen." #: ../../application.rst:22 msgid "Viewing the application" @@ -47,32 +43,34 @@ msgstr "Bekijk applicatie" #: ../../application.rst:23 msgid "" -"Now it's time to view the application in your browser to check whether the GUI " -"works as well. Just enter the ip address or hostname of your RaspberryPi in " -"your browser." +"Now it's time to view the application in your browser to check whether " +"the GUI works as well. Just enter the ip address or hostname of your " +"RaspberryPi in your browser." msgstr "" -"Dit is het moment om de applicatie te bekijken in je browser om te zien of " -"alles naar behoren werkt. Vul het IP-adres van je RaspberryPi in je browser." +"Dit is het moment om de applicatie te bekijken in je browser om te zien " +"of alles naar behoren werkt. Vul het IP-adres van je RaspberryPi in je " +"browser." #: ../../application.rst:25 msgid "" -"Did you install using a monitor attached to the RaspberryPi and you don't know " -"what address your device has? Just type ``ifconfig | grep addr`` and it should " -"display an ip address, for example::" +"Did you install using a monitor attached to the RaspberryPi and you don't" +" know what address your device has? Just type ``ifconfig | grep addr`` " +"and it should display an ip address, for example::" msgstr "" -"Heb je de applicatie geinstalleerd met een monitor aan je RaspberryPi en weet " -"je het IP-adres niet? Typ in dat geval het volgende in en je krijgt het IP-" -"adres te zien: ``ifconfig | grep addr``" +"Heb je de applicatie geinstalleerd met een monitor aan je RaspberryPi en " +"weet je het IP-adres niet? Typ in dat geval het volgende in en je krijgt " +"het IP-adres te zien: ``ifconfig | grep addr``" #: ../../application.rst:31 msgid "" -"In this example the ip address is ``192.168.178.150``. If possible, you should " -"assign a static ip address to your device in your router. This will make sure " -"you will always be able to find the application at the same location." +"In this example the ip address is ``192.168.178.150``. If possible, you " +"should assign a static ip address to your device in your router. This " +"will make sure you will always be able to find the application at the " +"same location." msgstr "" -"In dit voorbeeld is het IP-adres ``192.168.178.150``. Het is aan te raden om je " -"apparaat een vast IP-adres te geven in je router. Dit zorgt ervoor dat je de " -"applicatie altijd op dezelfde locatie kan terugvinden." +"In dit voorbeeld is het IP-adres ``192.168.178.150``. Het is aan te raden" +" om je apparaat een vast IP-adres te geven in je router. Dit zorgt ervoor" +" dat je de applicatie altijd op dezelfde locatie kan terugvinden." #: ../../application.rst:35 msgid "Reboot test" @@ -80,14 +78,14 @@ msgstr "Herstart-test" #: ../../application.rst:36 msgid "" -"You surely want to ``reboot`` your device and check whether everything comes up " -"automatically again with ``sudo supervisorctl status``. This will make sure " -"your data logger 'survives' any power surges." +"You surely want to ``reboot`` your device and check whether everything " +"comes up automatically again with ``sudo supervisorctl status``. This " +"will make sure your data logger 'survives' any power surges." msgstr "" -"Herstart het apparaat met ``reboot`` om te testen of alles automatisch opstart. " -"Je zou na de herstart alles moeten zien draaien via ``sudo supervisorctl " -"status``. Dit zorgt ervoor dat je datalogger eventuele stroomstoringen " -"overleeft (of wanneer je zelf de stroom eraf haalt)." +"Herstart het apparaat met ``reboot`` om te testen of alles automatisch " +"opstart. Je zou na de herstart alles moeten zien draaien via ``sudo " +"supervisorctl status``. Dit zorgt ervoor dat je datalogger eventuele " +"stroomstoringen overleeft (of wanneer je zelf de stroom eraf haalt)." #: ../../application.rst:40 msgid "Data preservation & backups" @@ -95,23 +93,23 @@ msgstr "Databehoud & backups" #: ../../application.rst:44 msgid "" -"You **should (or must)** make sure to periodically BACKUP your data! It's one " -"of the most common mistakes to skip or ignore this. Actually, it happened to " -"myself quite soon after I started, as I somehow managed to corrupt my SD " -"storage card, losing all my data on it. It luckily happened only a month after " -"running my own readings, but imagine all the data you'll lose when it will " -"contain readings taken over several years." +"You **should (or must)** make sure to periodically BACKUP your data! It's" +" one of the most common mistakes to skip or ignore this. Actually, it " +"happened to myself quite soon after I started, as I somehow managed to " +"corrupt my SD storage card, losing all my data on it. It luckily happened" +" only a month after running my own readings, but imagine all the data " +"you'll lose when it will contain readings taken over several years." msgstr "" -"Zorg ervoor dat je regelmatig **back-ups maakt**! Het is een veelvoorkomende " -"fout om dat niet te doen en zelf liep ik er ook al vrij snel tegenaan. Gelukkig " -"gebeurde dat vrij vroeg in het traject en was ik 'slechts' een maand aan " -"gegevens kwijt. Stel je voor dat je jaren met historische data kwijtraakt, zou " -"zonde zijn!" +"Zorg ervoor dat je regelmatig **back-ups maakt**! Het is een " +"veelvoorkomende fout om dat niet te doen en zelf liep ik er ook al vrij " +"snel tegenaan. Gelukkig gebeurde dat vrij vroeg in het traject en was ik " +"'slechts' een maand aan gegevens kwijt. Stel je voor dat je jaren met " +"historische data kwijtraakt, zou zonde zijn!" #: ../../application.rst:48 msgid "" -"The SD card is by far **the weakest link** of this setup and **will** fail you " -"some day." +"The SD card is by far **the weakest link** of this setup and **will** " +"fail you some day." msgstr "" "De SD-kaart is verreweg **de zwakste schakel** in dit geheel and **gaat " "gegarandeerd kapot** op een dag." @@ -122,86 +120,88 @@ msgstr "De applicatie maakt standaard elke nacht een back-up." #: ../../application.rst:52 msgid "" -"You can find them in the ``backups`` folder of the application. They either " -"have a ``.sql`` or ``.gz`` extension, depending on whether compression is " -"enabled in the backup configuration." +"You can find them in the ``backups`` folder of the application. They " +"either have a ``.sql`` or ``.gz`` extension, depending on whether " +"compression is enabled in the backup configuration." msgstr "" -"Je kunt ze terugvinden in de ``backups`` map van de applicatie. Ze zijn te " -"herkennen aan een ``.sql`` of ``.gz`` extensie, afhankelijk de gekozen " -"compressie-instellingen in de back-upconfiguratie." +"Je kunt ze terugvinden in de ``backups`` map van de applicatie. Ze zijn " +"te herkennen aan een ``.sql`` of ``.gz`` extensie, afhankelijk de gekozen" +" compressie-instellingen in de back-upconfiguratie." #: ../../application.rst:53 msgid "" -"However, as the data is still stored **locally** on your 'vulnerable' SD card, " -"you must export it off your RaspberryPi as well." +"However, as the data is still stored **locally** on your 'vulnerable' SD " +"card, you must export it off your RaspberryPi as well." msgstr "" -"Echter, hiermee staan de gegevens nog steeds **lokaal** op je 'kwestsbare' SD-" -"kaart. Je zult die data dus moeten kopieren naar een locatie buiten je " -"RaspberryPi om." +"Echter, hiermee staan de gegevens nog steeds **lokaal** op je " +"'kwestsbare' SD-kaart. Je zult die data dus moeten kopieren naar een " +"locatie buiten je RaspberryPi om." #: ../../application.rst:55 msgid "" -"There is an builtin option to have backups synced to your **Dropbox**, *without " -"exposing your Dropbox account and your private files in it*." +"There is an builtin option to have backups synced to your **Dropbox**, " +"*without exposing your Dropbox account and your private files in it*." msgstr "" -"Er is ingebouwde ondersteuning om back-ups naar je **Dropbox**-account te " -"uploaden. *Zonder dat de applicatie toegang tot je (privé) bestanden in je " -"Dropbox-account heeft*." +"Er is ingebouwde ondersteuning om back-ups naar je **Dropbox**-account " +"te uploaden. *Zonder dat de applicatie toegang tot je (privé) bestanden " +"in je Dropbox-account heeft*." #: ../../application.rst:57 msgid "" -"Please either use this service or manage offloading backups on your own (see " -"below)." +"Please either use this service or manage offloading backups on your own " +"(see below)." msgstr "" -"Zorg ervoor dat je ofwel deze feature gebruikt ofwel zelf back-ups maakt en ze " -"regelmatig ergens heen kopieert (zie hieronder)." +"Zorg ervoor dat je ofwel deze feature gebruikt ofwel zelf back-ups maakt " +"en ze regelmatig ergens heen kopieert (zie hieronder)." #: ../../application.rst:59 msgid "You may also decide to run backups outside the application." msgstr "" -"Je kunt er tevens voor kiezen om zelf backups buiten de applicatie om te maken." +"Je kunt er tevens voor kiezen om zelf backups buiten de applicatie om te " +"maken." #: ../../application.rst:61 msgid "" -"There are example backup scripts available in ``dsmrreader/provisioning/" -"postgresql/psql-backup.sh`` for **PostgreSQL**, which I dump to a separately " -"USB stick mounted on my RaspberryPi." +"There are example backup scripts available in " +"``dsmrreader/provisioning/postgresql/psql-backup.sh`` for **PostgreSQL**," +" which I dump to a separately USB stick mounted on my RaspberryPi." msgstr "" -"Er zijn voorbeeldscripts voor back-ups beschikbaar in ``dsmrreader/provisioning/" -"postgresql/psql-backup.sh`` voor **PostgreSQL**. Deze gebruik ik zelf om " -"handmatig een back-up te maken naar een USB-stick die aan mijn RaspberryPi " -"hangt." +"Er zijn voorbeeldscripts voor back-ups beschikbaar in " +"``dsmrreader/provisioning/postgresql/psql-backup.sh`` voor " +"**PostgreSQL**. Deze gebruik ik zelf om handmatig een back-up te maken " +"naar een USB-stick die aan mijn RaspberryPi hangt." #: ../../application.rst:63 msgid "" -"For **MySQL/MariaDB** you can use ``dsmrreader/provisioning/mysql/mysql-backup." -"sh``." +"For **MySQL/MariaDB** you can use ``dsmrreader/provisioning/mysql/mysql-" +"backup.sh``." msgstr "" -"Voor **MySQL/MariaDB** kun je dit script gebruiken: ``dsmrreader/provisioning/" -"mysql/mysql-backup.sh``." +"Voor **MySQL/MariaDB** kun je dit script gebruiken: " +"``dsmrreader/provisioning/mysql/mysql-backup.sh``." #: ../../application.rst:65 msgid "" -"Make sure to schedule the backup scripts as cronjob and also verify that it " -"actually works, by running ``run-parts -v /etc/cron.daily``." +"Make sure to schedule the backup scripts as cronjob and also verify that " +"it actually works, by running ``run-parts -v /etc/cron.daily``." msgstr "" -"Zorg ervoor dat je de back-ups via een 'daily cronjob' hebt ingepland en test " -"dat door het volgende uit te voeren: ``run-parts -v /etc/cron.daily``." +"Zorg ervoor dat je de back-ups via een 'daily cronjob' hebt ingepland en " +"test dat door het volgende uit te voeren: ``run-parts -v " +"/etc/cron.daily``." #: ../../application.rst:67 msgid "" -"Also, check your free disk space once in a while. I will implement automatic " -"cleanup settings later, allowing you to choose your own retention (for all the " -"source readings)." +"Also, check your free disk space once in a while. I will implement " +"automatic cleanup settings later, allowing you to choose your own " +"retention (for all the source readings)." msgstr "" -"Kijk ook af en toe of je nog voldoende schijfruimte hebt. Later zal ik de " -"mogelijkheid toevoegen om oude metingen (niet de dagtotalen) te verwijderen na " -"een X-periode." +"Kijk ook af en toe of je nog voldoende schijfruimte hebt. Later zal ik de" +" mogelijkheid toevoegen om oude metingen (niet de dagtotalen) te " +"verwijderen na een X-periode." #: ../../application.rst:69 msgid "" -"Everything OK? Congratulations, this was the hardest part and now the fun " -"begins by monitoring your energy consumption." +"Everything OK? Congratulations, this was the hardest part and now the fun" +" begins by monitoring your energy consumption." msgstr "" "Alles gelukt? Gefeliciteerd! Dit was het lastigste gedeelte en nu kun je " "eindelijk gebruikmaken van de applicatie en je energieverbruik meten." @@ -220,59 +220,60 @@ msgstr "Waarschuwing voor publiekelijke toegang" #: ../../application.rst:85 msgid "" -"If you expose your application to the outside world or a public network, you " -"might want to take additional steps:" +"If you expose your application to the outside world or a public network, " +"you might want to take additional steps:" msgstr "" "Wanneer je de applicatie koppelt aan het Internet wil je sowieso extra " "maatregelen nemen:" #: ../../application.rst:87 msgid "" -"Please make sure to **alter** the ``SECRET_KEY`` setting in your ``dsmrreader/" -"settings.py``." +"Please make sure to **alter** the ``SECRET_KEY`` setting in your " +"``dsmrreader/settings.py``." msgstr "" "Zorg ervoor dat je de instelling ``SECRET_KEY`` wijzigt in het bestand " "``dsmrreader/settings.py``." #: ../../application.rst:89 msgid "" -"Don't forget to run ``./post-deploy.sh`` in the project's root, which will " -"force the application to gracefully reload itself and apply the new settings " -"instantly." +"Don't forget to run ``./post-deploy.sh`` in the project's root, which " +"will force the application to gracefully reload itself and apply the new " +"settings instantly." msgstr "" -"Vergeet vooral niet om daarna ``./post-deploy.sh`` uit te voeren (staat in " -"hoogste map van project), die ervoor zorgt dat de hele applicatie zichzelf " -"herlaadt en daarmee de nieuwe instellingen per direct gebruikt." +"Vergeet vooral niet om daarna ``./post-deploy.sh`` uit te voeren (staat " +"in hoogste map van project), die ervoor zorgt dat de hele applicatie " +"zichzelf herlaadt en daarmee de nieuwe instellingen per direct gebruikt." #: ../../application.rst:91 msgid "" -"Install a firewall, such as ``ufw`` `UncomplicatedFirewall `_ and restrict traffic to port ``22`` (only for " -"yourself) and port ``80``." +"Install a firewall, such as ``ufw`` `UncomplicatedFirewall " +"`_ and restrict traffic to" +" port ``22`` (only for yourself) and port ``80``." msgstr "" -"Installeer een firewall, zoals ``ufw`` (`UncomplicatedFirewall `_) and beperk al het toegestane verkeer tot " -"poort ``22`` (puur voor jezelf) en poort ``80``." +"Installeer een firewall, zoals ``ufw`` (`UncomplicatedFirewall " +"`_) and beperk al het " +"toegestane verkeer tot poort ``22`` (puur voor jezelf) en poort ``80``." #: ../../application.rst:93 msgid "" -"You should also have Nginx restrict application access when exposing it to the " -"Internet. Simply generate an htpasswd string `using one of the many generators " -"found online `_." +"You should also have Nginx restrict application access when exposing it " +"to the Internet. Simply generate an htpasswd string `using one of the " +"many generators found online `_." msgstr "" -"Je zou in Nginx de toegang moeten beperken door altijd inloggegevens te vragen " -"voor het bekijken van de applicatie. Genereer een zogenaamde htpasswd-" -"tekenreeks via `een van de vele websites die dit voor je kunnen doen `_." +"Je zou in Nginx de toegang moeten beperken door altijd inloggegevens te " +"vragen voor het bekijken van de applicatie. Genereer een zogenaamde " +"htpasswd-tekenreeks via `een van de vele websites die dit voor je kunnen " +"doen `_." #: ../../application.rst:97 msgid "" -"It's safe to a htpasswd generator, **just make sure to NEVER enter personal " -"credentials** there **used for other applications or personal accounts**." +"It's safe to a htpasswd generator, **just make sure to NEVER enter " +"personal credentials** there **used for other applications or personal " +"accounts**." msgstr "" "Je kunt een htpassw-generator veilig gebruiken **zolang je maar NOOIT " -"persoonlijke informatie of gegevens invoert die je ook op andere websites " -"gebruikt**!" +"persoonlijke informatie of gegevens invoert die je ook op andere websites" +" gebruikt**!" #: ../../application.rst:99 msgid "Paste the htpasswd string in ``/etc/nginx/htpasswd``." @@ -280,11 +281,11 @@ msgstr "Plak de htpasswd-tekenreeks in het bestand ``/etc/nginx/htpasswd``." #: ../../application.rst:101 msgid "" -"Open the site's vhost in ``/etc/nginx/sites-enabled/dsmr-webinterface`` and " -"**uncomment** the following lines (remove the ##)::" +"Open the site's vhost in ``/etc/nginx/sites-enabled/dsmr-webinterface`` " +"and **uncomment** the following lines (remove the ##)::" msgstr "" -"Open de website's vhost in ``/etc/nginx/sites-enabled/dsmr-webinterface`` en " -"schakel de volgende regels in (verwijder de ## tekens)::" +"Open de website's vhost in ``/etc/nginx/sites-enabled/dsmr-webinterface``" +" en schakel de volgende regels in (verwijder de ## tekens)::" #: ../../application.rst:106 msgid "Now make sure you didn't insert any typo's by running::" @@ -297,10 +298,12 @@ msgstr "En herlaad met::" #: ../../application.rst:114 msgid "" "You should be prompted for login credentials the next time your browser " -"accesses the application. For more information regarding this topic, see the " -"`Nginx docs `_." +"accesses the application. For more information regarding this topic, see " +"the `Nginx docs `_." msgstr "" -"Als het goed is krijg je de volgende keer dat je de applicatie bekijkt een pop-" -"up te zien, waarin je gevraagd wordt om in te loggen. Voor meer informatie over " -"dit onderwerp `zie de Nginx documentatie `_." +"Als het goed is krijg je de volgende keer dat je de applicatie bekijkt " +"een pop-up te zien, waarin je gevraagd wordt om in te loggen. Voor meer " +"informatie over dit onderwerp `zie de Nginx documentatie " +"`_." + diff --git a/docs/locale/nl/LC_MESSAGES/changelog.mo b/docs/locale/nl/LC_MESSAGES/changelog.mo index 145157ca4..ea0ef4d7b 100644 Binary files a/docs/locale/nl/LC_MESSAGES/changelog.mo and b/docs/locale/nl/LC_MESSAGES/changelog.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/changelog.po b/docs/locale/nl/LC_MESSAGES/changelog.po index 3649b4d2a..0eaabe326 100644 --- a/docs/locale/nl/LC_MESSAGES/changelog.po +++ b/docs/locale/nl/LC_MESSAGES/changelog.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader v1.x\n" "Report-Msgid-Bugs-To: Dennis Siemensma \n" -"POT-Creation-Date: 2017-03-17 10:56+0100\n" +"POT-Creation-Date: 2017-05-04 18:14+0200\n" "PO-Revision-Date: 2017-01-21 14:17+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" @@ -55,35 +55,108 @@ msgstr "" "fixes-new-features>`_." #: ../../changelog.rst:22 -msgid "v1.6.0 - 2017-xx-xx" +msgid "v1.7.0 - 2017-05-04" msgstr "" #: ../../changelog.rst:26 msgid "" +"Please note that the ``dsmr_datalogger.0007_dsmrreading_timestamp_index``" +" migration **will take quite some time**, as it adds an index on one of " +"the largest database tables!" +msgstr "" + +#: ../../changelog.rst:28 +msgid "" +"It takes **around two minutes** on a RaspberryPi 2 & 3 with ``4.3+ " +"million`` readings on PostgreSQL. Results may differ on **slower " +"RaspberryPi's** or **with MySQL**." +msgstr "" + +#: ../../changelog.rst:33 +msgid "" +"The API-docs for the new v2 API `can be found here `_." +msgstr "" + +#: ../../changelog.rst:36 ../../changelog.rst:45 ../../changelog.rst:55 +#: ../../changelog.rst:77 ../../changelog.rst:95 ../../changelog.rst:104 +#: ../../changelog.rst:114 ../../changelog.rst:123 ../../changelog.rst:133 +#: ../../changelog.rst:142 ../../changelog.rst:160 ../../changelog.rst:179 +#: ../../changelog.rst:204 ../../changelog.rst:217 ../../changelog.rst:232 +#: ../../changelog.rst:240 ../../changelog.rst:251 ../../changelog.rst:263 +#: ../../changelog.rst:280 ../../changelog.rst:288 ../../changelog.rst:296 +#: ../../changelog.rst:310 +msgid "**Tickets resolved in this release:**" +msgstr "**Tickets die opgelost zijn in deze release:**" + +#: ../../changelog.rst:38 +#, fuzzy +msgid "" +"[`#230 `_] " +"Support for exporting data via API" +msgstr "" +"De ondersteuning voor ``Python 3.3`` is **vervallen** wegens de Django " +"upgrade (`#103 `_)." + +#: ../../changelog.rst:43 +msgid "v1.6.2 - 2017-04-23" +msgstr "" + +#: ../../changelog.rst:47 +#, fuzzy +msgid "" +"[`#269 `_] " +"x-as gasgrafiek geeft rare waarden aan" +msgstr "" +"De ondersteuning voor ``Python 3.3`` is **vervallen** wegens de Django " +"upgrade (`#103 `_)." + +#: ../../changelog.rst:48 +#, fuzzy +msgid "" +"[`#303 `_] " +"Archive page's default day sorting" +msgstr "" +"De ondersteuning voor ``Python 3.3`` is **vervallen** wegens de Django " +"upgrade (`#103 `_)." + +#: ../../changelog.rst:53 +msgid "v1.6.1 - 2017-04-06" +msgstr "" + +#: ../../changelog.rst:57 +#, fuzzy +msgid "" +"[`#298 `_] " +"Update requirements (Django 1.10.7)" +msgstr "" +"De ondersteuning voor ``Python 3.3`` is **vervallen** wegens de Django " +"upgrade (`#103 `_)." + +#: ../../changelog.rst:62 +msgid "v1.6.0 - 2017-03-18" +msgstr "" + +#: ../../changelog.rst:66 +msgid "" "Support for ``MySQL`` has been **deprecated** since ``DSMR-reader v1.6`` " "and will be discontinued completely in a later release. Please use a " "PostgreSQL database instead. Users already running MySQL will be " "supported in easily migrating to PostgreSQL in the future." msgstr "" -#: ../../changelog.rst:31 +#: ../../changelog.rst:71 msgid "" "**Change in API:** The telegram creation API now returns an ``HTTP 201`` " "response when successful. An ``HTTP 200`` was returned in former " "versions. :doc:`View API docs`." msgstr "" -#: ../../changelog.rst:37 ../../changelog.rst:55 ../../changelog.rst:64 -#: ../../changelog.rst:74 ../../changelog.rst:83 ../../changelog.rst:93 -#: ../../changelog.rst:102 ../../changelog.rst:120 ../../changelog.rst:139 -#: ../../changelog.rst:164 ../../changelog.rst:177 ../../changelog.rst:192 -#: ../../changelog.rst:200 ../../changelog.rst:211 ../../changelog.rst:223 -#: ../../changelog.rst:240 ../../changelog.rst:248 ../../changelog.rst:256 -#: ../../changelog.rst:270 -msgid "**Tickets resolved in this release:**" -msgstr "**Tickets die opgelost zijn in deze release:**" - -#: ../../changelog.rst:39 +#: ../../changelog.rst:79 #, fuzzy msgid "" "[`#221 `_] " @@ -93,7 +166,7 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:40 +#: ../../changelog.rst:80 #, fuzzy msgid "" "[`#237 `_] " @@ -103,7 +176,7 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:41 +#: ../../changelog.rst:81 #, fuzzy msgid "" "[`#249 `_] " @@ -113,7 +186,7 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:42 +#: ../../changelog.rst:82 #, fuzzy msgid "" "[`#232 `_] " @@ -123,19 +196,19 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:43 +#: ../../changelog.rst:83 msgid "" "[`#260 `_] Add" " link to readthedocs in Django for Dropbox instructions." msgstr "" -#: ../../changelog.rst:44 +#: ../../changelog.rst:84 msgid "" "[`#211 `_] API" " request should return HTTP 201 instead of HTTP 200." msgstr "" -#: ../../changelog.rst:45 +#: ../../changelog.rst:85 #, fuzzy msgid "" "[`#191 `_] " @@ -145,7 +218,7 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:46 +#: ../../changelog.rst:86 #, fuzzy msgid "" "[`#251 `_] " @@ -155,7 +228,7 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:47 +#: ../../changelog.rst:87 #, fuzzy msgid "" "[`#257 `_] " @@ -165,7 +238,7 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:48 +#: ../../changelog.rst:88 #, fuzzy msgid "" "[`#274 `_] " @@ -175,81 +248,81 @@ msgstr "" "upgrade (`#103 `_)." -#: ../../changelog.rst:53 ../../changelog.rst:72 +#: ../../changelog.rst:93 ../../changelog.rst:112 msgid "v1.5.5 - 2017-01-19" msgstr "" -#: ../../changelog.rst:57 ../../changelog.rst:76 +#: ../../changelog.rst:97 ../../changelog.rst:116 msgid "" "Remove readonly restriction for editing statistics in admin interface " "(`#242 `_)." msgstr "" -#: ../../changelog.rst:62 ../../changelog.rst:81 +#: ../../changelog.rst:102 ../../changelog.rst:121 msgid "v1.5.4 - 2017-01-12" msgstr "" -#: ../../changelog.rst:66 ../../changelog.rst:85 +#: ../../changelog.rst:106 ../../changelog.rst:125 msgid "" "Improve datalogger for DSMR v5.0 (`#212 " "`_)." msgstr "" -#: ../../changelog.rst:67 ../../changelog.rst:86 +#: ../../changelog.rst:107 ../../changelog.rst:126 msgid "" "Fixed another bug in MinderGas API client implementation (`#228 " "`_)." msgstr "" -#: ../../changelog.rst:91 +#: ../../changelog.rst:131 msgid "v1.5.3 - 2017-01-11" msgstr "" -#: ../../changelog.rst:95 +#: ../../changelog.rst:135 msgid "" "Improve MinderGas API client implementation (`#228 " "`_)." msgstr "" -#: ../../changelog.rst:100 +#: ../../changelog.rst:140 msgid "v1.5.2 - 2017-01-09" msgstr "" -#: ../../changelog.rst:104 +#: ../../changelog.rst:144 msgid "" "Automatic refresh of dashboard charts (`#210 " "`_)." msgstr "" -#: ../../changelog.rst:105 +#: ../../changelog.rst:145 msgid "" "Mindergas.nl API: Tijdstip van verzending willekeurig maken (`#204 " "`_)." msgstr "" -#: ../../changelog.rst:106 +#: ../../changelog.rst:146 msgid "" "Extend API docs with additional example (`#185 " "`_)." msgstr "" -#: ../../changelog.rst:107 +#: ../../changelog.rst:147 msgid "" "Docs: How to restore backup (`#190 `_)." msgstr "" -#: ../../changelog.rst:108 +#: ../../changelog.rst:148 msgid "" "Log errors occured to file (`#181 `_)." msgstr "" -#: ../../changelog.rst:113 +#: ../../changelog.rst:153 msgid "v1.5.1 - 2017-01-04" msgstr "" -#: ../../changelog.rst:117 +#: ../../changelog.rst:157 msgid "" "This patch contains no new features and **only solves upgrading issues** " "for some users." @@ -257,7 +330,7 @@ msgstr "" "Deze patch bevat geen nieuwe features en **lost alleen upgrade-problemen " "op** voor sommige gebruikers." -#: ../../changelog.rst:122 +#: ../../changelog.rst:162 msgid "" "Fix for issues `#200 `_ & `#217 `_)." -#: ../../changelog.rst:132 +#: ../../changelog.rst:172 msgid "" "There is **experimental support** for ``Python 3.6`` and ``Python 3.7 " "(nightly)`` as the unittests are `now built against those versions `_ (`#167 `_)." -#: ../../changelog.rst:134 +#: ../../changelog.rst:174 msgid "**Legacy warning**" msgstr "**Waarschuwing voor legacy**" -#: ../../changelog.rst:136 +#: ../../changelog.rst:176 msgid "" "The migrations that were squashed together in (`#31 " "`_) have been " @@ -319,7 +392,7 @@ msgstr "" "**verwijderd**. Hier heb je alleen maar te maken wanneer je nog gebruik " "maakt van een dsmrreader-versie **vóór** ``v0.13 (β)``." -#: ../../changelog.rst:137 +#: ../../changelog.rst:177 msgid "" "If you are indeed still running ``< v0.13 (β)``, please upgrade to " "``v1.4`` first (!), followed by an upgrade to ``v1.5``." @@ -327,409 +400,409 @@ msgstr "" "Wanneer je nog steeds een versie van vóór ``< v0.13 (β)`` draait, upgrade" " dan eerst naar ``v1.4`` (!), gevolgd door een upgrade naar ``v1.5``." -#: ../../changelog.rst:141 +#: ../../changelog.rst:181 msgid "" "Verify telegrams' CRC (`#188 `_)." msgstr "" -#: ../../changelog.rst:142 +#: ../../changelog.rst:182 msgid "" "Display last 24 hours on dashboard (`#164 " "`_)." msgstr "" -#: ../../changelog.rst:143 +#: ../../changelog.rst:183 msgid "" "Status page visualisation (`#172 `_)." msgstr "" -#: ../../changelog.rst:144 +#: ../../changelog.rst:184 msgid "" "Store and display phases consumption (`#161 " "`_)." msgstr "" -#: ../../changelog.rst:145 +#: ../../changelog.rst:185 msgid "" "Weather graph not showing when no gas data is available (`#170 " "`_)." msgstr "" -#: ../../changelog.rst:146 +#: ../../changelog.rst:186 msgid "" "Upgrade to ChartJs 2.0 (`#127 `_)." msgstr "" -#: ../../changelog.rst:147 +#: ../../changelog.rst:187 msgid "" "Improve Statistics page performance (`#173 " "`_)." msgstr "" -#: ../../changelog.rst:148 +#: ../../changelog.rst:188 msgid "" "Version checker at github (`#166 `_)." msgstr "" -#: ../../changelog.rst:149 +#: ../../changelog.rst:189 msgid "" "Remove required login for dismissal of in-app notifications (`#179 " "`_)." msgstr "" -#: ../../changelog.rst:150 +#: ../../changelog.rst:190 msgid "" "Round numbers displayed in GUI to 2 decimals (`#183 " "`_)." msgstr "" -#: ../../changelog.rst:151 +#: ../../changelog.rst:191 msgid "" "Switch Nosetests to Pytest (+ pytest-cov) (`#167 " "`_)." msgstr "" -#: ../../changelog.rst:152 +#: ../../changelog.rst:192 msgid "" "PyLama code audit (+ pytest-cov) (`#158 " "`_)." msgstr "" -#: ../../changelog.rst:153 +#: ../../changelog.rst:193 msgid "" "Double upgrade of Django framework ``Django 1.8`` -> ``Django 1.9`` -> " "``Django 1.10`` (`#103 `_)." msgstr "" -#: ../../changelog.rst:154 +#: ../../changelog.rst:194 msgid "" "Force ``PYTHONUNBUFFERED`` for supervisor commands (`#176 " "`_)." msgstr "" -#: ../../changelog.rst:155 +#: ../../changelog.rst:195 msgid "" "Documentation updates for v1.5 (`#171 `_)." msgstr "" -#: ../../changelog.rst:156 +#: ../../changelog.rst:196 msgid "" "Requirements update for v1.5 (december 2016) (`#182 " "`_)." msgstr "" -#: ../../changelog.rst:157 +#: ../../changelog.rst:197 msgid "" "Improved backend process logging (`#184 " "`_)." msgstr "" -#: ../../changelog.rst:162 +#: ../../changelog.rst:202 msgid "v1.4.1 - 2016-12-12" msgstr "" -#: ../../changelog.rst:166 +#: ../../changelog.rst:206 msgid "" "Consumption chart hangs due to unique_key violation (`#174 " "`_)." msgstr "" -#: ../../changelog.rst:167 +#: ../../changelog.rst:207 msgid "" "NoReverseMatch at / Reverse for 'docs' (`#175 " "`_)." msgstr "" -#: ../../changelog.rst:172 +#: ../../changelog.rst:212 msgid "v1.4.0 - 2016-11-28" msgstr "" -#: ../../changelog.rst:175 +#: ../../changelog.rst:215 msgid "" "Support for ``Python 3.5`` has been added officially (`#55 " "`_)." msgstr "" -#: ../../changelog.rst:179 +#: ../../changelog.rst:219 msgid "" "Push notifications for Notify My Android / Prowl (iOS), written by Jeroen" " Peters (`#152 `_)." msgstr "" -#: ../../changelog.rst:180 +#: ../../changelog.rst:220 msgid "" "Support for both single and high/low tariff (`#130 " "`_)." msgstr "" -#: ../../changelog.rst:181 +#: ../../changelog.rst:221 msgid "" "Add new note from Dashboard has wrong time format (`#159 " "`_)." msgstr "" -#: ../../changelog.rst:182 +#: ../../changelog.rst:222 msgid "" "Display estimated price for current usage in Dashboard (`#155 " "`_)." msgstr "" -#: ../../changelog.rst:183 +#: ../../changelog.rst:223 msgid "" "Dropbox API v1 deprecated in June 2017 (`#142 " "`_)." msgstr "" -#: ../../changelog.rst:184 +#: ../../changelog.rst:224 msgid "" "Improve code coverage (`#151 `_)." msgstr "" -#: ../../changelog.rst:185 +#: ../../changelog.rst:225 msgid "" "Restyle configuration overview (`#156 `_)." msgstr "" -#: ../../changelog.rst:186 +#: ../../changelog.rst:226 msgid "" "Capability based push notifications (`#165 " "`_)." msgstr "" -#: ../../changelog.rst:191 +#: ../../changelog.rst:231 msgid "v1.3.2 - 2016-11-08" msgstr "" -#: ../../changelog.rst:194 +#: ../../changelog.rst:234 msgid "" "Requirements update (november 2016) (`#150 " "`_)." msgstr "" -#: ../../changelog.rst:199 +#: ../../changelog.rst:239 msgid "v1.3.1 - 2016-08-16" msgstr "" -#: ../../changelog.rst:202 +#: ../../changelog.rst:242 msgid "" "CSS large margin-bottom (`#144 `_)." msgstr "" -#: ../../changelog.rst:203 +#: ../../changelog.rst:243 msgid "" "Django security releases issued: 1.8.14 (`#147 " "`_)." msgstr "" -#: ../../changelog.rst:204 +#: ../../changelog.rst:244 msgid "" "Requirements update (August 2016) (`#148 " "`_)." msgstr "" -#: ../../changelog.rst:205 +#: ../../changelog.rst:245 msgid "" "Query performance improvements (`#149 `_)." msgstr "" -#: ../../changelog.rst:210 +#: ../../changelog.rst:250 msgid "v1.3.0 - 2016-07-15" msgstr "" -#: ../../changelog.rst:213 +#: ../../changelog.rst:253 msgid "" "API endpoint for datalogger (`#140 `_)." msgstr "" -#: ../../changelog.rst:214 +#: ../../changelog.rst:254 msgid "" "Colors for charts (`#137 `_)." msgstr "" -#: ../../changelog.rst:215 +#: ../../changelog.rst:255 msgid "" "Data export: Mindergas.nl (`#10 `_)." msgstr "" -#: ../../changelog.rst:216 +#: ../../changelog.rst:256 msgid "" "Requirement upgrade (`#143 `_)." msgstr "" -#: ../../changelog.rst:217 +#: ../../changelog.rst:257 msgid "" "Installation wizard for first time use (`#139 " "`_)." msgstr "" -#: ../../changelog.rst:222 +#: ../../changelog.rst:262 msgid "v1.2.0 - 2016-05-18" msgstr "" -#: ../../changelog.rst:225 +#: ../../changelog.rst:265 msgid "" "Energy supplier prices does not indicate tariff type (Django admin) " "(`#126 `_)." msgstr "" -#: ../../changelog.rst:226 +#: ../../changelog.rst:266 msgid "" "Requirements update (`#128 `_)." msgstr "" -#: ../../changelog.rst:227 +#: ../../changelog.rst:267 msgid "" "Force backup (`#123 `_)." msgstr "" -#: ../../changelog.rst:228 +#: ../../changelog.rst:268 msgid "" "Update clean-install.md (`#131 `_)." msgstr "" -#: ../../changelog.rst:229 +#: ../../changelog.rst:269 msgid "" "Improve data export field names (`#132 " "`_)." msgstr "" -#: ../../changelog.rst:230 +#: ../../changelog.rst:270 msgid "" "Display average temperature in archive (`#122 " "`_)." msgstr "" -#: ../../changelog.rst:231 +#: ../../changelog.rst:271 msgid "" "Pie charts on trends page overlap their canvas (`#136 " "`_)." msgstr "" -#: ../../changelog.rst:232 +#: ../../changelog.rst:272 msgid "" "'Slumber' consumption (`#115 `_)." msgstr "" -#: ../../changelog.rst:233 +#: ../../changelog.rst:273 msgid "" "Show lowest & highest Watt peaks (`#138 " "`_)." msgstr "" -#: ../../changelog.rst:234 +#: ../../changelog.rst:274 msgid "" "Allow day & hour statistics reset due to changing energy prices (`#95 " "`_)." msgstr "" -#: ../../changelog.rst:239 +#: ../../changelog.rst:279 msgid "v1.1.2 - 2016-05-01" msgstr "" -#: ../../changelog.rst:242 +#: ../../changelog.rst:282 msgid "" "Trends page giving errors (when lacking data) (`#125 " "`_)." msgstr "" -#: ../../changelog.rst:247 +#: ../../changelog.rst:287 msgid "v1.1.1 - 2016-04-27" msgstr "" -#: ../../changelog.rst:250 +#: ../../changelog.rst:290 msgid "" "Improve readme (`#124 `_)." msgstr "" -#: ../../changelog.rst:255 +#: ../../changelog.rst:295 msgid "v1.1.0 - 2016-04-23" msgstr "" -#: ../../changelog.rst:258 +#: ../../changelog.rst:298 msgid "" "Autorefresh dashboard (`#117 `_)." msgstr "" -#: ../../changelog.rst:259 +#: ../../changelog.rst:299 msgid "" "Improve line graphs' visibility (`#111 " "`_)." msgstr "" -#: ../../changelog.rst:260 +#: ../../changelog.rst:300 msgid "" "Easily add notes (`#110 `_)." msgstr "" -#: ../../changelog.rst:261 +#: ../../changelog.rst:301 msgid "" "Export data points in CSV format (`#2 `_)." msgstr "" -#: ../../changelog.rst:262 +#: ../../changelog.rst:302 msgid "" "Allow day/month/year comparison (`#94 `_)." msgstr "" -#: ../../changelog.rst:263 +#: ../../changelog.rst:303 msgid "" "Docs: Add FAQ and generic application info (`#113 " "`_)." msgstr "" -#: ../../changelog.rst:264 +#: ../../changelog.rst:304 msgid "" "Support for Iskra meter (DSMR 2.x) (`#120 " "`_)." msgstr "" -#: ../../changelog.rst:269 +#: ../../changelog.rst:309 msgid "v1.0.1 - 2016-04-07" msgstr "" -#: ../../changelog.rst:272 +#: ../../changelog.rst:312 msgid "" "Update licence to OSI compatible one (`#119 " "`_)." msgstr "" -#: ../../changelog.rst:277 +#: ../../changelog.rst:317 msgid "v1.0.0 - 2016-04-07" msgstr "" -#: ../../changelog.rst:278 +#: ../../changelog.rst:318 msgid "First official stable release." msgstr "Eerste officiële stabiele release." -#: ../../changelog.rst:283 +#: ../../changelog.rst:323 msgid "[β] v0.1 (2015-10-29) to 0.16 (2016-04-06)" msgstr "" -#: ../../changelog.rst:286 +#: ../../changelog.rst:326 msgid "" "All previous beta releases/changes have been combined to a single list " "below." @@ -737,472 +810,444 @@ msgstr "" "Alle vorige bèta releases/veranderingen zijn gecombineerd tot een enkele " "lijst hieronder." -#: ../../changelog.rst:288 +#: ../../changelog.rst:328 msgid "" "Move documentation to wiki or RTD (`#90 " "`_)." msgstr "" -#: ../../changelog.rst:289 +#: ../../changelog.rst:329 msgid "" "Translate README to Dutch (`#16 `_)." msgstr "" -#: ../../changelog.rst:290 +#: ../../changelog.rst:330 msgid "" "Delete (recent) history page (`#112 `_)." msgstr "" -#: ../../changelog.rst:291 +#: ../../changelog.rst:331 msgid "" "Display most recent temperature in dashboard (`#114 " "`_)." msgstr "" -#: ../../changelog.rst:292 +#: ../../changelog.rst:332 msgid "" "Upgrade Django to 1.8.12 (`#118 `_)." msgstr "" -#: ../../changelog.rst:294 +#: ../../changelog.rst:334 msgid "" "Redesign trends page (`#97 `_)." msgstr "" -#: ../../changelog.rst:295 +#: ../../changelog.rst:335 msgid "" "Support for summer time (`#105 `_)." msgstr "" -#: ../../changelog.rst:296 +#: ../../changelog.rst:336 msgid "" "Support for Daylight Saving Time (DST) transition (`#104 " "`_)." msgstr "" -#: ../../changelog.rst:297 +#: ../../changelog.rst:337 msgid "" "Add (error) hints to status page (`#106 " "`_)." msgstr "" -#: ../../changelog.rst:298 +#: ../../changelog.rst:338 msgid "" "Keep track of version (`#108 `_)." msgstr "" -#: ../../changelog.rst:300 +#: ../../changelog.rst:340 msgid "" "Django 1.8.11 released (`#82 `_)." msgstr "" -#: ../../changelog.rst:301 +#: ../../changelog.rst:341 msgid "" "Prevent tests from failing due to moment of execution (`#88 " "`_)." msgstr "" -#: ../../changelog.rst:302 +#: ../../changelog.rst:342 msgid "" "Statistics page meter positions are broken (`#93 " "`_)." msgstr "" -#: ../../changelog.rst:303 +#: ../../changelog.rst:343 msgid "" "Archive only shows graph untill 23:00 (11 pm) (`#77 " "`_)." msgstr "" -#: ../../changelog.rst:304 +#: ../../changelog.rst:344 msgid "" "Trends page crashes due to nullable fields average (`#100 " "`_)." msgstr "" -#: ../../changelog.rst:305 +#: ../../changelog.rst:345 msgid "" "Trends: Plot peak and off-peak relative to each other (`#99 " "`_)." msgstr "" -#: ../../changelog.rst:306 +#: ../../changelog.rst:346 msgid "" "Monitor requirements with requires.io (`#101 " "`_)." msgstr "" -#: ../../changelog.rst:307 +#: ../../changelog.rst:347 msgid "" "Terminology (`#41 `_)." msgstr "" -#: ../../changelog.rst:308 +#: ../../changelog.rst:348 msgid "" "Obsolete signals in dsmr_consumption (`#63 " "`_)." msgstr "" -#: ../../changelog.rst:309 +#: ../../changelog.rst:349 msgid "" "Individual app testing coverage (`#64 `_)." msgstr "" -#: ../../changelog.rst:310 +#: ../../changelog.rst:350 msgid "" "Support for extra devices on other M-bus (0-n:24.1) (`#92 " "`_)." msgstr "" -#: ../../changelog.rst:311 +#: ../../changelog.rst:351 msgid "" "Separate post-deployment commands (`#102 " "`_)." msgstr "" -#: ../../changelog.rst:313 +#: ../../changelog.rst:353 msgid "" "Show exceptions in production (webinterface) (`#87 " "`_)." msgstr "" -#: ../../changelog.rst:314 +#: ../../changelog.rst:354 msgid "" "Keep Supervisor processes running (`#79 " "`_)." msgstr "" -#: ../../changelog.rst:315 +#: ../../changelog.rst:355 msgid "" "Hourly stats of 22:00:00+00 every day lack gas (`#78 " "`_)." msgstr "" -#: ../../changelog.rst:316 +#: ../../changelog.rst:356 msgid "" "Test Travis-CI with MySQL + MariaDB + PostgreSQL (`#54 " "`_)." msgstr "" -#: ../../changelog.rst:317 +#: ../../changelog.rst:357 msgid "" "PostgreSQL tests + nosetests + coverage failure: unrecognized " "configuration parameter \"foreign_key_checks\" (`#62 " "`_)." msgstr "" -#: ../../changelog.rst:318 +#: ../../changelog.rst:358 msgid "" "Performance check (`#83 `_)." msgstr "" -#: ../../changelog.rst:319 +#: ../../changelog.rst:359 msgid "" "Allow month & year archive (`#66 `_)." msgstr "" -#: ../../changelog.rst:320 +#: ../../changelog.rst:360 msgid "" "Graphs keep increasing height on tablet (`#89 " "`_)." msgstr "" -#: ../../changelog.rst:322 +#: ../../changelog.rst:362 msgid "" "Delete StatsSettings(.track) settings model (`#71 " "`_)." msgstr "" -#: ../../changelog.rst:323 +#: ../../changelog.rst:363 msgid "" "Drop deprecated commands (`#22 `_)." msgstr "" -#: ../../changelog.rst:324 +#: ../../changelog.rst:364 msgid "" "Datalogger doesn't work properly with DSMR 4.2 (KAIFA-METER) (`#73 " "`_)." msgstr "" -#: ../../changelog.rst:325 +#: ../../changelog.rst:365 msgid "" "Dashboard month statistics costs does not add up (`#75 " "`_)." msgstr "" -#: ../../changelog.rst:326 +#: ../../changelog.rst:366 msgid "" "Log unhandled exceptions and errors (`#65 " "`_)." msgstr "" -#: ../../changelog.rst:327 +#: ../../changelog.rst:367 msgid "" "Datalogger crashes with IntegrityError because 'timestamp' is null (`#74 " "`_)." msgstr "" -#: ../../changelog.rst:328 +#: ../../changelog.rst:368 msgid "" "Trends are always shown in UTC (`#76 `_)." msgstr "" -#: ../../changelog.rst:329 +#: ../../changelog.rst:369 msgid "" "Squash migrations (`#31 `_)." msgstr "" -#: ../../changelog.rst:330 +#: ../../changelog.rst:370 msgid "" "Display 'electricity returned' graph in dashboard (`#81 " "`_)." msgstr "" -#: ../../changelog.rst:331 +#: ../../changelog.rst:371 msgid "" "Optional gas (and electricity returned) capabilities tracking (`#70 " "`_)." msgstr "" -#: ../../changelog.rst:332 +#: ../../changelog.rst:372 msgid "" "Add 'electricity returned' to trends page (`#84 " "`_)." msgstr "" -#: ../../changelog.rst:334 +#: ../../changelog.rst:374 msgid "" "Archive: View past days details (`#61 `_)." msgstr "" -#: ../../changelog.rst:335 +#: ../../changelog.rst:375 msgid "" "Dashboard: Consumption total for current month (`#60 " "`_)." msgstr "" -#: ../../changelog.rst:336 +#: ../../changelog.rst:376 msgid "" "Check whether gas readings are optional (`#34 " "`_)." msgstr "" -#: ../../changelog.rst:337 +#: ../../changelog.rst:377 msgid "" "Django security releases issued: 1.8.10 (`#68 " "`_)." msgstr "" -#: ../../changelog.rst:338 +#: ../../changelog.rst:378 msgid "" "Notes display in archive (`#69 `_)." msgstr "" -#: ../../changelog.rst:340 +#: ../../changelog.rst:380 msgid "" "Status page/alerts when features are disabled/unavailable (`#45 " "`_)." msgstr "" -#: ../../changelog.rst:341 +#: ../../changelog.rst:381 msgid "" "Integrate Travis CI (`#48 `_)." msgstr "" -#: ../../changelog.rst:342 +#: ../../changelog.rst:382 msgid "" "Testing coverage (`#38 `_)." msgstr "" -#: ../../changelog.rst:343 +#: ../../changelog.rst:383 msgid "" "Implement automatic backups & Dropbox cloud storage (`#44 " "`_)." msgstr "" -#: ../../changelog.rst:344 +#: ../../changelog.rst:384 msgid "" "Link code coverage service to repository (`#56 " "`_)." msgstr "" -#: ../../changelog.rst:345 +#: ../../changelog.rst:385 msgid "" "Explore timezone.localtime() as replacement for datetime.astimezone() " "(`#50 `_)." msgstr "" -#: ../../changelog.rst:346 +#: ../../changelog.rst:386 msgid "" "Align GasConsumption.read_at to represent the start of hour (`#40 " "`_)." msgstr "" -#: ../../changelog.rst:348 +#: ../../changelog.rst:388 msgid "" "Cleanup unused static files (`#47 `_)." msgstr "" -#: ../../changelog.rst:349 +#: ../../changelog.rst:389 msgid "" "Investigated mysql_tzinfo_to_sql — Load the Time Zone Tables (`#35 " "`_)." msgstr "" -#: ../../changelog.rst:350 +#: ../../changelog.rst:390 msgid "" "Make additional DSMR data optional (`#46 " "`_)." msgstr "" -#: ../../changelog.rst:351 +#: ../../changelog.rst:391 msgid "" "Localize graph x-axis (`#42 `_)." msgstr "" -#: ../../changelog.rst:352 +#: ../../changelog.rst:392 msgid "" "Added graph formatting string to gettext file (`#42 " "`_)." msgstr "" -#: ../../changelog.rst:353 +#: ../../changelog.rst:393 msgid "" "Different colors for peak & off-peak electricity (`#52 " "`_)." msgstr "" -#: ../../changelog.rst:354 +#: ../../changelog.rst:394 msgid "" "Admin: Note widget (`#51 `_)." msgstr "" -#: ../../changelog.rst:355 +#: ../../changelog.rst:395 msgid "" "Allow GUI to run without data (`#26 `_)." msgstr "" -#: ../../changelog.rst:357 +#: ../../changelog.rst:397 msgid "" "Moved project to GitHub (`#28 `_)." msgstr "" -#: ../../changelog.rst:358 +#: ../../changelog.rst:398 msgid "Added stdout to dsmr_backend to reflect progress." msgstr "" -#: ../../changelog.rst:359 +#: ../../changelog.rst:399 msgid "" "Restore note usage in GUI (`#39 `_)." msgstr "" -#: ../../changelog.rst:361 +#: ../../changelog.rst:401 msgid "" "Store daily, weekly, monthly and yearly statistics (`#3 " "`_)." msgstr "" -#: ../../changelog.rst:362 +#: ../../changelog.rst:402 msgid "" "Improved Recent History page performance a bit. (as result of `#3 " "`_)" msgstr "" -#: ../../changelog.rst:363 +#: ../../changelog.rst:403 msgid "" "Updates ChartJS library tot 1.1, disposing django-chartjs plugin. Labels " "finally work! (as result of `#3 `_)" msgstr "" -#: ../../changelog.rst:364 +#: ../../changelog.rst:404 msgid "" "Added trends page. (as result of `#3 `_)" msgstr "" -#: ../../changelog.rst:366 +#: ../../changelog.rst:406 msgid "" "Recent history setting: set range (`#29 " "`_)." msgstr "" -#: ../../changelog.rst:367 +#: ../../changelog.rst:407 msgid "" "Mock required for test: dsmr_weather.test_weather_tracking (`#32 " "`_)." msgstr "" -#: ../../changelog.rst:369 +#: ../../changelog.rst:409 msgid "" "Massive refactoring: Separating apps & using signals (`#19 " "`_)." msgstr "" -#: ../../changelog.rst:370 +#: ../../changelog.rst:410 msgid "" "README update: Exit character for cu (`#27 " "`_, by Jeroen " "Peters)." msgstr "" -#: ../../changelog.rst:371 +#: ../../changelog.rst:411 msgid "Fixed untranslated strings in admin interface." msgstr "" -#: ../../changelog.rst:372 +#: ../../changelog.rst:412 msgid "Upgraded Django to 1.8.9." msgstr "" -#~ msgid "Contents" -#~ msgstr "Inhoudsopgave" - -#~ msgid "" -#~ "Support for ``MySQL`` has been " -#~ "**deprecated** since ``DSMR-reader v1.6`` " -#~ "and will be discontinued completely in" -#~ " a later release. Please use a " -#~ "PostgreSQL database instead. Users already " -#~ "running MySQL will be supported in " -#~ "migrating at a later moment." -#~ msgstr "" - -#~ msgid "" -#~ "API request should return HTTP 201 " -#~ "instead of HTTP 200 (`#211 " -#~ "`_)." -#~ msgstr "" - -#~ msgid "" -#~ "Docs: Explain settings/options (`#232 " -#~ "`_)." -#~ msgstr "" -#~ "De ondersteuning voor ``Python 3.3`` is" -#~ " **vervallen** wegens de Django upgrade " -#~ "(`#103 `_)." - diff --git a/docs/locale/nl/LC_MESSAGES/contributing.po b/docs/locale/nl/LC_MESSAGES/contributing.po index bf5984338..0796937d0 100644 --- a/docs/locale/nl/LC_MESSAGES/contributing.po +++ b/docs/locale/nl/LC_MESSAGES/contributing.po @@ -67,5 +67,3 @@ msgstr "" "Tot slot, zorg ervoor dat pull requests altijd naar de ``development`` branch " "wijzen en niet de ``master``." -#~ msgid "Feedback" -#~ msgstr "Feedback" diff --git a/docs/locale/nl/LC_MESSAGES/credits.mo b/docs/locale/nl/LC_MESSAGES/credits.mo index b7415e7ad..960ac7218 100644 Binary files a/docs/locale/nl/LC_MESSAGES/credits.mo and b/docs/locale/nl/LC_MESSAGES/credits.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/credits.po b/docs/locale/nl/LC_MESSAGES/credits.po index 6d9a7031f..71e4e8bb2 100644 --- a/docs/locale/nl/LC_MESSAGES/credits.po +++ b/docs/locale/nl/LC_MESSAGES/credits.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader v1.x\n" "Report-Msgid-Bugs-To: Dennis Siemensma \n" -"POT-Creation-Date: 2017-02-18 00:28+0100\n" +"POT-Creation-Date: 2017-05-02 21:26+0200\n" "PO-Revision-Date: 2017-01-01 00:00+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" @@ -99,113 +99,115 @@ msgstr "" msgid "`Raspbian `_" msgstr "" -#: ../../credits.rst:34 +#: ../../credits.rst:33 msgid "`Django Project `_" msgstr "" -#: ../../credits.rst:36 +#: ../../credits.rst:34 msgid "`Django Solo `_" msgstr "" -#: ../../credits.rst:38 +#: ../../credits.rst:35 msgid "`Django Colorfield `_" msgstr "" -#: ../../credits.rst:40 +#: ../../credits.rst:36 msgid "`Supervisor `_" msgstr "" -#: ../../credits.rst:42 +#: ../../credits.rst:37 msgid "`MySQL `_" msgstr "" -#: ../../credits.rst:44 +#: ../../credits.rst:38 msgid "`MariaDB `_" msgstr "" -#: ../../credits.rst:46 +#: ../../credits.rst:39 msgid "`PostgreSQL `_" msgstr "" -#: ../../credits.rst:48 +#: ../../credits.rst:40 msgid "" "`Director Responsive Admin `_" msgstr "" -#: ../../credits.rst:50 +#: ../../credits.rst:41 msgid "" "Favicon made by `Freepik `_ from `flaticon.com " "`_" msgstr "" -#: ../../credits.rst:52 +#: ../../credits.rst:42 msgid "`Real Favicon Generator `_" msgstr "" -#: ../../credits.rst:54 +#: ../../credits.rst:43 msgid "`Bootstrap-datepicker `_" msgstr "" -#: ../../credits.rst:56 +#: ../../credits.rst:44 msgid "`Github `_" msgstr "" -#: ../../credits.rst:58 +#: ../../credits.rst:45 msgid "`TravisCI `_" msgstr "" -#: ../../credits.rst:60 +#: ../../credits.rst:46 msgid "`Codecov `_" msgstr "" -#: ../../credits.rst:62 +#: ../../credits.rst:47 msgid "`Read The Docs `_" msgstr "" -#: ../../credits.rst:64 +#: ../../credits.rst:48 msgid "`MW `_" msgstr "" -#: ../../credits.rst:66 +#: ../../credits.rst:49 msgid "" "`Full Page Screen Capture `_" msgstr "" -#: ../../credits.rst:68 +#: ../../credits.rst:50 msgid "`Buienradar `_" msgstr "" -#: ../../credits.rst:72 +#: ../../credits.rst:51 +msgid "`Django REST Framework `_" +msgstr "" + +#: ../../credits.rst:52 +msgid "`Swagger UI Themes `_" +msgstr "" + +#: ../../credits.rst:56 msgid "Misc" msgstr "Overig" -#: ../../credits.rst:74 +#: ../../credits.rst:58 msgid "Dutch Smart Meter reading specifications, data cables, examples and hints:" msgstr "Dutch Smart Meter reading specifications, data cables, examples and hints:" -#: ../../credits.rst:76 +#: ../../credits.rst:60 msgid "`Gé Janssen `_" msgstr "" -#: ../../credits.rst:78 +#: ../../credits.rst:62 msgid "" "`Joost van der Linde (smartmeterdashboard) " "`_" msgstr "" -#: ../../credits.rst:80 +#: ../../credits.rst:64 msgid "`SOS Solutions `_" msgstr "" -#: ../../credits.rst:82 +#: ../../credits.rst:66 msgid "`Nico Di Rocco `_" msgstr "" -#~ msgid "`Favicon `_" -#~ msgstr "" - -#~ msgid "Credits" -#~ msgstr "Credits" - diff --git a/docs/locale/nl/LC_MESSAGES/faq.po b/docs/locale/nl/LC_MESSAGES/faq.po index ac2333d39..19180d0a7 100644 --- a/docs/locale/nl/LC_MESSAGES/faq.po +++ b/docs/locale/nl/LC_MESSAGES/faq.po @@ -511,6 +511,3 @@ msgstr "" "`Maak een ticket aan op Github `_." -#~ msgid "Contents" -#~ msgstr "Inhoud" - diff --git a/docs/locale/nl/LC_MESSAGES/installation.mo b/docs/locale/nl/LC_MESSAGES/installation.mo index 5c5a79d24..6307aefd9 100644 Binary files a/docs/locale/nl/LC_MESSAGES/installation.mo and b/docs/locale/nl/LC_MESSAGES/installation.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/installation.po b/docs/locale/nl/LC_MESSAGES/installation.po index b69e30bd1..15a887b3d 100644 --- a/docs/locale/nl/LC_MESSAGES/installation.po +++ b/docs/locale/nl/LC_MESSAGES/installation.po @@ -1,15 +1,35 @@ - -# Copyright (C) 2016, Dennis Siemensma -# This file is distributed under the same license as the DSMR Reader -# package. +# Translations template for PROJECT. +# Copyright (C) 2017 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2017. # +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2017-05-04 18:35+0200\n" +"PO-Revision-Date: 2017-05-04 18:35+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.3.4\n" +"Language: nl\n" +"X-Generator: Poedit 1.8.7.1\n" + #: ../../installation.rst:2 msgid "Installation" msgstr "Installatie" #: ../../installation.rst:6 -msgid "The installation guide may take about *half an hour max* (for raspberryPi 2/3), but it greatly depends on your Linux skills and whether you need to understand every step described in this guide." -msgstr "Het installeren duurt naar verwachting zo'n *half uur* (op een RaspberryPi 2/3), maar hangt natuurlijk ook af van je eigen vaardigheid op de command line." +msgid "" +"The installation guide may take about *half an hour max* (for raspberryPi 2/3), " +"but it greatly depends on your Linux skills and whether you need to understand " +"every step described in this guide." +msgstr "" +"Het installeren duurt naar verwachting zo'n *half uur* (op een RaspberryPi " +"2/3), maar hangt natuurlijk ook af van je eigen vaardigheid op de command line." #: ../../installation.rst:14 msgid "Dependencies & requirements" @@ -20,64 +40,115 @@ msgid "**RaspberryPi 2 or 3**" msgstr "**RaspberryPi 2 of 3**" #: ../../installation.rst:19 -msgid "**Alternative #1**: You can also run it on any server near your smart meter, as long as it satisfies the other requirements." -msgstr "**Alternatief #1**: Je kunt dit natuurlijk ook draaien op een server vlakbij je slimme meter, zolang de vereisten maar ondersteund worden." +msgid "" +"**Alternative #1**: You can also run it on any server near your smart meter, as " +"long as it satisfies the other requirements." +msgstr "" +"**Alternatief #1**: Je kunt dit natuurlijk ook draaien op een server vlakbij je " +"slimme meter, zolang de vereisten maar ondersteund worden." #: ../../installation.rst:21 -msgid "**Alternative #2**: The application supports receiving P1 telegrams using an API, so you can also run it on a server outside your home. (:doc:`API DOCS`)" -msgstr "**Alternatief #2**: De applicatie ondersteunt het ontvangen van P1 telegrammen via een API, dus je kunt dit ook op een server buiten je huis draaien. (:doc:`API DOCS`)" +msgid "" +"**Alternative #2**: The application supports receiving P1 telegrams using an " +"API, so you can also run it on a server outside your home. (:doc:`API " +"DOCS`)" +msgstr "" +"**Alternatief #2**: De applicatie ondersteunt het ontvangen van P1 telegrammen " +"via een API, dus je kunt dit ook op een server buiten je huis draaien. (:doc:" +"`API DOCS`)" #: ../../installation.rst:25 -msgid "The RaspberryPi 1 tends to be **too slow** for this project, as it requires multi core processing." -msgstr "De RaspberryPi 1 lijkt **te traag** voor het draaien van dit project, gezien meerdere processoren vrijwel vereist zijn." +msgid "" +"The RaspberryPi 1 tends to be **too slow** for this project, as it requires " +"multi core processing." +msgstr "" +"De RaspberryPi 1 lijkt **te traag** voor het draaien van dit project, gezien " +"meerdere processoren vrijwel vereist zijn." #: ../../installation.rst:27 -msgid "You can however run just the datalogger client on an old RaspberryPi, :doc:`see for the API for a howto and example scripts`." -msgstr "Je kunt echter wel alleen een datalogger client draaien op een oude RaspberryPi, :doc:`zie de API -documentatie voor meer informatie en voorbeeldscripts`." +msgid "" +"You can however run just the datalogger client on an old RaspberryPi, :doc:`see " +"for the API for a howto and example scripts`." +msgstr "" +"Je kunt echter wel alleen een datalogger client draaien op een oude " +"RaspberryPi, :doc:`zie de API -documentatie voor meer informatie en " +"voorbeeldscripts`." #: ../../installation.rst:29 msgid "**Raspbian OS**" msgstr "**Raspbian OS**" #: ../../installation.rst:31 -msgid "Recommended and tested with, but any OS satisfying the requirements should do fine." -msgstr "Aanbevolen en mee getest, al zou elk OS die dezelfde vereisten ondersteunt prima moeten zijn." +msgid "" +"Recommended and tested with, but any OS satisfying the requirements should do " +"fine." +msgstr "" +"Aanbevolen en mee getest, al zou elk OS die dezelfde vereisten ondersteunt " +"prima moeten zijn." #: ../../installation.rst:33 msgid "**Python 3.4+**" msgstr "**Python 3.4+**" #: ../../installation.rst:37 -msgid "Support for ``Python 3.3`` has been **discontinued** since ``DSMR-reader v1.5`` (due to Django)." -msgstr "Ondersteuning voor ``Python 3.3`` is **vervallen** sinds ``DSMR-reader v1.5`` (vanwege Django)." +msgid "" +"Support for ``Python 3.3`` has been **discontinued** since ``DSMR-reader v1.5`` " +"(due to Django)." +msgstr "" +"Ondersteuning voor ``Python 3.3`` is **vervallen** sinds ``DSMR-reader v1.5`` " +"(vanwege Django)." #: ../../installation.rst:39 msgid "**PostgreSQL 9+ database**" msgstr "**PostgreSQL 9+ database**" #: ../../installation.rst:43 -msgid "Support for ``MySQL`` has been **deprecated** since ``DSMR-reader v1.6`` and will be discontinued completely in a later release. Please use a PostgreSQL database instead. Users already running MySQL will be supported in migrating at a later moment." -msgstr "Gebruik van ``MySQL`` wordt **afgeraden** sinds ``DSMR-reader v1.6`` en ondersteuning hiervoor verdwijnt helemaal in een toekomstige versie. Gebruik daarom PostgreSQL. Gebruikers die dit project al op MySQL draaien krijgen in de toekomst ondersteuning om te migreren." +msgid "" +"Support for ``MySQL`` has been **deprecated** since ``DSMR-reader v1.6`` and " +"will be discontinued completely in a later release. Please use a PostgreSQL " +"database instead. Users already running MySQL will be supported in migrating at " +"a later moment." +msgstr "" +"Gebruik van ``MySQL`` wordt **afgeraden** sinds ``DSMR-reader v1.6`` en " +"ondersteuning hiervoor verdwijnt helemaal in een toekomstige versie. Gebruik " +"daarom PostgreSQL. Gebruikers die dit project al op MySQL draaien krijgen in de " +"toekomst ondersteuning om te migreren." #: ../../installation.rst:46 -msgid "**Smart Meter** with support for **at least DSMR 4.x+** and a **P1 telegram port**" -msgstr "**Slimme meter** met ondersteuning voor **ten minste DSMR 4.x+** en een **P1 telegram poort**" +msgid "" +"**Smart Meter** with support for **at least DSMR 4.x+** and a **P1 telegram " +"port**" +msgstr "" +"**Slimme meter** met ondersteuning voor **ten minste DSMR 4.x+** en een **P1 " +"telegram poort**" #: ../../installation.rst:48 msgid "Tested so far with Landis+Gyr E350, Kaifa." msgstr "Tot nu toe getest met: Landis+Gyr E350, Kaifa." #: ../../installation.rst:50 -msgid "**Minimal 1 GB of disk space on RaspberryPi (card)** (for application installation & virtualenv)." -msgstr "**Minimaal 1 GB schijfruimte vereist op RaspberryPi (SD-kaart)** (ten behoeve van de applicatie en VirtualEnv)." +msgid "" +"**Minimal 1 GB of disk space on RaspberryPi (card)** (for application " +"installation & virtualenv)." +msgstr "" +"**Minimaal 1 GB schijfruimte vereist op RaspberryPi (SD-kaart)** (ten behoeve " +"van de applicatie en VirtualEnv)." #: ../../installation.rst:52 -msgid "More disk space is required for storing all reader data captured (optional). I generally advise to use a 8+ GB SD card." -msgstr "Meer schijfruimte is nodig voor het opslaan van alle metingen (optioneel). Over het algemeen adviseer ik minimaal een 8 GB SD-kaart." +msgid "" +"More disk space is required for storing all reader data captured (optional). I " +"generally advise to use a 8+ GB SD card." +msgstr "" +"Meer schijfruimte is nodig voor het opslaan van alle metingen (optioneel). Over " +"het algemeen adviseer ik minimaal een 8 GB SD-kaart." #: ../../installation.rst:53 -msgid "The readings will take about 90+ percent of the disk space. Retention is on it's way for a future release in 2017." -msgstr "De metingen nemen zo'n 90+ procent van alle schijfruimte in beslag. Er komt echter een optie voor retentie in een toekomstige release in 2017." +msgid "" +"The readings will take about 90+ percent of the disk space. Retention is on " +"it's way for a future release in 2017." +msgstr "" +"De metingen nemen zo'n 90+ procent van alle schijfruimte in beslag. Er komt " +"echter een optie voor retentie in een toekomstige release in 2017." #: ../../installation.rst:55 msgid "**Smart meter P1 data cable**" @@ -89,7 +160,8 @@ msgstr "Je kunt deze online bestellen voor ongeveer 15 a 20 Euro." #: ../../installation.rst:59 msgid "**Basic Linux knowledge for deployment, debugging and troubleshooting**" -msgstr "**Basiskennis Linux voor het uitrollen en mogelijk debuggen van problemen**" +msgstr "" +"**Basiskennis Linux voor het uitrollen en mogelijk debuggen van problemen**" #: ../../installation.rst:61 msgid "It just really helps if you know what you are doing." @@ -100,24 +172,47 @@ msgid "1. Database backend (PostgreSQL)" msgstr "1. Databaseopslag (PostgreSQL)" #: ../../installation.rst:67 -msgid "The application stores by default all readings taken from the serial cable. There is support for **PostgreSQL**, and there used to be support for **MySQL/MariaDB** as well. The latter is currently deprecated by this project and support will be discontinued in a future release." -msgstr "De applicatie slaat de P1-metingen standaard op. Er is ondersteuning voor **PostgreSQL**, en vroeger ook voor **MySQL/MariaDB**. Alleen wordt de laatstgenoemde momenteel afgeraden om te gebruiken (ondersteuning vervalt later)." +msgid "" +"The application stores by default all readings taken from the serial cable. " +"There is support for **PostgreSQL**, and there used to be support for **MySQL/" +"MariaDB** as well. The latter is currently deprecated by this project and " +"support will be discontinued in a future release." +msgstr "" +"De applicatie slaat de P1-metingen standaard op. Er is ondersteuning voor " +"**PostgreSQL**, en vroeger ook voor **MySQL/MariaDB**. Alleen wordt de " +"laatstgenoemde momenteel afgeraden om te gebruiken (ondersteuning vervalt " +"later)." #: ../../installation.rst:71 -msgid "Install PostgreSQL, ``postgresql-server-dev-all`` is required for the virtualenv installation later in this guide." -msgstr "Installeer PostgreSQL. Daarnaast is ``postgresql-server-dev-all`` nodig voor het installeren van de VirtualEnv later." +msgid "" +"Install PostgreSQL, ``postgresql-server-dev-all`` is required for the " +"virtualenv installation later in this guide." +msgstr "" +"Installeer PostgreSQL. Daarnaast is ``postgresql-server-dev-all`` nodig voor " +"het installeren van de VirtualEnv later." #: ../../installation.rst:73 ../../installation.rst:118 msgid "Install database::" msgstr "Installeer database::" #: ../../installation.rst:77 -msgid "Does Postgres not start due to locales? Try: ``dpkg-reconfigure locales``. Still no luck? Try editing ``/etc/environment``, add ``LC_ALL=\"en_US.utf-8\"`` and reboot." -msgstr "Start PostgreSQL niet wegens een fout in 'locales'? Probeer dan het volgende: ``dpkg-reconfigure locales``. Werkt het nog steeds niet? Open dan dit bestand ``/etc/environment``, voeg onderaan de regel ``LC_ALL=\"en_US.utf-8\"`` toe en herstart het systeem." +msgid "" +"Does Postgres not start due to locales? Try: ``dpkg-reconfigure locales``. " +"Still no luck? Try editing ``/etc/environment``, add ``LC_ALL=\"en_US.utf-8\"`` " +"and reboot." +msgstr "" +"Start PostgreSQL niet wegens een fout in 'locales'? Probeer dan het volgende: " +"``dpkg-reconfigure locales``. Werkt het nog steeds niet? Open dan dit bestand " +"``/etc/environment``, voeg onderaan de regel ``LC_ALL=\"en_US.utf-8\"`` toe en " +"herstart het systeem." #: ../../installation.rst:80 -msgid "(!) Ignore any '*could not change directory to \"/root\": Permission denied*' errors for the following three commands." -msgstr "(!) Negeer voor de volgende drie commando's de foutmelding: '*could not change directory to \"/root\": Permission denied*'." +msgid "" +"(!) Ignore any '*could not change directory to \"/root\": Permission denied*' " +"errors for the following three commands." +msgstr "" +"(!) Negeer voor de volgende drie commando's de foutmelding: '*could not change " +"directory to \"/root\": Permission denied*'." #: ../../installation.rst:82 ../../installation.rst:126 msgid "Create database user::" @@ -125,15 +220,19 @@ msgstr "Creëer databasegebruiker::" #: ../../installation.rst:86 msgid "Create database, owned by the database user we just created::" -msgstr "Creëer database, met als eigenaar de databasegebruiker die we net hebben aangemaakt::" +msgstr "" +"Creëer database, met als eigenaar de databasegebruiker die we net hebben " +"aangemaakt::" #: ../../installation.rst:90 msgid "Set password for database user::" msgstr "Stel wachtwoord in voor databasegebruiker::" #: ../../installation.rst:96 -msgid "**Optional**: Do you need to restore a **PostgreSQL** database backup as well?" -msgstr "**Optioneel**: Wil je ook nog een **PostgreSQL** database back-up herstellen?" +msgid "" +"**Optional**: Do you need to restore a **PostgreSQL** database backup as well?" +msgstr "" +"**Optioneel**: Wil je ook nog een **PostgreSQL** database back-up herstellen?" #: ../../installation.rst:98 ../../installation.rst:142 msgid "Restore an uncompressed (``.sql``) backup with::" @@ -152,12 +251,24 @@ msgid "(Legacy) MySQL/MariaDB" msgstr "(Legacy) MySQL/MariaDB" #: ../../installation.rst:112 ../../installation.rst:297 -msgid "Support for the MySQL database backend is deprecated and will be removed in a later release. Please use a PostgreSQL database instead. Users already running MySQL will be supported in migrating at a later moment." -msgstr "Gebruik van MySQL-databases wordt afgeraden en de ondersteuning hiervoor stopt in een latere release. Gebruik daarom PostgreSQL. Gebruikers die dit project al op MySQL draaien krijgen in de toekomst ondersteuning om te migreren." +msgid "" +"Support for the MySQL database backend is deprecated and will be removed in a " +"later release. Please use a PostgreSQL database instead. Users already running " +"MySQL will be supported in migrating at a later moment." +msgstr "" +"Gebruik van MySQL-databases wordt afgeraden en de ondersteuning hiervoor stopt " +"in een latere release. Gebruik daarom PostgreSQL. Gebruikers die dit project al " +"op MySQL draaien krijgen in de toekomst ondersteuning om te migreren." #: ../../installation.rst:115 -msgid "Install MariaDB. You can also choose to install the closed source MySQL, as they should be interchangeable anyway. ``libmysqlclient-dev`` is required for the virtualenv installation later in this guide." -msgstr "Installeer MariaDB. Je kunt er ook voor kiezen om het closed-source MySQL te installeren. Welke je ook kiest, ``libmysqlclient-dev`` is later nodig voor de VirtualEnv." +msgid "" +"Install MariaDB. You can also choose to install the closed source MySQL, as " +"they should be interchangeable anyway. ``libmysqlclient-dev`` is required for " +"the virtualenv installation later in this guide." +msgstr "" +"Installeer MariaDB. Je kunt er ook voor kiezen om het closed-source MySQL te " +"installeren. Welke je ook kiest, ``libmysqlclient-dev`` is later nodig voor de " +"VirtualEnv." #: ../../installation.rst:122 msgid "Create database::" @@ -180,44 +291,84 @@ msgid "2. Dependencies" msgstr "2. Afhankelijkheden" #: ../../installation.rst:153 -msgid "Now you'll have to install several utilities, required for the Nginx webserver, Gunicorn application server and cloning the application code from the Github repository::" -msgstr "Tijd om diverse tools te installeren. Deze zijn nodig voor de Nginx webserver, de Gunicorn applicatieserver en voor het binnenhalen van de code van de applicatie vanaf Github::" +msgid "" +"Now you'll have to install several utilities, required for the Nginx webserver, " +"Gunicorn application server and cloning the application code from the Github " +"repository::" +msgstr "" +"Tijd om diverse tools te installeren. Deze zijn nodig voor de Nginx webserver, " +"de Gunicorn applicatieserver en voor het binnenhalen van de code van de " +"applicatie vanaf Github::" #: ../../installation.rst:157 -msgid "Install ``cu``. The CU program allows easy testing for your DSMR serial connection. It's very basic but also very effective to simply test whether your serial cable setup works properly::" -msgstr "Installeer ``cu``. Met dit programmaatje kunnen kun je vrij gemakkelijk de DSMR-verbinding testen naar je slimme meter toe. Erg handig om te kijken of dat überhaupt al lekker werkt::" +msgid "" +"Install ``cu``. The CU program allows easy testing for your DSMR serial " +"connection. It's very basic but also very effective to simply test whether your " +"serial cable setup works properly::" +msgstr "" +"Installeer ``cu``. Met dit programmaatje kunnen kun je vrij gemakkelijk de DSMR-" +"verbinding testen naar je slimme meter toe. Erg handig om te kijken of dat " +"überhaupt al lekker werkt::" #: ../../installation.rst:164 msgid "3. Application user" msgstr "3. Applicatiegebruiker" #: ../../installation.rst:165 -msgid "The application runs as ``dsmr`` user by default. This way we do not have to run the application as ``root``, which is a bad practice anyway." -msgstr "De applicatie draait standaard onder de gebruiker ``dsmr``. Hierdoor heeft het geen ``root``-rechten (nodig), wat over het algemeen zeer afgeraden wordt." +msgid "" +"The application runs as ``dsmr`` user by default. This way we do not have to " +"run the application as ``root``, which is a bad practice anyway." +msgstr "" +"De applicatie draait standaard onder de gebruiker ``dsmr``. Hierdoor heeft het " +"geen ``root``-rechten (nodig), wat over het algemeen zeer afgeraden wordt." #: ../../installation.rst:167 -msgid "Create user with homedir. The application code and virtualenv will reside in this directory as well::" -msgstr "Maak een aparte gebruiker aan met eigen homedir. De code voor de applicatie en VirtualEnv zetten we later hier in::" +msgid "" +"Create user with homedir. The application code and virtualenv will reside in " +"this directory as well::" +msgstr "" +"Maak een aparte gebruiker aan met eigen homedir. De code voor de applicatie en " +"VirtualEnv zetten we later hier in::" #: ../../installation.rst:171 -msgid "Our user also requires dialout permissions. So allow the user to perform a dialout by adding it to the ``dialout`` group::" -msgstr "De gebruiker heeft ook toegang nodig om de kabel te kunnen uitlezen. Hiervoor voegen de we gebruiker toe aan de groep ``dialout``::" +msgid "" +"Our user also requires dialout permissions. So allow the user to perform a " +"dialout by adding it to the ``dialout`` group::" +msgstr "" +"De gebruiker heeft ook toegang nodig om de kabel te kunnen uitlezen. Hiervoor " +"voegen de we gebruiker toe aan de groep ``dialout``::" #: ../../installation.rst:175 -msgid "Either proceed to the next heading **for a test reading** or continue at chapter 4." -msgstr "Ga ofwel door naar het volgende hoofdstuk **voor een testmeting** of ga direct door naar stap 4." +msgid "" +"Either proceed to the next heading **for a test reading** or continue at " +"chapter 4." +msgstr "" +"Ga ofwel door naar het volgende hoofdstuk **voor een testmeting** of ga direct " +"door naar stap 4." #: ../../installation.rst:179 msgid "Your first reading (optional)" msgstr "Je allereerste (optionele) meting" #: ../../installation.rst:183 -msgid "**OPTIONAL**: You may skip this section as it's not required for the application to install. However, if you have never read your meter's P1 telegram port before, I recommend to perform an initial reading to make sure everything works as expected." -msgstr "**OPTIONEEL**: Je kunt deze stap overslaan wanneer je al eerder een (test)meting hebt gedaan met je slimme meter. Ik raad het dus vooral aan als je nog nooit eerder je P1-poort hebt uitgelezen. Hiermee verzeker je jezelf ook dat de applicatie straks dezelfde (werkende) toegang heeft voor de metingen." +msgid "" +"**OPTIONAL**: You may skip this section as it's not required for the " +"application to install. However, if you have never read your meter's P1 " +"telegram port before, I recommend to perform an initial reading to make sure " +"everything works as expected." +msgstr "" +"**OPTIONEEL**: Je kunt deze stap overslaan wanneer je al eerder een " +"(test)meting hebt gedaan met je slimme meter. Ik raad het dus vooral aan als je " +"nog nooit eerder je P1-poort hebt uitgelezen. Hiermee verzeker je jezelf ook " +"dat de applicatie straks dezelfde (werkende) toegang heeft voor de metingen." #: ../../installation.rst:185 -msgid "Now login as the user we have just created, to perform our very first reading! ::" -msgstr "Log nu in als de gebruiker die we zojuist hebben aangemaakt voor de eerste testmeting! ::" +msgid "" +"Now login as the user we have just created, to perform our very first " +"reading! ::" +msgstr "" +"Log nu in als de gebruiker die we zojuist hebben aangemaakt voor de eerste " +"testmeting! ::" #: ../../installation.rst:189 msgid "Test with ``cu`` for **DSMR 4+**::" @@ -228,112 +379,214 @@ msgid "Or test with ``cu`` for **DSMR 2.2** (untested)::" msgstr "Of test met ``cu`` voor **DSMR 2.2** (ongetest)::" #: ../../installation.rst:197 -msgid "You now should see something similar to ``Connected.`` and a wall of text and numbers *within 10 seconds*. Nothing? Try different BAUD rate, as mentioned above. You might also check out a useful blog, `such as this one (Dutch) `_." -msgstr "Je zou nu iets moeten zien als ``Connected.``. Vervolgens krijg je, als het goed is, binnen tien seconden een hele lap tekst te zijn met een hoop cijfers. Werkt het niet? Probeer dan een andere BAUD-waarde, zoals hierboven beschreven. Of `kijk op een nuttig weblog `_." +msgid "" +"You now should see something similar to ``Connected.`` and a wall of text and " +"numbers *within 10 seconds*. Nothing? Try different BAUD rate, as mentioned " +"above. You might also check out a useful blog, `such as this one (Dutch) " +"`_." +msgstr "" +"Je zou nu iets moeten zien als ``Connected.``. Vervolgens krijg je, als het " +"goed is, binnen tien seconden een hele lap tekst te zijn met een hoop cijfers. " +"Werkt het niet? Probeer dan een andere BAUD-waarde, zoals hierboven beschreven. " +"Of `kijk op een nuttig weblog `_." #: ../../installation.rst:199 -msgid "To exit cu, type \"``q.``\", hit Enter and wait for a few seconds. It should exit with the message ``Disconnected.``." -msgstr "Om cu af te sluiten, typ \"``q.``\", druk op Enter en wacht voor een paar seconden. Het programma sluit af met de melding ``Disconnected.``." +msgid "" +"To exit cu, type \"``q.``\", hit Enter and wait for a few seconds. It should " +"exit with the message ``Disconnected.``." +msgstr "" +"Om cu af te sluiten, typ \"``q.``\", druk op Enter en wacht voor een paar " +"seconden. Het programma sluit af met de melding ``Disconnected.``." #: ../../installation.rst:203 msgid "4. Webserver/Nginx (part 1)" msgstr "4. Webserver/Nginx (deel 1)" #: ../../installation.rst:205 -msgid "*We will now prepare the webserver, Nginx. It will serve all application's static files directly and proxy any application requests to the backend, Gunicorn controlled by Supervisor, which we will configure later on.*" -msgstr "*We configureren vervolgens de webserver (Nginx). Deze serveert alle statische bestanden en geeft de applicatie-verzoeken door naar de backend, waar de applicatie in Gunicorn draait onder Supervisor. Deze stellen we later in.*" - -#: ../../installation.rst:207 -msgid "Make sure you are still acting here as ``root`` or ``sudo`` user." -msgstr "Zorg ervoor dat je hier nog steeds ``root``- of ``sudo``-gebruiker bent." +msgid "" +"*We will now prepare the webserver, Nginx. It will serve all application's " +"static files directly and proxy any application requests to the backend, " +"Gunicorn controlled by Supervisor, which we will configure later on.*" +msgstr "" +"*We configureren vervolgens de webserver (Nginx). Deze serveert alle statische " +"bestanden en geeft de applicatie-verzoeken door naar de backend, waar de " +"applicatie in Gunicorn draait onder Supervisor. Deze stellen we later in.*" + +#: ../../installation.rst:207 ../../installation.rst:350 +msgid "" +"Make sure you are acting here as ``root`` or ``sudo`` user. If not, press CTRL " +"+ D to log out of the ``dsmr`` user." +msgstr "" +"Zorg ervoor dat je hier ``root``- of ``sudo``-gebruiker bent. Zo niet, druk op " +"CTRL + D om uit te loggen als ``dsmr`` gebruiker." #: ../../installation.rst:209 -msgid "Django will later copy all static files to the directory below, used by Nginx to serve statics. Therefor it requires (write) access to it::" -msgstr "Django kopieert alle statische bestanden naar een aparte map, die weer door Nginx gebruikt wordt. Daarom is er tevens (schrijf)toegang voor nodig::" +msgid "" +"Django will later copy all static files to the directory below, used by Nginx " +"to serve statics. Therefor it requires (write) access to it::" +msgstr "" +"Django kopieert alle statische bestanden naar een aparte map, die weer door " +"Nginx gebruikt wordt. Daarom is er tevens (schrijf)toegang voor nodig::" #: ../../installation.rst:217 msgid "5. Clone project code from Github" msgstr "5. Kloon project code vanaf Github" #: ../../installation.rst:218 -msgid "Now is the time to clone the code from the repository into the homedir we created." -msgstr "Nu kunnen we de code van de applicatie van Github downloaden en in de homedir zetten die we net aangemaakt hebben." +msgid "" +"Now is the time to clone the code from the repository into the homedir we " +"created." +msgstr "" +"Nu kunnen we de code van de applicatie van Github downloaden en in de homedir " +"zetten die we net aangemaakt hebben." #: ../../installation.rst:220 -msgid "Make sure you are now acting as ``dsmr`` user (if not then enter: ``sudo su - dsmr``)" -msgstr "Zorg ervoor dat je ingelogd bent als ``dsmr``-gebruiker (zo niet, typ dan: ``sudo su - dsmr``)" +msgid "" +"Make sure you are now acting as ``dsmr`` user (if not then enter: ``sudo su - " +"dsmr``)" +msgstr "" +"Zorg ervoor dat je ingelogd bent als ``dsmr``-gebruiker (zo niet, typ dan: " +"``sudo su - dsmr``)" #: ../../installation.rst:222 msgid "Clone the repository::" msgstr "Kloon de repository::" #: ../../installation.rst:226 -msgid "This may take a few seconds. When finished, you should see a new folder called ``dsmr-reader``, containing a clone of the Github repository." -msgstr "Dit kan enkele seconden in beslag nemen. Als het goed is zie je hierna een map genaamd ``dsmr-reader``, met daarin een kopie van de repository zoals die op Github staat." +msgid "" +"This may take a few seconds. When finished, you should see a new folder called " +"``dsmr-reader``, containing a clone of the Github repository." +msgstr "" +"Dit kan enkele seconden in beslag nemen. Als het goed is zie je hierna een map " +"genaamd ``dsmr-reader``, met daarin een kopie van de repository zoals die op " +"Github staat." #: ../../installation.rst:230 msgid "6. Virtualenv" msgstr "6. VirtualEnv" #: ../../installation.rst:232 -msgid "The dependencies our application uses are stored in a separate environment, also called **VirtualEnv**." -msgstr "Alle (externe) afhankelijkheden worden opgeslagen in een aparte omgeving, ook wel **VirtualEnv** genoemd." +msgid "" +"The dependencies our application uses are stored in a separate environment, " +"also called **VirtualEnv**." +msgstr "" +"Alle (externe) afhankelijkheden worden opgeslagen in een aparte omgeving, ook " +"wel **VirtualEnv** genoemd." #: ../../installation.rst:234 -msgid "Although it's just a folder inside our user's homedir, it's very effective as it allows us to keep dependencies isolated or to run different versions of the same package on the same machine. `More information about this subject can be found here `_." -msgstr "Dit is allemaal erg handig, ondanks dat het feitelijk niets meer voorstelt dan een aparte map binnen de homedir van onze gebruiker. Hierdoor kunnen we namelijk meerdere versie van software op hetzelfde systeem installeren, zonder dat dat elkaar bijt. Meer informatie hierover `kan hier gevonden worden `_." +msgid "" +"Although it's just a folder inside our user's homedir, it's very effective as " +"it allows us to keep dependencies isolated or to run different versions of the " +"same package on the same machine. `More information about this subject can be " +"found here `_." +msgstr "" +"Dit is allemaal erg handig, ondanks dat het feitelijk niets meer voorstelt dan " +"een aparte map binnen de homedir van onze gebruiker. Hierdoor kunnen we " +"namelijk meerdere versie van software op hetzelfde systeem installeren, zonder " +"dat dat elkaar bijt. Meer informatie hierover `kan hier gevonden worden `_." #: ../../installation.rst:237 -msgid "Make sure you are still acting as ``dsmr`` user (if not then enter: ``sudo su - dsmr``)" -msgstr "Zorg ervoor dat je nog steeds ingelogd bent als ``dsmr``-gebruiker (zo niet, typ dan: ``sudo su - dsmr``)" +msgid "" +"Make sure you are still acting as ``dsmr`` user (if not then enter: ``sudo su - " +"dsmr``)" +msgstr "" +"Zorg ervoor dat je nog steeds ingelogd bent als ``dsmr``-gebruiker (zo niet, " +"typ dan: ``sudo su - dsmr``)" #: ../../installation.rst:239 msgid "Create folder for the virtualenv(s) of this user::" msgstr "Maak map aan voor de VirtualEnv van deze gebruiker::" #: ../../installation.rst:243 -msgid "Create a new virtualenv, we usually use the same name for it as the application or project::" -msgstr "Maak een nieuwe VirtualEnv aan. Het is gebruikelijk om hiervoor dezelfde naam te gebruiken als die van de applicatie of het project." +msgid "" +"Create a new virtualenv, we usually use the same name for it as the application " +"or project::" +msgstr "" +"Maak een nieuwe VirtualEnv aan. Het is gebruikelijk om hiervoor dezelfde naam " +"te gebruiken als die van de applicatie of het project." #: ../../installation.rst:249 -msgid "Note that it's important to specify **Python 3** as the default interpreter." -msgstr "N.B.: het is belangrijk dat je voor deze VirtualEnv aangeeft dat **Python 3** de gewenste standaardversie is::" +msgid "" +"Note that it's important to specify **Python 3** as the default interpreter." +msgstr "" +"N.B.: het is belangrijk dat je voor deze VirtualEnv aangeeft dat **Python 3** " +"de gewenste standaardversie is::" #: ../../installation.rst:251 -msgid "Put both commands below in the ``dsmr`` user's ``~/.bashrc`` file with your favorite text editor::" -msgstr "Zet beide commands in het ``~/.bashrc`` bestand van de ``dsmr`` gebruiker met je favoriete bestandseditor::" +msgid "" +"Put both commands below in the ``dsmr`` user's ``~/.bashrc`` file with your " +"favorite text editor::" +msgstr "" +"Zet beide commands in het ``~/.bashrc`` bestand van de ``dsmr`` gebruiker met " +"je favoriete bestandseditor::" #: ../../installation.rst:257 -msgid "This will both **activate** the virtual environment and cd you into the right directory on your **next login** as ``dsmr`` user." -msgstr "Hiermee wordt zowel de VirtualEnv geactiveerd en ga je direct naar de juiste map. Dit werkt de **eerstvolgende keer** dat je inlogt als ``dsmr`` gebruiker." +msgid "" +"This will both **activate** the virtual environment and cd you into the right " +"directory on your **next login** as ``dsmr`` user." +msgstr "" +"Hiermee wordt zowel de VirtualEnv geactiveerd en ga je direct naar de juiste " +"map. Dit werkt de **eerstvolgende keer** dat je inlogt als ``dsmr`` gebruiker." #: ../../installation.rst:261 -msgid "You can easily test whether you've configured this correctly by logging out the ``dsmr`` user (CTRL + D) and login again using ``sudo su - dsmr``." -msgstr "Je kunt dit vrij gemakkelijk testen door uit te loggen als ``dsmr`` gebruiker (met CTRL + D) en vervolgens weer in te loggen met ``sudo su - dsmr``." +msgid "" +"You can easily test whether you've configured this correctly by logging out the " +"``dsmr`` user (CTRL + D) and login again using ``sudo su - dsmr``." +msgstr "" +"Je kunt dit vrij gemakkelijk testen door uit te loggen als ``dsmr`` gebruiker " +"(met CTRL + D) en vervolgens weer in te loggen met ``sudo su - dsmr``." #: ../../installation.rst:263 -msgid "You should see the terminal have a ``(dsmrreader)`` prefix now, for example: ``(dsmrreader)dsmr@rasp:~/dsmr-reader $``" -msgstr "Als het goed is heeft je terminal nu een ``(dsmrreader)`` prefix, bijvoorbeeld: ``(dsmrreader)dsmr@rasp:~/dsmr-reader $``" +msgid "" +"You should see the terminal have a ``(dsmrreader)`` prefix now, for example: " +"``(dsmrreader)dsmr@rasp:~/dsmr-reader $``" +msgstr "" +"Als het goed is heeft je terminal nu een ``(dsmrreader)`` prefix, bijvoorbeeld: " +"``(dsmrreader)dsmr@rasp:~/dsmr-reader $``" #: ../../installation.rst:265 -msgid "Make sure you've read and executed the note above, because you'll need it for the next chapter." -msgstr "Zorg ervoor dat je de bovenstaande notitie hebt gelezen en uitgevoerd, aangezien ze nodig zijn voor het volgende hoofdstuk." +msgid "" +"Make sure you've read and executed the note above, because you'll need it for " +"the next chapter." +msgstr "" +"Zorg ervoor dat je de bovenstaande notitie hebt gelezen en uitgevoerd, " +"aangezien ze nodig zijn voor het volgende hoofdstuk." #: ../../installation.rst:269 msgid "7. Application configuration & setup" msgstr "7. Applicatieconfiguratie" #: ../../installation.rst:270 -msgid "The application will also need the appropriate database client, which is not installed by default. For this I created two ready-to-use requirements files, which will also install all other dependencies required, such as the Django framework." -msgstr "De applicatie heeft een databaseconnector nodig om de gegevens te kunnen opslaan. Daarvoor heb ik een tweetal requirements-bestanden gemaakt, waar alles al in staat wat nodig is. Zoals bijvoorbeeld Django en de databaseverbinding." +msgid "" +"The application will also need the appropriate database client, which is not " +"installed by default. For this I created two ready-to-use requirements files, " +"which will also install all other dependencies required, such as the Django " +"framework." +msgstr "" +"De applicatie heeft een databaseconnector nodig om de gegevens te kunnen " +"opslaan. Daarvoor heb ik een tweetal requirements-bestanden gemaakt, waar alles " +"al in staat wat nodig is. Zoals bijvoorbeeld Django en de databaseverbinding." #: ../../installation.rst:273 -msgid "The ``base.txt`` contains requirements which the application needs anyway, no matter which backend you've choosen." -msgstr "Het bestand ``base.txt`` bevat alle afhankelijkheden die de applicatie sowieso nodig heeft, ongeacht de databasekeuze die je gemaakt hebt." +msgid "" +"The ``base.txt`` contains requirements which the application needs anyway, no " +"matter which backend you've choosen." +msgstr "" +"Het bestand ``base.txt`` bevat alle afhankelijkheden die de applicatie sowieso " +"nodig heeft, ongeacht de databasekeuze die je gemaakt hebt." #: ../../installation.rst:277 -msgid "**Installation of the requirements below might take a while**, depending on your Internet connection, RaspberryPi speed and resources (generally CPU) available. Nothing to worry about. :]" -msgstr "**De installatie van de volgende afhankelijkheden kan enige tijd in beslag nemen**. Dit varieert en is sterk afhankelijk van de snelheid van je Internet-verbinding en je RaspberryPi. Je hoeft je dus niet zorgen te maken wanneer dit lang lijkt te duren. :]" +msgid "" +"**Installation of the requirements below might take a while**, depending on " +"your Internet connection, RaspberryPi speed and resources (generally CPU) " +"available. Nothing to worry about. :]" +msgstr "" +"**De installatie van de volgende afhankelijkheden kan enige tijd in beslag " +"nemen**. Dit varieert en is sterk afhankelijk van de snelheid van je Internet-" +"verbinding en je RaspberryPi. Je hoeft je dus niet zorgen te maken wanneer dit " +"lang lijkt te duren. :]" #: ../../installation.rst:280 msgid "PostgreSQL" @@ -344,8 +597,16 @@ msgid "Did you choose PostgreSQL? Then execute these two lines::" msgstr "Heb je gekozen voor PostgreSQL? Voer dan deze twee regels uit::" #: ../../installation.rst:288 -msgid "Did everything install without fatal errors? If the database client refuses to install due to missing files/configs, make sure you've installed ``postgresql-server-dev-all`` earlier in the process, when you installed the database server itself." -msgstr "Zonder problemen alles kunnen installeren? Mocht de database client niet willen installeren wegens missende bestanden, controleer dan of je eerder tijdens de installatie het volgende hebt uitgevoerd: als het goed is heb je ``postgresql-server-dev-all`` geïnstalleerd, tegelijkertijd met de databaseserver." +msgid "" +"Did everything install without fatal errors? If the database client refuses to " +"install due to missing files/configs, make sure you've installed ``postgresql-" +"server-dev-all`` earlier in the process, when you installed the database server " +"itself." +msgstr "" +"Zonder problemen alles kunnen installeren? Mocht de database client niet willen " +"installeren wegens missende bestanden, controleer dan of je eerder tijdens de " +"installatie het volgende hebt uitgevoerd: als het goed is heb je ``postgresql-" +"server-dev-all`` geïnstalleerd, tegelijkertijd met de databaseserver." #: ../../installation.rst:291 msgid "Continue to chapter 8 (Bootstrapping)." @@ -356,40 +617,71 @@ msgid "Or did you choose MySQL/MariaDB? Execute these two commands::" msgstr "Of heb je gekozen voor MySQL/MariaDB? Voer dan deze twee commando's uit::" #: ../../installation.rst:306 -msgid "Did everything install without fatal errors? If the database client refuses to install due to missing files/configs, make sure you've installed ``libmysqlclient-dev`` earlier in the process, when you installed the database server itself." -msgstr "Zonder problemen alles kunnen installeren? Mocht de database client niet willen installeren wegens missende bestanden, controleer dan of je eerder tijdens de installatie het volgende hebt uitgevoerd: als het goed is heb je ``libmysqlclient-dev`` geïnstalleerd, tegelijkertijd met de databaseserver." +msgid "" +"Did everything install without fatal errors? If the database client refuses to " +"install due to missing files/configs, make sure you've installed " +"``libmysqlclient-dev`` earlier in the process, when you installed the database " +"server itself." +msgstr "" +"Zonder problemen alles kunnen installeren? Mocht de database client niet willen " +"installeren wegens missende bestanden, controleer dan of je eerder tijdens de " +"installatie het volgende hebt uitgevoerd: als het goed is heb je " +"``libmysqlclient-dev`` geïnstalleerd, tegelijkertijd met de databaseserver." #: ../../installation.rst:311 msgid "8. Bootstrapping" msgstr "8. Initialisatie" #: ../../installation.rst:312 -msgid "Now it's time to bootstrap the application and check whether all settings are good and requirements are met." -msgstr "Tijd om te kijken of alles goed is ingesteld. We gaan de applicatie proberen te initialiseren." +msgid "" +"Now it's time to bootstrap the application and check whether all settings are " +"good and requirements are met." +msgstr "" +"Tijd om te kijken of alles goed is ingesteld. We gaan de applicatie proberen te " +"initialiseren." #: ../../installation.rst:314 msgid "Execute this to initialize the database we've created earlier::" msgstr "Voer dit uit om de database te initialiseren::" #: ../../installation.rst:318 -msgid "Prepare static files for webinterface. This will copy all static files to the directory we created for Nginx earlier in the process. It allows us to have Nginx serve static files outside our project/code root." -msgstr "Ga nu bezit met de statische bestanden voor de webinterface. Dit kopieert alle statische bestanden in de map die we eerder, vlak na de installatie voor Nginx, hebben aangemaakt. Het zorgt ervoor dat Nginx deze bestanden kan hosten buiten de code-bestanden." +msgid "" +"Prepare static files for webinterface. This will copy all static files to the " +"directory we created for Nginx earlier in the process. It allows us to have " +"Nginx serve static files outside our project/code root." +msgstr "" +"Ga nu bezit met de statische bestanden voor de webinterface. Dit kopieert alle " +"statische bestanden in de map die we eerder, vlak na de installatie voor Nginx, " +"hebben aangemaakt. Het zorgt ervoor dat Nginx deze bestanden kan hosten buiten " +"de code-bestanden." #: ../../installation.rst:321 msgid "Sync static files::" msgstr "Synchroniseer statische bestanden::" #: ../../installation.rst:325 -msgid "Create an application superuser. Django will prompt you for a password. The credentials generated can be used to access the administration panel inside the application. Alter username and email if you prefer other credentials, but email is not used in the application anyway." -msgstr "Maak een gebruiker aan voor binnen de applicatie. Django vraagt je om een wachtwoord te kiezen. Met deze gegevens kun je het beheerderspaneel binnen de applicatie gebruiken. Indien gewenst kun je een andere gebruikersnaam kiezen. Het e-mailadres maakt niet uit, want de applicatie ondersteunt toch geen e-mail." +msgid "" +"Create an application superuser. Django will prompt you for a password. The " +"credentials generated can be used to access the administration panel inside the " +"application. Alter username and email if you prefer other credentials, but " +"email is not used in the application anyway." +msgstr "" +"Maak een gebruiker aan voor binnen de applicatie. Django vraagt je om een " +"wachtwoord te kiezen. Met deze gegevens kun je het beheerderspaneel binnen de " +"applicatie gebruiken. Indien gewenst kun je een andere gebruikersnaam kiezen. " +"Het e-mailadres maakt niet uit, want de applicatie ondersteunt toch geen e-mail." #: ../../installation.rst:328 msgid "Create your user::" msgstr "Creëer je eigen gebruiker::" #: ../../installation.rst:334 -msgid "Because you have shell access you may reset your user's password at any time (in case you forget it). Just enter this for a password reset::" -msgstr "Wachtwoord ooit vergeten? Via de command line kun je je wachtwoord (bij verlies) hiermee aanpassen::" +msgid "" +"Because you have shell access you may reset your user's password at any time " +"(in case you forget it). Just enter this for a password reset::" +msgstr "" +"Wachtwoord ooit vergeten? Via de command line kun je je wachtwoord (bij " +"verlies) hiermee aanpassen::" #: ../../installation.rst:338 msgid "You've almost completed the installation now." @@ -400,36 +692,61 @@ msgid "9. Webserver/Nginx (part 2)" msgstr "9. Webserver/Nginx (deel 2)" #: ../../installation.rst:346 -msgid "This installation guide asumes you run the Nginx webserver for this application only." -msgstr "Deze installatiehandleiding gaat er vanuit dat je de Nginx webserver alleen gebruikt voor deze applicatie." +msgid "" +"This installation guide asumes you run the Nginx webserver for this application " +"only." +msgstr "" +"Deze installatiehandleiding gaat er vanuit dat je de Nginx webserver alleen " +"gebruikt voor deze applicatie." #: ../../installation.rst:348 -msgid "It's possible to have other applications use Nginx as well, but that requires you to remove the wildcard in the ``dsmr-webinterface`` vhost, which you will copy below." -msgstr "Het is uiteraard mogelijk dat andere applicaties ook Nginx gebruiken, maar daarvoor zul je de wildcard moet weghalen in de ``dsmr-webinterface`` vhost, die je hieronder kopieert." - -#: ../../installation.rst:350 -msgid "Go back to ``root`` / ``sudo`` user to configure the webserver (press ``CTRL + D`` once)." -msgstr "Ga terug naar de ``root`` / ``sudo``-gebruiker om de webserver in te stellen (druk op ``CTRL + D``)." +msgid "" +"It's possible to have other applications use Nginx as well, but that requires " +"you to remove the wildcard in the ``dsmr-webinterface`` vhost, which you will " +"copy below." +msgstr "" +"Het is uiteraard mogelijk dat andere applicaties ook Nginx gebruiken, maar " +"daarvoor zul je de wildcard moet weghalen in de ``dsmr-webinterface`` vhost, " +"die je hieronder kopieert." #: ../../installation.rst:352 -msgid "Remove the default Nginx vhost (**only when you do not use it yourself, see the note above**)::" -msgstr "Verwijder de standaard vhost van Nginx **wanneer je deze niet zelf gebruikt** (zie de notitie hierboven)::" +msgid "" +"Remove the default Nginx vhost (**only when you do not use it yourself, see the " +"note above**)::" +msgstr "" +"Verwijder de standaard vhost van Nginx **wanneer je deze niet zelf gebruikt** " +"(zie de notitie hierboven)::" #: ../../installation.rst:356 -msgid "Copy application vhost, **it will listen to any hostname** (wildcard), but you may change that if you feel like you need to. It won't affect the application anyway::" -msgstr "Kopieer de vhost voor de applicatie. Deze luistert standaard naar **elke hostname** (wildcard), maar dat is natuurlijk naar eigen wens aan te passen::" +msgid "" +"Copy application vhost, **it will listen to any hostname** (wildcard), but you " +"may change that if you feel like you need to. It won't affect the application " +"anyway::" +msgstr "" +"Kopieer de vhost voor de applicatie. Deze luistert standaard naar **elke " +"hostname** (wildcard), maar dat is natuurlijk naar eigen wens aan te passen::" #: ../../installation.rst:360 -msgid "Let Nginx verify vhost syntax and reload Nginx when ``configtest`` passes::" -msgstr "Laat Nginx controleren of je geen typefouten hebt gemaakt en herlaad Nginx vervolgens wanneer de ``configtest`` lukt::" +msgid "" +"Let Nginx verify vhost syntax and reload Nginx when ``configtest`` passes::" +msgstr "" +"Laat Nginx controleren of je geen typefouten hebt gemaakt en herlaad Nginx " +"vervolgens wanneer de ``configtest`` lukt::" #: ../../installation.rst:369 msgid "10. Supervisor" msgstr "10. Supervisor" #: ../../installation.rst:370 -msgid "Now we configure `Supervisor `_, which is used to run our application's web interface and background jobs used. It's also configured to bring the entire application up again after a shutdown or reboot." -msgstr "We gaan nu `Supervisor `_ configureren, die gebruikt wordt om de applicatie en achtergrondprocessen te draaien. Tevens zorgt Supervisor ervoor dat deze processen bij het (opnieuw) opstarten automatisch draaien." +msgid "" +"Now we configure `Supervisor `_, which is used to run " +"our application's web interface and background jobs used. It's also configured " +"to bring the entire application up again after a shutdown or reboot." +msgstr "" +"We gaan nu `Supervisor `_ configureren, die gebruikt " +"wordt om de applicatie en achtergrondprocessen te draaien. Tevens zorgt " +"Supervisor ervoor dat deze processen bij het (opnieuw) opstarten automatisch " +"draaien." #: ../../installation.rst:373 msgid "Copy the configuration file for Supervisor::" @@ -440,12 +757,22 @@ msgid "Login to ``supervisorctl`` management console::" msgstr "Wissel naar de ``supervisorctl`` beheerconsole::" #: ../../installation.rst:381 -msgid "Enter these commands (**listed after the** ``>``). It will ask Supervisor to recheck its config directory and use/reload the files::" -msgstr "Voer de volgende commando's in (**degene na de** ``>``). Dit zorgt ervoor dat Supervisor zijn eigen configuratie opnieuw controleert en toepast::" +msgid "" +"Enter these commands (**listed after the** ``>``). It will ask Supervisor to " +"recheck its config directory and use/reload the files::" +msgstr "" +"Voer de volgende commando's in (**degene na de** ``>``). Dit zorgt ervoor dat " +"Supervisor zijn eigen configuratie opnieuw controleert en toepast::" #: ../../installation.rst:387 -msgid "Three processes should be started or running. Make sure they don't end up in ``ERROR`` or ``BACKOFF`` state, so refresh with the ``status`` command a few times." -msgstr "Er draaien als het goed is altijd **drie** processen. Kijk goed of ze uiteindelijk niet in ``ERROR`` of ``BACKOFF`` status terecht zijn gekomen. Je kunt het overzicht verversen door ``status`` te typen." +msgid "" +"Three processes should be started or running. Make sure they don't end up in " +"``ERROR`` or ``BACKOFF`` state, so refresh with the ``status`` command a few " +"times." +msgstr "" +"Er draaien als het goed is altijd **drie** processen. Kijk goed of ze " +"uiteindelijk niet in ``ERROR`` of ``BACKOFF`` status terecht zijn gekomen. Je " +"kunt het overzicht verversen door ``status`` te typen." #: ../../installation.rst:389 msgid "When still in ``supervisorctl``'s console, type::" @@ -456,23 +783,40 @@ msgid "Example of everything running well::" msgstr "Voorbeeld van wanneer alles naar behoren draait::" #: ../../installation.rst:399 -msgid "Want to check whether the datalogger works? Just tail it's log in supervisor with::" -msgstr "Wil je controleren of de datalogger zijn werk goed doet? Bekijk dan het logbestand in supervisor met::" +msgid "" +"Want to check whether the datalogger works? Just tail it's log in supervisor " +"with::" +msgstr "" +"Wil je controleren of de datalogger zijn werk goed doet? Bekijk dan het " +"logbestand in supervisor met::" #: ../../installation.rst:403 -msgid "You should see similar output as the ``cu``-command printed earlier in the installation process." -msgstr "Uiteindelijk zou je ongeveer dezelfde lap tekst moeten zien als toen we handmatig gemeten hebben met het ``cu``-programma." +msgid "" +"You should see similar output as the ``cu``-command printed earlier in the " +"installation process." +msgstr "" +"Uiteindelijk zou je ongeveer dezelfde lap tekst moeten zien als toen we " +"handmatig gemeten hebben met het ``cu``-programma." #: ../../installation.rst:405 -msgid "Want to quit supervisor? ``CTRL + C`` to stop tailing and then ``CTRL + D`` once to exit supervisor command line." -msgstr "Wil je uit supervisor? Druk dan op ``CTRL + C`` om het logbestand niet meer te bekijken en vervolgens op ``CTRL + D`` om uit supervisor te gaan." +msgid "" +"Want to quit supervisor? ``CTRL + C`` to stop tailing and then ``CTRL + D`` " +"once to exit supervisor command line." +msgstr "" +"Wil je uit supervisor? Druk dan op ``CTRL + C`` om het logbestand niet meer te " +"bekijken en vervolgens op ``CTRL + D`` om uit supervisor te gaan." #: ../../installation.rst:408 -msgid "You now should have everything up and running! We're almost done and just need to do a few last things on the next page." -msgstr "Alles zou nu moeten werken! We zijn bijna klaar, op een paar laatste dingen na in het volgende hoofdstuk." +msgid "" +"You now should have everything up and running! We're almost done and just need " +"to do a few last things on the next page." +msgstr "" +"Alles zou nu moeten werken! We zijn bijna klaar, op een paar laatste dingen na " +"in het volgende hoofdstuk." #~ msgid "" +#~ "Go back to ``root`` / ``sudo`` user to configure the webserver (press ``CTRL " +#~ "+ D`` once)." #~ msgstr "" - -#~ msgid "Contents" -#~ msgstr "Inhoudsopgave" +#~ "Ga terug naar de ``root`` / ``sudo``-gebruiker om de webserver in te stellen " +#~ "(druk op ``CTRL + D``)." diff --git a/docs/locale/nl/LC_MESSAGES/licence.mo b/docs/locale/nl/LC_MESSAGES/licence.mo index a90e65eb9..65e8f241e 100644 Binary files a/docs/locale/nl/LC_MESSAGES/licence.mo and b/docs/locale/nl/LC_MESSAGES/licence.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/licence.po b/docs/locale/nl/LC_MESSAGES/licence.po index a315fb785..50ee834bd 100644 --- a/docs/locale/nl/LC_MESSAGES/licence.po +++ b/docs/locale/nl/LC_MESSAGES/licence.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader v1.x\n" "Report-Msgid-Bugs-To: Dennis Siemensma \n" -"POT-Creation-Date: 2016-01-01 00:00+0100\n" +"POT-Creation-Date: 2017-05-02 21:26+0200\n" "PO-Revision-Date: 2017-01-01 00:00+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" @@ -25,10 +25,6 @@ msgstr "Licentie (GNU GPLv3)" msgid "**Copyright (C) 2015/2016 Dennis Siemensma**" msgstr "" -#: ../../licence.rst -msgid "Licence" -msgstr "" - #: ../../licence.rst:12 msgid "GNU GENERAL PUBLIC LICENSE" msgstr "" diff --git a/docs/locale/nl/LC_MESSAGES/screenshots.po b/docs/locale/nl/LC_MESSAGES/screenshots.po index 384ca2f29..41aec1d7a 100644 --- a/docs/locale/nl/LC_MESSAGES/screenshots.po +++ b/docs/locale/nl/LC_MESSAGES/screenshots.po @@ -159,7 +159,3 @@ msgid "" msgstr "" "Er zijn nog diverse andere beheerpagina's beschikbaar voor de overige " "configuratie-instellingen." - -#~ msgid "Contents" -#~ msgstr "Inhoud" - diff --git a/dsmr_api/authentication.py b/dsmr_api/authentication.py new file mode 100644 index 000000000..e1f8e0d1f --- /dev/null +++ b/dsmr_api/authentication.py @@ -0,0 +1,28 @@ +from django.contrib.auth.models import User +from django.conf import settings +from rest_framework import authentication +from rest_framework import exceptions + +from dsmr_api.models import APISettings + + +class HeaderAuthentication(authentication.BaseAuthentication): + """ + Authentication is provided by sending the API key via a 'X-AUTHKEY' header on each request. + The API key can be found (and changed) inside the admin interface of the application. + """ + def authenticate(self, request): + api_settings = APISettings.get_solo() + + if not api_settings.allow: + raise exceptions.PermissionDenied('API is disabled') + + if request.META.get('HTTP_X_AUTHKEY') != api_settings.auth_key: + raise exceptions.AuthenticationFailed('Invalid auth key') + + try: + user = User.objects.get(username=settings.DSMRREADER_REST_FRAMEWORK_API_USER) + except User.DoesNotExist: + raise exceptions.APIException('API user not found') + + return (user, None) diff --git a/dsmr_api/filters.py b/dsmr_api/filters.py new file mode 100644 index 000000000..89a20eac0 --- /dev/null +++ b/dsmr_api/filters.py @@ -0,0 +1,55 @@ +from django_filters import rest_framework as filters + +from dsmr_datalogger.models.reading import DsmrReading +from dsmr_consumption.models.consumption import GasConsumption, ElectricityConsumption +from dsmr_stats.models.statistics import DayStatistics, HourStatistics + + +class DsmrReadingFilter(filters.FilterSet): + FIELD = 'timestamp' + timestamp__gte = filters.DateTimeFilter(name=FIELD, lookup_expr='gte') + timestamp__lte = filters.DateTimeFilter(name=FIELD, lookup_expr='lte') + + class Meta: + model = DsmrReading + fields = ['timestamp'] + + +class ElectricityConsumptionFilter(filters.FilterSet): + FIELD = 'read_at' + read_at__gte = filters.DateTimeFilter(name=FIELD, lookup_expr='gte') + read_at__lte = filters.DateTimeFilter(name=FIELD, lookup_expr='lte') + + class Meta: + model = ElectricityConsumption + fields = ['read_at'] + + +class GasConsumptionFilter(filters.FilterSet): + FIELD = 'read_at' + read_at__gte = filters.DateTimeFilter(name=FIELD, lookup_expr='gte') + read_at__lte = filters.DateTimeFilter(name=FIELD, lookup_expr='lte') + + class Meta: + model = GasConsumption + fields = ['read_at'] + + +class DayStatisticsFilter(filters.FilterSet): + FIELD = 'day' + day__gte = filters.DateFilter(name=FIELD, lookup_expr='gte') + day__lte = filters.DateFilter(name=FIELD, lookup_expr='lte') + + class Meta: + model = DayStatistics + fields = ['day'] + + +class HourStatisticsFilter(filters.FilterSet): + FIELD = 'hour_start' + hour_start__gte = filters.DateTimeFilter(name=FIELD, lookup_expr='gte') + hour_start__lte = filters.DateTimeFilter(name=FIELD, lookup_expr='lte') + + class Meta: + model = HourStatistics + fields = ['hour_start'] diff --git a/dsmr_api/fixtures/dsmr_api/test_day_statistics.json b/dsmr_api/fixtures/dsmr_api/test_day_statistics.json new file mode 100644 index 000000000..c24093565 --- /dev/null +++ b/dsmr_api/fixtures/dsmr_api/test_day_statistics.json @@ -0,0 +1,135 @@ +[ +{ + "model": "dsmr_stats.daystatistics", + "pk": 756, + "fields": { + "day": "2015-12-11", + "total_cost": "0.08", + "electricity1": "0.000", + "electricity2": "0.349", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "0.00", + "electricity2_cost": "0.08", + "gas": "0.000", + "gas_cost": "0.00", + "lowest_temperature": "0.0", + "highest_temperature": "0.0", + "average_temperature": "0.0" + } +}, +{ + "model": "dsmr_stats.daystatistics", + "pk": 757, + "fields": { + "day": "2015-12-12", + "total_cost": "0.01", + "electricity1": "0.027", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "0.01", + "electricity2_cost": "0.00", + "gas": "0.000", + "gas_cost": "0.00", + "lowest_temperature": "0.0", + "highest_temperature": "0.0", + "average_temperature": "0.0" + } +}, +{ + "model": "dsmr_stats.daystatistics", + "pk": 758, + "fields": { + "day": "2015-12-13", + "total_cost": "0.16", + "electricity1": "0.210", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "0.05", + "electricity2_cost": "0.00", + "gas": "0.175", + "gas_cost": "0.11", + "lowest_temperature": "0.0", + "highest_temperature": "0.0", + "average_temperature": "0.0" + } +}, +{ + "model": "dsmr_stats.daystatistics", + "pk": 759, + "fields": { + "day": "2015-12-14", + "total_cost": "0.03", + "electricity1": "0.110", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "0.03", + "electricity2_cost": "0.00", + "gas": "0.000", + "gas_cost": "0.00", + "lowest_temperature": "0.0", + "highest_temperature": "0.0", + "average_temperature": "0.0" + } +}, +{ + "model": "dsmr_stats.daystatistics", + "pk": 760, + "fields": { + "day": "2015-12-15", + "total_cost": "0.48", + "electricity1": "0.547", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "0.12", + "electricity2_cost": "0.00", + "gas": "0.604", + "gas_cost": "0.36", + "lowest_temperature": "0.0", + "highest_temperature": "0.0", + "average_temperature": "0.0" + } +}, +{ + "model": "dsmr_stats.daystatistics", + "pk": 761, + "fields": { + "day": "2015-12-16", + "total_cost": "0.07", + "electricity1": "0.293", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "0.07", + "electricity2_cost": "0.00", + "gas": "0.000", + "gas_cost": "0.00", + "lowest_temperature": "0.0", + "highest_temperature": "0.0", + "average_temperature": "0.0" + } +}, +{ + "model": "dsmr_stats.daystatistics", + "pk": 762, + "fields": { + "day": "2015-12-17", + "total_cost": "0.02", + "electricity1": "0.053", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "electricity1_cost": "0.02", + "electricity2_cost": "0.00", + "gas": "0.000", + "gas_cost": "0.00", + "lowest_temperature": "0.0", + "highest_temperature": "0.0", + "average_temperature": "0.0" + } +} +] diff --git a/dsmr_api/fixtures/dsmr_api/test_dsmrreading.json b/dsmr_api/fixtures/dsmr_api/test_dsmrreading.json new file mode 100644 index 000000000..054323189 --- /dev/null +++ b/dsmr_api/fixtures/dsmr_api/test_dsmrreading.json @@ -0,0 +1,53 @@ +[ +{ + "fields": { + "electricity_delivered_1": "528.750", + "extra_device_timestamp": "2016-07-01T18:00:00Z", + "extra_device_delivered": "845.888", + "electricity_returned_2": "123.456", + "electricity_currently_delivered": "0.247", + "timestamp": "2016-07-01T18:00:00Z", + "electricity_returned_1": "0.456", + "electricity_currently_returned": "0.123", + "processed": false, + "electricity_delivered_2": "525.500" + }, + "model": "dsmr_datalogger.dsmrreading", + "pk": 1 +}, +{ + "fields": { + "electricity_delivered_1": "529.750", + "extra_device_timestamp": "2016-07-01T19:00:00Z", + "extra_device_delivered": "845.898", + "electricity_returned_2": "123.567", + "electricity_currently_delivered": "0.750", + "timestamp": "2016-07-01T19:00:00Z", + "electricity_returned_1": "0.567", + "electricity_currently_returned": "0.123", + "processed": false, + "electricity_delivered_2": "527.500" + }, + "model": "dsmr_datalogger.dsmrreading", + "pk": 2 +}, +{ + "fields": { + "electricity_delivered_1": "530.750", + "extra_device_timestamp": "2016-07-01T20:00:00Z", + "extra_device_delivered": "845.898", + "electricity_returned_2": "123.678", + "electricity_currently_delivered": "1.123", + "timestamp": "2016-07-01T20:00:00Z", + "electricity_returned_1": "0.678", + "electricity_currently_returned": "0.123", + "processed": false, + "electricity_delivered_2": "528.500", + "phase_currently_delivered_l1": "0.123", + "phase_currently_delivered_l2": "0.456", + "phase_currently_delivered_l3": "0.789" + }, + "model": "dsmr_datalogger.dsmrreading", + "pk": 3 +} +] \ No newline at end of file diff --git a/dsmr_api/fixtures/dsmr_api/test_electricity_consumption.json b/dsmr_api/fixtures/dsmr_api/test_electricity_consumption.json new file mode 100644 index 000000000..0664c7bbf --- /dev/null +++ b/dsmr_api/fixtures/dsmr_api/test_electricity_consumption.json @@ -0,0 +1,873 @@ +[ +{ + "pk": 95, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.158", + "delivered_1": "595.187", + "read_at": "2015-12-11T23:00:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 96, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.157", + "delivered_1": "595.190", + "read_at": "2015-12-11T23:01:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 97, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.159", + "delivered_1": "595.193", + "read_at": "2015-12-11T23:02:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 98, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.158", + "delivered_1": "595.196", + "read_at": "2015-12-11T23:03:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 99, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.083", + "delivered_1": "595.197", + "read_at": "2015-12-11T23:04:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 100, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.069", + "delivered_1": "595.198", + "read_at": "2015-12-11T23:05:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 101, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.064", + "delivered_1": "595.200", + "read_at": "2015-12-11T23:06:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 102, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.112", + "delivered_1": "595.201", + "read_at": "2015-12-11T23:07:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 103, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.123", + "delivered_1": "595.203", + "read_at": "2015-12-11T23:08:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 104, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.120", + "delivered_1": "595.205", + "read_at": "2015-12-11T23:09:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 105, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.122", + "delivered_1": "595.207", + "read_at": "2015-12-11T23:10:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 106, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.123", + "delivered_1": "595.210", + "read_at": "2015-12-11T23:11:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 107, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.122", + "delivered_1": "595.212", + "read_at": "2015-12-11T23:12:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 108, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.121", + "delivered_1": "595.213", + "read_at": "2015-12-11T23:13:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 109, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.078", + "delivered_1": "595.215", + "read_at": "2015-12-11T23:14:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 110, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.074", + "delivered_1": "595.216", + "read_at": "2015-12-11T23:15:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 111, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.074", + "delivered_1": "595.217", + "read_at": "2015-12-11T23:16:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 112, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.074", + "delivered_1": "595.219", + "read_at": "2015-12-11T23:17:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 113, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.072", + "delivered_1": "595.220", + "read_at": "2015-12-11T23:18:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 114, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.053", + "delivered_1": "595.221", + "read_at": "2015-12-11T23:19:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 115, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.050", + "delivered_1": "595.222", + "read_at": "2015-12-11T23:20:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 116, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.048", + "delivered_1": "595.223", + "read_at": "2015-12-11T23:21:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 117, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.048", + "delivered_1": "595.223", + "read_at": "2015-12-11T23:22:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 118, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.049", + "delivered_1": "595.224", + "read_at": "2015-12-11T23:23:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 119, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.049", + "delivered_1": "595.225", + "read_at": "2015-12-11T23:24:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 120, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.049", + "delivered_1": "595.226", + "read_at": "2015-12-11T23:25:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 121, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.048", + "delivered_1": "595.227", + "read_at": "2015-12-11T23:26:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 122, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.048", + "delivered_1": "595.227", + "read_at": "2015-12-11T23:27:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 123, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.049", + "delivered_1": "595.228", + "read_at": "2015-12-11T23:28:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 124, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.121", + "delivered_1": "595.231", + "read_at": "2015-12-11T23:29:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 125, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.138", + "delivered_1": "595.233", + "read_at": "2015-12-11T23:30:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 126, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.133", + "delivered_1": "595.235", + "read_at": "2015-12-11T23:31:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 127, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.131", + "delivered_1": "595.237", + "read_at": "2015-12-11T23:32:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 128, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.130", + "delivered_1": "595.239", + "read_at": "2015-12-11T23:33:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 129, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.130", + "delivered_1": "595.241", + "read_at": "2015-12-11T23:34:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 130, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.130", + "delivered_1": "595.244", + "read_at": "2015-12-11T23:35:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 131, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.130", + "delivered_1": "595.246", + "read_at": "2015-12-11T23:36:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 132, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.128", + "delivered_1": "595.248", + "read_at": "2015-12-11T23:37:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 133, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.149", + "delivered_1": "595.250", + "read_at": "2015-12-11T23:38:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 134, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.193", + "delivered_1": "595.254", + "read_at": "2015-12-11T23:39:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 135, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.192", + "delivered_1": "595.257", + "read_at": "2015-12-11T23:40:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 136, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.191", + "delivered_1": "595.260", + "read_at": "2015-12-11T23:41:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 137, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.190", + "delivered_1": "595.263", + "read_at": "2015-12-11T23:42:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 138, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.129", + "delivered_1": "595.266", + "read_at": "2015-12-11T23:43:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 139, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.113", + "delivered_1": "595.268", + "read_at": "2015-12-11T23:44:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 140, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.112", + "delivered_1": "595.269", + "read_at": "2015-12-11T23:45:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 141, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.112", + "delivered_1": "595.271", + "read_at": "2015-12-11T23:46:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 142, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.113", + "delivered_1": "595.273", + "read_at": "2015-12-11T23:47:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 143, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.112", + "delivered_1": "595.275", + "read_at": "2015-12-11T23:48:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 144, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.112", + "delivered_1": "595.277", + "read_at": "2015-12-11T23:49:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 145, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.110", + "delivered_1": "595.279", + "read_at": "2015-12-11T23:50:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 146, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.111", + "delivered_1": "595.281", + "read_at": "2015-12-11T23:51:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 147, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.111", + "delivered_1": "595.283", + "read_at": "2015-12-11T23:52:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 148, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.112", + "delivered_1": "595.285", + "read_at": "2015-12-11T23:53:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 149, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.110", + "delivered_1": "595.286", + "read_at": "2015-12-11T23:54:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 150, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.111", + "delivered_1": "595.288", + "read_at": "2015-12-11T23:55:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 151, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.112", + "delivered_1": "595.290", + "read_at": "2015-12-11T23:56:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 152, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.111", + "delivered_1": "595.292", + "read_at": "2015-12-11T23:57:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 153, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.110", + "delivered_1": "595.294", + "read_at": "2015-12-11T23:58:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 154, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.110", + "delivered_1": "595.296", + "read_at": "2015-12-11T23:59:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 155, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.111", + "delivered_1": "595.298", + "read_at": "2015-12-12T00:00:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 156, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.111", + "delivered_1": "595.300", + "read_at": "2015-12-12T00:01:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 214, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.108", + "delivered_1": "595.427", + "read_at": "2015-12-12T00:59:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 215, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.109", + "delivered_1": "595.428", + "read_at": "2015-12-12T01:00:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 216, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.110", + "delivered_1": "595.430", + "read_at": "2015-12-12T01:01:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 217, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.109", + "delivered_1": "595.428", + "read_at": "2015-12-15T01:00:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +}, +{ + "pk": 218, + "fields": { + "returned_1": "0.000", + "delivered_2": "593.558", + "currently_returned": "0.000", + "returned_2": "0.000", + "currently_delivered": "0.110", + "delivered_1": "595.430", + "read_at": "2015-12-15T01:01:00Z" + }, + "model": "dsmr_consumption.electricityconsumption" +} +] diff --git a/dsmr_api/fixtures/dsmr_api/test_gas_consumption.json b/dsmr_api/fixtures/dsmr_api/test_gas_consumption.json new file mode 100644 index 000000000..f949aad27 --- /dev/null +++ b/dsmr_api/fixtures/dsmr_api/test_gas_consumption.json @@ -0,0 +1,281 @@ +[ +{ + "pk": 1, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-11T20:00:00Z", + "delivered": "956.212", + "currently_delivered": "0.000" + } +}, +{ + "pk": 2, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-11T21:00:00Z", + "delivered": "956.221", + "currently_delivered": "0.009" + } +}, +{ + "pk": 3, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-11T22:00:00Z", + "delivered": "956.230", + "currently_delivered": "0.009" + } +}, +{ + "pk": 4, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-11T23:00:00Z", + "delivered": "956.739", + "currently_delivered": "0.509" + } +}, +{ + "pk": 5, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T00:00:00Z", + "delivered": "957.893", + "currently_delivered": "1.154" + } +}, +{ + "pk": 6, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T01:00:00Z", + "delivered": "958.559", + "currently_delivered": "0.666" + } +}, +{ + "pk": 7, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T02:00:00Z", + "delivered": "958.568", + "currently_delivered": "0.009" + } +}, +{ + "pk": 8, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T03:00:00Z", + "delivered": "958.577", + "currently_delivered": "0.009" + } +}, +{ + "pk": 9, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T04:00:00Z", + "delivered": "958.586", + "currently_delivered": "0.009" + } +}, +{ + "pk": 10, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T05:00:00Z", + "delivered": "958.595", + "currently_delivered": "0.009" + } +}, +{ + "pk": 11, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T06:00:00Z", + "delivered": "958.595", + "currently_delivered": "0.000" + } +}, +{ + "pk": 12, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T07:00:00Z", + "delivered": "959.336", + "currently_delivered": "0.741" + } +}, +{ + "pk": 13, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T08:00:00Z", + "delivered": "960.444", + "currently_delivered": "1.108" + } +}, +{ + "pk": 14, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T09:00:00Z", + "delivered": "960.509", + "currently_delivered": "0.065" + } +}, +{ + "pk": 15, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T10:00:00Z", + "delivered": "960.509", + "currently_delivered": "0.000" + } +}, +{ + "pk": 16, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T11:00:00Z", + "delivered": "960.518", + "currently_delivered": "0.009" + } +}, +{ + "pk": 17, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T12:00:00Z", + "delivered": "960.527", + "currently_delivered": "0.009" + } +}, +{ + "pk": 18, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T13:00:00Z", + "delivered": "960.536", + "currently_delivered": "0.009" + } +}, +{ + "pk": 19, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T14:00:00Z", + "delivered": "960.545", + "currently_delivered": "0.009" + } +}, +{ + "pk": 20, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T15:00:00Z", + "delivered": "960.554", + "currently_delivered": "0.009" + } +}, +{ + "pk": 21, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T16:00:00Z", + "delivered": "960.564", + "currently_delivered": "0.010" + } +}, +{ + "pk": 22, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T17:00:00Z", + "delivered": "961.651", + "currently_delivered": "1.087" + } +}, +{ + "pk": 23, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T18:00:00Z", + "delivered": "962.309", + "currently_delivered": "0.658" + } +}, +{ + "pk": 24, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T19:00:00Z", + "delivered": "962.319", + "currently_delivered": "0.010" + } +}, +{ + "pk": 25, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T20:00:00Z", + "delivered": "962.328", + "currently_delivered": "0.009" + } +}, +{ + "pk": 26, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T21:00:00Z", + "delivered": "962.337", + "currently_delivered": "0.009" + } +}, +{ + "pk": 27, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T22:00:00Z", + "delivered": "962.346", + "currently_delivered": "0.009" + } +}, +{ + "pk": 28, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-12T23:00:00Z", + "delivered": "962.365", + "currently_delivered": "0.019" + } +}, +{ + "pk": 29, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-13T00:00:00Z", + "delivered": "962.540", + "currently_delivered": "0.175" + } +}, +{ + "pk": 30, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-13T01:00:00Z", + "delivered": "962.549", + "currently_delivered": "0.009" + } +}, +{ + "pk": 31, + "model": "dsmr_consumption.gasconsumption", + "fields": { + "read_at": "2015-12-13T02:00:00Z", + "delivered": "962.558", + "currently_delivered": "0.009" + } +} +] \ No newline at end of file diff --git a/dsmr_api/fixtures/dsmr_api/test_hour_statistics.json b/dsmr_api/fixtures/dsmr_api/test_hour_statistics.json new file mode 100644 index 000000000..9bbaf6260 --- /dev/null +++ b/dsmr_api/fixtures/dsmr_api/test_hour_statistics.json @@ -0,0 +1,158 @@ +[ +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14551, + "fields": { + "hour_start": "2015-12-11T21:00:00Z", + "electricity1": "0.000", + "electricity2": "0.349", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14552, + "fields": { + "hour_start": "2015-12-11T23:00:00Z", + "electricity1": "0.027", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14553, + "fields": { + "hour_start": "2015-12-12T23:00:00Z", + "electricity1": "0.068", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.019" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14554, + "fields": { + "hour_start": "2015-12-13T00:00:00Z", + "electricity1": "0.129", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.175" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14555, + "fields": { + "hour_start": "2015-12-13T01:00:00Z", + "electricity1": "0.011", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14556, + "fields": { + "hour_start": "2015-12-13T23:00:00Z", + "electricity1": "0.108", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14557, + "fields": { + "hour_start": "2015-12-14T00:00:00Z", + "electricity1": "0.001", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14558, + "fields": { + "hour_start": "2015-12-14T23:00:00Z", + "electricity1": "0.327", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.009" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14559, + "fields": { + "hour_start": "2015-12-15T00:00:00Z", + "electricity1": "0.115", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.604" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14560, + "fields": { + "hour_start": "2015-12-15T01:00:00Z", + "electricity1": "0.100", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14561, + "fields": { + "hour_start": "2015-12-15T23:00:00Z", + "electricity1": "0.211", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.010" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14562, + "fields": { + "hour_start": "2015-12-16T00:00:00Z", + "electricity1": "0.079", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +}, +{ + "model": "dsmr_stats.hourstatistics", + "pk": 14563, + "fields": { + "hour_start": "2015-12-16T23:00:00Z", + "electricity1": "0.053", + "electricity2": "0.000", + "electricity1_returned": "0.000", + "electricity2_returned": "0.000", + "gas": "0.000" + } +} +] diff --git a/dsmr_api/management/__init__.py b/dsmr_api/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_api/management/commands/__init__.py b/dsmr_api/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_api/management/commands/dsmr_api_test_v2.py b/dsmr_api/management/commands/dsmr_api_test_v2.py new file mode 100644 index 000000000..2dd270d0b --- /dev/null +++ b/dsmr_api/management/commands/dsmr_api_test_v2.py @@ -0,0 +1,71 @@ +import pprint +import urllib +import json + +import requests +from django.core.management.base import BaseCommand, CommandError +from django.conf import settings + +from dsmr_api.models import APISettings + + +class Command(BaseCommand): + help = 'Testscript for testing the v2 API.' + API_KEY = None + + def handle(self, **options): + if not settings.DEBUG: + raise CommandError('Only meant for development environment!') + + self.API_KEY = APISettings.get_solo().auth_key + + self._test_dsmrreading() + self._test_electricity_consumption() + self._test_gas_consumption() + self._test_day_statistics() + self._test_hour_statistics() + + def _test_dsmrreading(self): + URI = 'datalogger/dsmrreading' + self._request(uri=URI, limit=2, ordering='-timestamp', timestamp__gte='2017-04-11 05:59:00') +# self._request(method='post', uri=URI, **{ +# 'electricity_currently_delivered': 1.500, +# 'electricity_currently_returned': 0.025, +# 'electricity_delivered_1': 2000, +# 'electricity_delivered_2': 3000, +# 'electricity_returned_1': 0, +# 'electricity_returned_2': 0, +# 'timestamp': '2017-04-15T00:00:00+02', +# }) + + def _test_electricity_consumption(self): + URI = 'consumption/electricity' + self._request(uri=URI, limit=5, ordering='-read_at', read_at__lte='2017-04-10 03:00:00') + + def _test_gas_consumption(self): + URI = 'consumption/gas' + self._request(uri=URI, limit=5, ordering='-read_at', read_at__gte='2017-04-10 03:00:00') + + def _test_day_statistics(self): + URI = 'statistics/day' + self._request(uri=URI, day='2017-01-01') + self._request(uri=URI, ordering='-day', day__gte='2016-01-01', day__lte='2016-02-01') + + def _test_hour_statistics(self): + URI = 'statistics/hour' + self._request(uri=URI, limit=3, hour_start__gte='2016-01-01 12:00:00') + + def _request(self, uri, method='get', **data): + if method == 'get': + uri = '{}?{}'.format(uri, urllib.parse.urlencode(data)) + data = None + + print(' {} /api/v2/{}'.format(method.upper(), uri)) + response = getattr(requests, method)( + 'http://localhost:8000/api/v2/{}'.format(uri), + headers={'X-AUTHKEY': self.API_KEY}, + data=data, + ) + print(response) + + pprint.pprint(json.loads(response.text), indent=4) diff --git a/dsmr_api/migrations/0003_create_api_user.py b/dsmr_api/migrations/0003_create_api_user.py new file mode 100644 index 000000000..16f0d95f1 --- /dev/null +++ b/dsmr_api/migrations/0003_create_api_user.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.6 on 2017-03-21 19:56 +from __future__ import unicode_literals + +from django.db import migrations +from django.conf import settings + + +def migrate_forward(apps, schema_editor): + User = apps.get_model('auth', 'User') + + if User.objects.filter(username=settings.DSMRREADER_REST_FRAMEWORK_API_USER).exists(): + return + + User.objects.create( + username=settings.DSMRREADER_REST_FRAMEWORK_API_USER, + email='root@localhost' + ) + + +def migrate_backward(apps, schema_editor): + User = apps.get_model('auth', 'User') + User.objects.filter(username=settings.DSMRREADER_REST_FRAMEWORK_API_USER).delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_api', '0002_generate_random_auth_key'), + ] + + operations = [ + migrations.RunPython(migrate_forward, migrate_backward), + ] diff --git a/dsmr_api/static/dsmr_api/theme-muted.css b/dsmr_api/static/dsmr_api/theme-muted.css new file mode 100644 index 000000000..1053b77f9 --- /dev/null +++ b/dsmr_api/static/dsmr_api/theme-muted.css @@ -0,0 +1,1660 @@ +/* Original style from softwaremaniacs.org (c) Ivan Sagalaev */ +.swagger-section pre code { + display: block; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section pre code, +.swagger-section pre .subst, +.swagger-section pre .tag .title, +.swagger-section pre .lisp .title, +.swagger-section pre .clojure .built_in, +.swagger-section pre .nginx .title { + color: black; +} +.swagger-section pre .string, +.swagger-section pre .title, +.swagger-section pre .constant, +.swagger-section pre .parent, +.swagger-section pre .tag .value, +.swagger-section pre .rules .value, +.swagger-section pre .rules .value .number, +.swagger-section pre .preprocessor, +.swagger-section pre .ruby .symbol, +.swagger-section pre .ruby .symbol .string, +.swagger-section pre .aggregate, +.swagger-section pre .template_tag, +.swagger-section pre .django .variable, +.swagger-section pre .smalltalk .class, +.swagger-section pre .addition, +.swagger-section pre .flow, +.swagger-section pre .stream, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .apache .cbracket, +.swagger-section pre .tex .command, +.swagger-section pre .tex .special, +.swagger-section pre .erlang_repl .function_or_atom, +.swagger-section pre .markdown .header { + color: #800; +} +.swagger-section pre .comment, +.swagger-section pre .annotation, +.swagger-section pre .template_comment, +.swagger-section pre .diff .header, +.swagger-section pre .chunk, +.swagger-section pre .markdown .blockquote { + color: #888; +} +.swagger-section pre .number, +.swagger-section pre .date, +.swagger-section pre .regexp, +.swagger-section pre .literal, +.swagger-section pre .smalltalk .symbol, +.swagger-section pre .smalltalk .char, +.swagger-section pre .go .constant, +.swagger-section pre .change, +.swagger-section pre .markdown .bullet, +.swagger-section pre .markdown .link_url { + color: #080; +} +.swagger-section pre .label, +.swagger-section pre .javadoc, +.swagger-section pre .ruby .string, +.swagger-section pre .decorator, +.swagger-section pre .filter .argument, +.swagger-section pre .localvars, +.swagger-section pre .array, +.swagger-section pre .attr_selector, +.swagger-section pre .important, +.swagger-section pre .pseudo, +.swagger-section pre .pi, +.swagger-section pre .doctype, +.swagger-section pre .deletion, +.swagger-section pre .envvar, +.swagger-section pre .shebang, +.swagger-section pre .apache .sqbracket, +.swagger-section pre .nginx .built_in, +.swagger-section pre .tex .formula, +.swagger-section pre .erlang_repl .reserved, +.swagger-section pre .prompt, +.swagger-section pre .markdown .link_label, +.swagger-section pre .vhdl .attribute, +.swagger-section pre .clojure .attribute, +.swagger-section pre .coffeescript .property { + color: #88F; +} +.swagger-section pre .keyword, +.swagger-section pre .id, +.swagger-section pre .phpdoc, +.swagger-section pre .title, +.swagger-section pre .built_in, +.swagger-section pre .aggregate, +.swagger-section pre .css .tag, +.swagger-section pre .javadoctag, +.swagger-section pre .phpdoc, +.swagger-section pre .yardoctag, +.swagger-section pre .smalltalk .class, +.swagger-section pre .winutils, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .go .typename, +.swagger-section pre .tex .command, +.swagger-section pre .markdown .strong, +.swagger-section pre .request, +.swagger-section pre .status { + font-weight: bold; +} +.swagger-section pre .markdown .emphasis { + font-style: italic; +} +.swagger-section pre .nginx .built_in { + font-weight: normal; +} +.swagger-section pre .coffeescript .javascript, +.swagger-section pre .javascript .xml, +.swagger-section pre .tex .formula, +.swagger-section pre .xml .javascript, +.swagger-section pre .xml .vbscript, +.swagger-section pre .xml .css, +.swagger-section pre .xml .cdata { + opacity: 0.5; +} +.swagger-section .hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section .hljs, +.swagger-section .hljs-subst { + color: #444; +} +.swagger-section .hljs-keyword, +.swagger-section .hljs-attribute, +.swagger-section .hljs-selector-tag, +.swagger-section .hljs-meta-keyword, +.swagger-section .hljs-doctag, +.swagger-section .hljs-name { + font-weight: bold; +} +.swagger-section .hljs-built_in, +.swagger-section .hljs-literal, +.swagger-section .hljs-bullet, +.swagger-section .hljs-code, +.swagger-section .hljs-addition { + color: #1F811F; +} +.swagger-section .hljs-regexp, +.swagger-section .hljs-symbol, +.swagger-section .hljs-variable, +.swagger-section .hljs-template-variable, +.swagger-section .hljs-link, +.swagger-section .hljs-selector-attr, +.swagger-section .hljs-selector-pseudo { + color: #BC6060; +} +.swagger-section .hljs-type, +.swagger-section .hljs-string, +.swagger-section .hljs-number, +.swagger-section .hljs-selector-id, +.swagger-section .hljs-selector-class, +.swagger-section .hljs-quote, +.swagger-section .hljs-template-tag, +.swagger-section .hljs-deletion { + color: #880000; +} +.swagger-section .hljs-title, +.swagger-section .hljs-section { + color: #880000; + font-weight: bold; +} +.swagger-section .hljs-comment { + color: #888888; +} +.swagger-section .hljs-meta { + color: #2B6EA1; +} +.swagger-section .hljs-emphasis { + font-style: italic; +} +.swagger-section .hljs-strong { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap { + line-height: 1; + font-family: "Droid Sans", sans-serif; + min-width: 760px; + max-width: 960px; + margin-left: auto; + margin-right: auto; + /* JSONEditor specific styling */ +} +.swagger-section .swagger-ui-wrap b, +.swagger-section .swagger-ui-wrap strong { + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap q, +.swagger-section .swagger-ui-wrap blockquote { + quotes: none; +} +.swagger-section .swagger-ui-wrap p { + line-height: 1.4em; + padding: 0 0 10px; + color: #333333; +} +.swagger-section .swagger-ui-wrap q:before, +.swagger-section .swagger-ui-wrap q:after, +.swagger-section .swagger-ui-wrap blockquote:before, +.swagger-section .swagger-ui-wrap blockquote:after { + content: none; +} +.swagger-section .swagger-ui-wrap .heading_with_menu h1, +.swagger-section .swagger-ui-wrap .heading_with_menu h2, +.swagger-section .swagger-ui-wrap .heading_with_menu h3, +.swagger-section .swagger-ui-wrap .heading_with_menu h4, +.swagger-section .swagger-ui-wrap .heading_with_menu h5, +.swagger-section .swagger-ui-wrap .heading_with_menu h6 { + display: block; + clear: none; + float: left; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + width: 60%; +} +.swagger-section .swagger-ui-wrap table { + border-collapse: collapse; + border-spacing: 0; +} +.swagger-section .swagger-ui-wrap table thead tr th { + padding: 5px; + font-size: 0.9em; + color: #666666; + border-bottom: 1px solid #999999; +} +.swagger-section .swagger-ui-wrap table tbody tr:last-child td { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap table tbody tr.offset { + background-color: #f0f0f0; +} +.swagger-section .swagger-ui-wrap table tbody tr td { + padding: 6px; + font-size: 0.9em; + border-bottom: 1px solid #cccccc; + vertical-align: top; + line-height: 1.3em; +} +.swagger-section .swagger-ui-wrap ol { + margin: 0px 0 10px; + padding: 0 0 0 18px; + list-style-type: decimal; +} +.swagger-section .swagger-ui-wrap ol li { + padding: 5px 0px; + font-size: 0.9em; + color: #333333; +} +.swagger-section .swagger-ui-wrap ol, +.swagger-section .swagger-ui-wrap ul { + list-style: none; +} +.swagger-section .swagger-ui-wrap h1 a, +.swagger-section .swagger-ui-wrap h2 a, +.swagger-section .swagger-ui-wrap h3 a, +.swagger-section .swagger-ui-wrap h4 a, +.swagger-section .swagger-ui-wrap h5 a, +.swagger-section .swagger-ui-wrap h6 a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap h1 a:hover, +.swagger-section .swagger-ui-wrap h2 a:hover, +.swagger-section .swagger-ui-wrap h3 a:hover, +.swagger-section .swagger-ui-wrap h4 a:hover, +.swagger-section .swagger-ui-wrap h5 a:hover, +.swagger-section .swagger-ui-wrap h6 a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap h1 span.divider, +.swagger-section .swagger-ui-wrap h2 span.divider, +.swagger-section .swagger-ui-wrap h3 span.divider, +.swagger-section .swagger-ui-wrap h4 span.divider, +.swagger-section .swagger-ui-wrap h5 span.divider, +.swagger-section .swagger-ui-wrap h6 span.divider { + color: #aaaaaa; +} +.swagger-section .swagger-ui-wrap a { + color: #547f00; +} +.swagger-section .swagger-ui-wrap a img { + border: none; +} +.swagger-section .swagger-ui-wrap article, +.swagger-section .swagger-ui-wrap aside, +.swagger-section .swagger-ui-wrap details, +.swagger-section .swagger-ui-wrap figcaption, +.swagger-section .swagger-ui-wrap figure, +.swagger-section .swagger-ui-wrap footer, +.swagger-section .swagger-ui-wrap header, +.swagger-section .swagger-ui-wrap hgroup, +.swagger-section .swagger-ui-wrap menu, +.swagger-section .swagger-ui-wrap nav, +.swagger-section .swagger-ui-wrap section, +.swagger-section .swagger-ui-wrap summary { + display: block; +} +.swagger-section .swagger-ui-wrap pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; +} +.swagger-section .swagger-ui-wrap pre code { + line-height: 1.6em; + background: none; +} +.swagger-section .swagger-ui-wrap .content > .content-type > div > label { + clear: both; + display: block; + color: #0F6AB4; + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap .content pre { + font-size: 12px; + margin-top: 5px; + padding: 5px; +} +.swagger-section .swagger-ui-wrap .icon-btn { + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .info_title { + padding-bottom: 10px; + font-weight: bold; + font-size: 25px; +} +.swagger-section .swagger-ui-wrap .footer { + margin-top: 20px; +} +.swagger-section .swagger-ui-wrap p.big, +.swagger-section .swagger-ui-wrap div.big p { + font-size: 1em; + margin-bottom: 10px; +} +.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input { + width: 500px !important; +} +.swagger-section .swagger-ui-wrap .info_license { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_tos { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .message-fail { + color: #cc0000; +} +.swagger-section .swagger-ui-wrap .info_url { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_email { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_name { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_description { + padding-bottom: 10px; + font-size: 15px; +} +.swagger-section .swagger-ui-wrap .markdown ol li, +.swagger-section .swagger-ui-wrap .markdown ul li { + padding: 3px 0px; + line-height: 1.4em; + color: #333333; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input { + display: block; + padding: 4px; + width: auto; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title { + font-size: 1.3em; +} +.swagger-section .swagger-ui-wrap table.fullwidth { + width: 100%; +} +.swagger-section .swagger-ui-wrap .model-signature { + font-family: "Droid Sans", sans-serif; + font-size: 1em; + line-height: 1.5em; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a { + text-decoration: none; + color: #AAA; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap .model-signature .propType { + color: #5555aa; +} +.swagger-section .swagger-ui-wrap .model-signature pre:hover { + background-color: #ffffdd; +} +.swagger-section .swagger-ui-wrap .model-signature pre { + font-size: .85em; + line-height: 1.2em; + overflow: auto; + max-height: 200px; + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav { + display: block; + min-width: 230px; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li { + float: left; + margin: 0 5px 5px 0; + padding: 2px 5px 2px 0; + border-right: 1px solid #ddd; +} +.swagger-section .swagger-ui-wrap .model-signature .propOpt { + color: #555; +} +.swagger-section .swagger-ui-wrap .model-signature .snippet small { + font-size: 0.75em; +} +.swagger-section .swagger-ui-wrap .model-signature .propOptKey { + font-style: italic; +} +.swagger-section .swagger-ui-wrap .model-signature .description .strong { + font-weight: bold; + color: #000; + font-size: .9em; +} +.swagger-section .swagger-ui-wrap .model-signature .description div { + font-size: 0.9em; + line-height: 1.5em; + margin-left: 1em; +} +.swagger-section .swagger-ui-wrap .model-signature .description .stronger { + font-weight: bold; + color: #000; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper { + border-spacing: 0; + position: absolute; + background-color: #ffffff; + border: 1px solid #bbbbbb; + display: none; + font-size: 11px; + max-width: 400px; + line-height: 30px; + color: black; + padding: 5px; + margin-left: 10px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th { + text-align: center; + background-color: #eeeeee; + border: 1px solid #bbbbbb; + font-size: 11px; + color: #666666; + font-weight: bold; + padding: 5px; + line-height: 15px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:first-child, +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:last-child { + display: inline; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:not(:first-child):before { + display: block; + content: ''; +} +.swagger-section .swagger-ui-wrap .model-signature .description span:last-of-type.propDesc.markdown > p:only-child { + margin-right: -3px; +} +.swagger-section .swagger-ui-wrap .model-signature .propName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-container { + clear: both; +} +.swagger-section .swagger-ui-wrap .body-textarea { + width: 300px; + height: 100px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap .markdown p code, +.swagger-section .swagger-ui-wrap .markdown li code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #f0f0f0; + color: black; + padding: 1px 3px; +} +.swagger-section .swagger-ui-wrap .required { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .editor_holder { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap .editor_holder label { + font-weight: normal!important; + /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */ +} +.swagger-section .swagger-ui-wrap .editor_holder label.required { + font-weight: bold!important; +} +.swagger-section .swagger-ui-wrap input.parameter { + width: 300px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap h1 { + color: black; + font-size: 1.5em; + line-height: 1.3em; + padding: 10px 0 10px 0; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .heading_with_menu { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap .heading_with_menu ul { + display: block; + clear: none; + float: right; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + margin-top: 10px; +} +.swagger-section .swagger-ui-wrap h2 { + color: black; + font-size: 1.3em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap h2 span.sub { + font-size: 0.7em; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap h2 span.sub a { + color: #777777; +} +.swagger-section .swagger-ui-wrap span.weak { + color: #666666; +} +.swagger-section .swagger-ui-wrap .message-success { + color: #89BF04; +} +.swagger-section .swagger-ui-wrap caption, +.swagger-section .swagger-ui-wrap th, +.swagger-section .swagger-ui-wrap td { + text-align: left; + font-weight: normal; + vertical-align: middle; +} +.swagger-section .swagger-ui-wrap .code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea { + font-family: "Droid Sans", sans-serif; + height: 250px; + padding: 4px; + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select { + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label { + display: block; + float: left; + clear: none; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input { + display: block; + float: left; + clear: none; + margin: 0 5px 0 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label { + color: black; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label { + display: block; + clear: both; + width: auto; + padding: 0 0 3px; + color: #666666; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr { + padding-left: 3px; + color: #888888; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints { + margin-left: 0; + font-style: italic; + font-size: 0.9em; + margin: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap span.blank, +.swagger-section .swagger-ui-wrap span.empty { + color: #888888; + font-style: italic; +} +.swagger-section .swagger-ui-wrap .markdown h3 { + color: #547f00; +} +.swagger-section .swagger-ui-wrap .markdown h4 { + color: #666666; +} +.swagger-section .swagger-ui-wrap .markdown pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; + margin: 0 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown pre code { + line-height: 1.6em; + overflow: auto; +} +.swagger-section .swagger-ui-wrap div.gist { + margin: 20px 0 25px 0 !important; +} +.swagger-section .swagger-ui-wrap ul#resources { + font-family: "Droid Sans", sans-serif; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource { + border-bottom: 1px solid #dddddd; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a { + color: #555555; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading { + border: 1px solid transparent; + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 14px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + border-right: 1px solid #dddddd; + color: #666666; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a { + color: #aaaaaa; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { + color: #999999; + padding-left: 0; + display: block; + clear: none; + float: left; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { + color: #999999; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0 0 10px; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 { + display: block; + clear: none; + float: left; + width: auto; + margin: 0; + padding: 0; + line-height: 1.1em; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path { + padding-left: 10px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated { + text-decoration: line-through; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a { + text-transform: uppercase; + text-decoration: none; + color: white; + display: inline-block; + width: 50px; + font-size: 0.7em; + text-align: center; + padding: 7px 0 4px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -o-border-radius: 2px; + -ms-border-radius: 2px; + -khtml-border-radius: 2px; + border-radius: 2px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 6px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a .markdown p { + color: inherit; + padding: 0; + line-height: inherit; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content { + border-top: none; + padding: 10px; + -moz-border-radius-bottomleft: 6px; + -webkit-border-bottom-left-radius: 6px; + -o-border-bottom-left-radius: 6px; + -ms-border-bottom-left-radius: 6px; + -khtml-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -moz-border-radius-bottomright: 6px; + -webkit-border-bottom-right-radius: 6px; + -o-border-bottom-right-radius: 6px; + -ms-border-bottom-right-radius: 6px; + -khtml-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + margin: 0 0 20px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 { + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a { + padding: 4px 0 0 10px; + display: inline-block; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit { + display: block; + clear: none; + float: left; + padding: 6px 8px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber { + background-image: url('../images/throbber.gif'); + width: 128px; + height: 16px; + display: block; + clear: none; + float: right; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error { + outline: 2px solid black; + outline-color: #cc0000; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] { + max-width: 300px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + padding: 10px; + font-size: 0.9em; + max-height: 400px; + overflow-y: auto; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading { + background-color: #f9f2e9; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a { + background-color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0e0ca; + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content { + background-color: #faf5ee; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #ffd20f; + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading { + background-color: #f5e8e8; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #e8c6c7; + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + background-color: #f7eded; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a { + color: #c8787a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading { + background-color: #e7f6ec; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a { + background-color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3e8d1; + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content { + background-color: #ebf7f0; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading { + background-color: #FCE9E3; + border: 1px solid #F5D5C3; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a { + background-color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0cecb; + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content { + background-color: #faf0ef; + border: 1px solid #f0cecb; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + border-top: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap p#colophon { + margin: 0 15px 40px 15px; + padding: 10px 0; + font-size: 0.8em; + border-top: 1px solid #dddddd; + font-family: "Droid Sans", sans-serif; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap p#colophon a { + text-decoration: none; + color: #547f00; +} +.swagger-section .swagger-ui-wrap h3 { + color: black; + font-size: 1.1em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown ol, +.swagger-section .swagger-ui-wrap .markdown ul { + font-family: "Droid Sans", sans-serif; + margin: 5px 0 10px; + padding: 0 0 0 18px; + list-style-type: disc; +} +.swagger-section .swagger-ui-wrap form.form_box { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box label { + color: #0f6ab4 !important; +} +.swagger-section .swagger-ui-wrap form.form_box input[type=submit] { + display: block; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box p.weak { + font-size: 0.8em; +} +.swagger-section .swagger-ui-wrap form.form_box p { + font-size: 0.9em; + padding: 0 0 15px; + color: #7e7b6d; +} +.swagger-section .swagger-ui-wrap form.form_box p a { + color: #646257; +} +.swagger-section .swagger-ui-wrap form.form_box p strong { + color: black; +} +.swagger-section .swagger-ui-wrap .operation-status td.markdown > p:last-child { + padding-bottom: 0; +} +.swagger-section .title { + font-style: bold; +} +.swagger-section .secondary_form { + display: none; +} +.swagger-section .main_image { + display: block; + margin-left: auto; + margin-right: auto; +} +.swagger-section .oauth_body { + margin-left: 100px; + margin-right: 100px; +} +.swagger-section .oauth_submit { + text-align: center; + display: inline-block; +} +.swagger-section .authorize-wrapper { + margin: 15px 0 10px; +} +.swagger-section .authorize-wrapper_operation { + float: right; +} +.swagger-section .authorize__btn:hover { + text-decoration: underline; + cursor: pointer; +} +.swagger-section .authorize__btn_operation:hover .authorize-scopes { + display: block; +} +.swagger-section .authorize-scopes { + position: absolute; + margin-top: 20px; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section .authorize-scopes .authorize__scope { + text-decoration: none; +} +.swagger-section .authorize__btn_operation { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .authorize__btn_operation_login { + background-position: 0 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .authorize__btn_operation_logout { + background-position: -30px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section #auth_container { + color: #fff; + display: inline-block; + border: none; + padding: 5px; + width: 87px; + height: 13px; +} +.swagger-section #auth_container .authorize__btn { + color: #fff; +} +.swagger-section .auth_container { + padding: 0 0 10px; + margin-bottom: 5px; + border-bottom: solid 1px #CCC; + font-size: 0.9em; +} +.swagger-section .auth_container .auth__title { + color: #547f00; + font-size: 1.2em; +} +.swagger-section .auth_container .basic_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .auth_container .auth__description { + color: #999999; + margin-bottom: 5px; +} +.swagger-section .auth_container .auth__button { + margin-top: 10px; + height: 30px; +} +.swagger-section .auth_container .key_auth__field { + margin: 5px 0; +} +.swagger-section .auth_container .key_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .api-popup-dialog { + position: absolute; + display: none; +} +.swagger-section .api-popup-dialog-wrapper { + z-index: 1000; + width: 500px; + background: #FFF; + padding: 20px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 13px; + color: #777; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +.swagger-section .api-popup-dialog-shadow { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.2; + background-color: gray; + z-index: 900; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog .error-msg { + padding-left: 5px; + padding-bottom: 5px; +} +.swagger-section .api-popup-dialog .api-popup-content { + max-height: 500px; + overflow-y: auto; +} +.swagger-section .api-popup-dialog .api-popup-authbtn { + height: 30px; +} +.swagger-section .api-popup-dialog .api-popup-cancel { + height: 30px; +} +.swagger-section .api-popup-scopes { + padding: 10px 20px; +} +.swagger-section .api-popup-scopes li { + padding: 5px 0; + line-height: 20px; +} +.swagger-section .api-popup-scopes li input { + position: relative; + top: 2px; +} +.swagger-section .api-popup-scopes .api-scope-desc { + padding-left: 20px; + font-style: italic; +} +.swagger-section .api-popup-actions { + padding-top: 10px; +} +.swagger-section .access { + float: right; +} +.swagger-section .auth { + float: right; +} +.swagger-section .api-ic { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .api-ic .api_information_panel { + position: relative; + margin-top: 20px; + margin-left: -5px; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section .api-ic .api_information_panel p .api-msg-enabled { + color: green; +} +.swagger-section .api-ic .api_information_panel p .api-msg-disabled { + color: red; +} +.swagger-section .api-ic:hover .api_information_panel { + position: absolute; + display: block; +} +.swagger-section .ic-info { + background-position: 0 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .ic-warning { + background-position: -60px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .ic-error { + background-position: -30px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .ic-off { + background-position: -90px 0; + width: 58px; + margin-top: -4px; + cursor: pointer; +} +.swagger-section .ic-on { + background-position: -160px 0; + width: 58px; + margin-top: -4px; + cursor: pointer; +} +.swagger-section #header { + background-color: #89bf04; + padding: 9px 14px 19px 14px; + height: 23px; + min-width: 775px; +} +.swagger-section #input_baseUrl { + width: 400px; +} +.swagger-section #api_selector { + display: block; + clear: none; + float: right; +} +.swagger-section #api_selector .input { + display: inline-block; + clear: none; + margin: 0 10px 0 0; +} +.swagger-section #api_selector input { + font-size: 0.9em; + padding: 3px; + margin: 0; +} +.swagger-section #input_apiKey { + width: 200px; +} +.swagger-section #explore, +.swagger-section #auth_container .authorize__btn { + display: block; + text-decoration: none; + font-weight: bold; + padding: 6px 8px; + font-size: 0.9em; + color: white; + background-color: #547f00; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -o-border-radius: 4px; + -ms-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; +} +.swagger-section #explore:hover, +.swagger-section #auth_container .authorize__btn:hover { + background-color: #547f00; +} +.swagger-section #header #logo { + font-size: 1.5em; + font-weight: bold; + text-decoration: none; + color: white; +} +.swagger-section #header #logo .logo__img { + display: block; + float: left; + margin-top: 2px; +} +.swagger-section #header #logo .logo__title { + display: inline-block; + padding: 5px 0 0 10px; +} +.swagger-section #content_message { + margin: 10px 15px; + font-style: italic; + color: #999999; +} +.swagger-section #message-bar { + min-height: 30px; + text-align: center; + padding-top: 10px; +} +.swagger-section .swagger-collapse:before { + content: "-"; +} +.swagger-section .swagger-expand:before { + content: "+"; +} +.swagger-section .error { + outline-color: #cc0000; + background-color: #f2dede; +} + +/** + * Swagger UI theme overrides + * + * Author: Mark Ostrander + * Github: https://github.com/ostranme/swagger-ui-themes + */ +.swagger-section .swagger-ui-wrap a { + color: #336E7B; +} +.swagger-section .swagger-ui-wrap pre { + background-color: #FFFCF7; + border: 1px solid #FFFCF7; +} +.swagger-section .swagger-ui-wrap .model-signature pre:hover { + background-color: #E8E8E8; +} +.swagger-section .swagger-ui-wrap .markdown h3 { + color: #222222; +} +.swagger-section .swagger-ui-wrap .markdown h4 { + color: #666666; +} +.swagger-section .swagger-ui-wrap .markdown pre { + background-color: #FFFCF7; + border: 1px solid #FFFCF7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a { + background-color: #633B63; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li { + color: #633B63; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a { + color: #633B63; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 { + color: #633B63; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a { + background-color: #C1786A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li { + color: #C1786A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a { + color: #C1786A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 { + color: #C1786A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a { + background-color: #64908A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li { + color: #64908A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a { + color: #64908A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 { + color: #64908A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a { + color: #64908A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a { + background-color: #C1976A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li { + color: #C1976A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a { + color: #C1976A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 { + color: #C1976A; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a { + background-color: #3F778E; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li { + color: #3F778E; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a { + color: #3F778E; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 { + color: #3F778E; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a { + background-color: #3F51B5; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li { + color: #3F51B5; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a { + color: #3F51B5; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content { + background-color: #F6F6F6; + border: 1px solid #DADFE1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 { + color: #3F51B5; +} +.swagger-section #header { + background-color: #6F7E88; +} +.swagger-section #explore, +.swagger-section #auth_container .authorize__btn { + color: #6F7E88; + background-color: #DADFE1; +} diff --git a/dsmr_api/tests/__init__.py b/dsmr_api/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_api/tests/v1/__init__.py b/dsmr_api/tests/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_api/tests/test_api.py b/dsmr_api/tests/v1/test_api.py similarity index 97% rename from dsmr_api/tests/test_api.py rename to dsmr_api/tests/v1/test_api.py index af9d9a3ed..67fe2b90e 100644 --- a/dsmr_api/tests/test_api.py +++ b/dsmr_api/tests/v1/test_api.py @@ -8,9 +8,9 @@ from dsmr_datalogger.models.reading import DsmrReading -class TestAPI(TestCase): +class TestAPIv1(TestCase): """ Test whether views render at all. """ - namespace = 'api' + namespace = 'api-v1' def setUp(self): self.client = Client() @@ -20,6 +20,7 @@ def setUp(self): # Since we're only having one call anyway. self._api_url = reverse('{}:datalogger-dsmrreading'.format(self.namespace)) + self.assertEqual(self._api_url, '/api/v1/datalogger/dsmrreading') self._telegram = ''.join([ "/KFM5KAIFA-METER\r\n", diff --git a/dsmr_api/tests/test_settings.py b/dsmr_api/tests/v1/test_settings.py similarity index 100% rename from dsmr_api/tests/test_settings.py rename to dsmr_api/tests/v1/test_settings.py diff --git a/dsmr_api/tests/v2/__init__.py b/dsmr_api/tests/v2/__init__.py new file mode 100644 index 000000000..383b0c9eb --- /dev/null +++ b/dsmr_api/tests/v2/__init__.py @@ -0,0 +1,30 @@ +import json + +from django.test.client import Client +from django.test import TestCase +from django.urls.base import reverse + +from dsmr_api.models import APISettings + + +class APIv2TestCase(TestCase): + NAMESPACE = 'api-v2' + + client = None + api_settings = None + + def setUp(self): + self.client = Client() + self.api_settings = APISettings.get_solo() + self.api_settings.allow = True + self.api_settings.save() + + def _request(self, view_name, method='get', expected_code=200, **kwargs): + response = getattr(self.client, method)( + reverse('{}:{}'.format(self.NAMESPACE, view_name)), + HTTP_X_AUTHKEY=self.api_settings.auth_key, + **kwargs + ) + self.assertEqual(response.status_code, expected_code, response.content) + result = str(response.content, 'utf-8') + return json.loads(result) diff --git a/dsmr_api/tests/v2/test_consumption.py b/dsmr_api/tests/v2/test_consumption.py new file mode 100644 index 000000000..9660635fd --- /dev/null +++ b/dsmr_api/tests/v2/test_consumption.py @@ -0,0 +1,77 @@ +from dsmr_api.tests.v2 import APIv2TestCase + + +class TestElectricity(APIv2TestCase): + fixtures = ['dsmr_api/test_electricity_consumption.json'] + + def test_get(self): + resultset = self._request('electricity-consumption', data={'limit': 100}) + self.assertEqual(resultset['count'], 67) + self.assertEqual(resultset['results'][0]['id'], 95) + self.assertEqual(resultset['results'][1]['id'], 96) + self.assertEqual(resultset['results'][65]['id'], 217) + self.assertEqual(resultset['results'][66]['id'], 218) + + # Limit. + resultset = self._request('electricity-consumption', data={'limit': 10}) + self.assertEqual(resultset['count'], 67) + self.assertEqual(len(resultset['results']), 10) + + # Sort. + resultset = self._request('electricity-consumption', data={'ordering': '-read_at', 'limit': 100}) + self.assertEqual(resultset['count'], 67) + self.assertEqual(resultset['results'][0]['id'], 218) + self.assertEqual(resultset['results'][1]['id'], 217) + self.assertEqual(resultset['results'][65]['id'], 96) + self.assertEqual(resultset['results'][66]['id'], 95) + + # Search + resultset = self._request('electricity-consumption', data={'read_at__gte': '2015-12-12 02:00:00'}) # Z+01:00 + self.assertEqual(resultset['count'], 4) + self.assertEqual(resultset['results'][0]['id'], 215) + self.assertEqual(resultset['results'][1]['id'], 216) + self.assertEqual(resultset['results'][2]['id'], 217) + self.assertEqual(resultset['results'][3]['id'], 218) + + resultset = self._request('electricity-consumption', data={'read_at__lte': '2015-12-12 02:00:00', 'limit': 100}) + self.assertEqual(resultset['count'], 64) + self.assertEqual(resultset['results'][0]['id'], 95) + self.assertEqual(resultset['results'][1]['id'], 96) + + +class TestGas(APIv2TestCase): + fixtures = ['dsmr_api/test_gas_consumption.json'] + + def test_get(self): + resultset = self._request('gas-consumption', data={'limit': 100}) + self.assertEqual(resultset['count'], 31) + self.assertEqual(resultset['results'][0]['id'], 1) + self.assertEqual(resultset['results'][1]['id'], 2) + self.assertEqual(resultset['results'][29]['id'], 30) + self.assertEqual(resultset['results'][30]['id'], 31) + + # Limit. + resultset = self._request('gas-consumption', data={'limit': 10}) + self.assertEqual(resultset['count'], 31) + self.assertEqual(len(resultset['results']), 10) + + # Sort. + resultset = self._request('gas-consumption', data={'ordering': '-read_at', 'limit': 100}) + self.assertEqual(resultset['count'], 31) + self.assertEqual(resultset['results'][0]['id'], 31) + self.assertEqual(resultset['results'][1]['id'], 30) + self.assertEqual(resultset['results'][29]['id'], 2) + self.assertEqual(resultset['results'][30]['id'], 1) + + # Search + resultset = self._request('gas-consumption', data={'read_at__gte': '2015-12-13 00:00:00'}) # Z+01:00 + self.assertEqual(resultset['count'], 4) + self.assertEqual(resultset['results'][0]['id'], 28) + self.assertEqual(resultset['results'][1]['id'], 29) + self.assertEqual(resultset['results'][2]['id'], 30) + self.assertEqual(resultset['results'][3]['id'], 31) + + resultset = self._request('gas-consumption', data={'read_at__lte': '2015-12-13 00:00:00', 'limit': 100}) + self.assertEqual(resultset['count'], 28) + self.assertEqual(resultset['results'][0]['id'], 1) + self.assertEqual(resultset['results'][1]['id'], 2) diff --git a/dsmr_api/tests/v2/test_dsmrreading.py b/dsmr_api/tests/v2/test_dsmrreading.py new file mode 100644 index 000000000..e9efd031d --- /dev/null +++ b/dsmr_api/tests/v2/test_dsmrreading.py @@ -0,0 +1,85 @@ +from dsmr_api.tests.v2 import APIv2TestCase +from dsmr_datalogger.models.reading import DsmrReading + + +class TestDsmrreading(APIv2TestCase): + fixtures = ['dsmr_api/test_dsmrreading.json'] + + def test_get(self): + resultset = self._request('dsmrreading') + self.assertEqual(resultset['count'], 3) + self.assertEqual(resultset['results'][0]['id'], 1) + self.assertEqual(resultset['results'][1]['id'], 2) + self.assertEqual(resultset['results'][2]['id'], 3) + + # Limit. + resultset = self._request('dsmrreading', data={'limit': 1}) + self.assertEqual(resultset['count'], 3) + self.assertEqual(len(resultset['results']), 1) + + # Sort. + resultset = self._request('dsmrreading', data={'ordering': '-timestamp'}) + self.assertEqual(resultset['count'], 3) + self.assertEqual(resultset['results'][0]['id'], 3) + self.assertEqual(resultset['results'][1]['id'], 2) + self.assertEqual(resultset['results'][2]['id'], 1) + + # Search + resultset = self._request('dsmrreading', data={'timestamp__gte': '2016-07-01 21:00:00'}) # Z+02:00 + self.assertEqual(resultset['count'], 2) + self.assertEqual(resultset['results'][0]['id'], 2) + self.assertEqual(resultset['results'][1]['id'], 3) + + resultset = self._request('dsmrreading', data={'timestamp__lte': '2016-07-01 21:00:00'}) # Z+02:00 + self.assertEqual(resultset['count'], 2) + self.assertEqual(resultset['results'][0]['id'], 1) + self.assertEqual(resultset['results'][1]['id'], 2) + + def test_post(self): + TELEGRAM = { + 'electricity_currently_delivered': 1.500, + 'electricity_currently_returned': 0.025, + 'electricity_delivered_1': 2000, + 'electricity_delivered_2': 3000, + 'electricity_returned_1': 0, + 'electricity_returned_2': 0, + 'timestamp': '2017-01-01T00:00:00+01:00', + } + self.assertEqual(DsmrReading.objects.all().count(), 3) + + resultset = self._request('dsmrreading', expected_code=201, method='post', data=TELEGRAM) + self.assertEqual(float(resultset['electricity_currently_delivered']), 1.5) + self.assertEqual(float(resultset['electricity_currently_returned']), 0.025) + self.assertEqual(float(resultset['electricity_delivered_1']), 2000) + self.assertEqual(float(resultset['electricity_delivered_2']), 3000) + self.assertEqual(float(resultset['electricity_returned_1']), 0) + self.assertEqual(float(resultset['electricity_returned_2']), 0) + self.assertEqual(resultset['timestamp'], '2017-01-01T00:00:00+01:00') + self.assertIsNone(resultset['phase_currently_delivered_l1']) + self.assertIsNone(resultset['phase_currently_delivered_l2']) + self.assertIsNone(resultset['phase_currently_delivered_l3']) + self.assertIsNone(resultset['extra_device_timestamp']) + self.assertIsNone(resultset['extra_device_delivered']) + self.assertEqual(DsmrReading.objects.all().count(), 4) + + # Again, with UTC. + TELEGRAM['timestamp'] = '2017-01-01T00:00:00Z' + resultset = self._request('dsmrreading', expected_code=201, method='post', data=TELEGRAM) + self.assertEqual(resultset['timestamp'], '2017-01-01T00:00:00Z') + self.assertEqual(DsmrReading.objects.all().count(), 5) + + # Now with optional data. + TELEGRAM['timestamp'] = '2017-01-02T00:00:00Z' + TELEGRAM['extra_device_timestamp'] = '2017-01-02T01:00:00Z' + TELEGRAM['extra_device_delivered'] = 1234 + TELEGRAM['phase_currently_delivered_l1'] = 0.5 + TELEGRAM['phase_currently_delivered_l2'] = 0.75 + TELEGRAM['phase_currently_delivered_l3'] = 0.25 + resultset = self._request('dsmrreading', expected_code=201, method='post', data=TELEGRAM) + self.assertEqual(resultset['timestamp'], '2017-01-02T00:00:00Z') + self.assertEqual(resultset['extra_device_timestamp'], '2017-01-02T01:00:00Z') + self.assertEqual(float(resultset['extra_device_delivered']), 1234) + self.assertEqual(float(resultset['phase_currently_delivered_l1']), 0.5) + self.assertEqual(float(resultset['phase_currently_delivered_l2']), 0.75) + self.assertEqual(float(resultset['phase_currently_delivered_l3']), 0.25) + self.assertEqual(DsmrReading.objects.all().count(), 6) diff --git a/dsmr_api/tests/v2/test_generic.py b/dsmr_api/tests/v2/test_generic.py new file mode 100644 index 000000000..ffc1b63ec --- /dev/null +++ b/dsmr_api/tests/v2/test_generic.py @@ -0,0 +1,44 @@ +from django.db.migrations.executor import MigrationExecutor +from django.urls.base import reverse +from django.db import connection +from django.apps import apps + +from dsmr_api.models import APISettings +from dsmr_api.tests.v2 import APIv2TestCase + + +class APIv2TestCase(APIv2TestCase): + def test_disabled(self): + """ Test API should be disabled. """ + APISettings.objects.all().update(allow=False) + + response = self.client.get(reverse('{}:dsmrreading'.format(self.NAMESPACE))) + self.assertEqual(response.status_code, 403) + + def test_anonymous(self): + """ Test API key is required. """ + response = self.client.get(reverse('{}:dsmrreading'.format(self.NAMESPACE))) + self.assertEqual(response.status_code, 403) + + def test_invalid_key(self): + """ Test API key is validated. """ + response = self.client.get( + reverse('{}:dsmrreading'.format(self.NAMESPACE)), + HTTP_X_AUTHKEY='INVALID-KEY' + ) + self.assertEqual(response.status_code, 403) + + @property + def app(self): + return apps.get_containing_app_config(type(self).__module__).name + + def test_user_does_not_exist(self): + """ Tests what happens when the API user was not created. """ + # Roll back migration creating the API user. + MigrationExecutor(connection=connection).migrate([(self.app, '0002_generate_random_auth_key')]) + + response = self.client.get( + reverse('{}:dsmrreading'.format(self.NAMESPACE)), + HTTP_X_AUTHKEY=self.api_settings.auth_key, + ) + self.assertEqual(response.status_code, 500) diff --git a/dsmr_api/tests/v2/test_statistics.py b/dsmr_api/tests/v2/test_statistics.py new file mode 100644 index 000000000..6a1370e31 --- /dev/null +++ b/dsmr_api/tests/v2/test_statistics.py @@ -0,0 +1,77 @@ +from dsmr_api.tests.v2 import APIv2TestCase + + +class TestDay(APIv2TestCase): + fixtures = ['dsmr_api/test_day_statistics.json'] + + def test_get(self): + resultset = self._request('day-statistics') + self.assertEqual(resultset['count'], 7) + self.assertEqual(resultset['results'][0]['id'], 756) + self.assertEqual(resultset['results'][1]['id'], 757) + self.assertEqual(resultset['results'][5]['id'], 761) + self.assertEqual(resultset['results'][6]['id'], 762) + + # Limit. + resultset = self._request('day-statistics', data={'limit': 5}) + self.assertEqual(resultset['count'], 7) + self.assertEqual(len(resultset['results']), 5) + + # Sort. + resultset = self._request('day-statistics', data={'ordering': '-day'}) + self.assertEqual(resultset['count'], 7) + self.assertEqual(resultset['results'][0]['id'], 762) + self.assertEqual(resultset['results'][1]['id'], 761) + self.assertEqual(resultset['results'][5]['id'], 757) + self.assertEqual(resultset['results'][6]['id'], 756) + + # Search + resultset = self._request('day-statistics', data={'day__gte': '2015-12-14'}) # Z+01:00 + self.assertEqual(resultset['count'], 4) + self.assertEqual(resultset['results'][0]['id'], 759) + self.assertEqual(resultset['results'][1]['id'], 760) + self.assertEqual(resultset['results'][2]['id'], 761) + self.assertEqual(resultset['results'][3]['id'], 762) + + resultset = self._request('day-statistics', data={'day__lte': '2015-12-14'}) + self.assertEqual(resultset['count'], 4) + self.assertEqual(resultset['results'][0]['id'], 756) + self.assertEqual(resultset['results'][1]['id'], 757) + + +class TestHour(APIv2TestCase): + fixtures = ['dsmr_api/test_hour_statistics.json'] + + def test_get(self): + resultset = self._request('hour-statistics') + self.assertEqual(resultset['count'], 13) + self.assertEqual(resultset['results'][0]['id'], 14551) + self.assertEqual(resultset['results'][1]['id'], 14552) + self.assertEqual(resultset['results'][11]['id'], 14562) + self.assertEqual(resultset['results'][12]['id'], 14563) + + # Limit. + resultset = self._request('hour-statistics', data={'limit': 10}) + self.assertEqual(resultset['count'], 13) + self.assertEqual(len(resultset['results']), 10) + + # Sort. + resultset = self._request('hour-statistics', data={'ordering': '-hour_start'}) + self.assertEqual(resultset['count'], 13) + self.assertEqual(resultset['results'][0]['id'], 14563) + self.assertEqual(resultset['results'][1]['id'], 14562) + self.assertEqual(resultset['results'][11]['id'], 14552) + self.assertEqual(resultset['results'][12]['id'], 14551) + + # Search + resultset = self._request('hour-statistics', data={'hour_start__gte': '2015-12-13 00:00:00'}) # Z+01:00 + self.assertEqual(resultset['count'], 11) + self.assertEqual(resultset['results'][0]['id'], 14553) + self.assertEqual(resultset['results'][1]['id'], 14554) + self.assertEqual(resultset['results'][2]['id'], 14555) + self.assertEqual(resultset['results'][3]['id'], 14556) + + resultset = self._request('hour-statistics', data={'hour_start__lte': '2015-12-13 00:00:00'}) + self.assertEqual(resultset['count'], 3) + self.assertEqual(resultset['results'][0]['id'], 14551) + self.assertEqual(resultset['results'][1]['id'], 14552) diff --git a/dsmr_api/urls/__init__.py b/dsmr_api/urls/__init__.py new file mode 100644 index 000000000..d50c22e62 --- /dev/null +++ b/dsmr_api/urls/__init__.py @@ -0,0 +1,7 @@ +from django.conf.urls import include, url + + +urlpatterns = [ + url(r'^v1/', include('dsmr_api.urls.v1', namespace='api-v1')), # Remote datalogger only. + url(r'^v2/', include('dsmr_api.urls.v2', namespace='api-v2')), # RESTful API. +] diff --git a/dsmr_api/urls.py b/dsmr_api/urls/v1.py similarity index 81% rename from dsmr_api/urls.py rename to dsmr_api/urls/v1.py index 5546db89f..e3a4afab6 100644 --- a/dsmr_api/urls.py +++ b/dsmr_api/urls/v1.py @@ -1,7 +1,7 @@ from django.conf.urls import url from django.views.decorators.csrf import csrf_exempt -from dsmr_api.views import DataloggerDsmrReading +from dsmr_api.views.v1 import DataloggerDsmrReading urlpatterns = [ diff --git a/dsmr_api/urls/v2.py b/dsmr_api/urls/v2.py new file mode 100644 index 000000000..79608e2c1 --- /dev/null +++ b/dsmr_api/urls/v2.py @@ -0,0 +1,35 @@ +from django.conf.urls import url, include + +from dsmr_api.views import v2 as views + + +datalogger_url_patterns = [ + url(r'^dsmrreading$', views.DsmrReadingViewSet.as_view({ + 'get': 'list', + 'post': 'create', + }), name='dsmrreading'), +] + +consumption_url_patterns = [ + url(r'^electricity$', views.ElectricityConsumptionViewSet.as_view({ + 'get': 'list', + }), name='electricity-consumption'), + url(r'^gas$', views.GasConsumptionViewSet.as_view({ + 'get': 'list', + }), name='gas-consumption'), +] + +statistics_url_patterns = [ + url(r'^day$', views.DayStatisticsViewSet.as_view({ + 'get': 'list', + }), name='day-statistics'), + url(r'^hour$', views.HourStatisticsViewSet.as_view({ + 'get': 'list', + }), name='hour-statistics'), +] + +urlpatterns = [ + url(r'^datalogger/', include(datalogger_url_patterns)), + url(r'^consumption/', include(consumption_url_patterns)), + url(r'^statistics/', include(statistics_url_patterns)), +] diff --git a/dsmr_api/views/__init__.py b/dsmr_api/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_api/views.py b/dsmr_api/views/v1.py similarity index 80% rename from dsmr_api/views.py rename to dsmr_api/views/v1.py index 6c8e9f62b..717d53c51 100644 --- a/dsmr_api/views.py +++ b/dsmr_api/views/v1.py @@ -3,7 +3,6 @@ from django.http.response import HttpResponseNotAllowed, HttpResponseForbidden,\ HttpResponseBadRequest, HttpResponseServerError, HttpResponse from django.views.generic.base import View -from django.utils.translation import ugettext as _ from dsmr_datalogger.exceptions import InvalidTelegramError from dsmr_api.models import APISettings @@ -19,16 +18,16 @@ def post(self, request): api_settings = APISettings.get_solo() if not api_settings.allow: - return HttpResponseNotAllowed(permitted_methods=['POST'], content=_('API is disabled')) + return HttpResponseNotAllowed(permitted_methods=['POST'], content='API is disabled') if request.META.get('HTTP_X_AUTHKEY') != api_settings.auth_key: - return HttpResponseForbidden(content=_('Invalid auth key')) + return HttpResponseForbidden(content='Invalid auth key') post_form = DsmrReadingForm(request.POST) if not post_form.is_valid(): logger.warning('API validation failed with POST data: {}'.format(request.POST)) - return HttpResponseBadRequest(_('Invalid data')) + return HttpResponseBadRequest('Invalid data') dsmr_reading = None @@ -39,6 +38,6 @@ def post(self, request): pass if not dsmr_reading: - return HttpResponseServerError(content=_('Failed to parse telegram')) + return HttpResponseServerError(content='Failed to parse telegram') return HttpResponse(status=201) diff --git a/dsmr_api/views/v2.py b/dsmr_api/views/v2.py new file mode 100644 index 000000000..ef15c68a2 --- /dev/null +++ b/dsmr_api/views/v2.py @@ -0,0 +1,55 @@ +from rest_framework import mixins, viewsets + +from dsmr_consumption.serializers.consumption import ElectricityConsumptionSerializer, GasConsumptionSerializer +from dsmr_consumption.models.consumption import ElectricityConsumption, GasConsumption +from dsmr_stats.serializers.statistics import DayStatisticsSerializer, HourStatisticsSerializer +from dsmr_stats.models.statistics import DayStatistics, HourStatistics +from dsmr_datalogger.serializers.reading import DsmrReadingSerializer +from dsmr_datalogger.models.reading import DsmrReading +from dsmr_api.filters import DsmrReadingFilter, DayStatisticsFilter, ElectricityConsumptionFilter,\ + GasConsumptionFilter, HourStatisticsFilter + + +class DsmrReadingViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): + FIELD = 'timestamp' + queryset = DsmrReading.objects.all() + serializer_class = DsmrReadingSerializer + filter_class = DsmrReadingFilter + ordering_fields = (FIELD, ) + ordering = FIELD + + +class ElectricityConsumptionViewSet(viewsets.ReadOnlyModelViewSet): + FIELD = 'read_at' + queryset = ElectricityConsumption.objects.all() + serializer_class = ElectricityConsumptionSerializer + filter_class = ElectricityConsumptionFilter + ordering_fields = (FIELD, ) + ordering = FIELD + + +class GasConsumptionViewSet(viewsets.ReadOnlyModelViewSet): + FIELD = 'read_at' + queryset = GasConsumption.objects.all() + serializer_class = GasConsumptionSerializer + filter_class = GasConsumptionFilter + ordering_fields = (FIELD, ) + ordering = FIELD + + +class DayStatisticsViewSet(viewsets.ReadOnlyModelViewSet): + FIELD = 'day' + queryset = DayStatistics.objects.all() + serializer_class = DayStatisticsSerializer + filter_class = DayStatisticsFilter + ordering_fields = (FIELD, ) + ordering = FIELD + + +class HourStatisticsViewSet(viewsets.ReadOnlyModelViewSet): + FIELD = 'hour_start' + queryset = HourStatistics.objects.all() + serializer_class = HourStatisticsSerializer + filter_class = HourStatisticsFilter + ordering_fields = (FIELD, ) + ordering = FIELD diff --git a/dsmr_backend/fixtures/dsmr_backend/test_dsmrreading.json b/dsmr_backend/fixtures/dsmr_backend/test_dsmrreading.json new file mode 100644 index 000000000..1a497bed2 --- /dev/null +++ b/dsmr_backend/fixtures/dsmr_backend/test_dsmrreading.json @@ -0,0 +1,53 @@ +[ +{ + "fields": { + "electricity_delivered_1": "528.750", + "extra_device_timestamp": "2015-11-10T19:00:00Z", + "extra_device_delivered": "845.888", + "electricity_returned_2": "123.456", + "electricity_currently_delivered": "0.247", + "timestamp": "2015-11-10T19:40:47Z", + "electricity_returned_1": "0.456", + "electricity_currently_returned": "0.123", + "processed": true, + "electricity_delivered_2": "525.500" + }, + "model": "dsmr_datalogger.dsmrreading", + "pk": 1 +}, +{ + "fields": { + "electricity_delivered_1": "529.750", + "extra_device_timestamp": "2015-11-10T19:00:00Z", + "extra_device_delivered": "845.898", + "electricity_returned_2": "123.567", + "electricity_currently_delivered": "0.750", + "timestamp": "2015-11-10T19:40:57Z", + "electricity_returned_1": "0.567", + "electricity_currently_returned": "0.123", + "processed": true, + "electricity_delivered_2": "527.500" + }, + "model": "dsmr_datalogger.dsmrreading", + "pk": 2 +}, +{ + "fields": { + "electricity_delivered_1": "530.750", + "extra_device_timestamp": "2015-11-10T20:00:00Z", + "extra_device_delivered": "845.898", + "electricity_returned_2": "123.678", + "electricity_currently_delivered": "1.123", + "timestamp": "2015-11-10T19:41:07Z", + "electricity_returned_1": "0.678", + "electricity_currently_returned": "0.123", + "processed": true, + "electricity_delivered_2": "528.500", + "phase_currently_delivered_l1": "0.123", + "phase_currently_delivered_l2": "0.456", + "phase_currently_delivered_l3": "0.789" + }, + "model": "dsmr_datalogger.dsmrreading", + "pk": 3 +} +] \ No newline at end of file diff --git a/dsmr_backend/management/commands/development_reset.py b/dsmr_backend/management/commands/development_reset.py index efcfb42fb..7fdca8d53 100644 --- a/dsmr_backend/management/commands/development_reset.py +++ b/dsmr_backend/management/commands/development_reset.py @@ -1,10 +1,12 @@ from django.core.management.base import BaseCommand, CommandError from django.utils.translation import ugettext as _ +from django.contrib.auth.models import User from django.conf import settings from dsmr_backup.models.settings import BackupSettings, DropboxSettings from dsmr_mindergas.models.settings import MinderGasSettings from dsmr_notification.models.settings import NotificationSetting +from dsmr_api.models import APISettings class Command(BaseCommand): @@ -15,7 +17,17 @@ def handle(self, **options): raise CommandError(_('Intended usage is NOT production! Only allowed when DEBUG = True')) # Just wipe all settings which can affect the environment. + APISettings.objects.update(allow=True, auth_key='test') BackupSettings.objects.update(daily_backup=False) DropboxSettings.objects.update(access_token=None) MinderGasSettings.objects.update(export=False, auth_token=None) NotificationSetting.objects.update(send_notification=False, api_key=None) + + try: + # Reset passwd. + admin = User.objects.get(pk=1) + except User.DoesNotExist: + pass + else: + admin.set_password('admin') + admin.save() diff --git a/dsmr_backend/management/commands/dsmr_backend.py b/dsmr_backend/management/commands/dsmr_backend.py index f9a330369..6405b7b58 100644 --- a/dsmr_backend/management/commands/dsmr_backend.py +++ b/dsmr_backend/management/commands/dsmr_backend.py @@ -12,7 +12,7 @@ class Command(InfiniteManagementCommandMixin, BaseCommand): help = _('Generates a generic event triggering apps for backend operations, cron-like.') name = __name__ # Required for PID file. - sleep_time = 5 + sleep_time = 1 def run(self, **options): """ InfiniteManagementCommandMixin listens to handle() and calls run() in a loop. """ diff --git a/dsmr_backend/management/commands/dsmr_backend_delete_aggregated_data.py b/dsmr_backend/management/commands/dsmr_backend_delete_aggregated_data.py new file mode 100644 index 000000000..c233bd2b9 --- /dev/null +++ b/dsmr_backend/management/commands/dsmr_backend_delete_aggregated_data.py @@ -0,0 +1,41 @@ +from django.core.management.base import BaseCommand, CommandError +from django.utils.translation import ugettext as _ + +import dsmr_consumption.services +import dsmr_stats.services +from dsmr_datalogger.models.reading import DsmrReading + + +class Command(BaseCommand): + help = _( + 'Deletes all aggregated data generated. Such as consumption and (day/hour) statistics. ' + 'This command does NOT affect any readings stored. In fact, you should NEVER run this, unless you still have ' + 'each and EVERY reading stored, as the application will attempt to recalculate all aggregated data deleted ' + 'retroactively, using each reading stored.') + + def add_arguments(self, parser): + super(Command, self).add_arguments(parser) + parser.add_argument( + '--ack-to-delete-my-data', + action='store_true', + dest='acked_warning', + default=False, + help=_('Required to acknowledge you that you WILL delete your statistics with this.') + ) + + def handle(self, **options): + if not options.get('acked_warning'): + raise CommandError(_( + 'Intended usage is NOT production! Force by using --ack-to-delete-my-data' + )) + + print(' - Clearing consumption data') + dsmr_consumption.services.clear_consumption() + + print(' - Clearing statistics') + dsmr_stats.services.clear_statistics() + + print(' - Resetting state of all readings for processing (might take a while)') + DsmrReading.objects.all().update(processed=False) + + self.stdout.write('Command completed.') diff --git a/dsmr_backend/tests/test_delete_aggregated_data.py b/dsmr_backend/tests/test_delete_aggregated_data.py new file mode 100644 index 000000000..26c98cc21 --- /dev/null +++ b/dsmr_backend/tests/test_delete_aggregated_data.py @@ -0,0 +1,27 @@ +from unittest import mock + +from django.test.testcases import TestCase +from django.core.management.base import CommandError + +from dsmr_backend.tests.mixins import InterceptStdoutMixin +from dsmr_datalogger.models.reading import DsmrReading + + +class TestManagementCommand(InterceptStdoutMixin, TestCase): + fixtures = ['dsmr_backend/test_dsmrreading.json'] + + @mock.patch('dsmr_consumption.services.clear_consumption') + @mock.patch('dsmr_stats.services.clear_statistics') + def test_dsmr_stats_clear_statistics(self, *mocks): + expected_error = 'Intended usage is NOT production! Force by using --ack-to-delete-my-data' + + self.assertTrue(DsmrReading.objects.filter(processed=True).exists()) + + with self.assertRaisesMessage(CommandError, expected_error): + self._intercept_command_stdout('dsmr_backend_delete_aggregated_data') + + self.assertFalse(all([x.called for x in mocks])) + self._intercept_command_stdout('dsmr_backend_delete_aggregated_data', acked_warning=True) + self.assertTrue(all([x.called for x in mocks])) + + self.assertFalse(DsmrReading.objects.filter(processed=True).exists()) diff --git a/dsmr_consumption/models/consumption.py b/dsmr_consumption/models/consumption.py index 280d53e73..309b836ba 100644 --- a/dsmr_consumption/models/consumption.py +++ b/dsmr_consumption/models/consumption.py @@ -74,7 +74,7 @@ class Meta: class GasConsumption(models.Model): - """ Interpolated gas reading, containing the actualy usage, based on the reading before (if any). """ + """ Interpolated gas reading, containing the actual usage, based on the reading before (if any). """ read_at = models.DateTimeField(unique=True) delivered = models.DecimalField( max_digits=9, diff --git a/dsmr_consumption/serializers/__init__.py b/dsmr_consumption/serializers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_consumption/serializers/consumption.py b/dsmr_consumption/serializers/consumption.py new file mode 100644 index 000000000..947579899 --- /dev/null +++ b/dsmr_consumption/serializers/consumption.py @@ -0,0 +1,15 @@ +from rest_framework import serializers + +from dsmr_consumption.models.consumption import ElectricityConsumption, GasConsumption + + +class ElectricityConsumptionSerializer(serializers.ModelSerializer): + class Meta: + model = ElectricityConsumption + fields = '__all__' + + +class GasConsumptionSerializer(serializers.ModelSerializer): + class Meta: + model = GasConsumption + fields = '__all__' diff --git a/dsmr_consumption/services.py b/dsmr_consumption/services.py index c3b240bbd..34832a902 100644 --- a/dsmr_consumption/services.py +++ b/dsmr_consumption/services.py @@ -16,7 +16,7 @@ def compact_all(): """ Compacts all unprocessed readings, capped by a max to prevent hanging backend. """ - for current_reading in DsmrReading.objects.unprocessed()[0:128]: + for current_reading in DsmrReading.objects.unprocessed()[0:1024]: compact(dsmr_reading=current_reading) @@ -288,3 +288,9 @@ def calculate_min_max_consumption_watt(): min_max[x] = int(min_max[x] * 1000) return min_max + + +def clear_consumption(): + """ Clears ALL consumption data ever generated. """ + ElectricityConsumption.objects.all().delete() + GasConsumption.objects.all().delete() diff --git a/dsmr_consumption/tests/test_services.py b/dsmr_consumption/tests/test_services.py index b7fe641c5..8f9131a13 100644 --- a/dsmr_consumption/tests/test_services.py +++ b/dsmr_consumption/tests/test_services.py @@ -270,6 +270,31 @@ def test_calculate_min_max_consumption_watt(self): self.assertEqual(min_max['min_watt'], 250) self.assertEqual(min_max['max_watt'], 6123) + def test_clear_consumption(self): + # Prepare some test data that should be deleted. + ElectricityConsumption.objects.create( + read_at=timezone.now(), + delivered_1=1, + returned_1=1, + delivered_2=2, + returned_2=2, + currently_delivered=0.25, + currently_returned=0, + ) + GasConsumption.objects.create( + read_at=timezone.now(), + delivered=100, + currently_delivered=1, + ) + + self.assertTrue(ElectricityConsumption.objects.exists()) + self.assertTrue(GasConsumption.objects.exists()) + + dsmr_consumption.services.clear_consumption() + + self.assertFalse(ElectricityConsumption.objects.exists()) + self.assertFalse(GasConsumption.objects.exists()) + class TestServicesDSMRv5(InterceptStdoutMixin, TestCase): """ Biggest difference is the interval of gas readings. """ diff --git a/dsmr_datalogger/migrations/0007_dsmrreading_timestamp_index.py b/dsmr_datalogger/migrations/0007_dsmrreading_timestamp_index.py new file mode 100644 index 000000000..e4594f930 --- /dev/null +++ b/dsmr_datalogger/migrations/0007_dsmrreading_timestamp_index.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.6 on 2017-03-21 22:00 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_datalogger', '0006_dsmr_firmware_v5'), + ] + + operations = [ + migrations.AlterField( + model_name='dsmrreading', + name='timestamp', + field=models.DateTimeField(db_index=True, help_text='Timestamp indicating when the reading was taken, according to the meter'), + ), + migrations.AlterField( + model_name='dsmrreading', + name='timestamp', + field=models.DateTimeField(db_index=True, help_text='Timestamp indicating when the reading was taken, according to the smart meter'), + ), + migrations.AlterField( + model_name='dsmrreading', + name='extra_device_delivered', + field=models.DecimalField(decimal_places=3, default=None, help_text='Last hourly value delivered to client according to the extra device connected', max_digits=9, null=True), + ), + migrations.AlterField( + model_name='dsmrreading', + name='extra_device_timestamp', + field=models.DateTimeField(default=None, help_text='Last hourly reading timestamp for the extra device connected', null=True), + ), + ] diff --git a/dsmr_datalogger/migrations/0008_dsmrreading_help_text.py b/dsmr_datalogger/migrations/0008_dsmrreading_help_text.py new file mode 100644 index 000000000..03884814d --- /dev/null +++ b/dsmr_datalogger/migrations/0008_dsmrreading_help_text.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-05-01 20:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_datalogger', '0007_dsmrreading_timestamp_index'), + ] + + operations = [ + migrations.AlterField( + model_name='dsmrreading', + name='electricity_currently_delivered', + field=models.DecimalField(decimal_places=3, help_text='Current electricity delivered in kW', max_digits=9), + ), + migrations.AlterField( + model_name='dsmrreading', + name='electricity_currently_returned', + field=models.DecimalField(decimal_places=3, help_text='Current electricity returned in kW', max_digits=9), + ), + migrations.AlterField( + model_name='dsmrreading', + name='electricity_delivered_1', + field=models.DecimalField(decimal_places=3, help_text='Meter position stating electricity delivered (low tariff) in kWh', max_digits=9), + ), + migrations.AlterField( + model_name='dsmrreading', + name='electricity_delivered_2', + field=models.DecimalField(decimal_places=3, help_text='Meter position stating electricity delivered (normal tariff) in kWh', max_digits=9), + ), + migrations.AlterField( + model_name='dsmrreading', + name='electricity_returned_1', + field=models.DecimalField(decimal_places=3, help_text='Meter position stating electricity returned (low tariff) in kWh', max_digits=9), + ), + migrations.AlterField( + model_name='dsmrreading', + name='electricity_returned_2', + field=models.DecimalField(decimal_places=3, help_text='Meter position stating electricity returned (normal tariff) in kWh', max_digits=9), + ), + migrations.AlterField( + model_name='dsmrreading', + name='extra_device_delivered', + field=models.DecimalField(decimal_places=3, default=None, help_text='Last value read from the extra device connected (gas meter)', max_digits=9, null=True), + ), + migrations.AlterField( + model_name='dsmrreading', + name='extra_device_timestamp', + field=models.DateTimeField(default=None, help_text='Last timestamp read from the extra device connected (gas meter)', null=True), + ), + migrations.AlterField( + model_name='dsmrreading', + name='phase_currently_delivered_l1', + field=models.DecimalField(decimal_places=3, default=None, help_text='Current electricity used by phase L1 (in kW)', max_digits=9, null=True), + ), + migrations.AlterField( + model_name='dsmrreading', + name='phase_currently_delivered_l2', + field=models.DecimalField(decimal_places=3, default=None, help_text='Current electricity used by phase L2 (in kW)', max_digits=9, null=True), + ), + migrations.AlterField( + model_name='dsmrreading', + name='phase_currently_delivered_l3', + field=models.DecimalField(decimal_places=3, default=None, help_text='Current electricity used by phase L3 (in kW)', max_digits=9, null=True), + ), + ] diff --git a/dsmr_datalogger/models/reading.py b/dsmr_datalogger/models/reading.py index 523894047..3fc3df140 100644 --- a/dsmr_datalogger/models/reading.py +++ b/dsmr_datalogger/models/reading.py @@ -18,70 +18,71 @@ class DsmrReading(models.Model): objects = DsmrReadingManager() timestamp = models.DateTimeField( - help_text=_("Timestamp indicating when the reading was taken, according to the meter") + db_index=True, + help_text=_("Timestamp indicating when the reading was taken, according to the smart meter") ) electricity_delivered_1 = models.DecimalField( max_digits=9, decimal_places=3, - help_text=_("Meter Reading electricity delivered to client (low tariff) in 0,001 kWh") + help_text=_("Meter position stating electricity delivered (low tariff) in kWh") ) electricity_returned_1 = models.DecimalField( max_digits=9, decimal_places=3, - help_text=_("Meter Reading electricity delivered by client (low tariff) in 0,001 kWh") + help_text=_("Meter position stating electricity returned (low tariff) in kWh") ) electricity_delivered_2 = models.DecimalField( max_digits=9, decimal_places=3, - help_text=_("Meter Reading electricity delivered to client (normal tariff) in 0,001 kWh") + help_text=_("Meter position stating electricity delivered (normal tariff) in kWh") ) electricity_returned_2 = models.DecimalField( max_digits=9, decimal_places=3, - help_text=_("Meter Reading electricity delivered by client (normal tariff) in 0,001 kWh") + help_text=_("Meter position stating electricity returned (normal tariff) in kWh") ) electricity_currently_delivered = models.DecimalField( max_digits=9, decimal_places=3, - help_text=_("Actual electricity power delivered (+P) in 1 Watt resolution") + help_text=_("Current electricity delivered in kW") ) electricity_currently_returned = models.DecimalField( max_digits=9, decimal_places=3, - help_text=_("Actual electricity power received (-P) in 1 Watt resolution") + help_text=_("Current electricity returned in kW") ) phase_currently_delivered_l1 = models.DecimalField( null=True, default=None, max_digits=9, decimal_places=3, - help_text=_("Instantaneous active power L1 (+P) in W resolution") + help_text=_("Current electricity used by phase L1 (in kW)") ) phase_currently_delivered_l2 = models.DecimalField( null=True, default=None, max_digits=9, decimal_places=3, - help_text=_("Instantaneous active power L2 (+P) in W resolution") + help_text=_("Current electricity used by phase L2 (in kW)") ) phase_currently_delivered_l3 = models.DecimalField( null=True, default=None, max_digits=9, decimal_places=3, - help_text=_("Instantaneous active power L3 (+P) in W resolution") + help_text=_("Current electricity used by phase L3 (in kW)") ) extra_device_timestamp = models.DateTimeField( null=True, default=None, - help_text=_("Last hourly reading timestamp") + help_text=_("Last timestamp read from the extra device connected (gas meter)") ) extra_device_delivered = models.DecimalField( null=True, default=None, max_digits=9, decimal_places=3, - help_text=_("Last hourly value delivered to client") + help_text=_("Last value read from the extra device connected (gas meter)") ) processed = models.BooleanField( default=False, diff --git a/dsmr_datalogger/serializers/__init__.py b/dsmr_datalogger/serializers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_datalogger/serializers/reading.py b/dsmr_datalogger/serializers/reading.py new file mode 100644 index 000000000..e9be68fd6 --- /dev/null +++ b/dsmr_datalogger/serializers/reading.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from dsmr_datalogger.models.reading import DsmrReading + + +class DsmrReadingSerializer(serializers.ModelSerializer): + class Meta: + model = DsmrReading + exclude = ('processed', ) diff --git a/dsmr_frontend/templates/dsmr_frontend/base.html b/dsmr_frontend/templates/dsmr_frontend/base.html index 8e8a8f051..87b41bf57 100644 --- a/dsmr_frontend/templates/dsmr_frontend/base.html +++ b/dsmr_frontend/templates/dsmr_frontend/base.html @@ -128,7 +128,7 @@
  • - {% trans "Documentation / FAQ" %} + {% trans "Help / FAQ" %}
  • diff --git a/dsmr_frontend/templates/dsmr_frontend/trends.html b/dsmr_frontend/templates/dsmr_frontend/trends.html index 6218348c5..a933aa750 100644 --- a/dsmr_frontend/templates/dsmr_frontend/trends.html +++ b/dsmr_frontend/templates/dsmr_frontend/trends.html @@ -184,7 +184,7 @@ var ctx = $("#" + chart_id).get(0).getContext("2d"); ctx.canvas.height = $("#" + chart_id).parent().height(); - var chart_instance = new Chart(ctx, { + new Chart(ctx, { type: 'bar', data: data, options: chart_options diff --git a/dsmr_frontend/tests/webinterface/test_export.py b/dsmr_frontend/tests/webinterface/test_export.py index da48c166b..2bb5e73d5 100644 --- a/dsmr_frontend/tests/webinterface/test_export.py +++ b/dsmr_frontend/tests/webinterface/test_export.py @@ -35,7 +35,7 @@ def test_export(self): response = self.client.get(view_url) self.assertEqual(response.status_code, 302) self.assertEqual( - response['Location'], 'admin/login/?next={}'.format(view_url) + response['Location'], '/admin/login/?next={}'.format(view_url) ) # Login and retest @@ -58,7 +58,7 @@ def test_export_as_csv(self): response = self.client.post(view_url) self.assertEqual(response.status_code, 302) self.assertEqual( - response['Location'], 'admin/login/?next={}'.format(view_url) + response['Location'], '/admin/login/?next={}'.format(view_url) ) # Login and retest, without post data. diff --git a/dsmr_frontend/tests/webinterface/test_trends.py b/dsmr_frontend/tests/webinterface/test_trends.py index 30465ffb4..732ddb8a8 100644 --- a/dsmr_frontend/tests/webinterface/test_trends.py +++ b/dsmr_frontend/tests/webinterface/test_trends.py @@ -324,7 +324,7 @@ def test_export(self): response = self.client.get(view_url) self.assertEqual(response.status_code, 302) self.assertEqual( - response['Location'], 'admin/login/?next={}'.format(view_url) + response['Location'], '/admin/login/?next={}'.format(view_url) ) # Login and retest @@ -347,7 +347,7 @@ def test_export_as_csv(self): response = self.client.post(view_url) self.assertEqual(response.status_code, 302) self.assertEqual( - response['Location'], 'admin/login/?next={}'.format(view_url) + response['Location'], '/admin/login/?next={}'.format(view_url) ) # Login and retest, without post data. diff --git a/dsmr_frontend/views/trends.py b/dsmr_frontend/views/trends.py index 27d5ae12b..cf1e5c4b2 100644 --- a/dsmr_frontend/views/trends.py +++ b/dsmr_frontend/views/trends.py @@ -41,7 +41,8 @@ def get_context_data(self, **kwargs): context_data['electricity_by_tariff_month'] = dsmr_stats.services.\ electricity_tariff_percentage(start_date=now.date() - relativedelta(months=1)) - + print(context_data['electricity_by_tariff_week']) + print(context_data['electricity_by_tariff_month']) graph_data = defaultdict(list) for current in average_consumption_by_hour: diff --git a/dsmr_stats/management/commands/dsmr_stats_clear_statistics.py b/dsmr_stats/management/commands/dsmr_stats_clear_statistics.py deleted file mode 100644 index fa2f8f06a..000000000 --- a/dsmr_stats/management/commands/dsmr_stats_clear_statistics.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.core.management.base import BaseCommand, CommandError -from django.utils.translation import ugettext as _ - -import dsmr_stats.services - - -class Command(BaseCommand): - help = _('Clears all statistics generated. Use this to regenerate them after altering prices.') - - def add_arguments(self, parser): - super(Command, self).add_arguments(parser) - parser.add_argument( - '--ack-to-delete-my-data', - action='store_true', - dest='acked_warning', - default=False, - help=_('Required to acknowledge you that you WILL delete your statistics with this.') - ) - - def handle(self, **options): - if not options.get('acked_warning'): - raise CommandError(_( - 'Intended usage is NOT production! Force by using --ack-to-delete-my-data' - )) - - dsmr_stats.services.clear_statistics() diff --git a/dsmr_stats/migrations/0011_note_model_index.py b/dsmr_stats/migrations/0011_note_model_index.py new file mode 100644 index 000000000..9c5feae24 --- /dev/null +++ b/dsmr_stats/migrations/0011_note_model_index.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-04-23 18:13 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_stats', '0010_statistics_models_index'), + ] + + operations = [ + migrations.AlterField( + model_name='note', + name='day', + field=models.DateField(db_index=True, verbose_name='Day'), + ), + ] diff --git a/dsmr_stats/models/note.py b/dsmr_stats/models/note.py index dd43f74c5..089d0bc2d 100644 --- a/dsmr_stats/models/note.py +++ b/dsmr_stats/models/note.py @@ -4,7 +4,7 @@ class Note(models.Model): """ Daily note someone might place for some remarks about something related to consumption. """ - day = models.DateField(verbose_name=_('Day')) + day = models.DateField(db_index=True, verbose_name=_('Day')) description = models.CharField(max_length=256, verbose_name=_('Description')) class Meta: diff --git a/dsmr_stats/serializers/__init__.py b/dsmr_stats/serializers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dsmr_stats/serializers/statistics.py b/dsmr_stats/serializers/statistics.py new file mode 100644 index 000000000..72e243683 --- /dev/null +++ b/dsmr_stats/serializers/statistics.py @@ -0,0 +1,15 @@ +from rest_framework import serializers + +from dsmr_stats.models.statistics import DayStatistics, HourStatistics + + +class DayStatisticsSerializer(serializers.ModelSerializer): + class Meta: + model = DayStatistics + fields = '__all__' + + +class HourStatisticsSerializer(serializers.ModelSerializer): + class Meta: + model = HourStatistics + fields = '__all__' diff --git a/dsmr_stats/tests/test_management_command.py b/dsmr_stats/tests/test_management_command.py deleted file mode 100644 index 54999ebc6..000000000 --- a/dsmr_stats/tests/test_management_command.py +++ /dev/null @@ -1,19 +0,0 @@ -from unittest import mock - -from django.test.testcases import TestCase -from django.core.management.base import CommandError - -from dsmr_backend.tests.mixins import InterceptStdoutMixin - - -class TestManagementCommand(InterceptStdoutMixin, TestCase): - def test_dsmr_stats_clear_statistics(self): - expected_error = 'Intended usage is NOT production! Force by using --ack-to-delete-my-data' - - with self.assertRaisesMessage(CommandError, expected_error): - self._intercept_command_stdout('dsmr_stats_clear_statistics') - - with mock.patch('dsmr_stats.services.clear_statistics') as service_mock: - self.assertFalse(service_mock.called) - self._intercept_command_stdout('dsmr_stats_clear_statistics', acked_warning=True) - self.assertTrue(service_mock.called) diff --git a/dsmrreader/__init__.py b/dsmrreader/__init__.py index e51142e18..902a2dfb7 100644 --- a/dsmrreader/__init__.py +++ b/dsmrreader/__init__.py @@ -17,6 +17,6 @@ from django.utils.version import get_version -VERSION = (1, 6, 2, 'final', 0) +VERSION = (1, 7, 0, 'final', 0) __version__ = get_version(VERSION) diff --git a/dsmrreader/config/base.py b/dsmrreader/config/base.py index e303d8a55..e7934cd6a 100644 --- a/dsmrreader/config/base.py +++ b/dsmrreader/config/base.py @@ -48,6 +48,8 @@ # Third party apps/plugins. 'solo.apps.SoloAppConfig', 'colorfield', + 'django_filters', + 'rest_framework', # Local project apps. 'dsmr_api.apps.AppConfig', @@ -100,7 +102,8 @@ WSGI_APPLICATION = 'dsmrreader.wsgi.application' -LOGIN_URL = 'admin/login/' +LOGIN_URL = 'admin:login' +LOGOUT_URL = 'admin:logout' # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases @@ -154,6 +157,27 @@ LOCALE_PATHS = (os.path.join(BASE_DIR, 'locales'), ) +""" Django Rest Framework. """ + +REST_FRAMEWORK = { + 'DEFAULT_FILTER_BACKENDS': ( + 'rest_framework.filters.OrderingFilter', + 'django_filters.rest_framework.DjangoFilterBackend', + ), + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'dsmr_api.authentication.HeaderAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework.renderers.JSONRenderer', + ), + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 25, +} + + """ DSMR Project settings. """ DSMRREADER_SUPPORTED_DB_VENDORS = ('postgresql', 'mysql') @@ -166,3 +190,5 @@ DSMRREADER_VERSION = dsmrreader.__version__ DSMRREADER_RAW_VERSION = dsmrreader.VERSION DSMRREADER_LATEST_VERSION_FILE = 'https://raw.githubusercontent.com/dennissiemensma/dsmr-reader/master/dsmrreader/__init__.py' + +DSMRREADER_REST_FRAMEWORK_API_USER = 'api-user' diff --git a/dsmrreader/locales/nl/LC_MESSAGES/django.mo b/dsmrreader/locales/nl/LC_MESSAGES/django.mo index 3a41ee715..8f1a5fca9 100644 Binary files a/dsmrreader/locales/nl/LC_MESSAGES/django.mo and b/dsmrreader/locales/nl/LC_MESSAGES/django.mo differ diff --git a/dsmrreader/locales/nl/LC_MESSAGES/django.po b/dsmrreader/locales/nl/LC_MESSAGES/django.po index 87ce1d7a6..584f42085 100644 --- a/dsmrreader/locales/nl/LC_MESSAGES/django.po +++ b/dsmrreader/locales/nl/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader v1.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-03-18 17:06+0100\n" -"PO-Revision-Date: 2017-03-17 00:36+0100\n" +"POT-Creation-Date: 2017-05-04 19:20+0200\n" +"PO-Revision-Date: 2017-05-01 22:56+0200\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" "Language: nl\n" @@ -36,18 +36,6 @@ msgstr "De autorisatiesleutel om toegang te krijgen tot deze API." msgid "API configuration" msgstr "API-configuratie" -msgid "API is disabled" -msgstr "API is uitgeschakeld" - -msgid "Invalid auth key" -msgstr "Ongeldige autorisatiesleutel" - -msgid "Invalid data" -msgstr "Ongeldige gegevens" - -msgid "Failed to parse telegram" -msgstr "Inlezen telegram mislukt" - msgid "Backend" msgstr "Achterkant" @@ -63,6 +51,15 @@ msgstr "Bedoeld voor gebruik buiten productie. Alleen mogelijk wanneer instellin msgid "Generates a generic event triggering apps for backend operations, cron-like." msgstr "Genereert een generiek signaal dat gebruikt kan worden door plugins voor achtergrondoperaties, zoals cron." +msgid "Deletes all aggregated data generated. Such as consumption and (day/hour) statistics. This command does NOT affect any readings stored. In fact, you should NEVER run this, unless you still have each and EVERY reading stored, as the application will attempt to recalculate all aggregated data deleted retroactively, using each reading stored." +msgstr "Verwijdert alle geaggregeerde gegevens, zoals verbruik en dag/uur-statistieken. Dit heeft GEEN invloed op alle opgeslagen metingen. Let op: draai dit NOOIT tenzij je absoluut zeker weet dat je ELKE meting nog opgeslagen hebt. De applicatie gaat namelijk proberen om alle geaggregeerde gegevens opnieuw uit te rekenen, aan de hand van de opgeslagen metingen." + +msgid "Required to acknowledge you that you WILL delete your statistics with this." +msgstr "Verplichte parameter om te bevestigen dat je beseft dat je hiermee (gegenereerde) data verwijdert." + +msgid "Intended usage is NOT production! Force by using --ack-to-delete-my-data" +msgstr "Bedoeld voor gebruik buiten productie. Forceer uitvoer met --ack-to-delete-my-data" + msgid "Forces single run, overriding Infinite Command mixin" msgstr "Forceert enkele uitvoer." @@ -231,14 +228,41 @@ msgstr "neem teruglevering elektriciteit mee (zonnepanelen)" msgid "Intended usage is NOT production! Force by using --ack-to-mess-up-my-data" msgstr "Bedoeld voor gebruik buiten productie. Forceer uitvoer met --ack-to-mess-up-my-data" -msgid "Timestamp indicating when the reading was taken, according to the meter" +msgid "Timestamp indicating when the reading was taken, according to the smart meter" msgstr "Moment waarop de meting is gedaan, volgens de meter." -msgid "Last hourly reading timestamp" -msgstr "Tijdstip van laatste meting" +msgid "Meter position stating electricity delivered (low tariff) in kWh" +msgstr "Meterstand van verbruikte elektriciteit (laagtarief) in kWh" + +msgid "Meter position stating electricity returned (low tariff) in kWh" +msgstr "Meterstand van teruggeleverde elektriciteit (laagtarief) in kWh" + +msgid "Meter position stating electricity delivered (normal tariff) in kWh" +msgstr "Meterstand van verbruikte elektriciteit (piektarief) in kWh" + +msgid "Meter position stating electricity returned (normal tariff) in kWh" +msgstr "Meterstand van teruggeleverde elektriciteit (piektarief) kWh" + +msgid "Current electricity delivered in kW" +msgstr "Huidig elektriciteitsverbruik in kW" + +msgid "Current electricity returned in kW" +msgstr "Huidige teruglevering elektriciteit in kW" + +msgid "Current electricity used by phase L1 (in kW)" +msgstr "Huidig elektriciteitsverbruik in fase L1 (in kW)" + +msgid "Current electricity used by phase L2 (in kW)" +msgstr "Huidig elektriciteitsverbruik in fase L2 (in kW)" -msgid "Last hourly value delivered to client" -msgstr "Meest recente meterstand" +msgid "Current electricity used by phase L3 (in kW)" +msgstr "Huidig elektriciteitsverbruik in fase L3 (in kW)" + +msgid "Last timestamp read from the extra device connected (gas meter)" +msgstr "Tijdstip van meting door de externe (gas)meter" + +msgid "Last value read from the extra device connected (gas meter)" +msgstr "Meterstand van de externe (gas)meter" msgid "Whether this reading has been processed for merging into statistics" msgstr "Geeft aan of deze meting al verwerkt is in het samenvoegen van statistieken" @@ -474,8 +498,8 @@ msgstr "Inloggen vereist" msgid "Configuration" msgstr "Configuratie" -msgid "Documentation / FAQ" -msgstr "Documentatie / FAQ" +msgid "Help / FAQ" +msgstr "Help / FAQ" msgid "Feedback" msgstr "Feedback" @@ -963,15 +987,6 @@ msgstr "Totale kosten: € {}" msgid "Trend & statistics" msgstr "Trends & statistieken" -msgid "Clears all statistics generated. Use this to regenerate them after altering prices." -msgstr "Verwijdert alle (dag/uur)statistieken. Gebruik dit wanneer je de energieprijzen hebt aangepast." - -msgid "Required to acknowledge you that you WILL delete your statistics with this." -msgstr "Verplichte parameter om te bevestigen dat je beseft dat je hiermee (gegenereerde) data verwijdert." - -msgid "Intended usage is NOT production! Force by using --ack-to-delete-my-data" -msgstr "Bedoeld voor gebruik buiten productie. Forceer uitvoer met --ack-to-delete-my-data" - msgid "Alters any stats generate to fake data. DO NOT USE in production! Used for integration checks." msgstr "Verandert alle bestaande statistieken. NIET GEBRUIKEN IN PRODUCTIE! Wordt gebruikt voor integratietests." @@ -1188,6 +1203,32 @@ msgstr "Nederlands" msgid "English" msgstr "Engels" +#~ msgid "API is disabled" +#~ msgstr "API is uitgeschakeld" + +#~ msgid "Invalid auth key" +#~ msgstr "Ongeldige autorisatiesleutel" + +#~ msgid "API user not found" +#~ msgstr "API gebruiker niet gevonden" + +#~ msgid "Invalid data" +#~ msgstr "Ongeldige gegevens" + +#~ msgid "Failed to parse telegram" +#~ msgstr "Inlezen telegram mislukt" + +#~ msgid "Last hourly value delivered to client according to the extra device connected" +#~ msgstr "Meest recente meterstand, volgens de externe (gas)meter" + +#, fuzzy +#~| msgid "Documentation / FAQ" +#~ msgid "API Documentation" +#~ msgstr "Documentatie / FAQ" + +#~ msgid "Clears all statistics generated. Use this to regenerate them after altering prices." +#~ msgstr "Verwijdert alle (dag/uur)statistieken. Gebruik dit wanneer je de energieprijzen hebt aangepast." + #~ msgid "The access token for your Dropbox account. You should register an App for your own Dropbox account (https://www.dropbox.com/developers/apps). Please select \"Permission type\" named \"App folder\" to restrict unneeded access. Backups will be synced to a dedicated folder in your account. After creating your App you should be able to generate an \"Access token\" and enter it here. For more information, see https://blogs.dropbox.com/developers/2014/05/generate-an-access-token-for-your-own-account" #~ msgstr "Het 'access token' voor je Dropbox account. Hiervoor moet je onder je eigen account een applicatie registeren (https://www.dropbox.com/developers/apps). Zorg ervoor dat je bij \"Permission type\" kiest voor de optie \"App folder\" om de gedeelde toegang tot je account te beperken. Backups worden hierdoor in je account op een geïsoleerde locatie gezet. Zodra je in Dropbox een applicatie hebt toegevoegd, kun je een zogeheten \"Access token\" genereren en hier invullen. Zie voor meer informatie: https://blogs.dropbox.com/developers/2014/05/generate-an-access-token-for-your-own-account" diff --git a/dsmrreader/provisioning/requirements/base.txt b/dsmrreader/provisioning/requirements/base.txt index 9217c82f1..d77f8a951 100644 --- a/dsmrreader/provisioning/requirements/base.txt +++ b/dsmrreader/provisioning/requirements/base.txt @@ -1,11 +1,14 @@ crcmod==1.7 +coreapi==2.3.0 django==1.10.7 django-colorfield==0.1.12 django-debug-toolbar==1.7 +django-filter==1.0.1 +djangorestframework==3.6.2 django-solo==1.1.2 dropbox==7.2.1 gunicorn==19.7.0 pyserial==3.3 python-dateutil==2.6.0 -raven==6.0.0 pytz==2016.10 +raven==6.0.0 diff --git a/dsmrreader/urls.py b/dsmrreader/urls.py index 2178d44e4..916bfc65a 100644 --- a/dsmrreader/urls.py +++ b/dsmrreader/urls.py @@ -1,18 +1,3 @@ -"""dsmrreader URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.8/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Add an import: from blog import urls as blog_urls - 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) -""" from django.conf.urls import include, url from django.contrib import admin from django.conf import settings @@ -20,7 +5,7 @@ urlpatterns = [ url(r'^admin/', include(admin.site.urls)), - url(r'^api/v1/', include('dsmr_api.urls', namespace='api')), + url(r'^api/', include('dsmr_api.urls')), url(r'^', include('dsmr_frontend.urls', namespace='frontend')), ] diff --git a/upgrade.sh b/upgrade.sh deleted file mode 100755 index 2194afa86..000000000 --- a/upgrade.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - - -# This script is simply an alias of the longer existing deploy.sh -./deploy.sh