From fb4849688de375b26df832cc4f3f148635b8875d Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Sun, 8 Nov 2020 19:54:37 +0100 Subject: [PATCH 01/26] minor bugfix layout --- frontend/src/components/Home.vue | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Home.vue b/frontend/src/components/Home.vue index b6259c74..697f9a3b 100644 --- a/frontend/src/components/Home.vue +++ b/frontend/src/components/Home.vue @@ -15,8 +15,10 @@ pharmacokinetics data enriched with the required meta-information for computational modeling and data integration.

- - + - - + PK-DB: pharmacokinetics database for individualized and stratified computational modeling
- Grzegorzewski J, Brandhorst J, Green K, Eleftheriadou D, Duport Y, Barthorscht F, Köller A, Ke DYJ, De Angelis S, König M.
+ Grzegorzewski J, Brandhorst J, Green K, Eleftheriadou D, Duport Y, Barthorscht F, Köller A, Ke DYJ, De Angelis S, König M.
Nucleic Acids Res. 2020 Nov 5:gkaa990. doi: 10.1093/nar/gkaa990. Epub ahead of print. PMID: 33151297 -
-
+ +

Data

From c9e87b2cc2e0ba0de5b6b2c016b3571e09dca15d Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Sun, 8 Nov 2020 23:41:44 +0100 Subject: [PATCH 02/26] updated server configuration for webroot authentification --- nginx/alpha.pk-db.com_proxy | 28 +++++++++++++++++++----- nginx/develop.pk-db.com_proxy | 40 ----------------------------------- nginx/pk-db.com | 11 +++++++++- 3 files changed, 33 insertions(+), 46 deletions(-) delete mode 100644 nginx/develop.pk-db.com_proxy diff --git a/nginx/alpha.pk-db.com_proxy b/nginx/alpha.pk-db.com_proxy index da3dbb66..649761f1 100644 --- a/nginx/alpha.pk-db.com_proxy +++ b/nginx/alpha.pk-db.com_proxy @@ -1,8 +1,6 @@ # ------------------ # alpha.pk-db.com # ------------------ -# sudo ln -s /etc/nginx/sites-available/alpha.pk-db.com /etc/nginx/sites-enabled/ -# ------------------ access_log /var/www/logs/alpha.pk-db.com_access.log; error_log /var/www/logs/alpha.pk-db.com_error.log; @@ -10,7 +8,27 @@ server { listen 80; listen [::]:80; - server_name alpha.pk-db.com; + server_name alpha.pk-db.com develop.pk-db.com; + + # letsencrypt webroot authenticator + location /.well-known/acme-challenge/ { + root /usr/share/nginx/letsencrypt; + } + + # https redirects + location = / { + return 301 https://alpha.pk-db.com$request_uri; + } +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name develop.pk-db.com; + ssl_certificate /etc/letsencrypt/live/alpha.pk-db.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/alpha.pk-db.com/privkey.pem; + return 301 https://alpha.pk-db.com$request_uri; } @@ -20,8 +38,8 @@ server { server_name alpha.pk-db.com; - ssl_certificate /etc/letsencrypt/live/pk-db.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/pk-db.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/alpha.pk-db.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/alpha.pk-db.com/privkey.pem; include /etc/nginx/snippets/ssl.conf; client_max_body_size 100m; diff --git a/nginx/develop.pk-db.com_proxy b/nginx/develop.pk-db.com_proxy deleted file mode 100644 index 14b2628c..00000000 --- a/nginx/develop.pk-db.com_proxy +++ /dev/null @@ -1,40 +0,0 @@ -# ------------------ -# develop.pk-db.com -# ------------------ -access_log /var/www/logs/develop.pk-db.com_access.log; -error_log /var/www/logs/develop.pk-db.com_error.log; - -server { - listen 80; - listen [::]:80; - - server_name develop.pk-db.com; - return 301 https://develop.pk-db.com$request_uri; -} - -server { - listen 443 ssl http2; - listen [::]:443 ssl http2; - - server_name develop.pk-db.com; - - ssl_certificate /etc/letsencrypt/live/pk-db.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/pk-db.com/privkey.pem; - include /etc/nginx/snippets/ssl.conf; - - client_max_body_size 100m; - proxy_connect_timeout 600; - proxy_send_timeout 600; - proxy_read_timeout 600; - send_timeout 600; - - location / { - # return 200 "ssl on proxy"; - proxy_pass http://172.107.0.1:8888; - proxy_set_header HOST $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-for $remote_addr; - proxy_set_header X-Forwarded-Proto https; - port_in_redirect off; - } -} \ No newline at end of file diff --git a/nginx/pk-db.com b/nginx/pk-db.com index 01efea5b..3c759087 100644 --- a/nginx/pk-db.com +++ b/nginx/pk-db.com @@ -9,7 +9,16 @@ server { listen [::]:80; server_name pk-db.com pk-db.de www.pk-db.com www.pk-db.de; - return 301 https://pk-db.com$request_uri; + + # letsencrypt webroot authenticator + location /.well-known/acme-challenge/ { + root /usr/share/nginx/letsencrypt; + } + + # https redirects + location = / { + return 301 https://pk-db.com$request_uri; + } } server { From b7480667a86459a4580b28e4ac6883bcca802ab7 Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Tue, 10 Nov 2020 11:21:00 +0100 Subject: [PATCH 03/26] closes #683 --- backend/pkdb_app/studies/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkdb_app/studies/views.py b/backend/pkdb_app/studies/views.py index b0bf088f..5b3fe3a0 100644 --- a/backend/pkdb_app/studies/views.py +++ b/backend/pkdb_app/studies/views.py @@ -593,7 +593,7 @@ def _pks(self, view_class: DocumentViewSet, query_dict: Dict, pk_field: str = "p def data_by_query_dict(self, query_dict, viewset, serializer, boost): view = viewset(request=self.request) - queryset = view.filter_queryset(view.get_queryset()) + queryset = view.get_queryset() if boost: queryset = queryset.filter("terms", **query_dict).source(serializer.Meta.fields) return [hit.to_dict() for hit in queryset.params(size=5000).scan()] From 77eec3590d09c6a83a36c8953b5bf6d56e9d7ea9 Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Tue, 10 Nov 2020 12:47:45 +0100 Subject: [PATCH 04/26] updated info closes issues --- release-notes/0.9.5.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 release-notes/0.9.5.md diff --git a/release-notes/0.9.5.md b/release-notes/0.9.5.md new file mode 100644 index 00000000..e66cbe2f --- /dev/null +++ b/release-notes/0.9.5.md @@ -0,0 +1,8 @@ +# Release notes for pkdb 0.9.5 + +## New features + +## Fixes +- interventions were missing in download (#683) + + From 0f1d4eef90cc3b16f229957cf2793a34113b2d35 Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Thu, 12 Nov 2020 15:16:12 +0100 Subject: [PATCH 05/26] minor --- backend/pkdb_app/interventions/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/pkdb_app/interventions/serializers.py b/backend/pkdb_app/interventions/serializers.py index ecd889df..d861fe84 100644 --- a/backend/pkdb_app/interventions/serializers.py +++ b/backend/pkdb_app/interventions/serializers.py @@ -2,6 +2,7 @@ Serializers for interventions. """ import itertools +from abc import ABC from rest_framework import serializers @@ -320,7 +321,7 @@ class InterventionElasticSerializerAnalysis(serializers.Serializer): measurement_type_label = serializers.SerializerMethodField() choice = serializers.SerializerMethodField() - choice_label =serializers.SerializerMethodField() + choice_label = serializers.SerializerMethodField() substance = serializers.SerializerMethodField() substance_label = serializers.SerializerMethodField() From c7e988f4804a86493a3f3321e674380e01650c0d Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Thu, 26 Nov 2020 17:10:55 +0100 Subject: [PATCH 06/26] bugfix in characteristica counts --- .../components/detail/CharacteristicaCard.vue | 21 +++++++------------ .../detail/CharacteristicaCardDeck.vue | 15 +++++++------ .../src/components/tables/GroupsTable.vue | 2 +- nginx/pk-db.com | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/detail/CharacteristicaCard.vue b/frontend/src/components/detail/CharacteristicaCard.vue index 1ab351c8..efb3e97f 100644 --- a/frontend/src/components/detail/CharacteristicaCard.vue +++ b/frontend/src/components/detail/CharacteristicaCard.vue @@ -94,15 +94,17 @@ }, subject_count(){ if (this.data.group_count){ - return this.data.group_count.toString() + return this.data.group_count } - return this.count.toString() + return this.count }, count_label(){ - if (this.count == this.subject_count){ - return this.count.toString() + if (this.subject_count == this.count) { + return this.subject_count.toString() + } else if (this.subject_count < this.count){ + return ">" + this.subject_count.toString() } else { - return this.count.toString() + "/" + this.subject_count + return this.count.toString() + "/" + this.subject_count.toString() } }, error() { @@ -153,15 +155,8 @@ } return value; }, - card_class() { - if (this.value){ - return "characteristica_card" - } - else { - return "characteristica_card" - } - + return "characteristica_card" } }, methods: { diff --git a/frontend/src/components/detail/CharacteristicaCardDeck.vue b/frontend/src/components/detail/CharacteristicaCardDeck.vue index 4ddf2347..40ac987b 100644 --- a/frontend/src/components/detail/CharacteristicaCardDeck.vue +++ b/frontend/src/components/detail/CharacteristicaCardDeck.vue @@ -14,10 +14,8 @@ - - @@ -38,6 +36,10 @@ type: Array, required: true }, + group_count: { + type: Number, + required: true + }, layout: { type: Boolean, default: true @@ -71,10 +73,11 @@ sortedCharacteristica: function () { // split characteristica in choices and values and sort them - var choices = [] - var values = [] - for (var index in this.characteristica){ - var c = this.characteristica[index] + let choices = [] + let values = [] + for (let index in this.characteristica){ + let c = this.characteristica[index] + c.group_count = this.group_count; if (c.choice){ choices.push(c); } else { diff --git a/frontend/src/components/tables/GroupsTable.vue b/frontend/src/components/tables/GroupsTable.vue index 24560421..0041c077 100644 --- a/frontend/src/components/tables/GroupsTable.vue +++ b/frontend/src/components/tables/GroupsTable.vue @@ -41,7 +41,7 @@ --> diff --git a/nginx/pk-db.com b/nginx/pk-db.com index 3c759087..0df56de9 100644 --- a/nginx/pk-db.com +++ b/nginx/pk-db.com @@ -18,7 +18,7 @@ server { # https redirects location = / { return 301 https://pk-db.com$request_uri; - } + } } server { From 4a5012f27ea3cb881991751120897c6342410702 Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Thu, 26 Nov 2020 17:40:22 +0100 Subject: [PATCH 07/26] working on info_node in pkdata --- backend/pkdb_app/studies/views.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/pkdb_app/studies/views.py b/backend/pkdb_app/studies/views.py index 5b3fe3a0..8e4b0d05 100644 --- a/backend/pkdb_app/studies/views.py +++ b/backend/pkdb_app/studies/views.py @@ -30,6 +30,8 @@ from pkdb_app.data.serializers import TimecourseSerializer from pkdb_app.data.views import SubSetViewSet from pkdb_app.documents import UUID_PARAM +from pkdb_app.info_nodes.serializers import InfoNodeElasticSerializer +from pkdb_app.info_nodes.views import InfoNodeElasticViewSet from pkdb_app.interventions.serializers import InterventionElasticSerializerAnalysis from pkdb_app.outputs.serializers import OutputInterventionSerializer from pkdb_app.subjects.serializers import GroupCharacteristicaSerializer, IndividualCharacteristicaSerializer @@ -594,13 +596,13 @@ def _pks(self, view_class: DocumentViewSet, query_dict: Dict, pk_field: str = "p def data_by_query_dict(self, query_dict, viewset, serializer, boost): view = viewset(request=self.request) queryset = view.get_queryset() + if query_dict is not None: + queryset = queryset.filter("terms", **query_dict) if boost: - queryset = queryset.filter("terms", **query_dict).source(serializer.Meta.fields) + queryset = queryset.source(serializer.Meta.fields) return [hit.to_dict() for hit in queryset.params(size=5000).scan()] else: - queryset = queryset.filter("terms", **query_dict) - return serializer(queryset.params(size=5000).scan(), many=True).data @@ -761,7 +763,12 @@ def serialize_scatters(ids): "timecourses": Sheet("Timecourses", {"pk": pkdata.ids["timecourses"]}, SubSetViewSet, TimecourseSerializer, None, False), "scatters": Sheet("Scatter", {"subset_pk": pkdata.ids["scatters"]}, None, None, serialize_scatters, - None), + False), + "info_nodes": Sheet("InfoNodes", None, + InfoNodeElasticViewSet, + InfoNodeElasticSerializer, + None, + True), } # Create archive From c60e4a17a6ff715a8238607c21501b1e1b23c4ec Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Thu, 26 Nov 2020 17:58:43 +0100 Subject: [PATCH 08/26] info nodes in download --- backend/pkdb_app/info_nodes/serializers.py | 11 ++++++++++- backend/pkdb_app/studies/views.py | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/backend/pkdb_app/info_nodes/serializers.py b/backend/pkdb_app/info_nodes/serializers.py index 3c288e84..5d6cf529 100644 --- a/backend/pkdb_app/info_nodes/serializers.py +++ b/backend/pkdb_app/info_nodes/serializers.py @@ -247,4 +247,13 @@ class Meta: fields = ["sid", "name", "label", "deprecated", "ntype", "dtype", "description", "synonyms", "parents", "annotations", "xrefs","measurement_type", "substance", ] def get_synonyms(self, obj): - return [synonym["name"] for synonym in obj.synonyms] \ No newline at end of file + return [synonym["name"] for synonym in obj.synonyms] + + +class IndoNodeFlatSerializer(serializers.Serializer): + sid = serializers.CharField() + label = serializers.CharField() + ntype = serializers.CharField() + + class Meta: + fields =["sid", "name", "label", "ntype", "dtype"] diff --git a/backend/pkdb_app/studies/views.py b/backend/pkdb_app/studies/views.py index 8e4b0d05..38aa8ec9 100644 --- a/backend/pkdb_app/studies/views.py +++ b/backend/pkdb_app/studies/views.py @@ -30,7 +30,7 @@ from pkdb_app.data.serializers import TimecourseSerializer from pkdb_app.data.views import SubSetViewSet from pkdb_app.documents import UUID_PARAM -from pkdb_app.info_nodes.serializers import InfoNodeElasticSerializer +from pkdb_app.info_nodes.serializers import InfoNodeElasticSerializer, IndoNodeFlatSerializer from pkdb_app.info_nodes.views import InfoNodeElasticViewSet from pkdb_app.interventions.serializers import InterventionElasticSerializerAnalysis from pkdb_app.outputs.serializers import OutputInterventionSerializer @@ -766,9 +766,9 @@ def serialize_scatters(ids): False), "info_nodes": Sheet("InfoNodes", None, InfoNodeElasticViewSet, - InfoNodeElasticSerializer, + IndoNodeFlatSerializer, None, - True), + False), } # Create archive From 9973c3db5e87dd2565b52bb7bdfaaf48904c46d1 Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Tue, 1 Dec 2020 20:06:21 +0100 Subject: [PATCH 09/26] Suekrue added to avatars and information --- .../assets/images/avatars/balcisue_128.png | Bin 0 -> 49918 bytes frontend/src/components/Home.vue | 2 +- .../components/detail/CharacteristicaCard.vue | 2 +- frontend/src/components/home/About.vue | 8 +++++--- frontend/src/components/lib/UserAvatar.vue | 4 ++++ 5 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 frontend/public/assets/images/avatars/balcisue_128.png diff --git a/frontend/public/assets/images/avatars/balcisue_128.png b/frontend/public/assets/images/avatars/balcisue_128.png new file mode 100644 index 0000000000000000000000000000000000000000..3ea4624b6ef05caf9c891020f78319eebf2de84d GIT binary patch literal 49918 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%0A}cf1$%m-aLmcN*b1eLci}I|0)Q1GRHg+Vuk zP%55e03ceHgixM{P#OSW)cGQd35Z8dNwJ8r?TOrd^K-H>AI*b)>i?nXyu;akzdjzL zL@9|XQHrE$RT0##8KYX18jae!w%U8PQhV>wnzhxe5u^5sFWRV0Z9(ikp8WoJt}Fj1 z`F!sCKIgpOuXApPmPS!Ic2*M751HzuNm}29Yr{ZETHLc_p~3YYKi(C*z_qYYM2{Q3 zZLF9;cw5zdtDJj;J8DeQn!jwrQ;b;E&N(uHv?B=JVHAVg)5kMaZ6~XV%KnRS52l<` zdUkJ?^N&4{P7S`b$jxlHl^Ep33m=b<#9f`$q!uxX=}rwyk3Qw2#*oX?qwQyTyb-v> z0Wp3M1(D)chfKoKop)13Ayh#MQE(-)C^*dQ?PSL{9-%Ox3Lq#5z{aNfq42jy%L_?K zN@#&1g5Kn%FA-p~^}ZslKrh5x3sCgQ10DvM2r$?ok_kM6G#}g6Bl{rcQA#D77BndO zvIoFV9k49RCN0t+?nmy1dO8n;`aoL3<*BK~oD6q=aD9Xk&F(b9IsAEHJQW|`XXP3P zZMS(wjlIfZG4egn_T-o}{@W{1K3K{ubG}9SNQ7L}@ln)W`7dyNyL53uDy78N<`%mx zx*uR6OD5la@sfDi%)|4Z{QN%tR%cg(zfnQ#cO$eADlHl$` zk<(ledG#t*{sRd>)~;Sgvj1=!N1hvj0MmK1{EkS{0(8VH08!HXJ2y80)71?Qo(uzH z;m+hQZN`2s|@(-c|EXM;#yJPV|Gi%abjmWQxKrL4UcnpMvNFdo)-!Sg$lU()Hl^P%2ay~O5LUFcO=p34qpUA zg5Elkf#eZ?D@ssoCOl1uBunIk@Vqbh3%vIzUA3@qQvY!toFgob@E6rMmY19t(Hh+G zsM1ncp44bu2gF>BC@6Ep3T^Z>`3nH{(ELj`WX2s`k3^o7a#0M+3$xt=*y!`^o#e^NIe}h-tSqd% zxtiQvCuXBjK_TWOV;QgSxFWr-1Gl!Tm7!7PuFjikhy}rjxr7j1f()o|Rx?L)->cRC z_JG1JF_ffulE#ta%tp`lL}~z$$!xNDL=defQj+-dkqF@LDqB!L*fiC&GQt)G2a*t4 z!*bT4pFzH?tuWfDRZ+Y-Rh|meEGh?yV$;zy{hrxC=m_9zu3T_z#JN9%%{RgX(_bG* zF~lGHyq%18?bMItiua9IL0BSfE3#c-TFLq6e!J-VM6f7k$TI$RQMs}G5X-3%(?>p; zRSzz7F@!U#J2JD(dW&bt@Rq^My_b4-LPPy4qG_eX#W6K`Ts8Q%ODmvhEpk#VKU9-w zB~zJ3W1RutdamSHr%THGMTPrWNVcoy-#ZJE;dcP?_&HlcC$x>jbi-z55zi}t96P`S zOiu;^d^N<#{{1sLclgnre*BP_H%ndN?iGlbr?8GpQ+2bpHuCV$)e5SGOo8UPIyyNe zSaNhGXNo#GVrM|vT4{nz-=Ny?L67&vq4n*?Mn*;r4h)Zd@%QcPMItVc@yYEklG1>Du5y6tDZb}nX%|Fl1e zF;j(yesguKMLLNvfzw7f^q&^?H`xm_+a@MQHr#B~YLF+6ZJPuNLTuRO*kCZ(wSH*G zk-y%gT^dGgyvGb(F8M>zaMJw+=cIytY)s0qw(b)cAAMP3q7vy)&BNF}0B;8X&`*(5 zl;Cp*nXGH7=azq(lMarjV^jo6C>d4hr5-*Hk`NOE=@QGPJ>zRcR+g4Z$h2z@BB
E|m(gQtic|@AR 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�e*)j51i3$fAI+KQ!@=-=8KCH$$KYyBGbI#d~zq5^&>KLQ4NG9_Aa%y~oi z??sg3FF^_kE{JaZOo>4V(iczOCHL?idxrzs-}x5TP-5)6bG%>ecd^;Gv-MGmQFFYg z9$P@x0Q)64lvoy=H0{`^^q%KgykIX+MTeZ#J+Q9jwoUheIQUS`n` zY-zblUUa?4+_*BgTW1v(r#8`G$SOq)f^)&DOr{z_duqn>hk;XBpIYXhZJlj@bZ;{- z{5@B6EX`;`M_sDwr(G}@%<;>`QOFKS^z{rxKu`>yiDX9Z;BV&Ne-)xe|ImF?3*;Wx z=SIIBDJ@m?RG*rY>GoYWAT~xMskffazsvMWTE9dLOa9jbbC`Lmh#fpM;;h>lXQadNUd+Z=Fca=|4QO_cPPmjn=%^x-)#6^f3&&O}zW8&EEwY4UDG zoV@sPstD^^&-d?jCW~R`!vi0W=gC>i2|_znsa&!bOZ+0be3EHhzk)*ZCCKcD>WBZB z?hF&WWrLXyI-VMNg-wGdecjo<(}X@0KJ^7 zx`uk=jKhpOtKa_l{P6%fwUQi6DyaXIO^fkc2uSVoKXWPq$Q%R`FM?={OSyi>Res1= z!7MoH*3PoCSo9$ko3pBJ7Xokf+An>bm?FQZs@56q#Xs%=HoKP9CcedIK=QRN%Bo~W zYqsYa+kM5-sohU{U!AgC9iR(xWXLfninl_p7Mc)wfnEvxTMo57H!)`YmX?K>>9Kno z3O@sHJAKetiQ4o#K?LHrdEUMON}$37AI1U|gOU!iU4P)kfkF|a4#I#VjJF2Bq>gXU z>}xnC{Jg>3sv?=*taDMEltBA#NdBvwguCI6ZR6e}e!B0NNU;!Tz(?y>V-tzTR>%9C zxw8_??_q`7-EIph?=CBoM1|V$ zthBEa*L5kGBLf2;G^v#GT}Y2NHmCf4pysW6r-$jiD|ve$hD^Tehf=BAx63%SK;Do2I!g}E<(tY3VNh2}#_j8c9B5fMPX z2!1yhHJ!n(!7p?9x;-&@*{+kWTZ&sNQ9Aim50#|z(_`PS`~9)$MK3&4M#se0z1p4J{kVvk?_Mz)jt}wQbRf^chhO#DyELBti-GQ z6Z5jkBA3OSm6qa4BbFikRZ-VRV_ol0cPrh=HQ7^#KAX=CHB%X6DK(dTK|S6AS+1%} zG**63WX}l8J#Jc+sr19P&#M1G%-;(C&&F+5V0-5yt)|3YxbTzUtGnZ$}F!T-7@C^8}*^r|h(lfeU8lA7TMbsY2e}5>-qCwraYL9$h z$-W+<@Z4_D($M$q%x5C3jsj-&8Ss|` zEW10JHpCJcZ~e)W1Sgy~H}iLs;m&gT*dJRRok*U4-}(u>iyHSTng;RGdG%WIbpE_k ze;yg5%-Vhn9UUJr3XtNb;3mblj`b4Xu^aXi8LCA9$1W)3fyIVwAbBt2$9rPx>xXyN z|9xUGY7Mw9N~%%xs9#O_yWv^>qikR0J*)0=YUOr?p>GZ7c^2jB=?Nz>}=%_A_lbbsZGZ)%3E`(w9oF>lRcrCUb0$z`T zss8&}nxZyg@k>5mFS57WRyZ9~OsOGF3d{inQ|hiLX0Vsg&ZQ}WSL7Y255?lGL5cw9 ztP+4==Ax(y|K}Ss@_D(Nh3`I=Pq^eXZf>FKzWIeP)V#ZB+*C-3`-gZ_f+P0tVB!J`F%^pqA zW;={}pt#@5U_{<+i}aAV^N}oz0yO7n{U0kpHmgt`SXMtKp#dXwA zvre(T8r>RK#=%g8{E>vW&gPQyPjt+6>!FvR#6u1D{QAoJO57^%sL;mx57lM2H*PHk zit;>kTL|e#eoq~o>WZ2NC6O85eI!y z+B(k)qn1&%zmd6?`wZ=8pU(Z@EU}}*rNx!}i-3R0c6u$@Zr0X27o*Zw0~)=7r<3g& zMcm-I7Ek=>P1kJ~qhQCm{q*K(^zr<2n%z0HJ1!;ZqpR!CLnisfsFzP3c|m6{l&rl1 zT+L5s4Kg)Y=O&};d)X2^JCIKkVGPU)?1)PCWqAY--a`UCE!@22sjH(FOnRSBY$dH1 zzW)85H>><&#T7zzKrX1SBZ#sW5)w`TVJ%%?g{Z=~^~sADIynoxdS!}8Z~OyuRQ1^C zB=!QG$MC90-1^z)5cMM?wTXB6Us@x6JB#gMWODcgOfps#J$Jk`DOl>a$sgD_onEc= zHfswGjW?KZ57m^G(#sj?JDs5Dd=AOkK6#HIJ>)EfmXKSUL47-GEX6kjF) zzSe3_){8hw7)yKYp{iZ5Ig*~cXI{TRjP>DkV(W;fI+;=HFSDp6U(WE{1zsK=q*Zf^xEzvgtIy>*{y=d8rD#*&R1n z-G<*j@Or*F^|Xd%`r-XIY3GN}LD~Yy?^DZcgnXNPxCU%hlWSpg3gyAsv#bM z`Q*Z(e*e$NvXSY3*v{znCk&cZ`C^n=aKxH{ES?Ajr!f%o7*`FK{rk`s<Z-6xYqx)|}doQFe+ z2NZB;H|ebD9CbH@T0RYRjCEp<-DD0D|G(7CTgB(DdgdOP zybMu7E-;|4s>mS3oQvd55O#2hkqsPw*xOR$h)Oeh9?L4Vi%5+y%@v@`|Ff}(`Y;2* z>A#5vAxJq*T;jCqT!rl%GOQ3WLQ{P98CG3Oi}UCYNWOw7^`F}*L4f$k#W@TH`=@MF zQI%eC?9owY_>IY#UHi!<gV;?tEFwYVwet-&39+0dQlbmn-OXEoY3r!ej6UYa#k| zZ#Xj*Zn=ck&p`~>$)Ebb^!}@qJT|BTP?bBT++B^dmr)rorf!fsesf>*JI+=*mq5@i zmMpMVR#Z?x$Awp;8uLUu&%R`T2?B~AsE;{he3Zzh7&w#~sf|h65~|o8cqo8S>LjVb zZamZ%NOkRpFu;ZmtTYpfwZgsr^ItEXyuI3q%^Kv}-uY0Gt)btgGGo$Tvz_G0J^1lS zm`z5rqiUI^^E~eObJx=TzRQHR@x^#*(S+jL$*6jM1V}Mn_Vc_q^rvfE_RBjsKj3TG zgzwslHNY9BP?ReX6%p|r(g!8)>I>ljhSt?DI?cNbgnZ7_P%l2BZY**27ZHx*7ZgH9 zWWNYp6I%C`{JJ(Y*gg+A}07eDa z0C|+sAf@;%55l^%l}`Wa36xU%c8_1&-)3(y_&<^cA59BVeLM3g&ls_Xa|kevDlNGB z&7qAwdaLSrG<+IN`MS5`6X&FDb;^)V@r3E?lS>6al)2){mpk)nWy*c>z~GdYHuIKpB=WFj5F4k_Z@wankKipU(khHCK zoxab4l~iA@B(g*ERzGrwy*Up;{3I~HPe(mGJU*+{8t`o)n|QM|)hTJD`cc8fCgsV_ zv^wJw?bFF(ItK8&K)P~lS?SpHZymHjWyI3g4L8u3k98g8JS8;CJqpfuCz4&6KI(Ze zvsp$Fp;33}++L@WFzcA1SX)NI#f*%2FQ@}ZG)7Xl(Dz=_SX((fJgc_zt4zNceQ|Av zd{v<3j#iWYFii}w!&kcoU?W!S*m!y{RK=Cvc8DQ&YQMgzx*AMP{s;A<4GtleEu=fI zHsEbL>zPa-)Gei45qbTL_Z%cI2&djH_B7z=BDL-3kG6oiz#u|1&fbJ??)=R;{^q1= z#A;Ra=0fuKFN*)G=qwzX;MyoYkQkwikj^o>Ll_;>DM*f#4r!5YknR#3pp>sP!lX+C zq=wW`8V3$U7~S#h+rO~w-shg@oZq38pBYuDtocdrQX$ICqwa7 zNX9@|_?An!s9qyl!A|?~?m>A|+LB&{sDo`JR)?T|?C!>%H1>J0z`W zxOxAlo=u1ADo(S_Td_Oh`mU$z+AiezaI#0SY;c@f+6(59M{dph`FiPRI2U8x!~M0M z9{%x_mE%>b!@n5C^MeiaVBDbH{k+{0{iE@re?4uz?{xmrT%MsiD3sd8Y$g_W9<#qr zi49QPKL0FveXMj%;huAsd;cf1^-I)g;o7|Z?XF!Ydie}By>YkSv3a_nq{KMIb~Ci_ zxAQQh@*#;KM-zR~BOx-))(s8dIxvfpBN#GkQs3nvRbuIx{i0wCw!74}FF4^LRqvzF zy*8)+?Xs$5V;{)~PBkg7JV51{?%{jIXmTwUj5x_l!6*_vLrO2Tv_7duKj#1+`#OAv zzIZ6RB-vT>j{c{;XN3imU>p>luE35*$OcQx$@93kzei0jT~knu`5$g>Y~~v7i@o8e zqS#mZcZSAc@C8K*1so^$suB>I#h>zx?twVhgt)(THps5Jz#&< zl>XZZ*CKm$qqGdo53N#ZGc}8_-7vPAnrvfsDo5sMN@Zo=fd!=7uKDm(!K6?4mhL-D z&@g)R>-62g3)8r?=$8NhAG+ii7rkFwQ-UI8O9mE)GB(Wk%LEmO zZ9*&xwzRFSJA?h2VOOA5)c1?eGtTo+45g8EU!+0`7#mRlF&Ey)tqOYdK25txmgvi^ zY*dX;h5H|yjYoUlx@%L3(2`+p@&yX}u2-4JT{jzqfj~BF0U3;N!>L&uJC``n43R_Xobl z!X;e7GOjF4_(DnwaIt9lbh(n{P8ab~CNJs)bL7KFbh>$pfvb4Ygv+siRZbNi?#aac ziChJ-B@EUaPI>_yP-z)*FsiR9=NTJUAGeQs))w2D_-Gg|Hr5LmfU7A+WoAOeLJ{XMbxh<&&}L{ z9_p5`L+L+%^6-~SZ6O8m6i*0J6fPHam#tJR!nn)0n^_KJJdwMVBs5rU3#X;cB7Xlq z80h;-5|5&`(?6%BHTYGyye#X8kznoPRL-S5`eiGnD|g7-%_Dm=#n2NiKIam|m+TzE zso{ztr>554vsHIuTvB&ciO~-^RkqaS`yxo();jENh%5~AswMV)Jpyce==Li7a3-Og znvhi5J6tgS=bDlSKTMb+GgA|JjYgwKo&0yt_S#lMcd@rUYqvf3n{Bus)875X<{utl z{c^75F1=%R%H5xj*k?9M_nG&~xxKG;W_Tzxh|zva(HiVtX{HVSb5Q*O-{BeP#%e>_ zA0eK+?=q>;#=uXwifLsP4d25V7s9hcHzVoq3}ggIM&W2Oz2A@w2V{r{Rt?l(vv{+N z(9Mx>5==ou^)qOlZI)s&l$>$`i0c2i>_6}*JH^t!VgCzQ{Hbgr9k|N!S}GBJ8|P}< zN!#e-i%b<8Wii zr0^cDx_5chX%56vypIuMCxvI*13027$RD_V10{g8i#agJB4vOzgfi^U3}KNTg$9=q zd*jj<(@K+XRaEl)GmwlR={Ix>a#{l+ao>ulEE}n(TxT_MrNo~BmqXbC63FA->glel0L08t_Ezcho9fO@~F@E<}~M?BM>M-CbR&(fTWru#fPv$9XU& z3^T}pXFgGSf8Sns_Bk6 z=dj<5!;vz=!svcOacln#+zRdy>+Trn=X?#m+qE@$7J0{c)ZO8+h;>gSdf3e@^sA{O z?YOu;_@tdQ11HE)$qy{5oOnr|JLAPRiYuBFFIx!P&|sX1XlVvx~G*RxV)#k<4IsFR@HoBWc!)FdOCWECIwv$M0+Fzl^BAujOe z7a+-c4fviAXfl75KxcP%G5m4u3VB}s-#DoL2{AG5ce+t;RVE*LvOZUaG?<)iVBzr- z#wsq~r}~7i5i{ix2<+A{8oJ2p9&7o2lk1T?JMl{vL3Ae}#oM1}^r+`<1EmchQTclwg2ht&3wla3GyqfK5xId}%bL z+$~@L`G<6mWFLO4%HIG#=T9+1E=L?4*!Pl)Q|8EF*g*uO157`hEepn-MJ0>$%xdSu zr6gbv%JX7zOtE??10CGL;5-IV<;p&m8oqaf43@`{&dLg_Hkg@ge0;o9G8H@T4mJjB zZ22CC!~)Wi-E2`6qesEr?GYH(X!3yoB>1tqSQw_KtZob^Ivu+Cr4*?E+-CPw-vgE( z^xh9lN~V5klTPC_BoA4(7<-(nb7_wtBNy`L#*5`Kj0dL09diIoX!rLF&oW(%wLS&Y z-~!JkH1ql`GtRo94!zm2F{jG*e-|wW>BWbckOH$4wZTU_;so~xhhmi8-rnM%dT~sl zXc{~KDZ@cP!~qcX6D=*KXp}38HbxQx@X@>Kqx57m*HSEbxjFpt@87+7zzJ|nn`V^B zp~$ymh*3lc2eZXuwh`%-f3a|-wzhNZ{bX79?U^BiV@T=!A=`bBgLvs9;$6$)(`y@m zFRGychidEVvKC$o-4&{T;>b_n!$K2sRwWW;$rN$E|FNqJlle%AMzu9%4Vocag2Y{8 zi-sEPuf`t_s4((m8^=$CdB8O(YvxHQO?Zw8SlP2EkQ|xN_fL?r@P^`wOAlwh7u{Pf zyj(1alG@-;kP&E#!+^9jk(d3q+&DDson?muT(fzsB{sm8LCVTR-@dknajn;on=n>_j6AV*@!02|Bc@t zpOrLmZLc?LkN^)tT79Za0dvHQp)6hNXRAq*0C-j?BD@FqPW8_DqHeW?i>o26eM+T- zU^c<0Y301X$s>~DCuUl6)#7Y@0z{X8u|DeRk)M>3lImQ^c;ey}(carzs+LXDjq*l# z)O4aW3R54;wzYJbI`Pv|;aNj>)bRX?>%?C>?@Ujd{IRrtHKxI|J533lBFW1NG$q{4 z`Y{*U!7FpbYYh0%P@`LTX10|hv6&|4S%MfK2EJ$V>d{mNsIt>4Yc;<`df(m@wAuRJ zY2I&U+J>pqUo>?r+)B9%-Urbj%0Q%-Nj4?_s|rM67qtLNQZiyRT$R3|s>uCgqM-e4;R2 zYj+04YI-TAJrE~%X0a2zZW01k?Qjx$^?&KC3UD`!?fuQJYSQRS z*`i5S7Usy^uC=Ab)zF+x8@4>G^76r4!S$TLHUF z?;ZdwgeILj4;Fbjxa^shm*@0*#9b(9pNSI@aD(_B$IWN2#|R8pVqD zp%3S*Mt};K_$CX%Ry=)f5OjCEQ{$_Kbo1fy)=UW2V44K*<1=~u=I~qFkK4G(9(OB> zYhd^Hai{o_mE6*j99#H-BEG1ahjw8!`45>yElcJtbzAe-_XC_*n?5_e;g+?F^v1K$ zm8jds`}Cg*LCMh`j*2(i!yP3hD=RAjVO{DU<6rqK>8D5D8?GX z`6&;iDt$Fw!NV36UV+lLEI|Icy}kVvj{5Tt&_4O%{@3`Fzi>%~l-wv3i$K9dXahf@ z^&8UOL%k36n(9uv>pE9{U+RWgeodd}PWck{={Nt291i}f96=Yj>3j2sW`*>`Xi*ncTf0X|mY5^N(zvwom2JNdyAm>Xzz;k=*1Kl&svlDf3#Y>=5kYDEwRns7Ozq#H&ABknR3*erI!`LH>FgHHB3Q z7Ucv$vuorGkyg@nCIVvLO+6e7Yk%wITw^}aSo?mT4fc8_FAIHzGRN6_wx+vf7)2ru`UhIk zftjhF`9p1nEy5h&Nw1?xh95Yp0+Z*CWS<`VKOi?N3ESOhvTGFMc!Z&i9ZYWzj6AuDskZy_0}-C+m}RD02CMlia$nPE{dPv z#htG}9xFH05MYe`9m(UVI_&kdD8~AxNKjIMn#RwwfcBdkj1rr=k!8U# z4!)z9gO7&vws-aP-s$)VrRM&8I}BTr#Ko*gmjltIG9Eq9`KlN_a#t8ostf}o(o@?p1s=A#cPQE zB$New^dX(#{Zu&@UXsy@8`rN_UqET50!vs86eHRQM3oiP3p3TNw*d}F0DiKG7>;hq z=feY7N1F3E2p*KO6L7qxVUFFxqh|1972wM^$@hK+#KfY`<`&k7kba&K%p>ZhOY3Of z6jY>^`sUyArUn8iXW2 zc113Z&3*q7)_}n4?`%Lr%S2p8Ml{u@DSHqNNg;ntsCd!>l%HvnNAuE%vmX`U^JcI@ z#R06KWC?DWR0z0u{Vf&g198$#V2^95M0Na6CHZQP7j;jagtW<$NCtvaS5N3w?d3sY zpV+gAun~aGf7=&7o&C5SGL~T7=GxS?G?etlMQT7QtxT*nfFQ|pHl0KjO#&(A~ncqGWB@1@jtK+HzG#GAdh3h_T8 z4Q%FdT&*SE8L4ZmiRPbfo>7?UM6&)!m|a2bwAbfErIQ8^r zYfDcSHG(nr5KJtI@zXjJ2tE68%p%1JYc?N8JLl!L@6q9&Ch@FJb#pB4EyD(v5R%4g z78?%6RzTe~KX}m_%WY8gC4!Rxo61T65peOBKjJfLE-+#yFTI>Mqoy6&KcDQvq;4Hx;7isO=ql{<%FLRQ^Ys2ocD8QxQTh$w|;G$+?_ z?q)};lb3!SYF{6fqk!Q27-3=i}bU#~N864$f#BSgg<$W`pw$&rBk z!0$A{x{9j!hHy3ei3T`=HZ}&-{Tey{nD8iCIE6C-&dCXWGr=Jf51eLljBoV1?C{6i`AW$%tyIkzYZZz1K)^eP$Ne}+AJop%N<_zPpb8(@*#Y#{`c!mys|pl zepEITbGw07U~cqpLF0?Gy?_+iD?)w@i7bNf_=PSK9g8c63%(zX)Gep6{`c?Y!>c&7 z(fnUg!;T>yDHTV2N>KtLynahh*INJW<5FIj$DR9dqxWs3(4?wd<$4FOlVeg$<%HFl zO4Df0tc*ZIp(>SHI*8x>|*XVzXKR zh{ICfPEq&PWg8J$ca)^dyc@FP_p^mb=3nCUnyB2}Z|-f+)pCM9n{e>rAs1rQ!P?Q| zhv9)x%effy;_t+Ci)+a4kJJkRfcPH*vQtU{h;6kHX@9?bl55uCs>fia*7{)7tPTrW#0$984PIbIa@qD%pG_i^}=Th2J%6kN-<6rq*k%hCc)2- zhq`NA)w74PYY<^qC>d(c$TxW0K7!z{;mvumpCt{b1yjm%hnC_Dj>b}v9denO8JZPi zCOS3g1EGCOcs?x(rt|VQ ztZNGNBI%8SvX1GTnCRz#_q6umU&=tr>fK^3zq&nA(Sj+t5F;HpODokccdC<7{NM}0 z$v?0DG2+6T#SyojcGqWi5s6~7JbtUYuTzj$e?(K}7DJmmntR~8azU)Zg~Ps4EjKk;-``=pp?j;BPc*JhE+{`-rpSHBvAkRh1E_##a8to!WTze>J{*q6z9 zN2@xcRhz5#szm*3)gKZ;1g_ETb5iWH#Iu7Hgdy+fcI>;I(n1_h8y6%)oxPgO z{LMDS97&BPnXF(?D11tH_oe7rJlhzQf@b{{=8L(~RK*GzrbSY{5}J~iqn*S1m}G!{ zP?;v&?dcq{*9pkM4tL1hk%)c8+;^bb%a0onqf82FFT-Vv4Dn)C`axf>uCACGqqgRy zSA#Fs-#7fVZDdoQuD6mlq*`kuac>ne-a|Ua~Rdk(_HhYYHnw22!ph}z4MB5kQ(+c!_O{A>d-M{RElN2yI zz3ti4*>&9YpqkAU!J4Ky{8kgV8n%>FnU)|p{%5r!+qitYay4Gvlug7H&XZr=IjxvQ z#^a}qH&CXv&D@ z+V!4v-_e^VA8P*odf90q$k!Cvr&Km&*B-i8lv;Q{&30EvoS3P2u^uNlu5`K0InBD= zN29f2cn`nBcVm$^G#7CNYO(td=f4gN_ua0)C+F4v;dUE*pB0c)xlJSE>PUKWpWEY` z6Yh-Z|!V+QI2P)t^x@ksO=9-2q&H zX8>$}&VzEPyjBvEdVHg2I>}@BS@!W?4b4+WgCBj@>o=9)24|$bZ8ESjT)p+b`pNJj zF8~Qj1@8qOX&oA5e^J-HwuerpIqkiv=tqL@U_7ERN`ub=kUg9nPN^}+t4pvr@(kYi zXN9SeA2DfPI*T(hp+ukNUWzF}p%)iurWJ2 zHPi@>SAs@bF8zCbh0LyB$~_>kKMomvOU18Y@kjfgpa)wkD(d=Ca45s#D&>RsfJFx3 z1duq$r&#$tUVn~zQX)t&S7;O*oRdaIK!Lmj0n1o#zPKW_`>F5DGtSoFPhk?^`;r?_jV3nC(92jwcNAu~HgRlFTy0qldUlW&uo~u0 zuiEa1)>x6W3r3T{50!gocamD<_W!}wNnQ`Nz4ww=AT|6FMME;Y+o=JlGJ(u{bQEU{ z@14hivey64ON+*dokQKme%#+Gy{_9hpsvJUMY3<(5CiBIr|NQEBJDgz5JpFqL)cY^}v4ZNd0K8_EP#fZMa z^yX)`{I6p^c7$B~>f*nz1MAHG-YT@q2TKhTeFki@kPWo4knwHZU-Crx<3OULufEbh zOpKuHc8wk`&LxE4zgd+zis6h?{!o>{NX-j~{$SraIs4KpLfy!spA(*}YinGLztAuQT7=%>y zM}M^`?;m|EbKeXJJ(8gG4G)e!d&KhK6DtFlqq71G3E+Ed%-ttP0=EOvVzeWlfJmnL z+M05ZC=)-_>A zBvL|+91lmB?Kzmqm!Hby2^bJdrpfkcHtRbXbnW{0Z+1~^)^!RqMf>l>pFov>;uUPI z)Vi*P`Y~l(APv56MzFD=Q%Glkx1;E`xUfdrNhw;C--=#P@bU5{m zYE|UT+cbhi2e5@Z7N6mlY_XQC%tdw4%U!c6_Q>;K9#6Z!B>_A38~jyYeRJHZ!UDvL zp%g<}9OSe_^5{F34)Z5FU~UrGB2O-+?^cIcaOl!inI(_gMPU3#bQ98}PvYRt)s89T z;{sZBx!rH@vY3okQ=6Gbr&<;G$)p(K^|5i5&!uG)g$ga;JDj@PQQCWTWMMDBvyn!b zEj7HI|D@ML5?_le&}OA8ppaf%IPwa$B;8;~OvNlCdayeCT(!lHoD9!Nl{gBGTG_`m z1a_5x_!aC#k+9c)cB=2TYVUOo|JbD*-XuIH1C)_o$ftfuK&2aaC5^c26j8vRe*BJl z)1%caue%s)vv4<9W*2q#cT1`uSB${TRrBoMLP>t=Ug}qdk=r_t;=-+wwtXje3=Ror zYI=3w)+pefvD=AsZ69O`C13>LWo1FN^0bP}>%SZD=kxPq(48TzOt&!3@0{|}_fl9q zKW}NZrqYt+fK{^Sc>(F(ug-U+mpM|GR$c-sLlZvzxZMvshhxJr%h(oLQ0L12;UW8s zQSJQSSF(UCh<4Ws5umEdSTIYkF^FK;=@%~(g-bPYh~n!<3R!{PZ`04tA6?z5R7{87 zoQxt<=@76@K@@?7x@*uhvAmJZb~hkjMl6nT^hC&X;*+hOmI3j314rlzZ5uaVcuGv907 zsMXfq5@x?WVt5_8pHH&&vFzq>T;x8}5~7_0`Gl(B)p2}T@kl+g>DXZ^B0WpJV`*feKT9OfHX(=Bk%oP1zzwtb?l?dMN59j!NeLa!q-pAG ziPvtwOal#agtt61%NOGwKk_pJx!@{wcDVl=nM|sexVC4l;sDF7uKu#>`>FkRa%kvK z%*7h|e6ypOz<$b-IBIJ5UkT9x(7XClRH)h|_Hy>|CL}xEERWy&8(f3G!1#fsW65!; zmyc-h!TKR?@ARBT9jZ@vGB@}_m4hI=*pwu|jT6Fy={ZFSeevkKniok!sM|hP#|;`K z$5CD9>kECClQT1fUdR1OY2?Hn5sBF;k0J`Kh=+){kMD-o_ck zo~!JwRe7YgQxZ=WLf8YiO%3b;_V=4k*O$oQs#8gQJ#}D0;{pd$=J9X+OUFowh|JW@ z%SrL){15x4aSai_E%>-*AZErU&Y#{=+KbW?U8>NYwE67c-`L%cc9z%ZIP%vc^28@t z2^hHVNgb{O4upVcvvn3Rbuo4AxtEP%$3v-f9uF_$-m%%>c&8b&-fh=IbY-|T^xX(r zGt=BW3Y=07#C$7?M_ACH3_7cOYq~$x1r#LdJ3oCz$`_>31n)RFzO+|W0LzYNww-yn z8)xR4l^l0Jdqka7R}X(6p{~|wb1R_)=P^KjjK372x1rgAWF%>2n{QL4KVcCdOp%w! z@8Dp!cQBUg66xZpFyo}cZ#Bq;WE+=eHh&!WkGKgy16v-bNV@%2>?zMmtKJ-XSg%nr zJX6=u(7Hq>Ku4SZI_mn507M|421iQD6$|mH9ivkhn~txq6GPGO_Dmf+{9S$WD-dM} zu|kTB6t&8nzj7$(z@A2NyO(maQtkGr9Y`}Os<^PfY;Pi!_IQ(UH z`bdQlt$GTOX|hv^uNlBj(9at?V}UVBGFMO{fN$fM{HZfgcr%+QY5gV7SRlc^YF#ql zFa8u(<5)N3UJOyqSzpKVe6!^X$Go^w2jWGGl=|FDG4TrBZ@J#Tg-{aoY43KQ>-`%j zi`doP#~fGbvEv23e$A>B;g6$dGD&+U=x^Y?4m$Ua@*RA7x-zanNn`BtuMdGV*+=Lp z07NOl85*2?&b2mEsUKQsy!!>kkzRzw3B=g?0NQ@1g0oEGLUXxjGf(fi_#7z_F&=hR zBL+unOW5Ss4`liIWlMLip;s~4?8+*Tcpy4fN89Dq@JJ4IJ}*g%{}!=(TM8k8WPO z{YdjvrrO$CtMPYCOof~*3m)lvdqST2#ns?eEie00yzaz){L&aU4ncN$7o0)2YXxTq zd3PROcK*C{;LVTlhdCZEqSv>#-2y)pREC0c9MX(wkSvr`Y?bF9+r(%XpIyp+~2s4rIOiSH<+ z+2n67&{17YpB>T^pv&0P3@Kq78fVJkgYf^Hs-@mWe*XPD_9Y5RUg(o%1A}!(t;uGt z+a9`oq&C`i>D=Ky_yC_ssZ*lYr(|W1mgb6^rAq!8!lwDv%+$81Uo*BPo|-R4R4*|} zR7}iCEz7vt4+2cbOO#{?c|h$+Dex?Z9xg!kj8Z~JTd%)gg$RbSQtZ)W1Tt_0f|sHB@1Or-RwcNpzQGpf;& z?dmNrT+M|!_oJF+&p?jaiugQHu#zC_6-;Zk)q+qJwGkNbZZYRolYS{$?bDIkV0*(+ z?7Mu9>yGvK5TDqw7>wA_*iPGMyHwP6IB7L`JE!i;7k|)$p=d@4HYe3n?)!N35 z3$%-5*E<`CTJu@RFC50Yc~;vqQ0*lG*8Qbd5)H#}Ec|<*e^i69KOqLF%XmvNx(m6t zEe-!%Jrr#>=}B%aYBM4_NcvZ6Jd&NYT|!pHjSq^Y;TR8wC8c^SHn?w^? ztTq)tUjj&nq~3xwrL}H|QkK|>F&$Q0sxvyQmnBJd1{8E^EW_@fPrQmUBq87JA;T;i z5XJEttoHduA1n;Daq>PMR7t1J5mQJx!oE-BQ^4^wX= z;qc7?_B{DwX4tjjU0;eD(<|1dSaIvcuVZ7uaGcjJH!|vY182#U!Z|UW>r8Y6_TY^* zpLtWpU=+{thBh`{l)B=YunZa(lw@{-Xz5z~&hFCB*}T0&UGodNBmH*BC&ke%e~vv4 z02C{gp9SAdI=yJ-<9hHT71QNf^WzwgvRXHDRpJA2on8^D+^mN+Egy6Dle#=dRg_Xz zl_q{7X&tB~syhJF=8Ctc(u0w(BttV=sf@E6!2Yojf*nS>Xf1cHR$~q|6Vn8|RtwLx z6*+lD=)NiK;4C%#35Q;M1u=n3TKRwc#|9q*(8H706jBtnthVjGFGT4JoVluGWf&AA zZ~kpmXvPgZ`|Q!Sn6B|uGaE;;CganzG|OngZ4BkvG_xCOx>r0DZ(r}W7%F{Q*j+)h zHZ}wzqmh=x9;F25tni22UM6`VFkN9m5z%I`v&%CB(_fdvJ4`!z1j@kUf5?!kEr%{I zo2?RDzzhEq7dX0QLT{oOm2GCM4G^&$^JP{4Y@(`NWBzrO`+}0mKRzhQ%L^ZQ zNU120IXAM%$M8k?u*mu_xs$rR?aqnqC+;B;N^o-dWC0vy=HntDb73K=($cJhXvDTE zk7{iL>l|jb9l3sM3GmHAwR4hy{jifPb9GbG=WQ@U`i#59J6^W5abHFIVXldcduo5v ze4QOh{m(Duna_mnB9|=M&nJ7%zLp0Hi;yZ4K(IaRE(4k?gPNIaeLsJtmOG(mG{QdM zUdGz%3G3bTHjC{S|9+v)&RTXC)~?sjC>m@df}Bsi4i^>YKFt%$_LVwY{;9%qFjNF? zm1)zrJUiodDJvRA*Mu!w!Mw9TD%81n#`2#X>%Cj?HvH*>tkI93_L8Y&!^aOo&GDv7<06lLVT&yv0SF?c)m9GIq;mwos7%$=kNi znr3Kt*qN&Wdv?B5e|B}Xh(zwoEqLSU*!H!bEPAgAfM?M!XE@{+^ZDXIdkKV)&nR+Y zbYzq$lO@!K)WD?PF6bReOk!MipjlQ*1ukEyrvnuFX@yRI4&JmCA=J^(7DpK+?cnOL zoGLqv44F=4$?#8-*o&$_v3yQ5B;jvg{Y(I<>bQVa|Knd^aazeWdQsQ%NTCU&k z`LdqX^_P|oqP^)YWOVFkY^5uTQkCdz6{b7E+HdJ9;FunOVscCzG-?YIWp+VAckKfM zFL!1?e&K&NX=cP&Ur}q`DDZ)YV_W`Q&S>Kx&G#(1p~lz~(f;!cFyZ#_B$OwY`pI+S z?X-=f-6TCW7pt(_+=2CP52XI4L%exEWj`C)cunxhLRU?-DJPat0*XHsuS4XErIhD* ze;z9X%>d)fbQ%zUR0zHrjT%gc@0qAJEqeKc2)%D_0uvp^cW7cEcoqE!@5Q16b!_n@ z3b~(wGIDKhiS&zzy5Bn9+rwp~3rsC>-CFr6nkTFZ_D$bghch)UaowaFFM^_cGA$*G z)2ph;(uaR1vfTv-`-l6tkEE7#D%}<*3dQKY4eYDN@TF*Jjtl6jM6`d`?^6Qid_SI! zDrGL=5xhOIC<@4U3;4mXpr9zTeU?~Z%@yj`%;3IL#{qF69dHVbdl++!Byr)3rvW<| zs46R0;h-%w2aXzQjxS3w&ZeXs4xi%L)sk8ZbWX1;Rz>9)9;PR5DI7YmWXS>Oc^ zZqI#IyAM@NH|?Sh<&OkNm-qIK9 z$b%UPf1#V_Xv%D2OR+IqW@hZUuuW&gv4^YkWO_^a_bmFl2hn3HMg{-EnRWRTs2Cj| z9QLar>G=|J#6{I|dlN=W0_^#W+?gdPbK2D0NFF}NopdSuuZmpM_3Xu-PE*woWAJ_; zmg;kJ?-uNCxiWItB7!kTTjNVhBE^%m5?1Dut&NzVZZ4Ov&&(cwExg?rC=62IGH PK-DB: pharmacokinetics database for individualized and stratified computational modeling
- Grzegorzewski J, Brandhorst J, Green K, Eleftheriadou D, Duport Y, Barthorscht F, Köller A, Ke DYJ, De Angelis S, König M.
+ Grzegorzewski J, Brandhorst J, Green K, Eleftheriadou D, Duport Y, Bartsch F, Köller A, Ke DYJ, De Angelis S, König M.
Nucleic Acids Res. 2020 Nov 5:gkaa990. doi: 10.1093/nar/gkaa990. Epub ahead of print. PMID: 33151297
diff --git a/frontend/src/components/detail/CharacteristicaCard.vue b/frontend/src/components/detail/CharacteristicaCard.vue index efb3e97f..33fc3df0 100644 --- a/frontend/src/components/detail/CharacteristicaCard.vue +++ b/frontend/src/components/detail/CharacteristicaCard.vue @@ -89,7 +89,7 @@ if (!this.data.count){ return 1; // individual has no count ? FIXME bug } else { - return this.data.count + return this.data.countO } }, subject_count(){ diff --git a/frontend/src/components/home/About.vue b/frontend/src/components/home/About.vue index 676b4a8b..1726d727 100644 --- a/frontend/src/components/home/About.vue +++ b/frontend/src/components/home/About.vue @@ -17,6 +17,7 @@ +

If you have questions or feedback please contact @@ -34,10 +35,11 @@

How to cite

- PK-DB: pharmacokinetics database for individualized and stratified computational modeling
- Grzegorzewski J, Brandhorst J, Green K, Eleftheriadou D, Duport Y, Barthorscht F, Köller A, Ke DYJ, De Angelis S, König M.
- bioRxiv 760884; doi:
https://doi.org/10.1101/760884 + + PK-DB: pharmacokinetics database for individualized and stratified computational modeling
+ Grzegorzewski J, Brandhorst J, Green K, Eleftheriadou D, Duport Y, Bartsch F, Köller A, Ke DYJ, De Angelis S, König M.
Nucleic Acids Res. 2020 Nov 5:gkaa990. doi: 10.1093/nar/gkaa990. Epub ahead of print. PMID: 33151297 +

Licensing

diff --git a/frontend/src/components/lib/UserAvatar.vue b/frontend/src/components/lib/UserAvatar.vue index 9dc63d2b..304bc025 100644 --- a/frontend/src/components/lib/UserAvatar.vue +++ b/frontend/src/components/lib/UserAvatar.vue @@ -62,6 +62,8 @@ image = 'danny_128.png'; } else if (this.initials === 'SD'){ image = 'sara_128.png'; + } else if (this.initials === 'SB'){ + image = 'balcisue_128.png'; } } else if (this.username) { image = 'user_128.png'; @@ -87,6 +89,8 @@ image = 'danny_128.png'; } else if (this.username === 'SaraD-hub'){ image = 'sara_128.png'; + } else if (this.username === 'balcisue'){ + image = 'balcisue_128.png'; } } return img_dir + image From 88fbf55157f0ac502bc625513c08d00036b521ca Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Tue, 1 Dec 2020 20:39:58 +0100 Subject: [PATCH 10/26] bugfix regression --- frontend/src/components/detail/CharacteristicaCard.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/detail/CharacteristicaCard.vue b/frontend/src/components/detail/CharacteristicaCard.vue index 33fc3df0..c4ec1d9b 100644 --- a/frontend/src/components/detail/CharacteristicaCard.vue +++ b/frontend/src/components/detail/CharacteristicaCard.vue @@ -89,7 +89,7 @@ if (!this.data.count){ return 1; // individual has no count ? FIXME bug } else { - return this.data.countO + return this.data.count; } }, subject_count(){ From 878940bac70e1f70deeaf8077e9e4659dc3737ea Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Tue, 1 Dec 2020 22:47:51 +0100 Subject: [PATCH 11/26] bugfix user search --- frontend/src/components/search/UserSearch.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/search/UserSearch.vue b/frontend/src/components/search/UserSearch.vue index 82ed8cfe..26726b13 100644 --- a/frontend/src/components/search/UserSearch.vue +++ b/frontend/src/components/search/UserSearch.vue @@ -61,6 +61,7 @@ export default { {"username": "adriankl", "first_name": "Adrian", "last_name": "Koeller",}, {"username": "dannythekey", "first_name": "Danny", "last_name": "Ke",}, {"username": "SaraD-hub", "first_name": "Sara", "last_name": "De Angelis",}, + {"username": "balcisue", "first_name": "Sükrü", "last_name": "Balci",}, ], } }, From ff52774b4bec2826fa7dd5ddbd1190c498703d18 Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Wed, 2 Dec 2020 01:17:40 +0100 Subject: [PATCH 12/26] Fix #689, bump django version --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 71c0caaa..0fe2054a 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -5,7 +5,7 @@ json-logging>=1.2.6 psycopg2-binary>=2.8.5 # django -Django == 3.1.3 +Django == 3.1.4 django-model-utils>=4.0.0 django-extra-fields>=3.0.0 django-storages>=1.9.1 From 5593f59dc0ff7db6dc2176fc14a970637f16219c Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Fri, 4 Dec 2020 15:54:04 +0100 Subject: [PATCH 13/26] closes 691, closes #192 --- backend/pkdb_app/data/serializers.py | 7 ++++++- backend/pkdb_app/error_measures.py | 28 ++++++++++++------------- backend/pkdb_app/outputs/serializers.py | 2 +- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/backend/pkdb_app/data/serializers.py b/backend/pkdb_app/data/serializers.py index 345ad3a7..f95a8745 100644 --- a/backend/pkdb_app/data/serializers.py +++ b/backend/pkdb_app/data/serializers.py @@ -70,9 +70,14 @@ def create(self, validated_data): # subset_instance.save() return subset_instance + def validate_shared(self, shared): + if isinstance(shared,str): + raise serializers.ValidationError({"shared": "Shared field has to be a list not a string.", "detail": shared}) + + def _validate_time(self, time): if any(np.isnan(np.array(time))): - raise serializers.ValidationError({"time": "no time points are allowed to be nan", "detail": time}) + raise serializers.ValidationError({"time": "No time points are allowed to be nan", "detail": time}) def calculate_pks_from_timecourses(self, subset): # calculate pharmacokinetics outputs diff --git a/backend/pkdb_app/error_measures.py b/backend/pkdb_app/error_measures.py index b24cbbe9..77b73bf9 100644 --- a/backend/pkdb_app/error_measures.py +++ b/backend/pkdb_app/error_measures.py @@ -6,16 +6,16 @@ cv is coefficient of variation. sd/mean """ import numpy as np -import warnings - +def _is(value): + return value is not None and value is not np.nan def calculate_sd(se, count, cv, mean): """Calculates standard deviation from other error measurements.""" sd = None - is_se = sd is not None - is_count = count is not None - is_mean = mean is not None - is_cv = cv is not None + is_se = _is(se) + is_count = _is(count) + is_mean = _is(mean) + is_cv = _is(cv) if is_se and is_count: sd = np.multiply(se, np.sqrt(count)) @@ -28,10 +28,10 @@ def calculate_se(sd, count, cv, mean): """Calculates SE from given fields.""" se = None - is_sd = sd is not None - is_count = count is not None - is_mean = mean is not None - is_cv = cv is not None + is_sd = _is(sd) + is_count = _is(count) + is_mean = _is(mean) + is_cv = _is(cv) if is_sd and is_count: se = np.true_divide(sd, np.sqrt(count)) @@ -44,10 +44,10 @@ def calculate_cv(sd, count, se, mean): """Calculates CV from given fields""" cv = None - is_sd = sd is not None - is_count = count is not None - is_mean = mean is not None - is_se = se is not None + is_sd = _is(sd) + is_count = _is(count) + is_mean = _is(mean) + is_se = _is(se) # mean can be zero, CV not calculatable, resulting in -inf/inf # mean data must be cleaned before calculation diff --git a/backend/pkdb_app/outputs/serializers.py b/backend/pkdb_app/outputs/serializers.py index f1c0dbdc..7f4f8128 100644 --- a/backend/pkdb_app/outputs/serializers.py +++ b/backend/pkdb_app/outputs/serializers.py @@ -94,7 +94,7 @@ def to_internal_value(self, data): data = self.retransform_map_fields(data) data = self.to_internal_related_fields(data) - self.validate_wrong_keys(data, additional_fields=OutputExSerializer.Meta.fields) + self.validate_wrong_keys(data, additional_fields=OutputSerializer.Meta.fields) return super(serializers.ModelSerializer, self).to_internal_value(data) def validate(self, attrs): From e0bb6545218bebee1c4bc9f4981d39fe2a0bc02a Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Sun, 6 Dec 2020 17:01:17 +0100 Subject: [PATCH 14/26] error measures removed bug --- backend/pkdb_app/error_measures.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/pkdb_app/error_measures.py b/backend/pkdb_app/error_measures.py index 77b73bf9..7d998478 100644 --- a/backend/pkdb_app/error_measures.py +++ b/backend/pkdb_app/error_measures.py @@ -7,8 +7,11 @@ """ import numpy as np + def _is(value): return value is not None and value is not np.nan + + def calculate_sd(se, count, cv, mean): """Calculates standard deviation from other error measurements.""" sd = None From 18f3a818cce05b04f250893923d6f338519b6aff Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Mon, 7 Dec 2020 12:23:51 +0100 Subject: [PATCH 15/26] introduced a bug --- backend/pkdb_app/outputs/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkdb_app/outputs/serializers.py b/backend/pkdb_app/outputs/serializers.py index 7f4f8128..f1c0dbdc 100644 --- a/backend/pkdb_app/outputs/serializers.py +++ b/backend/pkdb_app/outputs/serializers.py @@ -94,7 +94,7 @@ def to_internal_value(self, data): data = self.retransform_map_fields(data) data = self.to_internal_related_fields(data) - self.validate_wrong_keys(data, additional_fields=OutputSerializer.Meta.fields) + self.validate_wrong_keys(data, additional_fields=OutputExSerializer.Meta.fields) return super(serializers.ModelSerializer, self).to_internal_value(data) def validate(self, attrs): From 19b830a42535a6914a7c6cb77ad9731025914f34 Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Mon, 7 Dec 2020 13:45:40 +0100 Subject: [PATCH 16/26] closes #695 --- frontend/src/components/Data.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Data.vue b/frontend/src/components/Data.vue index bfa4284d..0abd4450 100644 --- a/frontend/src/components/Data.vue +++ b/frontend/src/components/Data.vue @@ -29,7 +29,7 @@ export default { created() { if(this.sid){ this.getStudy(this.sid, true) - this.hide_search=false + this.hide_search=true }else{ this.loading = false } From 47164fb6cd597473a41a4ff2eae32a6c0d0f99f9 Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Wed, 9 Dec 2020 23:23:36 +0100 Subject: [PATCH 17/26] better message on wrong field in outputs --- backend/pkdb_app/outputs/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkdb_app/outputs/serializers.py b/backend/pkdb_app/outputs/serializers.py index f1c0dbdc..1866add7 100644 --- a/backend/pkdb_app/outputs/serializers.py +++ b/backend/pkdb_app/outputs/serializers.py @@ -195,7 +195,7 @@ def to_internal_value(self, data): [data.pop(field, None) for field in drop_fields] data["outputs"] = outputs data = self.transform_map_fields(data) - self.validate_wrong_keys(data) + self.validate_wrong_keys(data, OutputSerializer.Meta.fields) return super(serializers.ModelSerializer, self).to_internal_value(data) def validate_label_map(self, value) -> None: From 79e8ca26d62205d02c9938e7098298bb56c09af1 Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Sun, 13 Dec 2020 01:02:11 +0100 Subject: [PATCH 18/26] sorted options --- backend/pkdb_app/info_nodes/documents.py | 20 +++++++------------- backend/pkdb_app/info_nodes/models.py | 4 ++-- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/backend/pkdb_app/info_nodes/documents.py b/backend/pkdb_app/info_nodes/documents.py index 61c8b3c0..8a782bec 100644 --- a/backend/pkdb_app/info_nodes/documents.py +++ b/backend/pkdb_app/info_nodes/documents.py @@ -41,16 +41,13 @@ class InfoNodeDocument(Document): ntype = string_field('ntype') dtype = string_field('dtype') xrefs = ObjectField( - properties= - { - "name": string_field("name"), - "accession":string_field("accession"), - "url":string_field("url") - - }, multi=True) - - - # measurement type + properties={ + "name": string_field("name"), + "accession": string_field("accession"), + "url": string_field("url") + }, + multi=True + ) measurement_type = ObjectField( properties={ "choices": ObjectField( @@ -61,15 +58,12 @@ class InfoNodeDocument(Document): "units": basic_object("units", multi=True) } ) - # substance substance = ObjectField( properties={ - "chebi": string_field('chebi'), "mass": string_field('mass'), "charge": string_field('charge'), "formula": string_field('formula'), - }) class Django: diff --git a/backend/pkdb_app/info_nodes/models.py b/backend/pkdb_app/info_nodes/models.py index 6f825bc9..5e9fb210 100644 --- a/backend/pkdb_app/info_nodes/models.py +++ b/backend/pkdb_app/info_nodes/models.py @@ -315,7 +315,7 @@ def validate_choice(self, choice): self.info_node.DTypes.NumericCategorical]: if not self.is_valid_choice(choice): msg = f"The choice `{choice}` is not a valid choice for measurement type `{self.info_node.name}`. " \ - f"Allowed choices are: `{list(self.choices_list())}`." + f"Allowed choices are: `{sorted(self.choices_list())}`." raise ValueError({"choice": msg}) return self.choices.get(info_node__name=choice) else: @@ -325,7 +325,7 @@ def validate_choice(self, choice): raise ValueError({"choice": msg}) elif self.choices.exists(): msg = f"{choice}. A choice is required for `{self.info_node.name}`." \ - f" Allowed choices are: `{list(self.choices_list())}`." + f" Allowed choices are: `{sorted(self.choices_list())}`." raise ValueError({"choice": msg}) @property From e22acb0d253f3086ba8c1ea0e210ba5f331bb8cf Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Mon, 14 Dec 2020 15:54:57 +0100 Subject: [PATCH 19/26] closes #698 --- backend/pkdb_app/info_nodes/models.py | 2 +- backend/pkdb_app/outputs/serializers.py | 2 +- backend/pkdb_app/studies/models.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/pkdb_app/info_nodes/models.py b/backend/pkdb_app/info_nodes/models.py index 6f825bc9..6dd806e7 100644 --- a/backend/pkdb_app/info_nodes/models.py +++ b/backend/pkdb_app/info_nodes/models.py @@ -154,7 +154,7 @@ class MeasurementType(AbstractInfoNode): NO_UNIT = 'NO_UNIT' # todo: remove NO_UNIT and add extra keyword or add an extra measurement_type with optional no units. TIME_REQUIRED_MEASUREMENT_TYPES = ["cumulative amount", "cumulative metabolic ratio", "recovery", - "auc_end"] # todo: remove and add extra keyword. + "auc_end", "ptf"] # todo: remove and add extra keyword. CAN_NEGATIVE = [ "tmax" # tmax can be negative due to time offsets, i.e. pre-simulation with subsequent fall after intervention # this often happens in placebo simulations diff --git a/backend/pkdb_app/outputs/serializers.py b/backend/pkdb_app/outputs/serializers.py index 1866add7..1b3118f7 100644 --- a/backend/pkdb_app/outputs/serializers.py +++ b/backend/pkdb_app/outputs/serializers.py @@ -29,7 +29,7 @@ from ..utils import list_of_pk, _validate_required_key, create_multiple, _create, create_multiple_bulk_normalized, \ create_multiple_bulk -EXTRA_FIELDS = ["tissue", "method", "label","output_type"] +EXTRA_FIELDS = ["tissue", "method", "label", "output_type"] TIME_FIELDS = ["time", "time_unit"] OUTPUT_FIELDS = EXTRA_FIELDS + TIME_FIELDS diff --git a/backend/pkdb_app/studies/models.py b/backend/pkdb_app/studies/models.py index 7c8fa792..5eccb424 100644 --- a/backend/pkdb_app/studies/models.py +++ b/backend/pkdb_app/studies/models.py @@ -78,6 +78,7 @@ def study_name(self): return self.study.name return "" + class Author(models.Model): """ Author in reference. """ first_name = models.CharField(max_length=CHAR_MAX_LENGTH, blank=True) From 09e6a2289ad8e35453d988662baed1bfde2a7cfe Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Tue, 15 Dec 2020 11:34:01 +0100 Subject: [PATCH 20/26] closes #690 --- backend/pkdb_app/info_nodes/models.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/pkdb_app/info_nodes/models.py b/backend/pkdb_app/info_nodes/models.py index 5253b229..fe1dd44c 100644 --- a/backend/pkdb_app/info_nodes/models.py +++ b/backend/pkdb_app/info_nodes/models.py @@ -153,8 +153,15 @@ class MeasurementType(AbstractInfoNode): ) NO_UNIT = 'NO_UNIT' # todo: remove NO_UNIT and add extra keyword or add an extra measurement_type with optional no units. - TIME_REQUIRED_MEASUREMENT_TYPES = ["cumulative amount", "cumulative metabolic ratio", "recovery", - "auc_end", "ptf"] # todo: remove and add extra keyword. + TIME_REQUIRED_MEASUREMENT_TYPES = [ + "concentration", + "cumulative amount", + "metabolic_ratio", + "cumulative metabolic ratio", + "recovery", + "auc_end", + "ptf", + ] # todo: remove and add extra keyword. CAN_NEGATIVE = [ "tmax" # tmax can be negative due to time offsets, i.e. pre-simulation with subsequent fall after intervention # this often happens in placebo simulations From 3d92ea449679c37a1051d0e67b9c2e8c113e8bd5 Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Wed, 16 Dec 2020 21:57:04 +0100 Subject: [PATCH 21/26] closes #700 --- backend/pkdb_app/info_nodes/models.py | 19 ++++++++++++------- backend/pkdb_app/info_nodes/serializers.py | 6 ++++-- backend/pkdb_app/interventions/serializers.py | 4 ++-- backend/pkdb_app/outputs/serializers.py | 4 ---- backend/pkdb_app/serializers.py | 19 +++++++++++++++++++ backend/pkdb_app/utils.py | 12 +++++++++++- 6 files changed, 48 insertions(+), 16 deletions(-) diff --git a/backend/pkdb_app/info_nodes/models.py b/backend/pkdb_app/info_nodes/models.py index fe1dd44c..ca787533 100644 --- a/backend/pkdb_app/info_nodes/models.py +++ b/backend/pkdb_app/info_nodes/models.py @@ -11,8 +11,7 @@ from pkdb_app.behaviours import Sidable from pkdb_app.info_nodes.units import ureg -from pkdb_app.utils import CHAR_MAX_LENGTH, CHAR_MAX_LENGTH_LONG, \ - _validate_required_key_and_value +from pkdb_app.utils import CHAR_MAX_LENGTH, CHAR_MAX_LENGTH_LONG, _validate_required_key_and_value_or_nr from rest_framework import serializers @@ -156,12 +155,15 @@ class MeasurementType(AbstractInfoNode): TIME_REQUIRED_MEASUREMENT_TYPES = [ "concentration", "cumulative amount", - "metabolic_ratio", + "metabolic ratio", "cumulative metabolic ratio", "recovery", "auc_end", "ptf", ] # todo: remove and add extra keyword. + TIME_REQUIRED_MEASUREMENT_TYPES_NR_ALLOWED = [ + + ] CAN_NEGATIVE = [ "tmax" # tmax can be negative due to time offsets, i.e. pre-simulation with subsequent fall after intervention # this often happens in placebo simulations @@ -376,15 +378,18 @@ def validate_complete(self, data): d_choice = self.validate_choice(choice) time_unit = data.get("time_unit", None) - if time_unit: + time = data.get("time", None) + + if time_unit and time_unit != "NR": self.validate_time_unit(time_unit) if self.time_required: details = f"for measurement type `{self.info_node.name}`" - _validate_required_key_and_value(data, "time", details=details) - _validate_required_key_and_value(data, "time_unit", details=details) + if time != "NR": + _validate_required_key_and_value_or_nr(data, "time_unit", details=details) + _validate_required_key_and_value_or_nr(data, "time", details=details) - return {"choice":d_choice} + return {"choice": d_choice} class Choice(AbstractInfoNode): diff --git a/backend/pkdb_app/info_nodes/serializers.py b/backend/pkdb_app/info_nodes/serializers.py index 5d6cf529..868bc493 100644 --- a/backend/pkdb_app/info_nodes/serializers.py +++ b/backend/pkdb_app/info_nodes/serializers.py @@ -4,7 +4,7 @@ from pkdb_app.info_nodes.documents import InfoNodeDocument from pkdb_app.info_nodes.models import InfoNode, Synonym, Annotation, Unit, MeasurementType, Substance, Choice, Route, \ Form, Tissue, Application, Method, CrossReference -from pkdb_app.serializers import WrongKeyValidationSerializer, ExSerializer, SidNameLabelSerializer +from pkdb_app.serializers import WrongKeyValidationSerializer, ExSerializer, SidNameLabelSerializer, FloatNRField from pkdb_app.utils import update_or_create_multiple from rest_framework.fields import empty @@ -28,6 +28,8 @@ class MeasurementTypeableSerializer(EXMeasurementTypeableSerializer): ) choice = serializers.CharField(allow_null=True) + time = FloatNRField(allow_null=True) + def to_representation(self, instance): rep = super().to_representation(instance) @@ -38,7 +40,7 @@ class SynonymSerializer(WrongKeyValidationSerializer): pk = serializers.IntegerField(read_only=True) class Meta: model = Synonym - fields = ["name","pk"] + fields = ["name", "pk"] def to_internal_value(self, data): return {"name": data} diff --git a/backend/pkdb_app/interventions/serializers.py b/backend/pkdb_app/interventions/serializers.py index d861fe84..f85f34cb 100644 --- a/backend/pkdb_app/interventions/serializers.py +++ b/backend/pkdb_app/interventions/serializers.py @@ -109,7 +109,7 @@ def validate(self, attrs): for info_node in ['substance', 'measurement_type', 'form', 'application', 'route']: if info_node in attrs: if attrs[info_node] is not None: - attrs[info_node] = getattr(attrs[info_node],info_node) + attrs[info_node] = getattr(attrs[info_node], info_node) attrs["choice"] = attrs["measurement_type"].validate_complete(data=attrs)["choice"] @@ -166,7 +166,7 @@ def to_internal_value(self, data): interventions_from_file = self.entries_from_file(intervention) interventions.extend(interventions_from_file) - drop_fields = INTERVENTION_FIELDS + INTERVENTION_MAP_FIELDS + EX_MEASUREMENTTYPE_FIELDS + drop_fields = INTERVENTION_FIELDS + INTERVENTION_MAP_FIELDS + EX_MEASUREMENTTYPE_FIELDS [data.pop(field, None) for field in drop_fields] # ---------------------------------- # finished diff --git a/backend/pkdb_app/outputs/serializers.py b/backend/pkdb_app/outputs/serializers.py index 1b3118f7..215ae1d9 100644 --- a/backend/pkdb_app/outputs/serializers.py +++ b/backend/pkdb_app/outputs/serializers.py @@ -110,8 +110,6 @@ def validate(self, attrs): _validate_required_key(attrs, "output_type") self._validate_timecourse(attrs) - - try: attrs['measurement_type'] = attrs['measurement_type'].measurement_type @@ -119,8 +117,6 @@ def validate(self, attrs): if key in attrs: if attrs[key] is not None: attrs[key] = getattr(attrs[key], key) - - attrs["choice"] = attrs["measurement_type"].validate_complete(data=attrs)["choice"] except ValueError as err: diff --git a/backend/pkdb_app/serializers.py b/backend/pkdb_app/serializers.py index 4a758a01..b6d04251 100644 --- a/backend/pkdb_app/serializers.py +++ b/backend/pkdb_app/serializers.py @@ -740,21 +740,25 @@ class NameSerializer(serializers.Serializer): class Meta: fields = ["pk","name", "study_sid"] + class PkStringSerializer(serializers.Serializer): pk = serializers.CharField() class Meta: fields = ["pk"] + class SidNameSerializer(serializers.Serializer): sid = serializers.CharField(allow_null=True) name = serializers.CharField(allow_null=True) + class SidNameLabelSerializer(serializers.Serializer): sid = serializers.CharField(allow_null=True) name = serializers.CharField(allow_null=True) label = serializers.CharField(allow_null=True) + def validate_dict(dic): if not isinstance(dic, dict): raise serializers.ValidationError( @@ -762,7 +766,22 @@ def validate_dict(dic): "detail": dic} ) + class StudySmallElasticSerializer(serializers.ModelSerializer): class Meta: model = Study fields = ['pk', 'sid', 'name'] # ,'url'] + + +class FloatNRField(serializers.FloatField): + + def to_internal_value(self, data): + if data == "NR": + return data + if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH: + self.fail('max_string_length') + + try: + return float(data) + except (TypeError, ValueError): + self.fail('invalid') diff --git a/backend/pkdb_app/utils.py b/backend/pkdb_app/utils.py index 3eed0fe7..20699faf 100644 --- a/backend/pkdb_app/utils.py +++ b/backend/pkdb_app/utils.py @@ -196,13 +196,23 @@ def set_keys(d, value, *keys): d = d[key] d[keys[-1]] = value + def _validate_required_key_and_value(attrs, key, details=None, extra_message: str = ""): - if pd.isnull(attrs.get(key,None)) or pd.isna(attrs.get(key,None)): + if pd.isnull(attrs.get(key, None)) or pd.isna(attrs.get(key, None)): error_json = {key: f"The key <{key}> is required. {extra_message}"} if details: error_json["details"] = details raise serializers.ValidationError(error_json) + +def _validate_required_key_and_value_or_nr(attrs, key, details=None, extra_message: str = ""): + value = attrs.get(key, None) + if value != "NR": + _validate_required_key_and_value(attrs, key, details, extra_message) + else: + attrs[key] = None + + def _validate_required_key(attrs, key, details=None, extra_message: str = ""): if key not in attrs: error_json = {key: f"The key <{key}> is required. {extra_message}"} From 64c3ff9e8862dde898cfeb620d91dd00b23e7c2e Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Thu, 17 Dec 2020 00:44:53 +0100 Subject: [PATCH 22/26] minor updates to validation of time required --- backend/pkdb_app/info_nodes/models.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/backend/pkdb_app/info_nodes/models.py b/backend/pkdb_app/info_nodes/models.py index ca787533..e7362aa2 100644 --- a/backend/pkdb_app/info_nodes/models.py +++ b/backend/pkdb_app/info_nodes/models.py @@ -11,7 +11,8 @@ from pkdb_app.behaviours import Sidable from pkdb_app.info_nodes.units import ureg -from pkdb_app.utils import CHAR_MAX_LENGTH, CHAR_MAX_LENGTH_LONG, _validate_required_key_and_value_or_nr +from pkdb_app.utils import CHAR_MAX_LENGTH, CHAR_MAX_LENGTH_LONG, _validate_required_key_and_value_or_nr, \ + _validate_required_key_and_value from rest_framework import serializers @@ -385,9 +386,16 @@ def validate_complete(self, data): if self.time_required: details = f"for measurement type `{self.info_node.name}`" + if time != "NR": - _validate_required_key_and_value_or_nr(data, "time_unit", details=details) - _validate_required_key_and_value_or_nr(data, "time", details=details) + _validate_required_key_and_value(data, "time", details=details) + if time_unit != "NR": + _validate_required_key_and_value(data, "time_unit", details=details) + + if time == "NR": + data["time"] = None + if time_unit == "NR": + data["time_unit"] = None return {"choice": d_choice} From 65cc24489076c6677d44a64951923a464b0dc706 Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Sat, 19 Dec 2020 00:55:17 +0100 Subject: [PATCH 23/26] allow interventions to be none closes #701 --- backend/pkdb_app/info_nodes/models.py | 31 +++++++++++----------- backend/pkdb_app/info_nodes/serializers.py | 5 ---- backend/pkdb_app/outputs/models.py | 4 +-- backend/pkdb_app/outputs/serializers.py | 20 +++++++------- backend/pkdb_app/serializers.py | 9 ++++--- backend/pkdb_app/subjects/serializers.py | 2 +- backend/pkdb_app/utils.py | 10 +++---- 7 files changed, 38 insertions(+), 43 deletions(-) diff --git a/backend/pkdb_app/info_nodes/models.py b/backend/pkdb_app/info_nodes/models.py index e7362aa2..85ff53db 100644 --- a/backend/pkdb_app/info_nodes/models.py +++ b/backend/pkdb_app/info_nodes/models.py @@ -368,7 +368,7 @@ def validate_numeric(self, data): f"for all measurement types except " f"<{self.CAN_NEGATIVE}>.", "detail": data}) - def validate_complete(self, data): + def validate_complete(self, data, time_allowed: bool = True): """Complete validation.""" # check unit @@ -378,24 +378,25 @@ def validate_complete(self, data): choice = data.get("choice", None) d_choice = self.validate_choice(choice) - time_unit = data.get("time_unit", None) - time = data.get("time", None) + if time_allowed: + time_unit = data.get("time_unit", None) + time = data.get("time", None) - if time_unit and time_unit != "NR": - self.validate_time_unit(time_unit) + if time_unit and time_unit != "NR": + self.validate_time_unit(time_unit) - if self.time_required: - details = f"for measurement type `{self.info_node.name}`" + if self.time_required: + details = f"for measurement type `{self.info_node.name}`" - if time != "NR": - _validate_required_key_and_value(data, "time", details=details) - if time_unit != "NR": - _validate_required_key_and_value(data, "time_unit", details=details) + if time != "NR": + _validate_required_key_and_value(data, "time", details=details) + if time_unit != "NR": + _validate_required_key_and_value(data, "time_unit", details=details) - if time == "NR": - data["time"] = None - if time_unit == "NR": - data["time_unit"] = None + if time == "NR": + data["time"] = None + if time_unit == "NR": + data["time_unit"] = None return {"choice": d_choice} diff --git a/backend/pkdb_app/info_nodes/serializers.py b/backend/pkdb_app/info_nodes/serializers.py index 868bc493..0c96d975 100644 --- a/backend/pkdb_app/info_nodes/serializers.py +++ b/backend/pkdb_app/info_nodes/serializers.py @@ -31,11 +31,6 @@ class MeasurementTypeableSerializer(EXMeasurementTypeableSerializer): time = FloatNRField(allow_null=True) - def to_representation(self, instance): - rep = super().to_representation(instance) - return rep - - class SynonymSerializer(WrongKeyValidationSerializer): pk = serializers.IntegerField(read_only=True) class Meta: diff --git a/backend/pkdb_app/outputs/models.py b/backend/pkdb_app/outputs/models.py index 0dd85740..7f987412 100644 --- a/backend/pkdb_app/outputs/models.py +++ b/backend/pkdb_app/outputs/models.py @@ -109,7 +109,7 @@ class OutputTypes(models.TextChoices): group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.CASCADE) individual = models.ForeignKey(Individual, null=True, blank=True, on_delete=models.CASCADE) - interventions = models.ManyToManyField(Intervention, through="OutputIntervention", related_name="outputs") + interventions = models.ManyToManyField(Intervention, through="OutputIntervention", related_name="outputs", null=True, blank=True) subset = models.ForeignKey('data.Subset', on_delete=models.CASCADE, null=True, blank=True, related_name="pks") tissue = models.ForeignKey(Tissue, related_name="outputs", null=True, blank=True, on_delete=models.CASCADE) @@ -130,7 +130,7 @@ def null_attr(self, attr): def is_timecourse(self): if self.timecourse: - if self.timecourse.data.data_type=="timecourse": + if self.timecourse.data.data_type == "timecourse": return True return False diff --git a/backend/pkdb_app/outputs/serializers.py b/backend/pkdb_app/outputs/serializers.py index 215ae1d9..76cc3cde 100644 --- a/backend/pkdb_app/outputs/serializers.py +++ b/backend/pkdb_app/outputs/serializers.py @@ -19,7 +19,7 @@ CommentElasticSerializer from ..interventions.models import Intervention from ..serializers import ( - ExSerializer, StudySmallElasticSerializer, SidNameLabelSerializer) + ExSerializer, StudySmallElasticSerializer, SidNameLabelSerializer, PKField) from ..subjects.models import Group, DataFile, Individual from ..subjects.serializers import ( EXTERN_FILE_FIELDS, GroupSmallElasticSerializer, IndividualSmallElasticSerializer) @@ -51,6 +51,7 @@ # Outputs # ---------------------------------- + class OutputSerializer(MeasurementTypeableSerializer): group = serializers.PrimaryKeyRelatedField( @@ -62,13 +63,10 @@ class OutputSerializer(MeasurementTypeableSerializer): required=False, allow_null=True, ) - interventions = serializers.PrimaryKeyRelatedField( + interventions = PKField( queryset=Intervention.objects.all(), - many=True, - read_only=False, - required=False, - allow_null=True, - ) + many=True, required=True, allow_null=True) + tissue = utils.SlugRelatedField( slug_field="name", queryset=InfoNode.objects.filter(ntype=InfoNode.NTypes.Tissue), @@ -91,10 +89,12 @@ class Meta: def to_internal_value(self, data): data.pop("comments", None) data.pop("descriptions", None) + data.pop("image", None) + data.pop("source", None) data = self.retransform_map_fields(data) data = self.to_internal_related_fields(data) - self.validate_wrong_keys(data, additional_fields=OutputExSerializer.Meta.fields) + self.validate_wrong_keys(data) return super(serializers.ModelSerializer, self).to_internal_value(data) def validate(self, attrs): @@ -103,7 +103,6 @@ def validate(self, attrs): self.validate_group_individual_output(attrs) _validate_required_key(attrs, "measurement_type") - _validate_required_key(attrs, "substance") _validate_required_key(attrs, "tissue") _validate_required_key(attrs, "interventions") @@ -173,7 +172,6 @@ def to_internal_value(self, data): for output in temp_outputs: outputs_from_file = self.entries_from_file(output) outputs.extend(outputs_from_file) - # ---------------------------------- # finished # ---------------------------------- @@ -191,7 +189,7 @@ def to_internal_value(self, data): [data.pop(field, None) for field in drop_fields] data["outputs"] = outputs data = self.transform_map_fields(data) - self.validate_wrong_keys(data, OutputSerializer.Meta.fields) + self.validate_wrong_keys(data) return super(serializers.ModelSerializer, self).to_internal_value(data) def validate_label_map(self, value) -> None: diff --git a/backend/pkdb_app/serializers.py b/backend/pkdb_app/serializers.py index b6d04251..dfda994b 100644 --- a/backend/pkdb_app/serializers.py +++ b/backend/pkdb_app/serializers.py @@ -8,6 +8,7 @@ from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.db.models import Q from rest_framework import serializers +from rest_framework.fields import empty from rest_framework.settings import api_settings from pkdb_app.behaviours import map_field @@ -354,8 +355,6 @@ def make_entry(self, entry, template, data, source): try: entry_value = getattr(entry, values[1]) - - except AttributeError: raise serializers.ValidationError( @@ -464,7 +463,7 @@ def to_representation(self, instance): request = self.context.get('request') # url representation of file - for file in ["source","image"]: + for file in ["source", "image"]: if file in rep: if "||" not in str(rep[file]): rep[file] = request.build_absolute_uri(getattr(instance, file).file.url) @@ -530,6 +529,8 @@ def to_internal_related_fields(self, data): f"intervention <{intervention}> does not exist, check interventions." ) data["interventions"] = interventions + else: + data["interventions"] = [] return data @@ -784,4 +785,4 @@ def to_internal_value(self, data): try: return float(data) except (TypeError, ValueError): - self.fail('invalid') + self.fail('invalid') \ No newline at end of file diff --git a/backend/pkdb_app/subjects/serializers.py b/backend/pkdb_app/subjects/serializers.py index 657bb3db..2d12099e 100644 --- a/backend/pkdb_app/subjects/serializers.py +++ b/backend/pkdb_app/subjects/serializers.py @@ -102,7 +102,7 @@ def validate(self, attrs): if attrs[info_node] is not None: attrs[info_node] = getattr(attrs[info_node], info_node) - attrs["choice"] = attrs["measurement_type"].validate_complete(data=attrs)["choice"] + attrs["choice"] = attrs["measurement_type"].validate_complete(data=attrs, time_allowed=False)["choice"] except ValueError as err: raise serializers.ValidationError(err) diff --git a/backend/pkdb_app/utils.py b/backend/pkdb_app/utils.py index 20699faf..797b5f76 100644 --- a/backend/pkdb_app/utils.py +++ b/backend/pkdb_app/utils.py @@ -149,20 +149,20 @@ def _create(validated_data, model_manager=None, model_serializer=None, return instance, popped_data -def initialize_normed(notnorm_instance): - norm = copy.copy(notnorm_instance) +def initialize_normed(not_norm_instance): + norm = copy.copy(not_norm_instance) norm.pk = None norm.normed = True norm.normalize() - norm.raw_id = notnorm_instance.pk + norm.raw_id = not_norm_instance.pk try: - norm.individual_id = notnorm_instance.individual.pk + norm.individual_id = not_norm_instance.individual.pk except AttributeError: pass try: - norm.group_id = notnorm_instance.group.pk + norm.group_id = not_norm_instance.group.pk except AttributeError: pass From e8d5de7fe5bb2655a11dd7a977b67a9dad6a2c2d Mon Sep 17 00:00:00 2001 From: Jan Grzegorzewski Date: Sun, 20 Dec 2020 17:59:14 +0100 Subject: [PATCH 24/26] debug --- backend/pkdb_app/outputs/serializers.py | 4 ++-- backend/pkdb_app/subjects/serializers.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/pkdb_app/outputs/serializers.py b/backend/pkdb_app/outputs/serializers.py index 76cc3cde..6ae32488 100644 --- a/backend/pkdb_app/outputs/serializers.py +++ b/backend/pkdb_app/outputs/serializers.py @@ -19,7 +19,7 @@ CommentElasticSerializer from ..interventions.models import Intervention from ..serializers import ( - ExSerializer, StudySmallElasticSerializer, SidNameLabelSerializer, PKField) + ExSerializer, StudySmallElasticSerializer, SidNameLabelSerializer) from ..subjects.models import Group, DataFile, Individual from ..subjects.serializers import ( EXTERN_FILE_FIELDS, GroupSmallElasticSerializer, IndividualSmallElasticSerializer) @@ -63,7 +63,7 @@ class OutputSerializer(MeasurementTypeableSerializer): required=False, allow_null=True, ) - interventions = PKField( + interventions = serializers.PrimaryKeyRelatedField( queryset=Intervention.objects.all(), many=True, required=True, allow_null=True) diff --git a/backend/pkdb_app/subjects/serializers.py b/backend/pkdb_app/subjects/serializers.py index 2d12099e..30367aca 100644 --- a/backend/pkdb_app/subjects/serializers.py +++ b/backend/pkdb_app/subjects/serializers.py @@ -92,9 +92,7 @@ def validate_count(count): raise serializers.ValidationError(f"count <{count}> has to be greater or equal to 1. ") return count - def validate(self, attrs): - try: # perform via dedicated function on categorials for info_node in ['substance', 'measurement_type']: From 7d446c5a18149386629bfe9d0f29393afb279066 Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Tue, 12 Jan 2021 11:40:42 +0100 Subject: [PATCH 25/26] Paula Avatar added --- .../public/assets/images/avatars/paula_128.png | Bin 0 -> 38203 bytes frontend/src/components/home/About.vue | 1 + frontend/src/components/lib/UserAvatar.vue | 4 ++++ 3 files changed, 5 insertions(+) create mode 100644 frontend/public/assets/images/avatars/paula_128.png diff --git a/frontend/public/assets/images/avatars/paula_128.png b/frontend/public/assets/images/avatars/paula_128.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf0f49fbb40a2c2f6a01ed746da2193b4fc0f5a GIT binary patch literal 38203 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 B7YqOZ literal 0 HcmV?d00001 diff --git a/frontend/src/components/home/About.vue b/frontend/src/components/home/About.vue index 1726d727..70f78cc5 100644 --- a/frontend/src/components/home/About.vue +++ b/frontend/src/components/home/About.vue @@ -18,6 +18,7 @@ +

If you have questions or feedback please contact diff --git a/frontend/src/components/lib/UserAvatar.vue b/frontend/src/components/lib/UserAvatar.vue index 304bc025..cc216a63 100644 --- a/frontend/src/components/lib/UserAvatar.vue +++ b/frontend/src/components/lib/UserAvatar.vue @@ -64,6 +64,8 @@ image = 'sara_128.png'; } else if (this.initials === 'SB'){ image = 'balcisue_128.png'; + } else if (this.initials === 'PO'){ + image = 'paula_128.png'; } } else if (this.username) { image = 'user_128.png'; @@ -91,6 +93,8 @@ image = 'sara_128.png'; } else if (this.username === 'balcisue'){ image = 'balcisue_128.png'; + } else if (this.username === 'paula-ogata'){ + image = 'paula_128.png'; } } return img_dir + image From 1d06b00b4fc90cdd83cf343fc112fdac67a9280d Mon Sep 17 00:00:00 2001 From: Matthias Koenig Date: Tue, 19 Jan 2021 10:25:53 +0100 Subject: [PATCH 26/26] version bump for release --- backend/pkdb_app/_version.py | 2 +- frontend/package.json | 2 +- release-notes/0.9.5.md | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/pkdb_app/_version.py b/backend/pkdb_app/_version.py index 9f65c118..198533a2 100644 --- a/backend/pkdb_app/_version.py +++ b/backend/pkdb_app/_version.py @@ -1,4 +1,4 @@ """ Definition of version string. """ -__version__ = "0.9.4" +__version__ = "0.9.5" diff --git a/frontend/package.json b/frontend/package.json index 792b7f0c..c39f8c10 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "pkdb", - "version": "0.9.2", + "version": "0.9.5", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/release-notes/0.9.5.md b/release-notes/0.9.5.md index e66cbe2f..1ce3ba95 100644 --- a/release-notes/0.9.5.md +++ b/release-notes/0.9.5.md @@ -4,5 +4,7 @@ ## Fixes - interventions were missing in download (#683) +- bugfixes and additional validation rules +- security updates