From 92fee67729f7c94ac09a8428cb5f3c087b88a7c6 Mon Sep 17 00:00:00 2001 From: Dennis Siemensma Date: Sun, 18 Oct 2020 20:59:26 +0200 Subject: [PATCH] Added checks for database size and record count. Added menu badge on status errors. #1125 --- dsmr_backend/apps.py | 16 +++++++ dsmr_backend/services/backend.py | 42 +++++++++++++++++- dsmr_datalogger/apps.py | 16 +++++++ dsmr_datalogger/services/datalogger.py | 15 +++++++ dsmr_frontend/context_processors/__init__.py | 3 ++ .../templates/dsmr_frontend/base.html | 9 +++- dsmrreader/config/defaults.py | 5 +++ dsmrreader/config/test.py | 4 ++ dsmrreader/locales/nl/LC_MESSAGES/django.mo | Bin 98677 -> 99018 bytes dsmrreader/locales/nl/LC_MESSAGES/django.po | 6 +++ tools/test-all.sh | 6 +-- 11 files changed, 116 insertions(+), 6 deletions(-) diff --git a/dsmr_backend/apps.py b/dsmr_backend/apps.py index 7b6d09124..504dc66b3 100644 --- a/dsmr_backend/apps.py +++ b/dsmr_backend/apps.py @@ -52,3 +52,19 @@ def check_scheduled_processes(**kwargs): ) return issues + + +@receiver(request_status) +def postgresql_check_database_size(**kwargs): # pragma: nocover + import dsmr_backend.services.backend + + pretty_size, bytes_size = dsmr_backend.services.backend.postgresql_total_database_size() + + if bytes_size < settings.DSMRREADER_STATUS_WARN_OVER_EXCESSIVE_DATABASE_SIZE: + return + + return MonitoringStatusIssue( + __name__, + _('Database growing large: {}, consider data cleanup (if not already enabled)').format(pretty_size), + timezone.now() + ) diff --git a/dsmr_backend/services/backend.py b/dsmr_backend/services/backend.py index 7669900a1..f714ae37d 100644 --- a/dsmr_backend/services/backend.py +++ b/dsmr_backend/services/backend.py @@ -1,3 +1,4 @@ +import logging from distutils.version import StrictVersion import datetime @@ -7,6 +8,7 @@ from django.db.models import Q from django.utils import timezone from django.core.cache import cache +from django.db import connection from dsmr_backend import signals from dsmr_backend.dto import MonitoringStatusIssue @@ -16,6 +18,9 @@ from dsmr_weather.models.settings import WeatherSettings +logger = logging.getLogger('dsmrreader') + + def get_capabilities(capability=None): """ Returns the capabilities of the data tracked, such as whether the meter supports gas readings or @@ -24,7 +29,7 @@ def get_capabilities(capability=None): Optionally returns a single capability when requested. """ # Caching time should be limited, but enough to make it matter, as this call is used A LOT. - capabilities = cache.get('capabilities') + capabilities = cache.get(settings.DSMRREADER_CAPABILITIES_CACHE) if capabilities is None: capabilities = { @@ -66,7 +71,7 @@ def get_capabilities(capability=None): capabilities['electricity_returned'] = False capabilities['any'] = any(capabilities.values()) - cache.set('capabilities', capabilities) + cache.set(settings.DSMRREADER_CAPABILITIES_CACHE, capabilities) # Single selection. if capability is not None: @@ -94,6 +99,16 @@ def is_timestamp_passed(timestamp): return timezone.now() >= timestamp +def request_cached_monitoring_status(): + cached_monitoring_status = cache.get(settings.DSMRREADER_MONITORING_CACHE) + + if cached_monitoring_status is None: + # This will also update the cache. + return request_monitoring_status() + + return cached_monitoring_status # pragma: nocover + + def request_monitoring_status(): """ Requests all apps to report any issues for monitoring. """ responses = signals.request_status.send_robust(None) @@ -103,6 +118,10 @@ def request_monitoring_status(): if not current_response: continue + if isinstance(current_response, Exception): + logger.warning(current_response) + continue + if not isinstance(current_response, (list, tuple)): current_response = [current_response] @@ -112,6 +131,9 @@ def request_monitoring_status(): issues = sorted(issues, key=lambda x: x.since, reverse=True) + # Always invalidate and update cache + cache.set(settings.DSMRREADER_MONITORING_CACHE, issues) + return issues @@ -139,3 +161,19 @@ def hours_in_day(day): # Unchanged else: return 24 + + +def postgresql_total_database_size(): # pragma: nocover + if connection.vendor != 'postgresql': + return + + with connection.cursor() as cursor: + database_name = settings.DATABASES['default']['NAME'] + size_sql = """ + SELECT pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname)) as pretty_size, + pg_catalog.pg_database_size(d.datname) as bytes_size + FROM pg_catalog.pg_database d + WHERE d.datname = %s; + """ + cursor.execute(size_sql, [database_name]) + return cursor.fetchone() diff --git a/dsmr_datalogger/apps.py b/dsmr_datalogger/apps.py index a1ff11a42..cda572281 100644 --- a/dsmr_datalogger/apps.py +++ b/dsmr_datalogger/apps.py @@ -38,3 +38,19 @@ def check_recent_readings(**kwargs): _('No recent readings received'), latest_reading.timestamp ) + + +@receiver(request_status) +def check_reading_count(**kwargs): # pragma: nocover + import dsmr_datalogger.services.datalogger + + reading_count = dsmr_datalogger.services.datalogger.postgresql_approximate_reading_count() + + if reading_count < settings.DSMRREADER_STATUS_WARN_OVER_EXCESSIVE_READING_COUNT: + return + + return MonitoringStatusIssue( + __name__, + _('Approximately {} readings stored, consider data cleanup (if not already enabled)').format(reading_count), + timezone.now() + ) diff --git a/dsmr_datalogger/services/datalogger.py b/dsmr_datalogger/services/datalogger.py index 77ce57e07..f61813d4c 100644 --- a/dsmr_datalogger/services/datalogger.py +++ b/dsmr_datalogger/services/datalogger.py @@ -2,6 +2,7 @@ from django.db.models.expressions import F from django.utils import timezone +from django.db import connection import serial from dsmr_datalogger.models.reading import DsmrReading @@ -206,3 +207,17 @@ def _get_dsmrreader_mapping(version): }) return mapping + + +def postgresql_approximate_reading_count(): # pragma: nocover + if connection.vendor != 'postgresql': + return + + # A live count is too slow on huge datasets, this is accurate enough: + with connection.cursor() as cursor: + cursor.execute( + 'SELECT reltuples as approximate_row_count FROM pg_class WHERE relname = %s;', + [DsmrReading._meta.db_table] + ) + reading_count = cursor.fetchone()[0] + return int(reading_count) diff --git a/dsmr_frontend/context_processors/__init__.py b/dsmr_frontend/context_processors/__init__.py index 547ca2205..4162fd94f 100644 --- a/dsmr_frontend/context_processors/__init__.py +++ b/dsmr_frontend/context_processors/__init__.py @@ -1,9 +1,12 @@ from django.conf import settings +import dsmr_backend.services.backend + def version(request): return { 'dsmr_version': settings.DSMRREADER_VERSION, + 'request_cached_monitoring_status': dsmr_backend.services.backend.request_cached_monitoring_status(), 'DSMRREADER_MAIN_BRANCH': settings.DSMRREADER_MAIN_BRANCH, 'LANGUAGE_CODE': request.LANGUAGE_CODE, } diff --git a/dsmr_frontend/templates/dsmr_frontend/base.html b/dsmr_frontend/templates/dsmr_frontend/base.html index ed311cf98..e02c79ff6 100644 --- a/dsmr_frontend/templates/dsmr_frontend/base.html +++ b/dsmr_frontend/templates/dsmr_frontend/base.html @@ -103,7 +103,14 @@
  • -   {% translate "About & support" %} +   + + {% translate "About & support" %} + + {% if request_cached_monitoring_status %} +   {{ request_cached_monitoring_status|length }} + {% endif %} +
  • diff --git a/dsmrreader/config/defaults.py b/dsmrreader/config/defaults.py index 0ce17a7ac..048216eeb 100644 --- a/dsmrreader/config/defaults.py +++ b/dsmrreader/config/defaults.py @@ -62,6 +62,8 @@ DSMRREADER_STATUS_READING_OFFSET_MINUTES = 30 DSMRREADER_STATUS_MAX_UNPROCESSED_READINGS = 100 +DSMRREADER_STATUS_WARN_OVER_EXCESSIVE_READING_COUNT = 20 * 1000 * 1000 # In millions +DSMRREADER_STATUS_WARN_OVER_EXCESSIVE_DATABASE_SIZE = 1 * 1000 * 1000 * 1000 # In GB # The cooldown period until the next status notification will be sent. DSMRREADER_STATUS_NOTIFICATION_COOLDOWN_HOURS = 12 @@ -79,3 +81,6 @@ DSMRREADER_BUIENRADAR_API_URL = 'https://data.buienradar.nl/2.0/feed/json' DSMRREADER_DATALOGGER_MIN_SLEEP_FOR_RECONNECT = 1.0 + +DSMRREADER_CAPABILITIES_CACHE = 'capabilities' +DSMRREADER_MONITORING_CACHE = 'monitoring_status' diff --git a/dsmrreader/config/test.py b/dsmrreader/config/test.py index ab3b46214..e5563a1c4 100644 --- a/dsmrreader/config/test.py +++ b/dsmrreader/config/test.py @@ -4,6 +4,10 @@ from dsmrreader.config.development import * +# Cache may cause weird stuff during automated testing. +for k in CACHES.keys(): + CACHES[k]['TIMEOUT'] = 0 + # Never use this in production! SECRET_KEY = secrets.token_hex(64) diff --git a/dsmrreader/locales/nl/LC_MESSAGES/django.mo b/dsmrreader/locales/nl/LC_MESSAGES/django.mo index 928bdd59188d036936b702a23e966f76b701dd64..5e4f67eaa5c43bcb107aa2be0c1aaff8bf975616 100644 GIT binary patch delta 17849 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 VH0);}MSfE7;w7Apat_6y_ zOOaBbxb*!!XD0XZ-u-m=&VOchcXnoW&Pix{WQ_02uYBFtQhCpCIKmS-P8MvJ(Q(Rp zJI;zol{(I{3XW3*mtzjRhI!DdqKP9=aUINyLs8dRhM93c>b#rik3N-*$uWiFxSjN5 ze5lBc0hr%f0uvKg#6(yNQ(|58!*=L{-7q!wMqeC@rEoGv;}J}UzLm|5v*H-yP)y7H zofTvPDcFOA&L7wU>sE1`WVja7;7-ita-1`$j=ZbtI2kZ$HFJJ0^dl~c?XWbK!G)+N zzll7t^ALmZJyzBYGgl{rZBQpHM?KL4)RTWeUD)};Jc&Q1B@D6_MPF5;I$9TXosO6k ze?qlikLti-)O9bRJ2{zaWTNm1@{g0VhU0`_FZ>FpVR}qk(@aqW>b&BZ39DdYY=i1R zH`EPAqDE>AM&S2Y4ELk%_pv4eo|{aOT4oV7$9%-&Q9a#=8sY=UXgSwWHwv$9Mx-!m zZllq&HZU1+JJgW(#w0idS=Y`uOo~6E+O4Y1_$Mc`jRGG$iVg7;24L=3dn3$29E}CB zrFE)xH)f~&IqCt@)-jg1_C(!q9_l(jTYq=k%yZP7r>pBYoiPt;3cf?VPMa_*o<@zx zGt?6W)H6?%6SEN)LOpRTYR;QsDE3B;)NE{oYp^l8{p&jp59_qSa$b(J4&#acVHghJ zG+K|u#&M4G8ZRUNIHx(A*>LhT;c0LPuEo%%j?)P*U<8(H<~T2~C!WH#&CN&`ZQ<#l z+leMKlZra1CwqXJV^>S_9w$LPNmcaW^=pBens%+s8tI8TZ-jL=dK0fhEyB&HDgF)B zp(CjC-(W$#@QGTR6N;ebs03;*t6&Ir!6=-BMR70Y$G51V&fUgw7GM!vjAw8Q4r0MzR`9fNTdy0scl*aj~!ggCGxdjJ+f-LN61$F8Uwk3wB=HiqLy zTYnw3C|}z+B@2N;aKcg7nT)#bLR3c%cCzpPEeh1rBrN@0m>t!yD(Z>4qfY39sqq`s zkS@e3xDx&GCF*VYgbgrr7ZxUdiGKJ5Q{YPsM&GWCzn(a(t9gB@p@y^*szc*ZCoaIE zF6In%fverj)cl3ri9ca>Z+1R{q{LIcG+XjKOhUW}wMbW651~ftrkjkO>^4wEI9t+?()QI>FFhiQtS_bn_9%mh8U1hz9A$tG4 z2bxtKit1S@^u<`zDsO~(!ak^uPPOF=F&ps?)DvDs&FK@X-yjo*phhs-8f$Hj*|@(m zSQ(sc-Gb`KdDN3VvL+sE;xJ^bIh9aDTHVI=P*W3!>QGzMM%4$kMiycrT!p&s4Rog^ z^NCCiOvAe}0$Za_xP=b;k8#lJ~ zU57FLI$=Box?ntN(X2;}$S#b;>(*4mO!L=e88*T8sFB%) zy6;Ih8TITgs;4h7JNk?;C+0@I?}ab}mdCQ#6r*u2YNXDiE_fT);~UhCR*W=LwGA^9 z@5XF+6?NVRH<|Qg{J%0c2u96e3Dl=nMbyaDL%p}1Q5{)hJ#4*&sVIMsSupu1V_s`j z45Pd~YVAzMbm*Q&rX88(s0$|_ZAKytweyw0a##-a#G`Dy6E!lfG3JR_pw@BULCx6!8&9|K8q^JrVQzecnxZrl zjCoK)8-sd{8)0JXikjMfsJS13TByz>GKq0M#^W;Vf|bAKJ3Ow%DwuJi=}>FyFw~RG zMjxDyLAV&TnD?O;Dj#g)g_x80 zmeubY^HnV$s=Tv}zd=3NQPdQj#jN-ggE83@#y^5g{wXXFY=)(9D{AgvVQDNm)szoJ zm2X6qzeTN$V&C#j83*8TOghc%e3vjgapLJ_YVx6`raUIc#?u*pe=?mY(2#wJNpTeF zwVaAt3(HVzVL$4EH&IXO&0TeVIBEnVQEyRKRQu7G34gHhZ#F(}k;UNvICbL_Mf`0hz2Wb}G~c za^!n+;#E|GSEwgWG|PO~3q`H|SWJgKF)fa@&coEi>##B&z{Z$tw&T>qj+g>BA{}x& z2gqosPojGE02|^nYt=bs^)Eop`F*U2x#pUUqzh_a*od0KH^`|@z&x|q7Nc&s2X+0^ z$g*&*c*>dop7G`hMxZ(}6SLwfOoPWzuhmV=i>~=5j=(_Tniz@AF$qq^;y4c*;aSvm zA{LmbD34k@RWYfXOm}5)FebxssG*&KnQ;YX!9%DE-Ng*}4g)at4`%MOVHx5W%#UuY zhO4kDzCo>}azC0cIMvXtA&Vu`0NY?T+=5y0yp3PlIL$)Gc}#gEs>4Bx%#h|o&3RGO zVylK4iQ1?RH$~m27wWvPP}iTbi1{x-W+er2c-0!U*lev+P*1Q0)y``PD;Ohj6Yj)H z*#9T?4BUpAivCN@cg5uxN_+`f7LIF~iQ`c_;J#&UbHS$+RG?t!a?`V2ScLc*7RJ;+ z^Q{Q0pf0!^YvW1Gg~7j=MOzsM5XWO4OuNGT&>4gIhjgLV=em#*BOqw?yPm@ymxqx z*j<0UnWMZL%nf3!y|5+?en7qde_2y*G;t9Op}diGkaaHBpnN;(`~c=D7~`yitaGrs z-v8go6rq9dW-|rR7)|WP@9;PDEYdA}4iL9N&2c=c11B*6pQ8_cv~eO9L^9&!mQ|3LtV`O)?ZmLD%fT!R&HlVi3{%JbAS`Z?Bb@xiFY%9*lmw_JumMy8_{!& zqg}CmW?z_!X^7WY_hT*MOQ^*hzTa#}ebA5lJEO_8!$}y3&oDi@4w%K35&ei`tW{A@ z6pMO|+M+*>#B%sGX2N5r5x9?q@FnU~FYKUMv{C5JNI^3)iLpPb!3a!?Q;-LCmY~+a zQY?r^P;bS1^uq#&O#5QinwXCAwwM72qaJ9w^%qnJ_a0*Y)q!giXajkJdZMI<&CZtx zwINkTwQFy6qdGX-#;Y-y_%Q0i4{f>65!3M?)J|F+Ro?>D!9hnDfAw%G1?u5))QxuA zhL^3cQ0-D4HCu5W)P<^`+PAUgLs1Vf+m>&}9K@$kH++e@PSRuM{9JA_Du_WfXl(6; z8uD?r{%7h$o-u*WHMoI)Qu+N*SvQtu@G_bD`rl+U>NZ@8?UzUF^r)6 zAIyl^c?zwmQkWAPpgJ(vIttanN$B<_vye;xF14=1#KhY%5gx=8cog-%UqPLB2X&)A zF)4aoGatu+Sedv12ID-`NNh)K>4#ArZE>CPPf2Frb;p^2Ut=dsbHfbfFw_ku;x~-Q zVpK;8-!e~75fwK7H2|TToMU8k6HS)PC{AmV4hfaUiPWp{O~|gGDd~i(x;E!j%|~?mJ|(c#=Ob zmO>3_7tDr3uqw_$t%=*#C#bjJHL88!pJojNquNEFKNdrEBnH*-hN!jD-o$RFHyK|F zMq*DKj~bdc=!d=!&4!X3ixNj#J6Y#rIOS)sAbzqIcx2*MsOx=;!MMP>%OmrDlZ+Ob z*JIOx%&3MFQCsK=%!-Gs_pIJe%&N|gs?TrZ8mQGBhXt@VYNQroKl~NjV8OpM|750< zIpD=tEWAOy@fpAI!kN$6Cb9nub~aYO-#_NWN-xdd6D-56)PF!WO=r_9+Tw)Q<~OF! z8#W=%D~Sw>)Ap^Inp*G7mLG@iHB`9C1YyDVra>%56E{Jv;+dF?3oXY0;>{mS`5x3F zJd1k5$LNFJAI%~Rz(C^Us42>ZI=?FF`t?5Y{_BJ(w&4uSPP_!e@DOVC{*5It*C(?j zx5T8x({ME|z|C0uU;Z)!{T!F)bv}<;gy~!^&)Nt=O?52BVrQ4z<$3>qrJyMV0bVZ8 z&u$%1Jzs!YgsV~S?`G>U3?aUWp_nL<%kyM;Fd1=C%z_oHtuQn3Fw}XoQ6uu3+cvn1 zdU7vsm*)qET&SUqwRW@)M_qUZ=D~HS3tq)w{1-D}Fn?9n6QfZ#{sqCO9%y3wXUmuCao zi|X)k)Dz!8UEeE(F&KmN`Co>N7DWrJivutUkDx}xmD1(el1p3Lq88Nz^ejTu2rNL| zU;}Cm{DJxL25LkCQ@K3veSa)Xd=VRRea*jFup|%S*A2*F-Box z;xeckR>ic~0k!uJ#UwbwItkT&7V6V>A*%fb^!)z+2pP@k4fG5#YLWemWiU%xm*@L_ zQ>;Kd1&iZpR0oo#Gab)kt%TZ&TUo!g?yXa?SYWuDoB^!%)xnz%FuV;yU6 z>vYuIZ?JAh4fz4ooF74r;04tC{Q$M&rOIU1QdSHmZj9P72fN8sB{L02;$_rSG|y~a zuWmStcq!_}FQ;q zdf#hde(ZroFy6+0*w~fL)JLJ}+haqVgPOu;s3(7q&CoZt#_M2u{wp(@jNaqZ7>&=Y5n(RRm($Kzit^>Cp}mJ%d@oQ#>%+GU-8cmGHswOK z`vNt!aW?La+S1+DvAKEw^`w(2(9q04J=q-8&~LW!MH|1eafWc0=ZDIosKwg?HKct} zYi6SL2UJHkqB^_>wN}p9`p4mJQ{c+ua@J6h5qshh?1v@unpM39^;Y=hGlro$UIq19 z)<%tB9BS?KN3ErCs1D4*A-EI^V2%j0i0iq@XiFW4dg5)?Ygmog$#2T5qvCEh-ekRC z%ir0!Vgb{x1L`wi4XWeYFc`034*ZB^(H&CI<#Z*}8a2nqQ56A&%%@a!R6HKb;~|W} zB#~xO)xcrI3velBEo?p;_M^UVd_vFuQp7CsvZxV?Lq^W+bS9&@>5Uqy(Wp847WIv2 zIqFmK7^>&@F&`!=YBreSs6|@=wRYU7MK=%izHhSeSsOpKaq=kDyZMStMsrxj8jD)h zjZt&l52xTT^uc7s%r~9X)@W;6)D%ucJ-|v-`~9dL@hIv(XHgG&4J)wzoJVA|SPGOd zLsTBspegEtoiGf)MlG^+sP-pOtNyn29cuNbEa~$6PB;|fh=*V!JcAnge9@*OrO~Yu zDwENtS5wsL?~JYSB38%fQZA<)j>RwV5H`d#rOg`XirTn#;A%XM+6RV}F>}8NwHUXe zKFs!`I^Loz@4t4geq~M1Ctxt~eAJG(6Ey-CurR(ueKX1*V=mkPOA~iRbznKFqg${G zzC%4=`*LO~2BSJQ0yV-vm2;b|b3FxGr3X=q?J8;wyg_v!O?lIyDAb~=jk-V&8xO%K z;yI{qOsB9Frl?@H+AmQfybHCaen(B=IX4;2&7Y{bEmzU3@|LK0l#PGHH^lo!bx{T%h=pHS^nS2i8aiNVB$un@ZIlF?9&Mtw_tiW;JZRm>a@MZI39 z@jDEx>hk=EwHP}S_pRpg{P1}Vb-}gO&G&)Zs0YaKh09rpQJ5buqJF;cui?_KeB4e2 zGFqj_QSWbPP4nfk0#+sNi`t5}V>lMBWmb1{)Ksj)#&`;wVcy#2vtc|25wAthlwcj= z6tOPPzp~W^WAy%?BvXlslyzL5zg%jB{Nvo=kAjp()iaB)BbFeZi|z0%4#$%9&0fA0 z#}Z#djZFInbb#xez>dUE8=CL`jT*U}yTrd@M(*$Y5NCS)D{8UbMy+68r#nG)Ho=!&J1FoRn@4U@i&LSLudK)q~ zHy z#9r-O&O}bA)!xLHP$!0VG_T>3PA<>y?{;7V+WB-gtGX#_V;O3lj@^iVMy;{*UCe9O z3-ua~>cac48%?AjPz}18RU3v{rIk>x(+KM%)W$RmLvbx?2 z#GOzhwFHB4HR?Ji-DEUmf1)n*8ujVr+uh~xz)mn~m1jq-;&NCHN1!&KJ*W$vKz)in zKy57VQ15@T9%g^YZjC~XP<7N4xm%OblMg`McpU1}>qpewZ$&jcV||Fak<-(Zr$Rk( zZq$V<+45$nj`v1g?^|2H9M!>HNC(}{X)@Zu9-waY0d=8Ny^P_gCy7BlX#-o{1J!umjD)DvAn zb>JUV2LpPW4hEs>i=#e6Vy)d!YhXOq!)cgP@BckzFi9Vm=ih87h#iRgS+8I$afQBS zWBCrhB>sd=u~$E{Q5{2V-5;?C=IC$A<52s;1Z;@6aWGK@6X8nKTeKE60y{AP@1nkP z{cU}VdhfkPnl%?Nk{juSY!qm(uZVi>7W0R`$+Y(3+YU)b3d+{vJwv5 z(~`Wd{@Ia*xH|P0NIF*H*QA~#t%V(=XR6?+W!osP{l~dP!BSh;&w2~BC<~|+#|qkY zq3jTOA6$uJNL|Tm_4}fZZKR!~RCH)7Wtzr8woZ#ypEnt})=1=VJ1?l@1>je#=5Ic} z$4kWDU{)@o`PRWqd5+(#tU=FzoYD{v*|I=uZqB_<>rtp<9Wt}d1@ccZJ=b+pw%tqP z&!Nxf-BeV;efSQC(qIJY;EmyTzdR8p9K(szlG1Q79qe090pkBXvQid8n-D5qk-}(K z2=n4#_2Q~MV?G55hc=eU_F@BYqRl7N&mg0BncdhaM%@_FcH-ZO{YVXL`(xxEl9rRk zlAe+D224gB?l)xWk*?W-0~9_cpNuq`l!^;o#Xa_dKjB)^JyIh3TRk@^yUe&ph& zg((XoE#SPeq&+m~NE}GoL>x&9CtnZ~j+{6)SQ^?zvn^&mx&bX28MJE1=} zc}ascq?4o|Pp$d;oyzv2zft}tNiX6m#O`QY zIE;cBHb0Ae6Ps^M<5R>s7F$hZ{wznk>!gcH6g|hww%uJ~eM{1jiLxOk>-n$fa*+R9 z^RMGJC+Xmm)cHgT$DdDJ3c`bt5ly$J>zMS(FOnjKGbA$i_k2zMfx=D{miw&nZeksqxp2a<#%5B`?xJn`YeGHoc~S*ZaoUxk z{0^xiDVjL9J+}jKD5<92f6qa%pNuc*Z}MYEairzcr^ke2k!^q78ueM-O&VnZ^ z*g~5p#5rtRZ`7yebJ7i4=69Jy?PiFp5s&ybpOfu6STAo=ge&$n@7;x9-#QgG9= zv_C=q3T4ws_dGTK{i8AUl}PvPIWPJC;rXF3(zel;i&&dK_}}yXlRHL^Z`jVCq$n<= zL+mB#<2&Jqp>PvHAf3u$Z`O>M?>U}hE$WNOuiF2AAT!!t%*6?lh~Ls+7iClI2^2YA z)J5P;%3fe*EJ5l_N=e-b+E*sMq3#dLrjw44e@ePQUPl({_hDVqx0F56`g=oRV%uOA z?k3i;loV|9iZf9+7!Oi5g|vYDGFu-)-H)Wdi1}H@bEG4`hP=LA>zngw;uWO)lvO6a zg`WTZvoDz>R7|7rE$YzMsg;!LC~AFAej4@qYW0sQ?D3JZSkfTcwI-di<@qSM`o^DpXk{K&cd zVuio&W%P&_lHy2dnXdNK_0j!vQ27r*W|EG%q>7}Z_9FSn7q@j}otBh8B=#reBn_nO zE@er%Xa?f8#Pf*xuKAy%6XiNmVjI#EQeo=r>izdObKol3I|$Ny+U6$U6Qu zUyuAm@=35I7yO3&O?%-`E~?|S!Sm;Y&u%)Kw%^#g9JGxmPEUP*?f;8EJ4xA+q)N6d z$X<97jep{#pK-jsa6aNjclXSSplF3S=v!np>Q?VQsHA$n$H>9i$hN2fH9Lem|OrfA1<@|=m^DnDZ!eO>u zAI?cQu8_}4@EvvUN$JU7(ewAVjfzrGfQtM00)NGXqkKXJyOB0fuOq;Aq6*hYL3|(g zliHCo(xwK!Cbd#ydvwRbTl`3{#<9v#@VtuxP-DoobxaF`Bq~esNBwj|+QI_-*=@oUo@q6n3B~>6lk2IXrK-b||s?Yx# zChx50gzi*aCO%GzARcZT+9pmm;>QF(+Jc!_m3BIgeAc!+`I)xd)bfUq*+%(l(kNSa z-t+zuyi90?J~TMO$!YHwUbtm8b!<1h9?O+2NH4CTL*e^2=&@~bclco;@New(i@0ef;GCe(Ss4j?FYZXX}*fLq07Z{eR5T_n`m) diff --git a/dsmrreader/locales/nl/LC_MESSAGES/django.po b/dsmrreader/locales/nl/LC_MESSAGES/django.po index 758042825..fe0cc872d 100644 --- a/dsmrreader/locales/nl/LC_MESSAGES/django.po +++ b/dsmrreader/locales/nl/LC_MESSAGES/django.po @@ -75,6 +75,9 @@ msgstr "De database engine \"{}\" wordt niet actief ondersteund, sommige functie msgid "Process behind schedule: {}" msgstr "Proces loopt achter op schema: {}" +msgid "Database growing large: {}, consider data cleanup (if not already enabled)" +msgstr "Database groeit gestaag: {}, overweeg dataopschoning (indien nog niet ingeschakeld)" + msgid "Resets the environment for development purposes. Not intended for production." msgstr "Reset de omgeving voor ontwikkelingsdoeleinden. Niet bedoeld voor gebruik in productie." @@ -534,6 +537,9 @@ msgstr "Nog nooit een meting ontvangen" msgid "No recent readings received" msgstr "Geen recente metingen ontvangen" +msgid "Approximately {} readings stored, consider data cleanup (if not already enabled)" +msgstr "Ongeveer {} metingen opgeslagen, overweeg dataopschoning (indien nog niet ingeschakeld)" + msgid "Performs an DSMR P1 telegram reading on the serial port." msgstr "Leest een DSMR P1 telegram uit van de seriĆ«le poort (meti." diff --git a/tools/test-all.sh b/tools/test-all.sh index fc2a46613..d6a4e9dd0 100755 --- a/tools/test-all.sh +++ b/tools/test-all.sh @@ -15,13 +15,13 @@ echo "OK" DJANGO_DATABASE_HOST=127.0.0.1 -DJANGO_DATABASE_PORT=dsmrreader +DJANGO_DATABASE_USER=dsmrreader DJANGO_DATABASE_PASSWORD=dsmrreader # Will be adjusted to 'test_*' by Django. DJANGO_DATABASE_NAME=dsmrreader export DJANGO_DATABASE_HOST -export DJANGO_DATABASE_PORT +export DJANGO_DATABASE_USER export DJANGO_DATABASE_PASSWORD export DJANGO_DATABASE_NAME @@ -29,7 +29,7 @@ export DJANGO_DATABASE_NAME echo "" DJANGO_DATABASE_ENGINE=django.db.backends.sqlite3 export DJANGO_DATABASE_ENGINE -echo "--- Testing: DJANGO_DATABASE_ENGINE" +echo "--- Testing: $DJANGO_DATABASE_ENGINE" pytest --cov --cov-report=html --cov-report=term --ds=dsmrreader.config.test -n 2