diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 8d64b7632..000000000 --- a/.coveragerc +++ /dev/null @@ -1,8 +0,0 @@ -[run] -branch = True -include = ./* -omit = ./*/migrations/* - ./dsmr_backend/mixins.py - -[html] -directory = coverage_report diff --git a/.travis.yml b/.travis.yml index 2269b5331..b8afa51ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: python python: - "3.3" - "3.4" + - "3.5" services: - postgresql @@ -32,7 +33,7 @@ before_script: - sh -c "if [ '$DB' = 'mysql' ]; then mysqladmin create dsmrreader; fi" script: - - ./manage.py test --noinput --with-coverage + - ./manage.py test --noinput after_success: - codecov diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..66a5b89c6 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,26 @@ +codecov: + notify: + require_ci_to_pass: true +comment: + behavior: default + layout: header, diff + require_changes: false +coverage: + precision: 2 + range: + - 70.0 + - 100.0 + round: down + status: + changes: false + patch: true + project: true +parsers: + gcov: + branch_detection: + conditional: true + loop: true + macro: false + method: false + javascript: + enable_partials: false \ No newline at end of file diff --git a/docs/HOWTO b/docs/HOWTO new file mode 100644 index 000000000..ffcfb950e --- /dev/null +++ b/docs/HOWTO @@ -0,0 +1,20 @@ +Running localhost docs server: + +- (cd into CURRENT folder first: cd docs) +- sphinx-autobuild . _build/html -p 10000 +- Visit http://127.0.0.1:10000 + + + +Updating translations after editing RST files: + +- (cd into CURRENT folder first: cd docs) +- make gettext && sphinx-intl update -p _build/locale -l nl + + + +Checking (Dutch) translations results by building locally: + +- (cd into CURRENT folder first: cd docs) +- make -e SPHINXOPTS="-D language='nl'" html +- view generated HTML in: docs/_build/html diff --git a/docs/_static/faq/notifications-notify-my-android-create-key.png b/docs/_static/faq/notifications-notify-my-android-create-key.png new file mode 100644 index 000000000..1fccd0aa2 Binary files /dev/null and b/docs/_static/faq/notifications-notify-my-android-create-key.png differ diff --git a/docs/_static/faq/notifications-notify-my-android-get-key.png b/docs/_static/faq/notifications-notify-my-android-get-key.png new file mode 100644 index 000000000..4d415931c Binary files /dev/null and b/docs/_static/faq/notifications-notify-my-android-get-key.png differ diff --git a/docs/_static/faq/notifications-prowl-create-key.png b/docs/_static/faq/notifications-prowl-create-key.png new file mode 100644 index 000000000..ecb5532cb Binary files /dev/null and b/docs/_static/faq/notifications-prowl-create-key.png differ diff --git a/docs/_static/faq/notifications-prowl-get-key.png b/docs/_static/faq/notifications-prowl-get-key.png new file mode 100644 index 000000000..84810108a Binary files /dev/null and b/docs/_static/faq/notifications-prowl-get-key.png differ diff --git a/docs/changelog.rst b/docs/changelog.rst index f153a43da..073432122 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,19 @@ Changelog ========= +v1.4.0 - 2016-11-28 +^^^^^^^^^^^^^^^^^^^ +- Push notifications for Notify My Android / Prowl (iOS), written by Jeroen Peters (`#152 `_). +- Support for both single and high/low tariff (`#130 `_). +- Add new note from Dashboard has wrong time format (`#159 `_). +- Display estimated price for current usage in Dashboard (`#155 `_). +- Dropbox API v1 deprecated in June 2017 (`#142 `_). +- Support for Python 3.5 (`#55 `_). +- Improve code coverage (`#151 `_). +- Restyle configuration overview (`#156 `_). +- Capability based push notifications (`#165 `_). + + v1.3.2 - 2016-11-08 ^^^^^^^^^^^^^^^^^^^ - Requirements update (november 2016) (`#150 `_). diff --git a/docs/contributing.rst b/docs/contributing.rst index f46d579de..35190953e 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -2,6 +2,14 @@ Contributing ============ Would you like to contribute or help this project in any way? + +Feedback +-------- +All feedback and any input is, as always, very much appreciated! `Please create an issue on Github `_. + +It doesn't matter whether you run into problems getting started in this guide or just want to get in touch, just fire away! + + P1 telegram snapshot -------------------- Please start by `creating an issue on Github `_ with a snapshot of a DSMR telegram. It will help me in improving support for multiple meter vendors and home situations. @@ -9,9 +17,3 @@ Please start by `creating an issue on Github `_. - -It doesn't matter whether you run into problems getting started in this guide or just want to get in touch, just fire away! \ No newline at end of file diff --git a/docs/credits.rst b/docs/credits.rst index 349f029ec..a5406eb35 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -41,8 +41,6 @@ Software - `Full Page Screen Capture `_ -- `Twitter Bootstrap Wizard `_ - Testers & contributors ---------------------- @@ -53,6 +51,7 @@ Testers & contributors - "WatskeBart" (`Github profile `_) - `Gert Schaafsma `_ - `Bert-Jan Vos `_ +- Sevickson (`Github profile `_) DSMR help diff --git a/docs/faq.rst b/docs/faq.rst index f8c06bd91..0e05983e4 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -1,6 +1,17 @@ Frequently Asked Questions (FAQ) ================================ + +I only pay for a single electricity tariff but I see two! +--------------------------------------------------------- +DSMR (and your energy supplier) always read both high and low tariff from your meter. +It's possible however that you are only paying for a single tariff. +In that case your energy supplier will simply merge both high and low tariffs to make it look like you have a single one. + +This application displays separate tariffs by default, but supports merging them to a single one as well. +Just make sure that you apply the **same price to both electricity 1 and 2** and enable the option '**Merge electricity tariffs**' in the frontend configuration. + + Dropbox: Automated backup sync ------------------------------ *How can I link my Dropbox account for backups?* @@ -46,11 +57,59 @@ Please note that due to policies of mindergas.nl it's not allowed to retroactive Therefor this is not supported by the application. You can however, enter them manually on their website. -Recalculate prices ------------------- +Usage notification: Daily usage statistics on your smartphone +------------------------------------------------------------- +*Which services for sending notifications are supported?* + +Currently, two mobile platforms are supported: Android and iOS. +The supported app for Android is `NotifyMyAndroid `_. +The supported app for iOS is `Prowl `_. + + +*How do I setup usage notifications?* + +Make sure you either have NotifyMyAndroid or Prowl installed on your smartphone. If you don't, visit your platforms app store to download the app and sign up for an account. Then, make sure to get your API key from the notificationservice that you prefer. For instruction on obtaining the API key, please read below. + +In the DSMR-reader settings for the Usagenotifications, tick the Send Notifications checkbox and select the notification service you want to use. Then copy the API key from the notification service and paste in into the the textbox for the API key. When you save these settings, your first notification should be sent after midnight. Don't worry, the notification will be sent with low priority and will not wake you up. + + +*How do I obtain my API key for NotifyMyAndroid?* + +After you have downloaded NotifyMyAndroid and signed up for an account you should be able to `login to your account `_. +Now go to "`My Account `_", you should see an overview of your current API keys if you have any. To create an API key for the DSMR-reader, please click **"Generate New Key"**. + +.. image:: _static/faq/notifications-notify-my-android-create-key.png + :target: _static/faq/notifications-notify-my-android-create-key.png + :alt: NotifyMyAndroid My Account overview + +When a new key is generated, you will see it immediatly. Your key is listed like in the screenshot below (the red box marks your API key). + +.. image:: _static/faq/notifications-notify-my-android-get-key.png + :target: _static/faq/notifications-notify-my-android-get-key.png + :alt: NotifyMyAndroid Get Your API Key + + +*How do I obtain my API key for Prowl?* + +After you have downloaded Prowl and signed up for an account you should be able to `login to your account `_. +Now go to "`API Keys `_", you should see an overview of your current API keys if you have any. To create an API key for the DSMR-reader, input a name and click **"Generate Key"**. + +.. image:: _static/faq/notifications-prowl-create-key.png + :target: _static/faq/notifications-prowl-key.png + :alt: Prowl My Account overview + +When a new key is generated, you will see it immediatly. Your key is listed like in the screenshot below (the red box marks your API key). + +.. image:: _static/faq/notifications-prowl-get-key.png + :target: _static/faq/notifications-prowl-get-key.png + :alt: Prowl Get Your API Key + + +Recalculate prices retroactively +-------------------------------- *I've adjusted my energy prices but there are no changes! How can I regenerate them with my new prices?* -You can flush your statistics by executing: +Statistics for each day are generated once, the day after. However, you can flush your statistics by executing: ``./manage.py dsmr_stats_clear_statistics --ack-to-delete-my-data`` diff --git a/docs/installation.rst b/docs/installation.rst index 1635e3edf..903760a02 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -13,7 +13,7 @@ Dependencies & requirements - Recommended and tested with, but any OS satisfying the requirements should do fine. -- **Python 3.3 / 3.4** +- **Python 3.3 / 3.4 / 3.5** - **PostgreSQL 9+ or MySQL / MariaDB 5.5+** - I highly recommend *PostgreSQL* due to builtin support for timezones. diff --git a/docs/locale/nl/LC_MESSAGES/changelog.po b/docs/locale/nl/LC_MESSAGES/changelog.po index f7ad8d520..28736308f 100644 --- a/docs/locale/nl/LC_MESSAGES/changelog.po +++ b/docs/locale/nl/LC_MESSAGES/changelog.po @@ -8,649 +8,733 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-07-12 22:40+0200\n" +"POT-Creation-Date: 2016-11-24 21:10+0100\n" "PO-Revision-Date: 2016-04-18 22:10+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.2.0\n" +"Generated-By: Babel 2.3.4\n" #: ../../changelog.rst:2 msgid "Changelog" msgstr "Wijzigingenoverzicht" #: ../../changelog.rst:6 -msgid "v1.3.0 - 2016-??-??" +msgid "v1.4.0 - 2016-xx-xx" msgstr "" #: ../../changelog.rst:7 msgid "" +"Dropbox API v1 deprecated (`#142 `_)." +msgstr "" + +#: ../../changelog.rst:8 +msgid "" +"Support for Python 3.5 (`#55 `_)." +msgstr "" + +#: ../../changelog.rst:9 +msgid "" +"Support for both single and high/low tariff (`#130 " +"`_)." +msgstr "" + +#: ../../changelog.rst:10 +msgid "" +"Improve code coverage (`#151 `_)." +msgstr "" + +#: ../../changelog.rst:11 +msgid "" +"Add new note from Dashboard has wrong time format (`#159 " +"`_)." +msgstr "" + +#: ../../changelog.rst:12 +msgid "" +"Display estimated price for current usage in Dashboard (`#155 " +"`_)." +msgstr "" + +#: ../../changelog.rst:13 +msgid "" +"Restyle configuration overview (`#156 `_)." +msgstr "" + +#: ../../changelog.rst:17 +msgid "v1.3.2 - 2016-11-08" +msgstr "" + +#: ../../changelog.rst:18 +msgid "" +"Requirements update (november 2016) (`#150 " +"`_)." +msgstr "" + +#: ../../changelog.rst:22 +msgid "v1.3.1 - 2016-08-16" +msgstr "" + +#: ../../changelog.rst:23 +msgid "" +"CSS large margin-bottom (`#144 `_)." +msgstr "" + +#: ../../changelog.rst:24 +msgid "" +"Django security releases issued: 1.8.14 (`#147 " +"`_)." +msgstr "" + +#: ../../changelog.rst:25 +msgid "" +"Requirements update (August 2016) (`#148 " +"`_)." +msgstr "" + +#: ../../changelog.rst:26 +msgid "" +"Query performance improvements (`#149 `_)." +msgstr "" + +#: ../../changelog.rst:30 +msgid "v1.3.0 - 2016-07-15" +msgstr "" + +#: ../../changelog.rst:31 +msgid "" "API endpoint for datalogger (`#140 `_)." msgstr "" -#: ../../changelog.rst:8 +#: ../../changelog.rst:32 msgid "" "Colors for charts (`#137 `_)." msgstr "" -#: ../../changelog.rst:9 +#: ../../changelog.rst:33 msgid "" "Data export: Mindergas.nl (`#10 `_)." msgstr "" -#: ../../changelog.rst:10 +#: ../../changelog.rst:34 msgid "" "Requirement upgrade (`#143 `_)." msgstr "" -#: ../../changelog.rst:11 +#: ../../changelog.rst:35 msgid "" "Installation wizard for first time use (`#139 " "`_)." msgstr "" -#: ../../changelog.rst:15 +#: ../../changelog.rst:39 msgid "v1.2.0 - 2016-05-18" msgstr "" -#: ../../changelog.rst:16 +#: ../../changelog.rst:40 msgid "" "Energy supplier prices does not indicate tariff type (Django admin) " "(`#126 `_)." msgstr "" -#: ../../changelog.rst:17 +#: ../../changelog.rst:41 msgid "" "Requirements update (`#128 `_)." msgstr "" -#: ../../changelog.rst:18 +#: ../../changelog.rst:42 msgid "" "Force backup (`#123 `_)." msgstr "" -#: ../../changelog.rst:19 +#: ../../changelog.rst:43 msgid "" "Update clean-install.md (`#131 `_)." msgstr "" -#: ../../changelog.rst:20 +#: ../../changelog.rst:44 msgid "" "Improve data export field names (`#132 " "`_)." msgstr "" -#: ../../changelog.rst:21 +#: ../../changelog.rst:45 msgid "" "Display average temperature in archive (`#122 " "`_)." msgstr "" -#: ../../changelog.rst:22 +#: ../../changelog.rst:46 msgid "" "Pie charts on trends page overlap their canvas (`#136 " "`_)." msgstr "" -#: ../../changelog.rst:23 +#: ../../changelog.rst:47 msgid "" "'Slumber' consumption (`#115 `_)." msgstr "" -#: ../../changelog.rst:24 +#: ../../changelog.rst:48 msgid "" "Show lowest & highest Watt peaks (`#138 " "`_)." msgstr "" -#: ../../changelog.rst:25 +#: ../../changelog.rst:49 msgid "" "Allow day & hour statistics reset due to changing energy prices (`#95 " "`_)." msgstr "" -#: ../../changelog.rst:29 +#: ../../changelog.rst:53 msgid "v1.1.2 - 2016-05-01" msgstr "" -#: ../../changelog.rst:30 +#: ../../changelog.rst:54 msgid "" "Trends page giving errors (when lacking data) (`#125 " "`_)." msgstr "" -#: ../../changelog.rst:34 +#: ../../changelog.rst:58 msgid "v1.1.1 - 2016-04-27" msgstr "" -#: ../../changelog.rst:35 +#: ../../changelog.rst:59 msgid "" "Improve readme (`#124 `_)." msgstr "" -#: ../../changelog.rst:39 +#: ../../changelog.rst:63 msgid "v1.1.0 - 2016-04-23" msgstr "" -#: ../../changelog.rst:40 +#: ../../changelog.rst:64 msgid "" "Autorefresh dashboard (`#117 `_)." msgstr "" -#: ../../changelog.rst:41 +#: ../../changelog.rst:65 msgid "" "Improve line graphs' visibility (`#111 " "`_)." msgstr "" -#: ../../changelog.rst:42 +#: ../../changelog.rst:66 msgid "" "Easily add notes (`#110 `_)." msgstr "" -#: ../../changelog.rst:43 +#: ../../changelog.rst:67 msgid "" "Export data points in CSV format (`#2 `_)." msgstr "" -#: ../../changelog.rst:44 +#: ../../changelog.rst:68 msgid "" "Allow day/month/year comparison (`#94 `_)." msgstr "" -#: ../../changelog.rst:45 +#: ../../changelog.rst:69 msgid "" "Docs: Add FAQ and generic application info (`#113 " "`_)." msgstr "" -#: ../../changelog.rst:46 +#: ../../changelog.rst:70 msgid "" "Support for Iskra meter (DSMR 2.x) (`#120 " "`_)." msgstr "" -#: ../../changelog.rst:50 +#: ../../changelog.rst:74 msgid "v1.0.1 - 2016-04-07" msgstr "" -#: ../../changelog.rst:51 +#: ../../changelog.rst:75 msgid "" "Update licence to OSI compatible one (`#119 " "`_)." msgstr "" -#: ../../changelog.rst:55 +#: ../../changelog.rst:79 msgid "v1.0.0 - 2016-04-07" msgstr "" -#: ../../changelog.rst:56 +#: ../../changelog.rst:80 msgid "First official stable release." msgstr "Eerste officiële stabiele release." -#: ../../changelog.rst:60 +#: ../../changelog.rst:84 msgid "[β] v0.1 (2015-10-29) to 0.16 (2016-04-06)" msgstr "" -#: ../../changelog.rst:61 +#: ../../changelog.rst:85 msgid "" "All previous beta releases/changes have been combined to a single list " "below:" msgstr "" -#: ../../changelog.rst:63 +#: ../../changelog.rst:87 msgid "" "Move documentation to wiki or RTD (`#90 " "`_)." msgstr "" -#: ../../changelog.rst:64 +#: ../../changelog.rst:88 msgid "" "Translate README to Dutch (`#16 `_)." msgstr "" -#: ../../changelog.rst:65 +#: ../../changelog.rst:89 msgid "" "Delete (recent) history page (`#112 `_)." msgstr "" -#: ../../changelog.rst:66 +#: ../../changelog.rst:90 msgid "" "Display most recent temperature in dashboard (`#114 " "`_)." msgstr "" -#: ../../changelog.rst:67 +#: ../../changelog.rst:91 msgid "" "Upgrade Django to 1.8.12 (`#118 `_)." msgstr "" -#: ../../changelog.rst:69 +#: ../../changelog.rst:93 msgid "" "Redesign trends page (`#97 `_)." msgstr "" -#: ../../changelog.rst:70 +#: ../../changelog.rst:94 msgid "" "Support for summer time (`#105 `_)." msgstr "" -#: ../../changelog.rst:71 +#: ../../changelog.rst:95 msgid "" "Support for Daylight Saving Time (DST) transition (`#104 " "`_)." msgstr "" -#: ../../changelog.rst:72 +#: ../../changelog.rst:96 msgid "" "Add (error) hints to status page (`#106 " "`_)." msgstr "" -#: ../../changelog.rst:73 +#: ../../changelog.rst:97 msgid "" "Keep track of version (`#108 `_)." msgstr "" -#: ../../changelog.rst:75 +#: ../../changelog.rst:99 msgid "" "Django 1.8.11 released (`#82 `_)." msgstr "" -#: ../../changelog.rst:76 +#: ../../changelog.rst:100 msgid "" "Prevent tests from failing due to moment of execution (`#88 " "`_)." msgstr "" -#: ../../changelog.rst:77 +#: ../../changelog.rst:101 msgid "" "Statistics page meter positions are broken (`#93 " "`_)." msgstr "" -#: ../../changelog.rst:78 +#: ../../changelog.rst:102 msgid "" "Archive only shows graph untill 23:00 (11 pm) (`#77 " "`_)." msgstr "" -#: ../../changelog.rst:79 +#: ../../changelog.rst:103 msgid "" "Trends page crashes due to nullable fields average (`#100 " "`_)." msgstr "" -#: ../../changelog.rst:80 +#: ../../changelog.rst:104 msgid "" "Trends: Plot peak and off-peak relative to each other (`#99 " "`_)." msgstr "" -#: ../../changelog.rst:81 +#: ../../changelog.rst:105 msgid "" "Monitor requirements with requires.io (`#101 " "`_)." msgstr "" -#: ../../changelog.rst:82 +#: ../../changelog.rst:106 msgid "" "Terminology (`#41 `_)." msgstr "" -#: ../../changelog.rst:83 +#: ../../changelog.rst:107 msgid "" "Obsolete signals in dsmr_consumption (`#63 " "`_)." msgstr "" -#: ../../changelog.rst:84 +#: ../../changelog.rst:108 msgid "" "Individual app testing coverage (`#64 `_)." msgstr "" -#: ../../changelog.rst:85 +#: ../../changelog.rst:109 msgid "" "Support for extra devices on other M-bus (0-n:24.1) (`#92 " "`_)." msgstr "" -#: ../../changelog.rst:86 +#: ../../changelog.rst:110 msgid "" "Separate post-deployment commands (`#102 " "`_)." msgstr "" -#: ../../changelog.rst:88 +#: ../../changelog.rst:112 msgid "" "Show exceptions in production (webinterface) (`#87 " "`_)." msgstr "" -#: ../../changelog.rst:89 +#: ../../changelog.rst:113 msgid "" "Keep Supervisor processes running (`#79 " "`_)." msgstr "" -#: ../../changelog.rst:90 +#: ../../changelog.rst:114 msgid "" "Hourly stats of 22:00:00+00 every day lack gas (`#78 " "`_)." msgstr "" -#: ../../changelog.rst:91 +#: ../../changelog.rst:115 msgid "" "Test Travis-CI with MySQL + MariaDB + PostgreSQL (`#54 " "`_)." msgstr "" -#: ../../changelog.rst:92 +#: ../../changelog.rst:116 msgid "" "PostgreSQL tests + nosetests + coverage failure: unrecognized " "configuration parameter \"foreign_key_checks\" (`#62 " "`_)." msgstr "" -#: ../../changelog.rst:93 +#: ../../changelog.rst:117 msgid "" "Performance check (`#83 `_)." msgstr "" -#: ../../changelog.rst:94 +#: ../../changelog.rst:118 msgid "" "Allow month & year archive (`#66 `_)." msgstr "" -#: ../../changelog.rst:95 +#: ../../changelog.rst:119 msgid "" "Graphs keep increasing height on tablet (`#89 " "`_)." msgstr "" -#: ../../changelog.rst:97 +#: ../../changelog.rst:121 msgid "" "Delete StatsSettings(.track) settings model (`#71 " "`_)." msgstr "" -#: ../../changelog.rst:98 +#: ../../changelog.rst:122 msgid "" "Drop deprecated commands (`#22 `_)." msgstr "" -#: ../../changelog.rst:99 +#: ../../changelog.rst:123 msgid "" "Datalogger doesn't work properly with DSMR 4.2 (KAIFA-METER) (`#73 " "`_)." msgstr "" -#: ../../changelog.rst:100 +#: ../../changelog.rst:124 msgid "" "Dashboard month statistics costs does not add up (`#75 " "`_)." msgstr "" -#: ../../changelog.rst:101 +#: ../../changelog.rst:125 msgid "" "Log unhandled exceptions and errors (`#65 " "`_)." msgstr "" -#: ../../changelog.rst:102 +#: ../../changelog.rst:126 msgid "" "Datalogger crashes with IntegrityError because 'timestamp' is null (`#74 " "`_)." msgstr "" -#: ../../changelog.rst:103 +#: ../../changelog.rst:127 msgid "" "Trends are always shown in UTC (`#76 `_)." msgstr "" -#: ../../changelog.rst:104 +#: ../../changelog.rst:128 msgid "" "Squash migrations (`#31 `_)." msgstr "" -#: ../../changelog.rst:105 +#: ../../changelog.rst:129 msgid "" "Display 'electricity returned' graph in dashboard (`#81 " "`_)." msgstr "" -#: ../../changelog.rst:106 +#: ../../changelog.rst:130 msgid "" "Optional gas (and electricity returned) capabilities tracking (`#70 " "`_)." msgstr "" -#: ../../changelog.rst:107 +#: ../../changelog.rst:131 msgid "" "Add 'electricity returned' to trends page (`#84 " "`_)." msgstr "" -#: ../../changelog.rst:109 +#: ../../changelog.rst:133 msgid "" "Archive: View past days details (`#61 `_)." msgstr "" -#: ../../changelog.rst:110 +#: ../../changelog.rst:134 msgid "" "Dashboard: Consumption total for current month (`#60 " "`_)." msgstr "" -#: ../../changelog.rst:111 +#: ../../changelog.rst:135 msgid "" "Check whether gas readings are optional (`#34 " "`_)." msgstr "" -#: ../../changelog.rst:112 +#: ../../changelog.rst:136 msgid "" "Django security releases issued: 1.8.10 (`#68 " "`_)." msgstr "" -#: ../../changelog.rst:113 +#: ../../changelog.rst:137 msgid "" "Notes display in archive (`#69 `_)." msgstr "" -#: ../../changelog.rst:115 +#: ../../changelog.rst:139 msgid "" "Status page/alerts when features are disabled/unavailable (`#45 " "`_)." msgstr "" -#: ../../changelog.rst:116 +#: ../../changelog.rst:140 msgid "" "Integrate Travis CI (`#48 `_)." msgstr "" -#: ../../changelog.rst:117 +#: ../../changelog.rst:141 msgid "" "Testing coverage (`#38 `_)." msgstr "" -#: ../../changelog.rst:118 +#: ../../changelog.rst:142 msgid "" "Implement automatic backups & Dropbox cloud storage (`#44 " "`_)." msgstr "" -#: ../../changelog.rst:119 +#: ../../changelog.rst:143 msgid "" "Link code coverage service to repository (`#56 " "`_)." msgstr "" -#: ../../changelog.rst:120 +#: ../../changelog.rst:144 msgid "" "Explore timezone.localtime() as replacement for datetime.astimezone() " "(`#50 `_)." msgstr "" -#: ../../changelog.rst:121 +#: ../../changelog.rst:145 msgid "" "Align GasConsumption.read_at to represent the start of hour (`#40 " "`_)." msgstr "" -#: ../../changelog.rst:123 +#: ../../changelog.rst:147 msgid "" "Cleanup unused static files (`#47 `_)." msgstr "" -#: ../../changelog.rst:124 +#: ../../changelog.rst:148 msgid "" "Investigated mysql_tzinfo_to_sql — Load the Time Zone Tables (`#35 " "`_)." msgstr "" -#: ../../changelog.rst:125 +#: ../../changelog.rst:149 msgid "" "Make additional DSMR data optional (`#46 " "`_)." msgstr "" -#: ../../changelog.rst:126 +#: ../../changelog.rst:150 msgid "" "Localize graph x-axis (`#42 `_)." msgstr "" -#: ../../changelog.rst:127 +#: ../../changelog.rst:151 msgid "" "Added graph formatting string to gettext file (`#42 " "`_)." msgstr "" -#: ../../changelog.rst:128 +#: ../../changelog.rst:152 msgid "" "Different colors for peak & off-peak electricity (`#52 " "`_)." msgstr "" -#: ../../changelog.rst:129 +#: ../../changelog.rst:153 msgid "" "Admin: Note widget (`#51 `_)." msgstr "" -#: ../../changelog.rst:130 +#: ../../changelog.rst:154 msgid "" "Allow GUI to run without data (`#26 `_)." msgstr "" -#: ../../changelog.rst:132 +#: ../../changelog.rst:156 msgid "" "Moved project to GitHub (`#28 `_)." msgstr "" -#: ../../changelog.rst:133 +#: ../../changelog.rst:157 msgid "Added stdout to dsmr_backend to reflect progress." msgstr "" -#: ../../changelog.rst:134 +#: ../../changelog.rst:158 msgid "" "Restore note usage in GUI (`#39 `_)." msgstr "" -#: ../../changelog.rst:136 +#: ../../changelog.rst:160 msgid "" "Store daily, weekly, monthly and yearly statistics (`#3 " "`_)." msgstr "" -#: ../../changelog.rst:137 +#: ../../changelog.rst:161 msgid "" "Improved Recent History page performance a bit. (as result of `#3 " "`_)" msgstr "" -#: ../../changelog.rst:138 +#: ../../changelog.rst:162 msgid "" "Updates ChartJS library tot 1.1, disposing django-chartjs plugin. Labels " "finally work! (as result of `#3 `_)" msgstr "" -#: ../../changelog.rst:139 +#: ../../changelog.rst:163 msgid "" "Added trends page. (as result of `#3 `_)" msgstr "" -#: ../../changelog.rst:141 +#: ../../changelog.rst:165 msgid "" "Recent history setting: set range (`#29 " "`_)." msgstr "" -#: ../../changelog.rst:142 +#: ../../changelog.rst:166 msgid "" "Mock required for test: dsmr_weather.test_weather_tracking (`#32 " "`_)." msgstr "" -#: ../../changelog.rst:144 +#: ../../changelog.rst:168 msgid "" "Massive refactoring: Separating apps & using signals (`#19 " "`_)." msgstr "" -#: ../../changelog.rst:145 +#: ../../changelog.rst:169 msgid "" "README update: Exit character for cu (`#27 " "`_, by Jeroen " "Peters)." msgstr "" -#: ../../changelog.rst:146 +#: ../../changelog.rst:170 msgid "Fixed untranslated strings in admin interface." msgstr "" -#: ../../changelog.rst:147 +#: ../../changelog.rst:171 msgid "Upgraded Django to 1.8.9." msgstr "" @@ -900,3 +984,6 @@ msgstr "" #~ msgid "v1.2.0 - 2016-xx-xx" #~ msgstr "" +#~ msgid "v1.3.0 - 2016-??-??" +#~ msgstr "" + diff --git a/docs/locale/nl/LC_MESSAGES/contributing.mo b/docs/locale/nl/LC_MESSAGES/contributing.mo index 109c95345..834f5ea16 100644 Binary files a/docs/locale/nl/LC_MESSAGES/contributing.mo and b/docs/locale/nl/LC_MESSAGES/contributing.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/contributing.po b/docs/locale/nl/LC_MESSAGES/contributing.po index e6a175308..10432df70 100644 --- a/docs/locale/nl/LC_MESSAGES/contributing.po +++ b/docs/locale/nl/LC_MESSAGES/contributing.po @@ -6,16 +6,16 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-30 22:43+0200\n" -"PO-Revision-Date: 2016-04-07 20:22+0100\n" +"POT-Creation-Date: 2016-11-24 21:41+0100\n" +"PO-Revision-Date: 2016-11-24 21:41+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.2.0\n" -"X-Generator: Poedit 1.5.4\n" +"Generated-By: Babel 2.3.4\n" "Language: nl\n" +"X-Generator: Poedit 1.8.7.1\n" #: ../../contributing.rst:2 msgid "Contributing" @@ -23,63 +23,60 @@ msgstr "Bijdrage leveren" #: ../../contributing.rst:3 msgid "Would you like to contribute or help this project in any way?" +msgstr "Wil je bijdragen aan dit project? Kijk dan hieronder hoe je kunt helpen." + +#: ../../contributing.rst:7 +msgid "Feedback" +msgstr "Feedback" + +#: ../../contributing.rst:8 +msgid "" +"All feedback and any input is, as always, very much appreciated! `Please " +"create an issue on Github `_." msgstr "" -"Wil je bijdragen aan dit project? Kijk dan hieronder hoe je kunt helpen." +"Feedback wordt, in alle soorten en maten, altijd erg gewaardeerd! Je kunt je " +"input delen door `op Github een ticket aan te maken met je vraag of opmerking " +"`_." -#: ../../contributing.rst:6 +#: ../../contributing.rst:10 +msgid "" +"It doesn't matter whether you run into problems getting started in this guide " +"or just want to get in touch, just fire away!" +msgstr "" +"Het maakt niet uit of je ergens tegenaan loopt of gewoon in contact wil komen, " +"vraag maar raak!" + +#: ../../contributing.rst:14 msgid "P1 telegram snapshot" msgstr "P1 telegram voorbeeld" -#: ../../contributing.rst:7 +#: ../../contributing.rst:15 msgid "" "Please start by `creating an issue on Github `_ with a snapshot of a DSMR " -"telegram. It will help me in improving support for multiple meter vendors " -"and home situations." +"dennissiemensma/dsmr-reader/issues/new>`_ with a snapshot of a DSMR telegram. " +"It will help me in improving support for multiple meter vendors and home " +"situations." msgstr "" -"Het helpt enorm wanneer je `op Github een ticket aanmaakt `_ met daarin een voorbeeld van " -"de DSMR-gegevens die je meter terugstuurt. Dit helpt mij bij het verbeteren " -"van de datalogger en om ondersteuning te bieden voor diverse meter-" -"leveranciers." +"Het helpt enorm wanneer je `op Github een ticket aanmaakt `_ met daarin een voorbeeld van de DSMR-" +"gegevens die je meter terugstuurt. Dit helpt mij bij het verbeteren van de " +"datalogger en om ondersteuning te bieden voor diverse meter-leveranciers." -#: ../../contributing.rst:9 +#: ../../contributing.rst:17 msgid "" "You can find the telegram by executing ``sudo supervisorctl tail -n 100 " "dsmr_datalogger`` on your DSMR-reader system." msgstr "" -"Het DSMR telegram kun je terugvinden door het volgende commando uit te " -"voeren als root-gebruiker: ``sudo supervisorctl tail -n 100 " -"dsmr_datalogger``." +"Het DSMR telegram kun je terugvinden door het volgende commando uit te voeren " +"als root-gebruiker: ``sudo supervisorctl tail -n 100 dsmr_datalogger``." -#: ../../contributing.rst:11 +#: ../../contributing.rst:19 msgid "" -"*You should omit your unique meter identification*, for privacy reasons, " -"which are the lines starting with ``0-0:96.1.1`` or ``0-1:96.1.0``, followed " -"by the meter ID, represented as a long string of many digits." +"*You should omit your unique meter identification*, for privacy reasons, which " +"are the lines starting with ``0-0:96.1.1`` or ``0-1:96.1.0``, followed by the " +"meter ID, represented as a long string of many digits." msgstr "" "*Laat, indien mogelijk, je unieke meter-identificatienummer weg* wegens " "privacy redenen. Dit is een lange nummerreeks in regels die beginnen met " "``0-0:96.1.1`` of ``0-1:96.1.0``." - -#: ../../contributing.rst:14 -msgid "Feedback" -msgstr "Feedback" - -#: ../../contributing.rst:15 -msgid "" -"Also all feedback and any input is, as always, very much appreciated! " -"`Please create an issue on Github `_." -msgstr "" -"Daarnaast wordt feedback, in alle soorten en maten, altijd erg gewaardeerd! " -"Je kunt je input delen door `op Github een ticket aan te maken met je vraag " -"of opmerking `_." - -#: ../../contributing.rst:17 -msgid "" -"It doesn't matter whether you run into problems getting started in this " -"guide or just want to get in touch, just fire away!" -msgstr "" -"Het maakt niet uit of je ergens tegenaan loopt of gewoon in contact wil " -"komen, vraag maar raak!" diff --git a/docs/locale/nl/LC_MESSAGES/credits.mo b/docs/locale/nl/LC_MESSAGES/credits.mo index a4f8821a8..01bd287d4 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 0e7d065b5..65b6f68ae 100644 --- a/docs/locale/nl/LC_MESSAGES/credits.po +++ b/docs/locale/nl/LC_MESSAGES/credits.po @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-07-12 22:40+0200\n" -"PO-Revision-Date: 2016-04-07 20:22+0100\n" +"POT-Creation-Date: 2016-11-24 21:10+0100\n" +"PO-Revision-Date: 2016-11-24 21:09+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.2.0\n" +"Generated-By: Babel 2.3.4\n" #: ../../credits.rst:2 msgid "Credits" @@ -26,9 +26,9 @@ msgid "" "Software used and contributers are listed below. Please note and respect " "their licences as well, if any apply." msgstr "" -"Alle gebruikte software en iedereen die bijgedragen heeft staat " -"hieronder. De externe software kan tevens onder licentie staan, zie " -"daarvoor de software zelf." +"Alle gebruikte software, en iedereen die heeft bijgedragen, staat " +"hieronder. De externe software kan tevens onder eigen licentie staan, zie" +" daarvoor de software zelf." #: ../../credits.rst:6 msgid "Software" @@ -55,7 +55,6 @@ msgid "`Django Solo `_" msgstr "`Django Solo `_" #: ../../credits.rst:16 -#, fuzzy msgid "`Django Colorfield `_" msgstr "`Django Solo `_" @@ -116,18 +115,14 @@ msgid "" "`Full Page Screen Capture `_" msgstr "" +"`Full Page Screen Capture `_" -#: ../../credits.rst:44 -msgid "" -"`Twitter Bootstrap Wizard `_" -msgstr "" - -#: ../../credits.rst:48 +#: ../../credits.rst:46 msgid "Testers & contributors" msgstr "Testers & mensen met bijdrage" -#: ../../credits.rst:50 +#: ../../credits.rst:48 msgid "" "`Jeroen Peters `_ (`Github " "profile `_)" @@ -135,7 +130,7 @@ msgstr "" "`Jeroen Peters `_ (`Github " "profile `_)" -#: ../../credits.rst:51 +#: ../../credits.rst:49 msgid "" "`Daniel ter Horst `_ (`Github" " profile `_)" @@ -143,8 +138,7 @@ msgstr "" "`Daniel ter Horst `_ (`Github" " profile `_)" -#: ../../credits.rst:52 -#, fuzzy +#: ../../credits.rst:50 msgid "" "`Sander de Leeuw `_" " (`Github profile `_)" @@ -152,32 +146,36 @@ msgstr "" "`Jeroen Peters `_ (`Github " "profile `_)" -#: ../../credits.rst:53 -#, fuzzy +#: ../../credits.rst:51 msgid "\"WatskeBart\" (`Github profile `_)" msgstr "`Github `_" -#: ../../credits.rst:54 +#: ../../credits.rst:52 msgid "`Gert Schaafsma `_" msgstr "`Gert Schaafsma `_" -#: ../../credits.rst:55 +#: ../../credits.rst:53 msgid "`Bert-Jan Vos `_" msgstr "`Bert-Jan Vos `_" -#: ../../credits.rst:59 +#: ../../credits.rst:54 +#, fuzzy +msgid "Sevickson (`Github profile `_)" +msgstr "`Github `_" + +#: ../../credits.rst:58 msgid "DSMR help" msgstr "DSMR hulp" -#: ../../credits.rst:61 +#: ../../credits.rst:60 msgid "Dutch Smart Meter reading specifications, data cables, examples and hints:" msgstr "Dutch Smart Meter reading specifications, data cables, examples and hints:" -#: ../../credits.rst:63 +#: ../../credits.rst:62 msgid "`Gé Janssen `_" msgstr "`Gé Janssen `_" -#: ../../credits.rst:65 +#: ../../credits.rst:64 msgid "" "`Joost van der Linde " "`_" @@ -185,7 +183,14 @@ msgstr "" "`Joost van der Linde " "`_" -#: ../../credits.rst:67 +#: ../../credits.rst:66 msgid "`SOS Solutions `_" msgstr "`SOS Solutions `_" +#~ msgid "" +#~ "`Twitter Bootstrap Wizard `_" +#~ msgstr "" +#~ "`Twitter Bootstrap Wizard `_" + diff --git a/docs/locale/nl/LC_MESSAGES/faq.mo b/docs/locale/nl/LC_MESSAGES/faq.mo index 6fe6cd75a..f0001d8a2 100644 Binary files a/docs/locale/nl/LC_MESSAGES/faq.mo and b/docs/locale/nl/LC_MESSAGES/faq.mo differ diff --git a/docs/locale/nl/LC_MESSAGES/faq.po b/docs/locale/nl/LC_MESSAGES/faq.po index 37e673532..dcaa82bcd 100644 --- a/docs/locale/nl/LC_MESSAGES/faq.po +++ b/docs/locale/nl/LC_MESSAGES/faq.po @@ -8,29 +8,59 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-07-12 23:37+0200\n" -"PO-Revision-Date: 2016-07-12 23:49+0100\n" -"Last-Translator: Dennis Siemensma \n" -"Language-Team: LANGUAGE \n" +"POT-Creation-Date: 2016-11-24 21:10+0100\n" +"PO-Revision-Date: 2016-11-26 18:02+0100\n" +"Last-Translator: Jeroen Peters \n" +"Language-Team: Dennis Siemensma \n" +"Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.2.0\n" +"Generated-By: Babel 2.3.4\n" "X-Generator: Poedit 1.5.4\n" #: ../../faq.rst:2 msgid "Frequently Asked Questions (FAQ)" msgstr "Veelgestelde vragen (FAQ)" -#: ../../faq.rst:5 +#: ../../faq.rst:6 +msgid "I only pay for a single electricity tariff but I see two!" +msgstr "Ik betaal voor een enkel tarief maar ik zie er twee!" + +#: ../../faq.rst:7 +msgid "" +"DSMR (and your energy supplier) always read both high and low tariff from " +"your meter. It's possible however that you are only paying for a single " +"tariff. In that case your energy supplier will simply merge both high and " +"low tariffs to make it look like you have a single one." +msgstr "" +"DSMR (en je energieleverancier) lezen altijd zowel hoog als laag tarief van " +"je meter uit. Het is desondanks mogelijk dat je betaalt voor slechts één " +"tarief. In dat geval voegt jouw energieleverancier het hoge en lage tarief " +"samen zodat het lijkt alsof je een enkel tarief hebt." + +#: ../../faq.rst:11 +msgid "" +"This application displays separate tariffs by default, but supports merging " +"them to a single one as well. Just make sure that you apply the **same price " +"to both electricity 1 and 2** and enable the option '**Merge electricity " +"tariffs**' in the frontend configuration." +msgstr "" +"Deze applicatie weergeeft standaard gescheiden tarieven, maar heeft wel " +"ondersteuning om ze samen te voegen (net zoals je energieleverancier doet). " +"Zorg er wel voor dat je **dezelfde prijs invult bij hoog en laag tarief voor " +"elektriciteit** en dat je de optie \"Voeg tarieven samen\" aanzet in de " +"interfaceconfiguratie." + +#: ../../faq.rst:16 msgid "Dropbox: Automated backup sync" msgstr "Dropbox: Automatisch backups uploaden" -#: ../../faq.rst:6 +#: ../../faq.rst:17 msgid "*How can I link my Dropbox account for backups?*" -msgstr "*How kan ik mijn Dropbox-account koppelen voor de backups?*" +msgstr "*Hoe kan ik mijn Dropbox-account koppelen voor de backups?*" -#: ../../faq.rst:8 +#: ../../faq.rst:19 msgid "" "Make sure you have a Dropbox-account or sign up for one. Now go to `Dropbox " "Apps `_ and click **\"Create app" @@ -40,7 +70,7 @@ msgstr "" "Apps `_ en klik op **\"Create app" "\"** rechtsbovenin." -#: ../../faq.rst:15 +#: ../../faq.rst:26 msgid "" "Choose the following options: (1) **Dropbox API** and (2) **App folder**. " "Then enter a name for your app (3), this will also be used as directory name " @@ -50,7 +80,7 @@ msgstr "" "vervolgens een naam voor je app in (3), deze wordt ook gebruikt als naam van " "de submap binnen je Apps-folder in Dropbox." -#: ../../faq.rst:22 +#: ../../faq.rst:33 msgid "" "The app should be created in developer-mode. You can generate an access " "token for yourself by clicking the **\"Generate\"** button somewhere below." @@ -59,7 +89,7 @@ msgstr "" "toegangstoken genereren door op de knop **\"Generate\"** te klikken, die " "verderop onderaan de pagina staat." -#: ../../faq.rst:28 +#: ../../faq.rst:39 msgid "" "Copy the generated access token to the DSMR-reader settings for the Dropbox-" "configuration. The DSMR-reader application should sync any backups created " @@ -69,15 +99,15 @@ msgstr "" "Dropbox-configuratie. DSMR-reader zou vrij vlot gemaakte backups moeten " "uploaden naar je Dropbox-account." -#: ../../faq.rst:32 +#: ../../faq.rst:43 msgid "Mindergas.nl: Automated gas meter position export" msgstr "Mindergas.nl: Automatisch gasmeterstanden exporteren" -#: ../../faq.rst:33 +#: ../../faq.rst:44 msgid "*How can I link my mindergas.nl account?*" -msgstr "*How kan ik mijn mindergas.nl-account koppelen?*" +msgstr "*Hoe kan ik mijn mindergas.nl-account koppelen?*" -#: ../../faq.rst:35 +#: ../../faq.rst:46 msgid "" "Make sure you have a Mindergas.nl-account or `signup for one `_. Now go to \"`Meterstand API `_\" en klik op de knop onder het " "kopje **\"Authenticatietoken\"**." -#: ../../faq.rst:42 +#: ../../faq.rst:53 msgid "" "Copy the authentication token generated and paste in into the DSMR-reader " "settings for the Mindergas.nl-configuration. Obviously the export only works " @@ -101,7 +131,7 @@ msgstr "" "wanneer er gas gemeten wordt, en wanneer je de optie 'Exporteer gegevens " "naar MinderGas' aangevinkt hebt in dezelfde configuratie." -#: ../../faq.rst:45 +#: ../../faq.rst:56 msgid "" "Please note that due to policies of mindergas.nl it's not allowed to " "retroactively upload meter positions using the API. Therefor this is not " @@ -113,11 +143,113 @@ msgstr "" "ondersteunt dat om die reden niet. je kunt oude meterstanden echter wel via " "hun website handmatig invoeren, indien gewenst." -#: ../../faq.rst:50 -msgid "Recalculate prices" -msgstr "Prijzen opnieuw berekenen" +#: ../../faq.rst:61 +msgid "Usage notification: Daily usage statistics on your smartphone" +msgstr "" +"Verbruiksnotificaties: Dagelijkse verbruiksstatistieken op je smartphone" + +#: ../../faq.rst:62 +msgid "*Which services for sending notifications are supported?*" +msgstr "*Welke services voor het sturen van notificaties worden ondersteund?*" + +#: ../../faq.rst:64 +msgid "" +"Currently, two mobile platforms are supported: Android and iOS. The " +"supported app for Android is `NotifyMyAndroid `_. The supported app for iOS is `Prowl `_." +msgstr "" +"Op dit moment worden twee platforms ondersteund: Android en iOS. De " +"ondersteunde app voor Android is `NotifyMyAndroid `_. De ondersteunde app voor iOS is `Prowl `_." + +#: ../../faq.rst:69 +msgid "*How do I setup usage notifications?*" +msgstr "*Hoe stel ik verbruiksnotificaties in?*" + +#: ../../faq.rst:71 +msgid "" +"Make sure you either have NotifyMyAndroid or Prowl installed on your " +"smartphone. If you don't, visit your platforms app store to download the app " +"and sign up for an account. Then, make sure to get your API key from the " +"notificationservice that you prefer. For instruction on obtaining the API " +"key, please read below." +msgstr "" +"Zorg ervoor dat je NotifyMyAndroid of Prowl hebt geïnstalleerd op je " +"smartphone. Als dat niet het geval is, bezoek dan de app store van je " +"platform, download de gewenste app en maak daarvoor een account. Daarna haal " +"je de API key voor de notificatieservice die je wilt gebruiken op. Om te " +"lezen hoe dat werkt, lees dan de instructies bij de volgende kopjes." + +#: ../../faq.rst:73 +msgid "" +"In the DSMR-reader settings for the Usagenotifications, tick the Send " +"Notifications checkbox and select the notification service you want to use. " +"Then copy the API key from the notification service and paste in into the " +"the textbox for the API key. When you save these settings, your first " +"notification should be sent after midnight. Don't worry, the notification " +"will be sent with low priority and will not wake you up." +msgstr "" +"In de DSMR-reader instellingen voor de Verbruiksnotificaties zet je een " +"vinkje bij Stuur Notificaties en selecteer daaronder welke " +"notificatieservice je wilt gebruiken. Kopieeër daarna de API key van de " +"notificatieservice en plak deze in het tekstveld voor de API key. Als je " +"deze instellingen opslaat zul je na middernacht je eerste notificatie " +"ontvangen. Geen nood, de notificatie wordt altijd verstuurd met lage " +"prioriteit, deze maakt je dus niet wakker." + +#: ../../faq.rst:76 +msgid "*How do I obtain my API key for NotifyMyAndroid?*" +msgstr "*Hoe haal ik de API key op voor NotifyMyAndroid?*" + +#: ../../faq.rst:78 +msgid "" +"After you have downloaded NotifyMyAndroid and signed up for an account you " +"should be able to `login to your account `_. Now go to \"`My Account `_\", you should see an overview of your current API keys if you " +"have any. To create an API key for the DSMR-reader, please click **" +"\"Generate New Key\"**." +msgstr "" +"Nadat je NotifyMyAndroid hebt gedownload en een account hebt aangemaakt zou " +"je moeten kunnen `inloggen op je account `_. Ga nu naar \"`My Account `_\" om een overzicht van je account de API keys te krijgen als " +"je deze al hebt. Om een API key voor de DSMR-reader te genereren klik je op " +"**\"Generate New Key\"**." + +#: ../../faq.rst:85 ../../faq.rst:101 +msgid "" +"When a new key is generated, you will see it immediatly. Your key is listed " +"like in the screenshot below (the red box marks your API key)." +msgstr "" +"Als er een nieuwe key is gegenereerd, zie je deze direct. De key is te " +"vinden zoals in onderstaande screenshot (er staat een rood kader om de key)." + +#: ../../faq.rst:92 +msgid "*How do I obtain my API key for Prowl?*" +msgstr "*Hoe haal ik de API key op voor Prowl?*" -#: ../../faq.rst:51 +#: ../../faq.rst:94 +msgid "" +"After you have downloaded Prowl and signed up for an account you should be " +"able to `login to your account `_. Now " +"go to \"`API Keys `_\", you " +"should see an overview of your current API keys if you have any. To create " +"an API key for the DSMR-reader, input a name and click **\"Generate Key\"**." +msgstr "" +"Nadat je Prowl hebt gedownload en een account hebt aangemaakt zou je moeten " +"kunnen `inloggen op je account `_. Ga nu " +"naar \"`API Keys `_\" om een " +"overzicht van je API keys te krijgen als je deze al hebt. Om een API key " +"voor de DSMR-reader te genereren geef je een naam op en klik je op **" +"\"Generate Key\"**." + +#: ../../faq.rst:109 +msgid "Recalculate prices retroactively" +msgstr "Prijzen opnieuw berekenen met terugwerkende kracht" + +#: ../../faq.rst:110 msgid "" "*I've adjusted my energy prices but there are no changes! How can I " "regenerate them with my new prices?*" @@ -125,15 +257,19 @@ msgstr "" "*Ik heb zojuist mijn energieprijzen aangepast, maar ik zie geen verschil! " "Hoe kan ik de nieuwe prijzen doorvoeren?*" -#: ../../faq.rst:53 -msgid "You can flush your statistics by executing:" -msgstr "Je kunt je statistieken verwijderen door het volgende uit te voeren:" +#: ../../faq.rst:112 +msgid "" +"Statistics for each day are generated once, the day after. However, you can " +"flush your statistics by executing:" +msgstr "" +"Dagstatistieken worden dagelijks eenmalig gegenereerd. Het is echter wel " +"mogelijk om deze te resetten door het volgende te doen:" -#: ../../faq.rst:55 +#: ../../faq.rst:114 msgid "``./manage.py dsmr_stats_clear_statistics --ack-to-delete-my-data``" msgstr "``./manage.py dsmr_stats_clear_statistics --ack-to-delete-my-data``" -#: ../../faq.rst:57 +#: ../../faq.rst:116 msgid "" "The application will delete all statistics and (slowly) regenerate them in " "the background. Just make sure the source data is still there." @@ -141,18 +277,22 @@ msgstr "" "De applicatie verwijdert alle statistics en genereert ze (langzaam) weer op " "de achtergrond. Zorg er wel voor dat alle brongegevens intact zijn." -#: ../../faq.rst:61 +#: ../../faq.rst:120 msgid "Feature/bug report" msgstr "Verzoek/fout melden" -#: ../../faq.rst:62 +#: ../../faq.rst:121 msgid "*How can I propose a feature or report a bug I've found?*" msgstr "*Hoe kan ik een verzoek indienen of een fout melden?*" -#: ../../faq.rst:64 +#: ../../faq.rst:123 msgid "" "`Just create a ticket at Github `_" msgstr "" "`Maak een ticket aan op Github `_" + +#~ msgid "You can flush your statistics by executing:" +#~ msgstr "" +#~ "Je kunt je statistieken verwijderen door het volgende uit te voeren:" diff --git a/docs/locale/nl/LC_MESSAGES/installation.po b/docs/locale/nl/LC_MESSAGES/installation.po index 2d4d929b1..8f85d6832 100644 --- a/docs/locale/nl/LC_MESSAGES/installation.po +++ b/docs/locale/nl/LC_MESSAGES/installation.po @@ -8,16 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: DSMR Reader 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-04-06 20:19+0200\n" +"POT-Creation-Date: 2016-11-18 20:07+0100\n" "PO-Revision-Date: 2016-04-07 20:07+0100\n" "Last-Translator: Dennis Siemensma \n" "Language-Team: Dennis Siemensma \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.2.0\n" -"Language: nl\n" -"X-Generator: Poedit 1.5.4\n" +"Generated-By: Babel 2.3.4\n" #: ../../installation.rst:2 msgid "Installation" @@ -25,13 +23,13 @@ msgstr "Installatie" #: ../../installation.rst:3 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." +"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." +"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:7 msgid "Dependencies & requirements" @@ -53,14 +51,15 @@ msgstr "**Raspbian OS**" #: ../../installation.rst:14 msgid "" -"Recommended and tested with, but any OS satisfying the requirements should " -"do fine." +"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." +"Aanbevolen en mee getest, al zou elk OS die dezelfde vereisten " +"ondersteunt prima moeten zijn." #: ../../installation.rst:16 -msgid "**Python 3.3 / 3.4**" +#, fuzzy +msgid "**Python 3.3 / 3.4 / 3.5**" msgstr "**Python 3.3 / 3.4**" #: ../../installation.rst:17 @@ -70,24 +69,24 @@ msgstr "**PostgreSQL 9+ of MySQL / MariaDB 5.5+**" #: ../../installation.rst:19 msgid "I highly recommend *PostgreSQL* due to builtin support for timezones." msgstr "" -"Ik raad **PostgreSQL** sterk aan wegens de goede ingebouwde ondersteuning " -"voor tijdzones." +"Ik raad **PostgreSQL** sterk aan wegens de goede ingebouwde ondersteuning" +" voor tijdzones." #: ../../installation.rst:21 msgid "" -"**Smart Meter** with support for **at least DSMR 4.0/4.2** and **P1 telegram " -"port**" +"**Smart Meter** with support for **at least DSMR 4.0/4.2** and **P1 " +"telegram port**" msgstr "" -"**Slimme meter** met ondersteuning voor **ten minste DSMR 4.0/4.2** en een " -"**P1 telegram poort**" +"**Slimme meter** met ondersteuning voor **ten minste DSMR 4.0/4.2** en " +"een **P1 telegram poort**" #: ../../installation.rst:23 msgid "" -"Tested so far with Landis+Gyr E350, Kaifa. Telegram port looks like an RJ11 " -"(phone) socket." +"Tested so far with Landis+Gyr E350, Kaifa. Telegram port looks like an " +"RJ11 (phone) socket." msgstr "" -"Tot nu toe getest met Landis+Gyr E350, Kaifa. Telegram poort (P1) ziet er " -"uit als een RJ11 (telefoon) aansluiting." +"Tot nu toe getest met Landis+Gyr E350, Kaifa. Telegram poort (P1) ziet er" +" uit als een RJ11 (telefoon) aansluiting." #: ../../installation.rst:25 msgid "" @@ -99,18 +98,18 @@ msgstr "" #: ../../installation.rst:27 msgid "" -"More disk space is required for storing all reader data captured (optional). " -"I generally advise to use a 8+ GB SD card." +"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." +"Meer schijfruimte is nodig voor het opslaan van alle metingen " +"(optioneel). Over het algemeen adviseer ik minimaal een 8 GB SD-kaart." #: ../../installation.rst:28 #, python-format msgid "" -"The readings will take 90+ % of the disk space. I plan however to add some " -"kind of retention to it later, keeping the data (of many years) far below " -"the 500 MB." +"The readings will take 90+ % of the disk space. I plan however to add " +"some kind of retention to it later, keeping the data (of many years) far " +"below the 500 MB." msgstr "" "De ruwe metingen nemen ongeveer 90+ % van de schijfruimte in gebruik. Ik " "bouw later overigens nog wel een optie in voor retentie, waardoor je de " @@ -127,7 +126,8 @@ msgstr "Je kunt deze online bestellen voor ongeveer 15 a 20 Euro." #: ../../installation.rst:34 msgid "**Basic Linux knowledge for deployment, debugging and troubleshooting**" msgstr "" -"**Basiskennis Linux voor het uitrollen en mogelijk debuggen van problemen**" +"**Basiskennis Linux voor het uitrollen en mogelijk debuggen van " +"problemen**" #: ../../installation.rst:36 msgid "It just really helps if you know what you are doing." @@ -139,23 +139,23 @@ msgstr "1. Databaseopslag" #: ../../installation.rst:42 msgid "" -"The application stores by default all readings taken from the serial cable. " -"Depending on your needs, you can choose for either (Option A.) " +"The application stores by default all readings taken from the serial " +"cable. Depending on your needs, you can choose for either (Option A.) " "**PostgreSQL** (Option B.) **MySQL/MariaDB**." msgstr "" "De applicatie slaat alle metingen op in een database. Afhankelijk van je " -"wensen en behoeftes heb je de keuze uit: (Keuze A.) **PostgreSQL** (Keuze " -"B.) **MySQL/MariaDB**." +"wensen en behoeftes heb je de keuze uit: (Keuze A.) **PostgreSQL** (Keuze" +" B.) **MySQL/MariaDB**." #: ../../installation.rst:44 msgid "" -"*If you have no idea what to choose, I generally advise to pick PostgreSQL, " -"as it has builtin support for (local) timezone handling (required for DST " -"transitions).*" +"*If you have no idea what to choose, I generally advise to pick " +"PostgreSQL, as it has builtin support for (local) timezone handling " +"(required for DST transitions).*" msgstr "" "*Als je geen idee hebt wat je moet kiezen, dan adviseer ik doorgaans om " -"PostgreSQL te kiezen. Dit vanwege de uitstekende ingebouwde ondersteuning " -"voor tijdzones (nodig voor zomer-/wintertijd).*" +"PostgreSQL te kiezen. Dit vanwege de uitstekende ingebouwde ondersteuning" +" voor tijdzones (nodig voor zomer-/wintertijd).*" #: ../../installation.rst:47 ../../installation.rst:213 msgid "(Option A.) PostgreSQL" @@ -166,8 +166,8 @@ 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 virtuelenv later." +"Installeer PostgreSQL. Daarnaast is ``postgresql-server-dev-all`` nodig " +"voor het installeren van de virtuelenv later." #: ../../installation.rst:50 ../../installation.rst:75 msgid "Install database::" @@ -175,14 +175,14 @@ msgstr "Installeer database::" #: ../../installation.rst:54 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." +"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." +"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:56 msgid "" @@ -212,13 +212,13 @@ msgstr "(Keuze B.) MySQL/MariaDB" #: ../../installation.rst:73 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." +"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." +"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:79 msgid "Create database::" @@ -239,22 +239,22 @@ msgstr "2. Afhankelijkheden" #: ../../installation.rst:98 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::" +"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::" +"webserver, de Gunicorn applicatieserver en voor het binnenhalen van de " +"code van de applicatie vanaf Github::" #: ../../installation.rst:102 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. ::" +"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 we vrij gemakkelijk de DSMR-" -"verbinding testen naar je slimme meter toe. Erg handig om te kijken of dat " -"überhaupt al lekker werkt." +"Installeer ``cu``. Met dit programmaatje kunnen we vrij gemakkelijk de " +"DSMR-verbinding testen naar je slimme meter toe. Erg handig om te kijken " +"of dat überhaupt al lekker werkt." #: ../../installation.rst:108 msgid "3. Application user" @@ -262,25 +262,25 @@ msgstr "3. Applicatiegebruiker" #: ../../installation.rst:109 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." +"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." +"De applicatie draait standaard onder de gebruiker ``dsmr``. Hierdoor " +"heeft het geen ``root``-rechten (nodig), wat over het algemeen zeer " +"afgeraden wordt." #: ../../installation.rst:111 msgid "" -"Create user with homedir. The application code and virtualenv will reside in " -"this directory as well::" +"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::" +"Maak een aparte gebruiker aan met eigen homedir. De code voor de " +"applicatie en virtualenv zetten we later hier in::" #: ../../installation.rst:115 msgid "" -"Our user also requires dialout permissions. So allow the user to perform a " -"dialout by adding it to the ``dialout`` group::" +"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``::" @@ -291,41 +291,43 @@ msgstr "4. Webserver/Nginx (deel 1)" #: ../../installation.rst:123 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.*" +"*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.*" +"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:125 msgid "" -"Django will copy all static files to a separate directory, used by Nginx to " -"serve statics. Therefor it requires (write) access to it::" +"Django will copy all static files to a separate directory, 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 heeft Django er tevens (schrijf)toegang toe " -"nodig::" +"Django kopieert alle statische bestanden naar een aparte map, die weer " +"door Nginx gebruikt wordt. Daarom heeft Django er tevens (schrijf)toegang" +" toe nodig::" #: ../../installation.rst:131 msgid "" -"Either proceed to the next heading for a test reading or continue at step 5." +"*The reason for splitting the webserver chapter in two steps, is because " +"the application requires the directory created above to exist. And Nginx " +"requires the application to exist (cloned) before running (and to copy " +"its virtual hosts file), resulting in an dependency loop.*" msgstr "" -"Ga ofwel door naar het volgende hoofdstuk voor een testmeting of ga direct " -"door naar stap 5." +"*Het configureren van de webserver kan alleen in twee stappen, omdat de " +"applicatie de bovenstaande map nodig heeft, maar Nginx heeft weer de " +"applicatie eerst nodig. Hiermee krijg je een kip-ei verhaal.*" #: ../../installation.rst:133 msgid "" -"*The reason for splitting the webserver chapter in two steps, is because the " -"application requires the directory created above to exist. And Nginx " -"requires the application to exist (cloned) before running (and to copy its " -"virtual hosts file), resulting in an dependency loop.*" +"Either proceed to the next heading for a test reading or continue at step" +" 5." msgstr "" -"*Het configureren van de webserver kan alleen in twee stappen, omdat de " -"applicatie de bovenstaande map nodig heeft, maar Nginx heeft weer de " -"applicatie eerst nodig. Hiermee krijg je een kip-ei verhaal.*" +"Ga ofwel door naar het volgende hoofdstuk voor een testmeting of ga " +"direct door naar stap 5." #: ../../installation.rst:137 msgid "Your first reading (optional)" @@ -335,22 +337,22 @@ msgstr "Je allereerste (optionele) meting" 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." +"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." +"(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:140 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! ::" +"Log nu in als de gebruiker die we zojuist hebben aangemaakt voor de " +"eerste testmeting! ::" #: ../../installation.rst:144 msgid "Test with ``cu`` for **DSMR 4+**::" @@ -362,24 +364,24 @@ msgstr "Of test met ``cu`` voor **DSMR 2.2** (ongetest)::" #: ../../installation.rst:152 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) `_." +"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 `_." +"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:154 msgid "" -"To exit cu, type \"``q.``\", hit Enter and wait for a few seconds. It should " -"exit with the message ``Disconnected.``." +"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.``." +"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:158 msgid "5. Clone project code from Github" @@ -387,19 +389,19 @@ msgstr "5. Kloon project code vanaf Github" #: ../../installation.rst:159 msgid "" -"Now is the time to clone the code from the repository into the homedir we " -"created." +"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:161 ../../installation.rst:178 msgid "" -"Make sure you are still acting as ``dsmr`` user (if not then enter: ``sudo " -"su - dsmr``)" +"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``)" +"Zorg ervoor dat je nog steeds ingelogd bent als ``dsmr``-gebruiker (zo " +"niet, typ dan: ``sudo su - dsmr``)" #: ../../installation.rst:163 msgid "Clone the repository::" @@ -410,9 +412,9 @@ 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." +"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:171 msgid "6. Virtualenv" @@ -420,25 +422,26 @@ msgstr "6. Virtualenv" #: ../../installation.rst:173 msgid "" -"The dependencies our application uses are stored in a separate environment, " -"also called **VirtualEnv**." +"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." +"Alle (externe) afhankelijkheden worden opgeslagen in een aparte omgeving," +" ook wel **VirtualEnv** genoemd." #: ../../installation.rst:175 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 `_." +"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 `_." +"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:180 msgid "Create folder for the virtualenv(s) of this user::" @@ -447,26 +450,26 @@ msgstr "Maak map aan voor de virtualenv(s) van deze gebruiker::" #: ../../installation.rst:184 msgid "" "Create a new virtualenv, we usually use the same name for it as the " -"application or project. Note that it's important to specify **python3** as " -"the default interpreter::" +"application or project. Note that it's important to specify **python3** " +"as the default interpreter::" msgstr "" "Maak een nieuwe virtualenv aan. Het is gebruikelijk om hiervoor dezelfde " -"naam te gebruiken als die van de applicatie of het project. Let op dat je " -"voor dit project aangeeft dat **python3** de gewenste standaardversie voor " -"Python is::" +"naam te gebruiken als die van de applicatie of het project. Let op dat je" +" voor dit project aangeeft dat **python3** de gewenste standaardversie " +"voor Python is::" #: ../../installation.rst:188 msgid "" "Now *activate* the environment. It effectively directs all aliases for " -"software installed in the virtualenv to the binaries inside the virtualenv. " -"I.e. the Python binary inside ``/usr/bin/python`` won't be used when the " -"virtualenv is activated, but ``/home/dsmr/.virtualenvs/dsmrreader/bin/" -"python`` will be instead." +"software installed in the virtualenv to the binaries inside the " +"virtualenv. I.e. the Python binary inside ``/usr/bin/python`` won't be " +"used when the virtualenv is activated, but " +"``/home/dsmr/.virtualenvs/dsmrreader/bin/python`` will be instead." msgstr "" -"Tijd om de virtualenv te *activeren*. Hierdoor worden diverse aliassen voor " -"software binnen deze omgeving veranderd naar die van de virtualenv. Voor " -"bijvoorbeeld Python wordt niet meer ``/usr/bin/python`` gebruikt, maar ``/" -"home/dsmr/.virtualenvs/dsmrreader/bin/python``." +"Tijd om de virtualenv te *activeren*. Hierdoor worden diverse aliassen " +"voor software binnen deze omgeving veranderd naar die van de virtualenv. " +"Voor bijvoorbeeld Python wordt niet meer ``/usr/bin/python`` gebruikt, " +"maar ``/home/dsmr/.virtualenvs/dsmrreader/bin/python``." #: ../../installation.rst:191 msgid "Activate virtualenv & cd to project::" @@ -474,17 +477,18 @@ msgstr "Activeer virtualenv & wissel map naar project::" #: ../../installation.rst:197 msgid "" -"You might want to put the ``source ~/.virtualenvs/dsmrreader/bin/activate`` " -"command above in the user's ``~/.bashrc`` (logout and login to test)." +"You might want to put the ``source " +"~/.virtualenvs/dsmrreader/bin/activate`` command above in the user's " +"``~/.bashrc`` (logout and login to test)." msgstr "" -"Het is aan te raden om ``source ~/.virtualenvs/dsmrreader/bin/activate`` op " -"te nemen in de ``~/.bashrc`` van de gebruiker. Vervolgens kun je het testen " -"door uit- en in te loggen." +"Het is aan te raden om ``source ~/.virtualenvs/dsmrreader/bin/activate`` " +"op te nemen in de ``~/.bashrc`` van de gebruiker. Vervolgens kun je het " +"testen door uit- en in te loggen." #: ../../installation.rst:199 msgid "" -"I also advice to put the ``cd ~/dsmr-reader`` in there as well, which will " -"cd you directly inside the project folder on login." +"I also advice to put the ``cd ~/dsmr-reader`` in there as well, which " +"will cd you directly inside the project folder on login." msgstr "" "Ik raad je aan om ``cd ~/dsmr-reader`` er ook in op te nemen, zodat je " "telkens bij het inloggen als deze gebruiker in de juist map zit." @@ -495,32 +499,32 @@ msgstr "7. Applicatieconfiguratie" #: ../../installation.rst:204 msgid "" -"Earlier in this guide you had to choose for either **(A.) PostgreSQL** or " -"**(B.) MySQL/MariaDB**. Our application needs to know which backend used in " -"order to communicate with it." +"Earlier in this guide you had to choose for either **(A.) PostgreSQL** or" +" **(B.) MySQL/MariaDB**. Our application needs to know which backend used" +" in order to communicate with it." msgstr "" -"Eerder tijdens de installatie moest je kiezen voor ofwel **(A.) PostgreSQL** " -"ofwel **(B.) MySQL/MariaDB**. De applicatie moet zelf ook weten met welke " -"variant hij communiceert." +"Eerder tijdens de installatie moest je kiezen voor ofwel **(A.) " +"PostgreSQL** ofwel **(B.) MySQL/MariaDB**. De applicatie moet zelf ook " +"weten met welke variant hij communiceert." #: ../../installation.rst:206 msgid "" -"Therefor I created two default (Django-)settings files you can copy, one for " -"each backend. The application will also need the appropriate database " -"client, which is not installed by default. For this I also created two ready-" -"to-use requirements files, which will also install all other dependencies " -"required, such as the Django framework." +"Therefor I created two default (Django-)settings files you can copy, one " +"for each backend. The application will also need the appropriate database" +" client, which is not installed by default. For this I also created two " +"ready-to-use requirements files, which will also install all other " +"dependencies required, such as the Django framework." msgstr "" -"Daarom heb ik twee standaard (Django-)configuraties gemaakt die je gewoon " -"kan kopiëren. Daarnaast heeft elke database zijn eigen database-client " -"nodig. Daarvoor heb ik ook een tweetal requirements-bestanden gemaakt, waar " -"alles al in staat wat nodig is. Zoals bijvoorbeeld Django en de " +"Daarom heb ik twee standaard (Django-)configuraties gemaakt die je gewoon" +" kan kopiëren. Daarnaast heeft elke database zijn eigen database-client " +"nodig. Daarvoor heb ik ook een tweetal requirements-bestanden gemaakt, " +"waar alles al in staat wat nodig is. Zoals bijvoorbeeld Django en de " "databaseverbinding." #: ../../installation.rst:208 msgid "" -"The ``base.txt`` contains requirements which the application needs anyway, " -"no matter which backend you've choosen." +"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." @@ -531,10 +535,10 @@ msgid "" "depending on your Internet connection, RaspberryPi speed and resources " "(generally CPU) available. Nothing to worry about. :]" msgstr "" -"(!) let op: **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. :]" +"(!) let op: **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:214 msgid "Did you choose PostgreSQL? Then execute these two lines::" @@ -542,23 +546,22 @@ msgstr "Heb je gekozen voor PostgreSQL? Voer dan deze twee regels uit::" #: ../../installation.rst:222 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::" +msgstr "Of heb je gekozen voor MySQL/MariaDB? Voer dan deze twee commando's uit::" #: ../../installation.rst:229 msgid "" "Did everything install without fatal errors? If either of the database " -"clients refuses to install due to missing files/configs, make sure you've " -"installed ``postgresql-server-dev-all`` (for **PostgreSQL**) or " +"clients refuses to install due to missing files/configs, make sure you've" +" installed ``postgresql-server-dev-all`` (for **PostgreSQL**) or " "``libmysqlclient-dev`` (for **MySQL**) earlier in the process, when you " "installed the database server itself." msgstr "" -"Zonder problemen alles kunnen installeren? Mocht een van de database clients " -"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`` (voor **PostgreSQL**) of " -"``libmysqlclient-dev`` (voor **MySQL**) geïnstalleerd, tegelijkertijd met de " -"databaseserver. Die zijn namelijk hiervoor nodig." +"Zonder problemen alles kunnen installeren? Mocht een van de database " +"clients 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`` (voor **PostgreSQL**) of" +" ``libmysqlclient-dev`` (voor **MySQL**) geïnstalleerd, tegelijkertijd " +"met de databaseserver. Die zijn namelijk hiervoor nodig." #: ../../installation.rst:235 msgid "8. Bootstrapping" @@ -566,11 +569,11 @@ msgstr "8. Initialisatie" #: ../../installation.rst:236 msgid "" -"Now it's time to bootstrap the application and check whether all settings " -"are good and requirements are met." +"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." +"Tijd om te kijken of alles goed is ingesteld. We gaan de applicatie " +"proberen te initialiseren." #: ../../installation.rst:238 msgid "Execute this to initialize the database we've created earlier::" @@ -578,9 +581,9 @@ msgstr "Voer dit uit om de database te initialiseren::" #: ../../installation.rst:242 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." +"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 "" "Prepareer statische bestanden voor de webinterface. Dit kopieert alle " "statische bestanden in de map die we eerder tijdens de installatie voor " @@ -593,23 +596,23 @@ msgstr "Synchroniseer statische bestanden::" #: ../../installation.rst:249 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 (yet) used in the application anyway." +"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 (yet) 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 (standaard gebruikersnaam is " +"Maak een gebruiker aan voor binnen de applicatie. Django vraagt je om een" +" wachtwoord te kiezen. Met deze gegevens (standaard gebruikersnaam is " "'admin') kun je het beheerderspaneel binnen de applicatie gebruiken." #: ../../installation.rst:252 msgid "" -"Since 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: ``./manage.py " -"changepassword admin``" +"Since 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: " +"``./manage.py changepassword admin``" msgstr "" -"Wachtwoord ooit vergeten? Via de command line kun je je wachtwoord hiermee " -"aanpassen: ``./manage.py changepassword admin``" +"Wachtwoord ooit vergeten? Via de command line kun je je wachtwoord " +"hiermee aanpassen: ``./manage.py changepassword admin``" #: ../../installation.rst:254 msgid "Create user inside application::" @@ -621,35 +624,37 @@ msgstr "9. Webserver/Nginx (deel 2)" #: ../../installation.rst:261 msgid "" -"Go back to ``root``/``sudo-user`` to config webserver (press ``CTRL + D`` " -"once)." +"Go back to ``root``/``sudo-user`` to config webserver (press ``CTRL + D``" +" once)." msgstr "" -"Ga terug naar de ``root``/``sudo-gebruiker`` om de webserver in te stellen " -"(druk eenmalig op ``CTRL + D``)." +"Ga terug naar de ``root``/``sudo-gebruiker`` om de webserver in te " +"stellen (druk eenmalig op ``CTRL + D``)." #: ../../installation.rst:263 msgid "" -"**OPTIONAL**: Remove the default Nginx vhost (*only when you do not use it " -"yourself*)::" +"**OPTIONAL**: Remove the default Nginx vhost (*only when you do not use " +"it yourself*)::" msgstr "" -"**OPTIONEEL**: Verwijder de standaard vhost van Nginx wanneer je deze niet " -"zelf gebruikt::" +"**OPTIONEEL**: Verwijder de standaard vhost van Nginx wanneer je deze " +"niet zelf gebruikt::" #: ../../installation.rst:267 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 " +"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.::" +"hostname* (wildcard), maar dat is natuurlijk naar eigen wens aan te " +"passen.::" #: ../../installation.rst:271 msgid "" -"Let Nginx verify vhost syntax and reload Nginx when ``configtest`` passes::" +"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::" +"Laat Nginx controleren of je geen typefouten hebt gemaakt en herlaad " +"Nginx vervolgens wanneer de ``configtest`` lukt::" #: ../../installation.rst:280 msgid "10. Supervisor" @@ -657,21 +662,21 @@ msgstr "10. Supervisor" #: ../../installation.rst:281 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." +"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." +"gebruikt wordt om de applicatie en achtergrondprocessen te draaien. " +"Tevens zorgt Supervisor ervoor dat deze processen bij het (opnieuw) " +"opstarten automatisch draaien." #: ../../installation.rst:284 -msgid "" -"Each job has it's own configuration file, so make sure to copy them all::" +msgid "Each job has it's own configuration file, so make sure to copy them all::" msgstr "" -"Elk process heeft zijn eigen configuratiebestand, dus kopieer ze allemaal::" +"Elk process heeft zijn eigen configuratiebestand, dus kopieer ze " +"allemaal::" #: ../../installation.rst:288 msgid "Login to ``supervisorctl`` management console::" @@ -682,17 +687,18 @@ 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::" +"Voer de volgende commando's in (degene na de ``>``). Dit zorgt ervoor dat" +" Supervisor zijn eigen configuratie opnieuw controleert en toepast::" #: ../../installation.rst:298 msgid "" -"Three processes should be started or running. Make sure they don't end up in " -"``ERROR`` or ``BACKOFF`` state, so refresh with '``status``' a few times." +"Three processes should be started or running. Make sure they don't end up" +" in ``ERROR`` or ``BACKOFF`` state, so refresh with '``status``' 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." +"uiteindelijk niet in ``ERROR`` of ``BACKOFF`` status terecht zijn " +"gekomen. Je kunt het overzicht verversen door ``status`` te typen." #: ../../installation.rst:300 msgid "When still in ``supervisorctl``'s console, type::" @@ -704,8 +710,8 @@ msgstr "Voorbeeld van wanneer alles naar behoren draait::" #: ../../installation.rst:310 msgid "" -"Want to check whether the datalogger works? Just tail it's log in supervisor " -"with::" +"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::" @@ -713,29 +719,30 @@ msgstr "" #: ../../installation.rst:314 msgid "" "Please note that due to Supervisor's output buffering **it might take a " -"minute or two before you see any output**. You should see similar output as " -"the ``cu``-command printed earlier in the installation process." +"minute or two before you see any output**. You should see similar output " +"as the ``cu``-command printed earlier in the installation process." msgstr "" -"Doordat Supervisor de **logbestanden buffert** kan het een minuut of twee " -"duren voordat je daadwerkelijk wat terugziet. Uiteindelijk zou je ongeveer " -"dezelfde lap tekst moeten zien als toen we handmatig gemeten hebben met het " -"``cu``-programma." +"Doordat Supervisor de **logbestanden buffert** kan het een minuut of twee" +" duren voordat je daadwerkelijk wat terugziet. Uiteindelijk zou je " +"ongeveer dezelfde lap tekst moeten zien als toen we handmatig gemeten " +"hebben met het ``cu``-programma." #: ../../installation.rst:316 msgid "" -"Want to quit supervisor? ``CTRL + C`` to stop tail and ``CTRL + D`` once to " -"exit supervisor command line." +"Want to quit supervisor? ``CTRL + C`` to stop tail and ``CTRL + D`` once " +"to exit supervisor command line." msgstr "" -"Wil je uit supervisor? Druk dan op ``CTRL + C`` om de logfile niet meer te " -"bekijken en vervolgens op ``CTRL + D`` om uit supervisor te gaan." +"Wil je uit supervisor? Druk dan op ``CTRL + C`` om de logfile niet meer " +"te bekijken en vervolgens op ``CTRL + D`` om uit supervisor te gaan." #: ../../installation.rst:319 msgid "" -"You now should have everything up and running! We're almost done, but only " -"need to check a just few more things in the next chapters." +"You now should have everything up and running! We're almost done, but " +"only need to check a just few more things in the next chapters." msgstr "" -"Alles zou nu moeten werken! We zijn bijna klaar, op een paar checks na in " -"het volgende hoofdstuk." +"Alles zou nu moeten werken! We zijn bijna klaar, op een paar checks na in" +" het volgende hoofdstuk." #~ msgid "Import example prices::" #~ msgstr "Importeer voorbeeldprijzen::" + diff --git a/dsmr_api/tests/test_settings.py b/dsmr_api/tests/test_settings.py index 1524fe8da..aae6fb552 100644 --- a/dsmr_api/tests/test_settings.py +++ b/dsmr_api/tests/test_settings.py @@ -13,6 +13,9 @@ def test_admin(self): """ Model should be registered in Django Admin. """ self.assertTrue(site.is_registered(APISettings)) + def test_to_string(self): + self.assertNotEqual(str(self.instance), '{} object'.format(self.instance.__class__.__name__)) + def test_allow(self): self.assertFalse(self.instance.allow) diff --git a/dsmr_backend/apps.py b/dsmr_backend/apps.py index 59a38d8fb..be937004f 100644 --- a/dsmr_backend/apps.py +++ b/dsmr_backend/apps.py @@ -13,7 +13,7 @@ class AppConfig(AppConfig): def ready(self): """ Performs an DB engine check, as we maintain some engine specific queries. """ - if (connection.vendor not in settings.DSMR_SUPPORTED_DB_VENDORS): + if (connection.vendor not in settings.DSMR_SUPPORTED_DB_VENDORS): # pragma: no cover # Temporary for backwards compatibility warnings.showwarning( _( diff --git a/dsmr_backend/management/commands/dsmr_backend.py b/dsmr_backend/management/commands/dsmr_backend.py index 250812bf7..c22f15d99 100644 --- a/dsmr_backend/management/commands/dsmr_backend.py +++ b/dsmr_backend/management/commands/dsmr_backend.py @@ -1,5 +1,6 @@ import traceback +from raven.contrib.django.raven_compat.models import client as raven_client from django.core.management.base import BaseCommand, CommandError from django.utils.translation import ugettext as _ from django.utils import timezone @@ -23,11 +24,18 @@ def run(self, **options): for current_receiver, current_response in responses: if isinstance(current_response, Exception): + try: + # Raven should capture each exception encountered (below). + raise current_response + except: + raven_client.captureException() + # Add and print traceback to help debugging any issues raised. exception_traceback = traceback.format_tb(current_response.__traceback__, limit=100) exception_traceback = "\n".join(exception_traceback) - self.stdout.write(' - {} :: {}'.format(current_receiver, exception_traceback)) + self.stdout.write(' >>> Uncaught exception :: {}'.format(current_response)) + self.stdout.write(' >>> {} :: {}'.format(current_receiver, exception_traceback)) self.stderr.write(exception_traceback) signal_failures.append(exception_traceback) diff --git a/dsmr_backend/mixins.py b/dsmr_backend/mixins.py index 27d51364e..047e9f871 100644 --- a/dsmr_backend/mixins.py +++ b/dsmr_backend/mixins.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _ from django.conf import settings +from django.contrib import admin class InfiniteManagementCommandMixin(object): @@ -73,3 +74,16 @@ def _signal_handler(self, signum, frame): # If we get called, then we must gracefully exit. self._keep_alive = False self.stdout.write('Detected signal #{}, exiting on next run...'.format(signum)) + + +class ReadOnlyAdminModel(admin.ModelAdmin): + """ Read only model for Django admin. """ + def __init__(self, *args, **kwargs): + super(ReadOnlyAdminModel, self).__init__(*args, **kwargs) + self.readonly_fields = self.model._meta.get_all_field_names() + + def has_add_permission(self, request, obj=None): + return False + + def has_delete_permission(self, request, obj=None): + return False diff --git a/dsmr_backend/tests/test_backend.py b/dsmr_backend/tests/test_backend.py index baa78e4d2..ad2704be5 100644 --- a/dsmr_backend/tests/test_backend.py +++ b/dsmr_backend/tests/test_backend.py @@ -31,6 +31,22 @@ def _fake_signal_troublemaker(*args, **kwargs): # We must disconnect to prevent other tests from failing, since this is no database action. dsmr_backend.signals.backend_called.disconnect(receiver=_fake_signal_troublemaker) + @mock.patch('raven.contrib.django.raven_compat.models.client.captureException') + def test_raven_handler(self, raven_mock): + """ Test whether Raven gets called as expectedly, sending any exceptions to Sentry. """ + + def _fake_signal_troublemaker(*args, **kwargs): + raise AssertionError("Please report me to Raven as I'm a very annoying crash!") + + dsmr_backend.signals.backend_called.connect(receiver=_fake_signal_troublemaker) + self.assertFalse(raven_mock.called) + + with self.assertRaises(CommandError): + # Signal should crash, rasing a command error. + self._intercept_command_stdout('dsmr_backend') + + self.assertTrue(raven_mock.called) + def test_supported_vendors(self): """ Check whether supported vendors is as expected. """ self.assertEqual( @@ -53,3 +69,10 @@ def test_pending_migrations(self): 'No changes detected\n', 'Pending model changes found, missing in migrations!' ) + + def test_internal_check(self): + """ Tests whether Django passes it's internal 'check' command. """ + self.assertEqual( + self._intercept_command_stdout('check', dry_run=True), + 'System check identified no issues (0 silenced).\n', + ) diff --git a/dsmr_backup/apps.py b/dsmr_backup/apps.py index 3d4028e3d..c2972841a 100644 --- a/dsmr_backup/apps.py +++ b/dsmr_backup/apps.py @@ -18,4 +18,4 @@ def _on_backend_called_signal(self, sender, **kwargs): # Import below prevents an AppRegistryNotReady error on Django init. import dsmr_backup.services.backup dsmr_backup.services.backup.check() - dsmr_backup.services.backup.sync() + dsmr_backup.services.backup.sync() # pragma: no cover diff --git a/dsmr_backup/services/backup.py b/dsmr_backup/services/backup.py index aa7cfe23c..51fb39e60 100644 --- a/dsmr_backup/services/backup.py +++ b/dsmr_backup/services/backup.py @@ -79,7 +79,7 @@ def create(): stdout=open(backup_file, 'w') # pragma: no cover ) # SQLite backup. - elif connection.vendor == 'sqlite': + elif connection.vendor == 'sqlite': # pragma: no cover backup_process = subprocess.Popen( [ 'sqlite3', @@ -87,7 +87,7 @@ def create(): '.dump', ], stdout=open(backup_file, 'w') - ) + ) # pragma: no cover else: raise NotImplementedError('Unsupported backup backend: {}'.format(connection.vendor)) # pragma: no cover diff --git a/dsmr_backup/services/dropbox.py b/dsmr_backup/services/dropbox.py index bfc63b8e8..22f9d6bef 100644 --- a/dsmr_backup/services/dropbox.py +++ b/dsmr_backup/services/dropbox.py @@ -2,8 +2,7 @@ from django.utils import timezone from django.conf import settings -from dropbox.client import DropboxClient -from dropbox import rest +import dropbox from dsmr_backup.models.settings import DropboxSettings import dsmr_backup.services.backup @@ -55,24 +54,38 @@ def upload_chunked(file_path): """ Uploads a file in chucks to Dropbox, allowing it to resume on (connection) failure. """ dropbox_settings = DropboxSettings.get_solo() file_name = os.path.split(file_path)[-1] + dest_path = '/{}'.format(file_name) # The slash indicates it's relative to the root of app folder. - # From Dropbox docs. - retries = 3 - client = DropboxClient(dropbox_settings.access_token) + dbx = dropbox.Dropbox(dropbox_settings.access_token) + write_mode = dropbox.files.WriteMode.overwrite - size = os.stat(file_path).st_size file_handle = open(file_path, 'rb') + file_size = os.path.getsize(file_path) - uploader = client.get_chunked_uploader(file_handle, size) + # Many thanks to https://stackoverflow.com/documentation/dropbox-api/409/uploading-a-file/1927/uploading-a-file-using-the-dropbox-python-sdk#t=201610181733061624381 + CHUNK_SIZE = 2 * 1024 * 1024 - while uploader.offset < size: - try: - uploader.upload_chunked(chunk_size=1 * 1024 * 1024) - except rest.ErrorResponse: # pragma: no cover - retries -= 1 # pragma: no cover + # Small uploads should be transfers at one go. + if file_size <= CHUNK_SIZE: + dbx.files_upload(file_handle.read(), dest_path, mode=write_mode) - if retries == 0: # pragma: no cover - raise IOError("Failed to upload to dropbox") # pragma: no cover + # Large uploads can be sent in chunks, by creating a session allowing multiple separate uploads. + else: + upload_session_start_result = dbx.files_upload_session_start(file_handle.read(CHUNK_SIZE)) - # This will commit the file and persist it in Dropbox. Due to rotating backups we MUST override. - uploader.finish(file_name, overwrite=True) + cursor = dropbox.files.UploadSessionCursor( + session_id=upload_session_start_result.session_id, + offset=file_handle.tell() + ) + commit = dropbox.files.CommitInfo(path=dest_path, mode=write_mode) + + # We keep sending the data in chunks, until we reach the last one, then we instruct Dropbox to finish the upload + # by combining all the chunks sent previously. + while file_handle.tell() < file_size: + if (file_size - file_handle.tell()) <= CHUNK_SIZE: + dbx.files_upload_session_finish(file_handle.read(CHUNK_SIZE), cursor, commit) + else: + dbx.files_upload_session_append(file_handle.read(CHUNK_SIZE), cursor.session_id, cursor.offset) + cursor.offset = file_handle.tell() + + file_handle.close() diff --git a/dsmr_backup/tests/models/test_settings.py b/dsmr_backup/tests/models/test_settings.py index 1aae12e42..87c7fbfae 100644 --- a/dsmr_backup/tests/models/test_settings.py +++ b/dsmr_backup/tests/models/test_settings.py @@ -15,6 +15,9 @@ def test_admin(self): """ Model should be registered in Django Admin. """ self.assertTrue(site.is_registered(BackupSettings)) + def test_to_string(self): + self.assertNotEqual(str(self.instance), '{} object'.format(self.instance.__class__.__name__)) + def test_daily_backup(self): self.assertTrue(self.instance.daily_backup) @@ -37,6 +40,9 @@ def test_admin(self): """ Model should be registered in Django Admin. """ self.assertTrue(site.is_registered(DropboxSettings)) + def test_to_string(self): + self.assertNotEqual(str(self.instance), '{} object'.format(self.instance.__class__.__name__)) + def test_access_token(self): self.assertIsNone(self.instance.access_token) diff --git a/dsmr_backup/tests/test_services.py b/dsmr_backup/tests/test_services.py index b488109dc..97d2bda37 100644 --- a/dsmr_backup/tests/test_services.py +++ b/dsmr_backup/tests/test_services.py @@ -101,6 +101,7 @@ def test_create(self, compress_mock, subprocess_mock): self.assertFalse(compress_mock.called) self.assertFalse(subprocess_mock.called) self.assertIsNone(BackupSettings.get_solo().latest_backup) + self.assertTrue(BackupSettings.get_solo().compress) dsmr_backup.services.backup.create() self.assertTrue(compress_mock.called) @@ -108,6 +109,24 @@ def test_create(self, compress_mock, subprocess_mock): self.assertIsNotNone(BackupSettings.get_solo().latest_backup) + @mock.patch('subprocess.Popen') + @mock.patch('dsmr_backup.services.backup.compress') + def test_create_without_compress(self, compress_mock, subprocess_mock): + backup_settings = BackupSettings.get_solo() + backup_settings.compress = False + backup_settings.save() + + self.assertFalse(compress_mock.called) + self.assertFalse(subprocess_mock.called) + self.assertIsNone(BackupSettings.get_solo().latest_backup) + self.assertFalse(BackupSettings.get_solo().compress) + + dsmr_backup.services.backup.create() + self.assertFalse(compress_mock.called) + self.assertTrue(subprocess_mock.called) + + self.assertIsNotNone(BackupSettings.get_solo().latest_backup) + def test_compress(self): TEST_STRING = b'TestTestTest-1234567890' # Temp file without automtic deletion, as compress() should do that as well. @@ -157,6 +176,19 @@ def test_sync_disabled(self, upload_chunked_mock): dsmr_backup.services.dropbox.sync() self.assertFalse(upload_chunked_mock.called) + @mock.patch('dsmr_backup.services.dropbox.upload_chunked') + @mock.patch('django.utils.timezone.now') + def test_sync_initial(self, now_mock, upload_chunked_mock): + now_mock.return_value = timezone.make_aware(timezone.datetime(2016, 1, 1)) + + # Initial project state. + dropbox_settings = DropboxSettings.get_solo() + dropbox_settings.latest_sync = None + dropbox_settings.save() + + dsmr_backup.services.dropbox.sync() + self.assertTrue(upload_chunked_mock.called) + @mock.patch('dsmr_backup.services.dropbox.upload_chunked') @mock.patch('django.utils.timezone.now') def test_sync(self, now_mock, upload_chunked_mock): @@ -214,16 +246,40 @@ def test_sync_last_modified(self, now_mock, upload_chunked_mock, get_backup_dire dsmr_backup.services.dropbox.sync() self.assertFalse(upload_chunked_mock.called) - @mock.patch('dropbox.client.DropboxClient.get_chunked_uploader') - def test_upload_chunked(self, chunked_uploader_mock): + @mock.patch('dropbox.Dropbox.files_upload') + @mock.patch('dropbox.Dropbox.files_upload_session_start') + @mock.patch('dropbox.Dropbox.files_upload_session_append') + @mock.patch('dropbox.Dropbox.files_upload_session_finish') + def test_upload_chunked(self, session_finish_mock, session_append_mock, session_start_mock, files_upload_mock): DATA = b'Lots of data.' - uploader_mock = mock.MagicMock() - type(uploader_mock).offset = mock.PropertyMock(side_effect=[0, 5, 10, len(DATA), len(DATA)]) - chunked_uploader_mock.return_value = uploader_mock + session_start_result = mock.MagicMock() + type(session_start_result).session_id = mock.PropertyMock(side_effect=['session-xxxxx']) + session_start_mock.return_value = session_start_result + + self.assertFalse(files_upload_mock.called) + self.assertFalse(session_start_mock.called) + self.assertFalse(session_append_mock.called) + self.assertFalse(session_finish_mock.called) with tempfile.NamedTemporaryFile() as temp_file: temp_file.write(DATA) temp_file.flush() dsmr_backup.services.dropbox.upload_chunked(temp_file.name) - self.assertTrue(uploader_mock.finish.called) + + # Only small file upload should be called. + self.assertTrue(files_upload_mock.called) + self.assertFalse(session_start_mock.called) + self.assertFalse(session_append_mock.called) + self.assertFalse(session_finish_mock.called) + + # Large file upload (> 2 MB chunks). + with tempfile.NamedTemporaryFile() as temp_file: + temp_file.write(DATA * 2 * 1024 * 1024) + temp_file.flush() + + dsmr_backup.services.dropbox.upload_chunked(temp_file.name) + + self.assertTrue(session_start_mock.called) + self.assertTrue(session_append_mock.called) + self.assertTrue(session_finish_mock.called) diff --git a/dsmr_consumption/migrations/0004_merge_electricity_tariffs.py b/dsmr_consumption/migrations/0004_merge_electricity_tariffs.py new file mode 100644 index 000000000..36e783cee --- /dev/null +++ b/dsmr_consumption/migrations/0004_merge_electricity_tariffs.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_consumption', '0003_electricity_consumption_indexes'), + ] + + operations = [ + migrations.AlterField( + model_name='energysupplierprice', + name='electricity_1_price', + field=models.DecimalField(max_digits=11, default=0, help_text='Are you using a single tariff? Please enter the same price twice and enable "Merge electricity tariffs" in the frontend configuration', decimal_places=5, verbose_name='Electricity 1 price (low tariff)'), + ), + migrations.AlterField( + model_name='energysupplierprice', + name='electricity_2_price', + field=models.DecimalField(max_digits=11, default=0, help_text='Are you using a single tariff? Please enter the same price twice and enable "Merge electricity tariffs" in the frontend configuration', decimal_places=5, verbose_name='Electricity 2 price (high tariff)'), + ), + ] diff --git a/dsmr_consumption/models/__init__.py b/dsmr_consumption/models/__init__.py index 05a1144f2..e69de29bb 100644 --- a/dsmr_consumption/models/__init__.py +++ b/dsmr_consumption/models/__init__.py @@ -1,3 +0,0 @@ -from .consumption import * -from .settings import * -from .energysupplier import * diff --git a/dsmr_consumption/models/energysupplier.py b/dsmr_consumption/models/energysupplier.py index 0a884b0ff..6ce4f0359 100644 --- a/dsmr_consumption/models/energysupplier.py +++ b/dsmr_consumption/models/energysupplier.py @@ -27,10 +27,24 @@ class EnergySupplierPrice(models.Model): help_text=_('For your own reference, i.e. the name of your supplier') ) electricity_1_price = models.DecimalField( - max_digits=11, decimal_places=5, default=0, verbose_name=_('Electricity 1 price (low tariff)') + max_digits=11, + decimal_places=5, + default=0, + verbose_name=_('Electricity 1 price (low tariff)'), + help_text=_( + 'Are you using a single tariff? Please enter the same price twice and enable "Merge electricity tariffs" ' + 'in the frontend configuration' + ) ) electricity_2_price = models.DecimalField( - max_digits=11, decimal_places=5, default=0, verbose_name=_('Electricity 2 price (high tariff)') + max_digits=11, + decimal_places=5, + default=0, + verbose_name=_('Electricity 2 price (high tariff)'), + help_text=_( + 'Are you using a single tariff? Please enter the same price twice and enable "Merge electricity tariffs" ' + 'in the frontend configuration' + ) ) gas_price = models.DecimalField(max_digits=11, decimal_places=5, default=0, verbose_name=_('Gas price')) diff --git a/dsmr_consumption/services.py b/dsmr_consumption/services.py index b885659f5..c74480aec 100644 --- a/dsmr_consumption/services.py +++ b/dsmr_consumption/services.py @@ -159,6 +159,9 @@ def day_consumption(day): consumption['electricity2_cost'] = round_decimal( consumption['electricity2'] * consumption['electricity2_unit_price'] ) + consumption['electricity_merged'] = consumption['electricity1'] + consumption['electricity2'] + consumption['electricity_returned_merged'] = consumption['electricity1_returned'] + consumption['electricity2_returned'] + consumption['electricity_cost_merged'] = consumption['electricity1_cost'] + consumption['electricity2_cost'] consumption['total_cost'] = round_decimal( consumption['electricity1_cost'] + consumption['electricity2_cost'] ) diff --git a/dsmr_consumption/tests/models/test_consumption.py b/dsmr_consumption/tests/models/test_consumption.py new file mode 100644 index 000000000..eaa2616a9 --- /dev/null +++ b/dsmr_consumption/tests/models/test_consumption.py @@ -0,0 +1,34 @@ +from django.test import TestCase +from django.utils import timezone + +from dsmr_consumption.models.consumption import ElectricityConsumption, GasConsumption + + +class TestElectricityConsumption(TestCase): + def setUp(self): + self.instance = ElectricityConsumption.objects.create( + read_at=timezone.now(), + delivered_1=2, + returned_1=2, + delivered_2=4, + returned_2=4, + currently_delivered=20, + currently_returned=40, + ) + + def test_str(self): + """ Model should override string formatting. """ + self.assertNotEqual(str(self.instance), 'ElectricityConsumption') + + +class TestGasConsumption(TestCase): + def setUp(self): + self.instance = GasConsumption.objects.create( + read_at=timezone.now(), + delivered=100, + currently_delivered=1, + ) + + def test_str(self): + """ Model should override string formatting. """ + self.assertNotEqual(str(self.instance), 'GasConsumption') diff --git a/dsmr_consumption/tests/models/test_energysupplier.py b/dsmr_consumption/tests/models/test_energysupplier.py index 8a1827d37..428959f5e 100644 --- a/dsmr_consumption/tests/models/test_energysupplier.py +++ b/dsmr_consumption/tests/models/test_energysupplier.py @@ -1,3 +1,4 @@ +from django.utils import timezone from django.test import TestCase from django.contrib.admin.sites import site @@ -5,6 +6,20 @@ class TestEnergySupplierPrice(TestCase): + def setUp(self): + self.instance = EnergySupplierPrice.objects.create( + start=timezone.now(), + end=timezone.now(), + description='Test', + electricity_1_price=1, + electricity_2_price=2, + gas_price=3, + ) + def test_admin(self): """ Model should be registered in Django Admin. """ self.assertTrue(site.is_registered(EnergySupplierPrice)) + + def test_str(self): + """ Model should override string formatting. """ + self.assertNotEqual(str(self.instance), 'EnergySupplierPrice') diff --git a/dsmr_consumption/tests/models/test_settings.py b/dsmr_consumption/tests/models/test_settings.py index dd716cbfa..3a989c825 100644 --- a/dsmr_consumption/tests/models/test_settings.py +++ b/dsmr_consumption/tests/models/test_settings.py @@ -13,6 +13,9 @@ def test_admin(self): """ Model should be registered in Django Admin. """ self.assertTrue(site.is_registered(ConsumptionSettings)) + def test_to_string(self): + self.assertNotEqual(str(self.instance), '{} object'.format(self.instance.__class__.__name__)) + def test_compactor_grouping_type(self): self.assertEqual( self.instance.compactor_grouping_type, ConsumptionSettings.COMPACTOR_GROUPING_BY_MINUTE diff --git a/dsmr_datalogger/admin.py b/dsmr_datalogger/admin.py index d70e4072b..b054d47e2 100644 --- a/dsmr_datalogger/admin.py +++ b/dsmr_datalogger/admin.py @@ -1,9 +1,28 @@ +from django.contrib.admin.filters import DateFieldListFilter from django.contrib import admin from solo.admin import SingletonModelAdmin +from dsmr_backend.mixins import ReadOnlyAdminModel from .models.settings import DataloggerSettings +from .models.reading import DsmrReading, MeterStatistics @admin.register(DataloggerSettings) class DataloggerSettingsAdmin(SingletonModelAdmin): list_display = ('com_port', ) + + +@admin.register(DsmrReading) +class DsmrReadingAdmin(ReadOnlyAdminModel): + """ Read only model. """ + ordering = ['-timestamp'] + list_display = ('timestamp', 'electricity_currently_delivered', 'electricity_currently_returned') + list_filter = ( + ('timestamp', DateFieldListFilter), + ) + + +@admin.register(MeterStatistics) +class MeterStatisticsAdmin(ReadOnlyAdminModel): + """ Read only model. """ + list_display = ('timestamp', 'electricity_tariff', 'power_failure_count') diff --git a/dsmr_datalogger/management/commands/dsmr_fake_datasource.py b/dsmr_datalogger/management/commands/dsmr_fake_datasource.py index 44a8dc472..8cf325727 100644 --- a/dsmr_datalogger/management/commands/dsmr_fake_datasource.py +++ b/dsmr_datalogger/management/commands/dsmr_fake_datasource.py @@ -4,10 +4,12 @@ import time from django.core.management.base import BaseCommand, CommandError -from dsmr_backend.mixins import InfiniteManagementCommandMixin from django.utils.translation import ugettext as _ from django.core.management import call_command from django.utils import timezone +from django.conf import settings + +from dsmr_backend.mixins import InfiniteManagementCommandMixin class Command(InfiniteManagementCommandMixin, BaseCommand): @@ -41,6 +43,9 @@ def add_arguments(self, parser): def run(self, **options): """ InfiniteManagementCommandMixin listens to handle() and calls run() in a loop. """ + if not settings.DEBUG: + raise CommandError(_('Intended usage is NOT production! Only allowed when DEBUG = True')) + if not options.get('acked_warning'): raise CommandError(_('Intended usage is NOT production! Force by using --ack-to-mess-up-my-data')) diff --git a/dsmr_datalogger/migrations/0002_meta_names.py b/dsmr_datalogger/migrations/0002_meta_names.py new file mode 100644 index 000000000..e4e676f80 --- /dev/null +++ b/dsmr_datalogger/migrations/0002_meta_names.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_datalogger', '0001_squashed_0005_optional_gas_readings'), + ] + + operations = [ + migrations.AlterModelOptions( + name='dsmrreading', + options={'verbose_name': 'DSMR reading (read only)', 'default_permissions': (), 'ordering': ['timestamp'], 'verbose_name_plural': 'DSMR readings (read only)'}, + ), + migrations.AlterModelOptions( + name='meterstatistics', + options={'verbose_name': 'DSMR Meter statistics (read only)', 'default_permissions': (), 'verbose_name_plural': 'DSMR Meter statistics (read only)'}, + ), + ] diff --git a/dsmr_datalogger/models/__init__.py b/dsmr_datalogger/models/__init__.py index 8d06f6318..e69de29bb 100644 --- a/dsmr_datalogger/models/__init__.py +++ b/dsmr_datalogger/models/__init__.py @@ -1,2 +0,0 @@ -from .reading import * -from .settings import * diff --git a/dsmr_datalogger/models/reading.py b/dsmr_datalogger/models/reading.py index f3bf23896..2a3fe8b96 100644 --- a/dsmr_datalogger/models/reading.py +++ b/dsmr_datalogger/models/reading.py @@ -71,7 +71,8 @@ class DsmrReading(models.Model): class Meta: default_permissions = tuple() ordering = ['timestamp'] - verbose_name = _('DSMR reading') + verbose_name = _('DSMR reading (read only)') + verbose_name_plural = _('DSMR readings (read only)') def __str__(self): return '{}: {} kWh'.format(self.id, self.timestamp, self.electricity_currently_delivered) @@ -135,7 +136,8 @@ class MeterStatistics(SingletonModel): class Meta: default_permissions = tuple() - verbose_name = _('DSMR Meter statistics') + verbose_name = _('DSMR Meter statistics (read only)') + verbose_name_plural = verbose_name def __str__(self): return '{} @ {}'.format(self.__class__.__name__, self.timestamp) diff --git a/dsmr_datalogger/services.py b/dsmr_datalogger/services.py index a7070e510..7b120bc91 100644 --- a/dsmr_datalogger/services.py +++ b/dsmr_datalogger/services.py @@ -106,7 +106,7 @@ def _convert_legacy_dsmr_gas_line(parsed_reading, current_line, next_line): """ Legacy support for DSMR 2.x gas. """ legacy_gas_line = current_line - if next_line.startswith('('): + if next_line.startswith('('): # pragma: no cover legacy_gas_line = current_line + next_line legacy_gas_result = re.search( diff --git a/dsmr_datalogger/tests/models/test_dsmrreading.py b/dsmr_datalogger/tests/models/test_dsmrreading.py deleted file mode 100644 index 19ea066cc..000000000 --- a/dsmr_datalogger/tests/models/test_dsmrreading.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.test import TestCase - -from dsmr_datalogger.models.reading import DsmrReading, MeterStatistics - - -class TestDsmrReading(TestCase): - def test_ordering(self): - """ Test whether default model sorting is as expected. """ - self.assertEqual(DsmrReading()._meta.ordering, ['timestamp']) - - -class TestMeterStatistics(TestCase): - def test_ordering(self): - """ Test whether defaults allow the creation of any empty model. """ - MeterStatistics.get_solo() - self.assertTrue(MeterStatistics.objects.exists()) diff --git a/dsmr_datalogger/tests/models/test_reading.py b/dsmr_datalogger/tests/models/test_reading.py new file mode 100644 index 000000000..e190adfd2 --- /dev/null +++ b/dsmr_datalogger/tests/models/test_reading.py @@ -0,0 +1,39 @@ +from django.test import TestCase +from django.utils import timezone + +from dsmr_datalogger.models.reading import DsmrReading, MeterStatistics + + +class TestDsmrReading(TestCase): + def setUp(self): + self.instance = DsmrReading.objects.create( + timestamp=timezone.now(), + electricity_delivered_1=1, + electricity_returned_1=2, + electricity_delivered_2=3, + electricity_returned_2=4, + electricity_currently_delivered=5, + electricity_currently_returned=6, + ) + + def test_ordering(self): + """ Test whether default model sorting is as expected. """ + self.assertEqual(DsmrReading()._meta.ordering, ['timestamp']) + + def test_str(self): + """ Model should override string formatting. """ + self.assertNotEqual(str(self.instance), 'DsmrReading') + + +class TestMeterStatistics(TestCase): + def setUp(self): + self.instance = MeterStatistics.get_solo() + + def test_ordering(self): + """ Test whether defaults allow the creation of any empty model. """ + MeterStatistics.get_solo() + self.assertTrue(MeterStatistics.objects.exists()) + + def test_str(self): + """ Model should override string formatting. """ + self.assertNotEqual(str(self.instance), 'MeterStatistics') diff --git a/dsmr_datalogger/tests/models/test_settings.py b/dsmr_datalogger/tests/models/test_settings.py index 65c9e68c2..4b9a476f8 100644 --- a/dsmr_datalogger/tests/models/test_settings.py +++ b/dsmr_datalogger/tests/models/test_settings.py @@ -13,6 +13,9 @@ def test_admin(self): """ Model should be registered in Django Admin. """ self.assertTrue(site.is_registered(DataloggerSettings)) + def test_to_string(self): + self.assertNotEqual(str(self.instance), '{} object'.format(self.instance.__class__.__name__)) + def test_track(self): self.assertTrue(self.instance.track) diff --git a/dsmr_frontend/apps.py b/dsmr_frontend/apps.py index 71ac26399..c4ebe4ad9 100644 --- a/dsmr_frontend/apps.py +++ b/dsmr_frontend/apps.py @@ -5,3 +5,8 @@ class AppConfig(AppConfig): name = 'dsmr_frontend' verbose_name = _('Frontend') + + def ready(self): + # For some weird reason Django proposes this model for DELETION when executing 'makemigrations'. + # This seems to prevent it somehow... + from .models.message import Notification diff --git a/dsmr_frontend/fixtures/dsmr_frontend/EnergySupplierPrice.json b/dsmr_frontend/fixtures/dsmr_frontend/EnergySupplierPrice.json index cc02f28e6..25d124a9a 100644 --- a/dsmr_frontend/fixtures/dsmr_frontend/EnergySupplierPrice.json +++ b/dsmr_frontend/fixtures/dsmr_frontend/EnergySupplierPrice.json @@ -2,10 +2,10 @@ { "fields": { "start": "2015-01-01", - "electricity_1_price": "0.20559", + "electricity_1_price": "0.20123", "gas_price": "0.58685", "end": "2016-01-25", - "electricity_2_price": "0.21843", + "electricity_2_price": "0.40123", "description": "Nederlandse Energie Maatschappij" }, "model": "dsmr_consumption.energysupplierprice", diff --git a/dsmr_frontend/fixtures/dsmr_frontend/test_meterstatistics.json b/dsmr_frontend/fixtures/dsmr_frontend/test_meterstatistics.json new file mode 100644 index 000000000..c3daee008 --- /dev/null +++ b/dsmr_frontend/fixtures/dsmr_frontend/test_meterstatistics.json @@ -0,0 +1,18 @@ +[ +{ + "model": "dsmr_datalogger.meterstatistics", + "fields": { + "power_failure_count": 3, + "voltage_swell_count_l3": 0, + "voltage_sag_count_l2": 2, + "voltage_sag_count_l3": 0, + "voltage_sag_count_l1": 2, + "voltage_swell_count_l2": 0, + "long_power_failure_count": 0, + "electricity_tariff": 1, + "voltage_swell_count_l1": 0, + "timestamp": "2015-11-20T05:00:03Z" + }, + "pk": 1 +} +] diff --git a/dsmr_frontend/migrations/0006_notifications_initial.py b/dsmr_frontend/migrations/0006_notifications_initial.py index a1fa8e0d2..a697a3eb0 100644 --- a/dsmr_frontend/migrations/0006_notifications_initial.py +++ b/dsmr_frontend/migrations/0006_notifications_initial.py @@ -23,7 +23,7 @@ def insert_notifications(apps, schema_editor): message=dsmr_frontend.services.get_translated_string( text=_('Welcome to DSMR-reader! Please make sure to check your settings in the Configuration page!') ), - redirect_to='frontend:configuration' + redirect_to='admin:index' ) Notification.objects.create( diff --git a/dsmr_frontend/migrations/0007_merge_electricity_tariffs.py b/dsmr_frontend/migrations/0007_merge_electricity_tariffs.py new file mode 100644 index 000000000..82d10ff4b --- /dev/null +++ b/dsmr_frontend/migrations/0007_merge_electricity_tariffs.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_frontend', '0006_notifications_initial'), + ] + + operations = [ + migrations.AddField( + model_name='frontendsettings', + name='merge_electricity_tariffs', + field=models.BooleanField(default=False, help_text='Whether you are using a single electricity tariff and both (high/low) should be displayed merged', verbose_name='Merge electricity tariffs'), + ), + ] diff --git a/dsmr_frontend/migrations/0008_merge_electricity_tariffs_notification.py b/dsmr_frontend/migrations/0008_merge_electricity_tariffs_notification.py new file mode 100644 index 000000000..2258e89da --- /dev/null +++ b/dsmr_frontend/migrations/0008_merge_electricity_tariffs_notification.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations +from django.utils.translation import ugettext_lazy as _ + + +def insert_notifications(apps, schema_editor): + import dsmr_frontend.services + Notification = apps.get_model('dsmr_frontend', 'Notification') + + Notification.objects.create( + message=dsmr_frontend.services.get_translated_string( + text=_( + "It's now possible to have the electricity tariffs merged to a single one. This might come handy when " + "you pay your energy supplier fo a single tariff instead of high/low. You can enable this feature in " + " frontend configuration: 'Merge electricity tariffs'" + ) + ), + redirect_to='admin:index' + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('dsmr_frontend', '0007_merge_electricity_tariffs'), + ] + + operations = [ + migrations.RunPython(insert_notifications), + ] diff --git a/dsmr_frontend/models/__init__.py b/dsmr_frontend/models/__init__.py index 5165301ba..e69de29bb 100644 --- a/dsmr_frontend/models/__init__.py +++ b/dsmr_frontend/models/__init__.py @@ -1,2 +0,0 @@ -from .settings import * -from .message import * diff --git a/dsmr_frontend/models/settings.py b/dsmr_frontend/models/settings.py index 1f55d6417..84a706208 100644 --- a/dsmr_frontend/models/settings.py +++ b/dsmr_frontend/models/settings.py @@ -11,6 +11,11 @@ class FrontendSettings(SingletonModel): verbose_name=_('Reverse dashboard graphs'), help_text=_('Whether graphs are rendered with an reversed X-axis') ) + merge_electricity_tariffs = models.BooleanField( + default=False, + verbose_name=_('Merge electricity tariffs'), + help_text=_('Whether you are using a single electricity tariff and both (high/low) should be displayed merged') + ) electricity_delivered_color = ColorField( default='#F05050', diff --git a/dsmr_frontend/static/dsmr_frontend/js/jquery.bootstrap.wizard.js b/dsmr_frontend/static/dsmr_frontend/js/jquery.bootstrap.wizard.js deleted file mode 100644 index 505be4cf4..000000000 --- a/dsmr_frontend/static/dsmr_frontend/js/jquery.bootstrap.wizard.js +++ /dev/null @@ -1,255 +0,0 @@ -/*! - * jQuery twitter bootstrap wizard plugin - * Examples and documentation at: http://github.com/VinceG/twitter-bootstrap-wizard - * version 1.0 - * Requires jQuery v1.3.2 or later - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * Authors: Vadim Vincent Gabriel (http://vadimg.com), Jason Gill (www.gilluminate.com) - */ -;(function($) { -var bootstrapWizardCreate = function(element, options) { - var element = $(element); - var obj = this; - - // Merge options with defaults - var $settings = $.extend({}, $.fn.bootstrapWizard.defaults, options); - var $activeTab = null; - var $navigation = null; - - this.rebindClick = function(selector, fn) - { - selector.unbind('click', fn).bind('click', fn); - } - - this.fixNavigationButtons = function() { - // Get the current active tab - if(!$activeTab.length) { - // Select first one - $navigation.find('a:first').tab('show'); - $activeTab = $navigation.find('li:first'); - } - - // See if we're currently in the first/last then disable the previous and last buttons - $($settings.previousSelector, element).toggleClass('disabled', (obj.firstIndex() >= obj.currentIndex())); - $($settings.nextSelector, element).toggleClass('disabled', (obj.currentIndex() >= obj.navigationLength())); - - // We are unbinding and rebinding to ensure single firing and no double-click errors - obj.rebindClick($($settings.nextSelector, element), obj.next); - obj.rebindClick($($settings.previousSelector, element), obj.previous); - obj.rebindClick($($settings.lastSelector, element), obj.last); - obj.rebindClick($($settings.firstSelector, element), obj.first); - - if($settings.onTabShow && typeof $settings.onTabShow === 'function' && $settings.onTabShow($activeTab, $navigation, obj.currentIndex())===false){ - return false; - } - }; - - this.next = function(e) { - - // If we clicked the last then dont activate this - if(element.hasClass('last')) { - return false; - } - - if($settings.onNext && typeof $settings.onNext === 'function' && $settings.onNext($activeTab, $navigation, obj.nextIndex())===false){ - return false; - } - - // Did we click the last button - $index = obj.nextIndex(); - if($index > obj.navigationLength()) { - } else { - $navigation.find('li:eq('+$index+') a').tab('show'); - } - }; - - this.previous = function(e) { - - // If we clicked the first then dont activate this - if(element.hasClass('first')) { - return false; - } - - if($settings.onPrevious && typeof $settings.onPrevious === 'function' && $settings.onPrevious($activeTab, $navigation, obj.previousIndex())===false){ - return false; - } - - $index = obj.previousIndex(); - if($index < 0) { - } else { - $navigation.find('li:eq('+$index+') a').tab('show'); - } - }; - - this.first = function(e) { - if($settings.onFirst && typeof $settings.onFirst === 'function' && $settings.onFirst($activeTab, $navigation, obj.firstIndex())===false){ - return false; - } - - // If the element is disabled then we won't do anything - if(element.hasClass('disabled')) { - return false; - } - $navigation.find('li:eq(0) a').tab('show'); - - }; - this.last = function(e) { - if($settings.onLast && typeof $settings.onLast === 'function' && $settings.onLast($activeTab, $navigation, obj.lastIndex())===false){ - return false; - } - - // If the element is disabled then we won't do anything - if(element.hasClass('disabled')) { - return false; - } - $navigation.find('li:eq('+obj.navigationLength()+') a').tab('show'); - }; - this.currentIndex = function() { - return $navigation.find('li').index($activeTab); - }; - this.firstIndex = function() { - return 0; - }; - this.lastIndex = function() { - return obj.navigationLength(); - }; - this.getIndex = function(e) { - return $navigation.find('li').index(e); - }; - this.nextIndex = function() { - return $navigation.find('li').index($activeTab) + 1; - }; - this.previousIndex = function() { - return $navigation.find('li').index($activeTab) - 1; - }; - this.navigationLength = function() { - return $navigation.find('li').length - 1; - }; - this.activeTab = function() { - return $activeTab; - }; - this.nextTab = function() { - return $navigation.find('li:eq('+(obj.currentIndex()+1)+')').length ? $navigation.find('li:eq('+(obj.currentIndex()+1)+')') : null; - }; - this.previousTab = function() { - if(obj.currentIndex() <= 0) { - return null; - } - return $navigation.find('li:eq('+parseInt(obj.currentIndex()-1)+')'); - }; - this.show = function(index) { - return element.find('li:eq(' + index + ') a').tab('show'); - }; - this.disable = function(index) { - $navigation.find('li:eq('+index+')').addClass('disabled'); - }; - this.enable = function(index) { - $navigation.find('li:eq('+index+')').removeClass('disabled'); - }; - this.hide = function(index) { - $navigation.find('li:eq('+index+')').hide(); - }; - this.display = function(index) { - $navigation.find('li:eq('+index+')').show(); - }; - this.remove = function(args) { - var $index = args[0]; - var $removeTabPane = typeof args[1] != 'undefined' ? args[1] : false; - var $item = $navigation.find('li:eq('+$index+')'); - - // Remove the tab pane first if needed - if($removeTabPane) { - var $href = $item.find('a').attr('href'); - $($href).remove(); - } - - // Remove menu item - $item.remove(); - }; - - $navigation = element.find('ul:first', element); - $activeTab = $navigation.find('li.active', element); - - if(!$navigation.hasClass($settings.tabClass)) { - $navigation.addClass($settings.tabClass); - } - - // Load onInit - if($settings.onInit && typeof $settings.onInit === 'function'){ - $settings.onInit($activeTab, $navigation, 0); - } - - // Load onShow - if($settings.onShow && typeof $settings.onShow === 'function'){ - $settings.onShow($activeTab, $navigation, obj.nextIndex()); - } - - // Work the next/previous buttons - obj.fixNavigationButtons(); - - $('a[data-toggle="tab"]', $navigation).on('click', function (e) { - // Get the index of the clicked tab - var clickedIndex = $navigation.find('li').index($(e.currentTarget).parent('li')); - if($settings.onTabClick && typeof $settings.onTabClick === 'function' && $settings.onTabClick($activeTab, $navigation, obj.currentIndex(), clickedIndex)===false){ - return false; - } - }); - - $('a[data-toggle="tab"]', $navigation).on('shown', function (e) { // use shown instead of show to help prevent double firing - $element = $(e.target).parent(); - var nextTab = $navigation.find('li').index($element); - - // If it's disabled then do not change - if($element.hasClass('disabled')) { - return false; - } - - if($settings.onTabChange && typeof $settings.onTabChange === 'function' && $settings.onTabChange($activeTab, $navigation, obj.currentIndex(), nextTab)===false){ - return false; - } - - $activeTab = $element; // activated tab - obj.fixNavigationButtons(); - }); -}; -$.fn.bootstrapWizard = function(options) { - //expose methods - if (typeof options == 'string') { - var args = Array.prototype.slice.call(arguments, 1) - if(args.length === 1) { - args.toString(); - } - return this.data('bootstrapWizard')[options](args); - } - return this.each(function(index){ - var element = $(this); - // Return early if this element already has a plugin instance - if (element.data('bootstrapWizard')) return; - // pass options to plugin constructor - var wizard = new bootstrapWizardCreate(element, options); - // Store plugin object in this element's data - element.data('bootstrapWizard', wizard); - }); -}; - -// expose options -$.fn.bootstrapWizard.defaults = { - tabClass: 'nav nav-pills', - nextSelector: '.wizard li.next', - previousSelector: '.wizard li.previous', - firstSelector: '.wizard li.first', - lastSelector: '.wizard li.last', - onShow: null, - onInit: null, - onNext: null, - onPrevious: null, - onLast: null, - onFirst: null, - onTabChange: null, - onTabClick: null, - onTabShow: null -}; - -})(jQuery); diff --git a/dsmr_frontend/templates/dsmr_frontend/archive.html b/dsmr_frontend/templates/dsmr_frontend/archive.html index db0895b84..758491322 100644 --- a/dsmr_frontend/templates/dsmr_frontend/archive.html +++ b/dsmr_frontend/templates/dsmr_frontend/archive.html @@ -47,10 +47,13 @@
+
+ +