Compare commits

..

No commits in common. "62887d48b5a6f2e650222e88cc81c50f582b8995" and "75331a22d3c67893a2c358f6e5a969985759967b" have entirely different histories.

42 changed files with 1709 additions and 3369 deletions

View file

@ -6,8 +6,6 @@ Small and insignificant changes may not be included in this log.
## Unversioned Changes
* Upgraded Fyne to version 2.5.0. - *Simon Sarasova*
* Added neural network trait prediction to genetic analyses. - *Simon Sarasova*
* Improved the Create Genetic Models utility and neural network training code. Models are now able to predict traits with some accuracy. - *Simon Sarasova*
* Improved ReadMe.md. - *Simon Sarasova*
* Improved Seekia's slogan and Whitepaper.md. - *Simon Sarasova*

View file

@ -9,4 +9,4 @@ Many other people have written code for modules which are imported by Seekia. Th
Name | Date Of First Commit | Number Of Commits
--- | --- | ---
Simon Sarasova | June 13, 2023 | 267
Simon Sarasova | June 13, 2023 | 265

32
go.mod
View file

@ -5,7 +5,7 @@ replace seekia => ./
go 1.22
require (
fyne.io/fyne/v2 v2.5.0
fyne.io/fyne/v2 v2.4.5
github.com/chai2010/webp v1.1.1
github.com/cloudflare/circl v1.3.9
github.com/dgraph-io/badger/v4 v4.2.0
@ -15,15 +15,14 @@ require (
github.com/vmihailenco/msgpack/v5 v5.4.1
github.com/wcharczuk/go-chart/v2 v2.1.1
github.com/zeebo/blake3 v0.2.3
golang.org/x/crypto v0.23.0
golang.org/x/image v0.18.0
golang.org/x/crypto v0.21.0
golang.org/x/image v0.15.0
gorgonia.org/gorgonia v0.9.18
gorgonia.org/tensor v0.9.24
)
require (
fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e // indirect
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect
github.com/awalterschulze/gographviz v2.0.3+incompatible // indirect
github.com/blend/go-sdk v1.20220411.3 // indirect
@ -33,13 +32,13 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fredbi/uri v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fredbi/uri v1.0.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 // indirect
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240306074159-ea2d69986ecb // indirect
github.com/go-text/render v0.1.0 // indirect
github.com/go-text/typesetting v0.1.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
@ -52,25 +51,23 @@ require (
github.com/google/flatbuffers v23.5.26+incompatible // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
github.com/klauspost/compress v1.13.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 // indirect
github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.2.2 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/tevino/abool v1.2.0 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xtgo/set v1.0.0 // indirect
github.com/yuin/goldmark v1.7.1 // indirect
github.com/yuin/goldmark v1.5.5 // indirect
go.opencensus.io v0.23.0 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
@ -79,4 +76,5 @@ require (
gorgonia.org/dawson v1.2.0 // indirect
gorgonia.org/vecf32 v0.9.0 // indirect
gorgonia.org/vecf64 v0.9.0 // indirect
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect
)

83
go.sum
View file

@ -37,15 +37,13 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
fyne.io/fyne/v2 v2.5.0 h1:lEjEIso0Vi4sJXYngIMoXOM6aUjqnPjK7pBpxRxG9aI=
fyne.io/fyne/v2 v2.5.0/go.mod h1:9D4oT3NWeG+MLi/lP7ItZZyujHC/qqMJpoGTAYX5Uqc=
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
fyne.io/fyne/v2 v2.4.5 h1:W6jpAEmLoBbKyBB+EXqI7GMJ7kLgHQWCa0wZHUV2VfQ=
fyne.io/fyne/v2 v2.4.5/go.mod h1:SlOgbca0y80cRObu/JOhxIJdIgtoW7aCyqUVlTMgs0Y=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e h1:Hvs+kW2VwCzNToF3FmnIAzmivNgrclwPgoUdVSrjkP8=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
@ -124,19 +122,17 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
github.com/fredbi/uri v1.0.0 h1:s4QwUAZ8fz+mbTsukND+4V5f+mJ/wjaTokwstGUAemg=
github.com/fredbi/uri v1.0.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4=
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg=
github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a h1:ybgRdYvAHTn93HW79bLiBiJwVL4jVeyGQRZMgImoeWs=
github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY=
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 h1:+31CdF/okdokeFNoy9L/2PccG3JFidQT3ev64/r4pYU=
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -150,8 +146,9 @@ github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVin
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240306074159-ea2d69986ecb h1:S9I8pIVT5JHKDvmI1vQ0qs5fqxzUfhcZm/YbUC/8k1k=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240306074159-ea2d69986ecb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gota/gota v0.12.0/go.mod h1:UT+NsWpZC/FhaOyWb9Hui0jXg0Iq8e/YugZHTbyW/34=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
@ -249,8 +246,6 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -266,6 +261,7 @@ github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfre
github.com/gorgonia/bindgen v0.0.0-20180812032444-09626750019e/go.mod h1:YzKk63P9jQHkwAo2rXHBv02yPxDzoQT2cBV0x5bGV/8=
github.com/gorgonia/bindgen v0.0.0-20210223094355-432cd89e7765/go.mod h1:BLHSe436vhQKRfm6wxJgebeK4fDY+ER/8jV3vVH9yYU=
github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@ -290,8 +286,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8=
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
@ -309,6 +303,7 @@ github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
@ -337,10 +332,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
@ -351,8 +342,6 @@ github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuR
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -368,8 +357,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/rymdport/portal v0.2.2 h1:P2Q/4k673zxdFAsbD8EESZ7psfuO6/4jNu6EDrDICkM=
github.com/rymdport/portal v0.2.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
@ -388,6 +375,7 @@ github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -397,9 +385,12 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
@ -416,8 +407,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU=
github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
@ -452,8 +443,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -487,8 +478,8 @@ golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeap
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -504,8 +495,8 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg=
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc=
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda h1:O+EUvnBNPwI4eLthn8W5K+cS8zQZfgTABPLNm6Bna34=
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@ -563,8 +554,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -644,10 +635,11 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -662,8 +654,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -866,8 +858,8 @@ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@ -875,7 +867,6 @@ gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@ -904,6 +895,8 @@ gorgonia.org/vecf32 v0.9.0/go.mod h1:NCc+5D2oxddRL11hd+pCB1PEyXWOyiQxfZ/1wwhOXCA
gorgonia.org/vecf64 v0.7.0/go.mod h1:1y4pmcSd+wh3phG+InwWQjYrqwyrtN9h27WLFVQfV1Q=
gorgonia.org/vecf64 v0.9.0 h1:bgZDP5x0OzBF64PjMGC3EvTdOoMEcmfAh1VCUnZFm1A=
gorgonia.org/vecf64 v0.9.0/go.mod h1:hp7IOWCnRiVQKON73kkC/AUMtEXyf9kGlVrtPQ9ccVA=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 h1:oomkgU6VaQDsV6qZby2uz1Lap0eXmku8+2em3A/l700=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -424,7 +424,7 @@ func setBuildMateProfilePage_Wealth(window fyne.Window, previousPage func()){
}
isLowerBoundHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setWealthIsLowerBoundExplainerPage(window, currentPage)
setWealthOrIncomeIsLowerBoundExplainerPage(window, currentPage)
})
isLowerBoundCheckRow := container.NewHBox(layout.NewSpacer(), isLowerBoundCheck, isLowerBoundHelpButton, layout.NewSpacer())

View file

@ -674,95 +674,45 @@ func setPolygenicDiseaseLocusRiskWeightProbabilityExplainerPage(window fyne.Wind
}
func setDiscreteTraitNeuralNetworkPredictionExplainerPage(window fyne.Window, previousPage func()){
func setTraitOutcomeScoresExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Neural Network Prediction")
title := getPageTitleCentered("Help - Outcome Scores")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Discrete Trait Neural Network Prediction")
subtitle := getPageSubtitleCentered("Trait Outcome Scores")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Neural network prediction is calculated by inputing a genome's loci into a neural network.")
description5 := getLabelCentered("Each trait has multiple outcomes, and the neural network predicts a single outcome.")
description6 := getLabelCentered("The higher the quantity of tested loci, the more accurate the result is.")
description7 := getLabelCentered("The probability that a neural network's prediction is accurate is called its Confidence.")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description4 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description5 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7)
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5)
setPageContent(page, window)
}
func setOffspringDiscreteTraitNeuralNetworkPredictionExplainerPage(window fyne.Window, previousPage func()){
func setOffspringTraitOutcomeScoresExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Neural Network Prediction")
title := getPageTitleCentered("Help - Outcome Scores")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Offspring Discrete Trait Neural Network Prediction")
subtitle := getPageSubtitleCentered("Offspring Trait Outcome Scores")
description1 := getLabelCentered("Couple genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Neural network prediction is calculated by inputing a genome's loci into a neural network.")
description5 := getLabelCentered("Each trait has multiple outcomes, and the neural network predicts the probability of each outcome.")
description6 := getLabelCentered("The higher the quantity of known loci, the more accurate the result is.")
description7 := getLabelCentered("The probability that a neural network's prediction is accurate is called its Confidence.")
description8 := getLabelCentered("For couples, the Confidence is the average of the confidence of 100 prospective offspring predictions.")
description1 := getLabelCentered("Couple genetic analyses contain trait analyses for the offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description4 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description5 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8)
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5)
setPageContent(page, window)
}
func setDiscreteTraitRulesPredictionExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Rules Prediction")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Discrete Trait Rules Prediction")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Rule prediction is calculated by testing many predictive rules.")
description5 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description6 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description7 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description8 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8)
setPageContent(page, window)
}
func setOffspringDiscreteTraitRulesPredictionExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Rules Prediction")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Offspring Discrete Trait Rules Prediction")
description1 := getLabelCentered("Couple genetic analyses contain discrete trait analyses for the offspring.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Neural Networks and Rules.")
description3 := getLabelCentered("Each trait can be analyzed by either rules or a neural network.")
description4 := getLabelCentered("Rule prediction is calculated by testing many predictive rules.")
description5 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description6 := getLabelCentered("Points are added to an outcome to represent a higher probability.")
description7 := getLabelCentered("Points are subtracted from an outcome to represent a lower probability.")
description8 := getLabelCentered("The more rules that are tested, the higher the accuracy of the result will be.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8)
setPageContent(page, window)
}
func setDiscreteTraitRulesExplainerPage(window fyne.Window, previousPage func()){
func setTraitRulesExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Trait Rules")
@ -770,8 +720,8 @@ func setDiscreteTraitRulesExplainerPage(window fyne.Window, previousPage func())
subtitle := getPageSubtitleCentered("Trait Rules")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("Discrete traits has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -782,16 +732,16 @@ func setDiscreteTraitRulesExplainerPage(window fyne.Window, previousPage func())
setPageContent(page, window)
}
func setOffspringDiscreteTraitRulesExplainerPage(window fyne.Window, previousPage func()){
func setOffspringTraitRulesExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Trait Rules")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Discrete Trait Rules")
subtitle := getPageSubtitleCentered("Trait Rules")
description1 := getLabelCentered("Offspring genetic analyses contain discrete trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each discrete trait has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Offspring genetic analyses contain trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -803,15 +753,15 @@ func setOffspringDiscreteTraitRulesExplainerPage(window fyne.Window, previousPag
setPageContent(page, window)
}
func setDiscreteTraitQuantityOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
func setTraitNumberOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Quantity Of Rules Tested")
title := getPageTitleCentered("Help - Number Of Rules Tested")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Quantity Of Trait Rules Tested")
subtitle := getPageSubtitleCentered("Number Of Trait Rules Tested")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represent the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
@ -825,13 +775,13 @@ func setDiscreteTraitQuantityOfRulesTestedExplainerPage(window fyne.Window, prev
setPageContent(page, window)
}
func setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
func setOffspringTraitNumberOfRulesTestedExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Quantity Of Rules Tested")
title := getPageTitleCentered("Help - Number Of Rules Tested")
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered("Offspring Trait Quantity Of Rules Tested")
subtitle := getPageSubtitleCentered("Offspring Trait Number Of Rules Tested")
description1 := getLabelCentered("Offspring genetic analyses contain trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
@ -856,8 +806,8 @@ func setOffspringProbabilityOfPassingTraitRuleExplainerPage(window fyne.Window,
subtitle := getPageSubtitleCentered("Offspring Probability Of Passing Trait Rule")
description1 := getLabelCentered("Offspring genetic analyses contain discrete trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each discrete trait has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Offspring genetic analyses contain trait analyses for a couple's offspring.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -871,7 +821,8 @@ func setOffspringProbabilityOfPassingTraitRuleExplainerPage(window fyne.Window,
setPageContent(page, window)
}
func setPersonPassesDiscreteTraitRuleExplainerPage(window fyne.Window, previousPage func()){
func setPersonPassesTraitRuleExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Trait Rules")
@ -879,8 +830,8 @@ func setPersonPassesDiscreteTraitRuleExplainerPage(window fyne.Window, previousP
subtitle := getPageSubtitleCentered("Person Passes Trait Rule")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("Each discrete trait has multiple outcomes, and each outcome has an associated score.")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
@ -891,7 +842,7 @@ func setPersonPassesDiscreteTraitRuleExplainerPage(window fyne.Window, previousP
setPageContent(page, window)
}
func setGenomePassesDiscreteTraitRuleExplainerPage(window fyne.Window, previousPage func()){
func setGenomePassesTraitRuleExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Genome Passes Rule")
@ -899,23 +850,22 @@ func setGenomePassesDiscreteTraitRuleExplainerPage(window fyne.Window, previousP
subtitle := getPageSubtitleCentered("Genome Passes Trait Rule")
description1 := getLabelCentered("Person genetic analyses contain discrete trait analyses.")
description2 := getLabelCentered("There are 2 discrete trait analysis methods: Rules and Neural Networks.")
description3 := getLabelCentered("For rule-based analyses, each trait's outcome has an associated score.")
description4 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description5 := getLabelCentered("Each outcome's score is determined by trait rules.")
description6 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
description7 := getLabelCentered("If a person passes a rule, its effects will be applied to their outcome(s).")
description8 := getLabelCentered("If a person has imported multiple genomes, each genome may or may not pass the rule.")
description9 := getLabelCentered("If one genome passes and another does not, it means that one genome has an invalid value.")
description10 := getLabelCentered("This happens because genome sequencing technology is not perfectly accurate.")
description1 := getLabelCentered("Person genetic analyses contain trait analyses.")
description2 := getLabelCentered("Each trait has multiple outcomes, and each outcome has an associated score.")
description3 := getLabelCentered("A higher score represents a higher probability, and a lower score represents the opposite.")
description4 := getLabelCentered("Each outcome's score is determined by trait rules.")
description5 := getLabelCentered("A trait rule will add/subtract values to outcome(s).")
description6 := getLabelCentered("If a person passes a rule, its effects will be applied to their outcome(s).")
description7 := getLabelCentered("If a person has imported multiple genomes, each genome may or may not pass the rule.")
description8 := getLabelCentered("If one genome passes and another does not, it means that one genome has an invalid value.")
description9 := getLabelCentered("This happens because genome sequencing technology is not perfectly accurate.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8, description9, description10)
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8, description9)
setPageContent(page, window)
}
func setDiscreteTraitRuleOutcomeEffectsExplainerPage(window fyne.Window, previousPage func()){
func setTraitRuleOutcomeEffectsExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Outcome Effects")
@ -935,7 +885,7 @@ func setDiscreteTraitRuleOutcomeEffectsExplainerPage(window fyne.Window, previou
}
func setWealthIsLowerBoundExplainerPage(window fyne.Window, previousPage func()){
func setWealthOrIncomeIsLowerBoundExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Is Lower Bound")
@ -955,7 +905,7 @@ func setWealthIsLowerBoundExplainerPage(window fyne.Window, previousPage func())
func setMemoExplainerPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Help - Memo")
backButton := getBackButtonCentered(previousPage)
@ -1076,7 +1026,7 @@ func setHostingHelpPage(window fyne.Window, previousPage func()){
description1 := getLabelCentered("Be a Seekia host.")
description2 := getLabelCentered("Your computer will seed content to the network.")
description3 := getLabelCentered("You can choose to host as much or as little as you desire.")
//TODO: Add buttons to show more help
//TODO: Describe how hosting works and associated risks

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,6 @@ import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/widget"
import "seekia/resources/geneticPredictionModels"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits"
@ -71,15 +70,11 @@ func setViewPersonGeneticAnalysisPage(window fyne.Window, personIdentifier strin
polygenicDiseasesButton := widget.NewButton("Polygenic Diseases", func(){
setViewPersonGeneticAnalysisPolygenicDiseasesPage(window, personIdentifier, analysisObject, currentPage)
})
discreteTraitsButton := widget.NewButton("Discrete Traits", func(){
setViewPersonGeneticAnalysisDiscreteTraitsPage(window, personIdentifier, analysisObject, currentPage)
})
numericTraitsButton := widget.NewButton("Numeric Traits", func(){
//TODO
showUnderConstructionDialog(window)
traitsButton := widget.NewButton("Traits", func(){
setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisObject, currentPage)
})
categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, discreteTraitsButton, numericTraitsButton))
categoryButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, generalButton, monogenicDiseasesButton, polygenicDiseasesButton, traitsButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), warningLabel1, warningLabel2, widget.NewSeparator(), personNameRow, numberOfAnalyzedGenomesRow, widget.NewSeparator(), categoryButtonsGrid)
@ -99,7 +94,7 @@ func setViewPersonGeneticAnalysisMonogenicDiseasesPage(window fyne.Window, analy
getMonogenicDiseasesContainer := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil){ return nil, err }
// Outputs:
@ -245,8 +240,8 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window,
title := getPageTitleCentered("Viewing Genetic Analysis - " + diseaseName)
backButton := getBackButtonCentered(previousPage)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -285,6 +280,8 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseDetailsPage(window fyne.Window,
totalNumberOfVariants := len(diseaseVariantsMap)
totalNumberOfVariantsString := helpers.ConvertIntToString(totalNumberOfVariants)
emptyLabelA := widget.NewLabel("")
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -739,7 +736,7 @@ func setViewPersonGeneticAnalysisMonogenicDiseaseVariantDetailsPage(window fyne.
getGenomesHaveVariantGrid := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
if (err != nil) { return nil, err }
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -897,7 +894,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
getPolygenicDiseasesContainer := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil){ return nil, err }
// Outputs:
@ -938,7 +935,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseasesPage(window fyne.Window, perso
diseaseNameText := getBoldLabelCentered(diseaseName)
personRiskScoreKnown, _, personRiskScoreFormatted, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier)
personRiskScoreKnown, _, personRiskScoreFormatted, _, _, conflictExists, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, mainGenomeIdentifier)
if (err != nil) { return nil, err }
getPersonRiskScoreLabelText := func()string{
@ -1018,7 +1015,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
backButton := getBackButtonCentered(previousPage)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -1057,6 +1054,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
totalNumberOfLoci := len(diseaseLociMap)
totalNumberOfLociString := helpers.ConvertIntToString(totalNumberOfLoci)
emptyLabelA := widget.NewLabel("")
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -1089,7 +1087,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
viewHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setCombinedGenomesExplainerPage(window, currentPage)
})
genomeNameLabel := getBoldLabel(genomeName)
genomeNameCell := container.NewHBox(layout.NewSpacer(), viewHelpButton, genomeNameLabel, layout.NewSpacer())
@ -1098,7 +1096,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseDetailsPage(window fyne.Window,
genomeNameCell := getGenomeNameCell()
diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier)
diseaseRiskScoreKnown, _, diseaseRiskScoreFormatted, _, numberOfLociTested, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(analysisObject, diseaseName, genomeIdentifier)
if (err != nil) { return err }
getRiskScoreLabelText := func()string{
@ -1624,7 +1622,7 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
getGenomesLocusInfoGrid := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
if (err != nil) { return nil, err }
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -1767,19 +1765,19 @@ func setViewPersonGeneticAnalysisPolygenicDiseaseLocusDetailsPage(window fyne.Wi
}
func setViewPersonGeneticAnalysisDiscreteTraitsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){
func setViewPersonGeneticAnalysisTraitsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, previousPage func()){
currentPage := func(){setViewPersonGeneticAnalysisDiscreteTraitsPage(window, personIdentifier, analysisObject, previousPage)}
currentPage := func(){setViewPersonGeneticAnalysisTraitsPage(window, personIdentifier, analysisObject, previousPage)}
title := getPageTitleCentered("Viewing Genetic Analysis - Discrete Traits")
title := getPageTitleCentered("Viewing Genetic Analysis - Traits")
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Below is an analysis of the discrete traits for this person's genome.")
description := getLabelCentered("Below is an analysis of the traits for this person's genome.")
getTraitsContainer := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil){ return nil, err }
// Outputs:
@ -1800,154 +1798,102 @@ func setViewPersonGeneticAnalysisDiscreteTraitsPage(window fyne.Window, personId
mainGenomeIdentifier, err := getMainGenomeIdentifier()
if (err != nil){ return nil, err }
emptyLabel1 := widget.NewLabel("")
traitNameLabel := getItalicLabelCentered("Trait Name")
emptyLabel2 := widget.NewLabel("")
predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome")
outcomeScoresLabel := getItalicLabelCentered("Outcome Scores")
quantityOfLabel := getItalicLabelCentered("Quantity Of")
lociKnownLabel := getItalicLabelCentered("Loci Known")
emptyLabel3 := widget.NewLabel("")
conflictExistsLabel := getItalicLabelCentered("Conflict Exists?")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
emptyLabel := widget.NewLabel("")
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
predictedOutcomeColumn := container.NewVBox(emptyLabel2, predictedOutcomeLabel, widget.NewSeparator())
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel, lociKnownLabel, widget.NewSeparator())
conflictExistsColumn := container.NewVBox(emptyLabel3, conflictExistsLabel, widget.NewSeparator())
viewButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
traitNameColumn := container.NewVBox(traitNameLabel, widget.NewSeparator())
outcomeScoresColumn := container.NewVBox(outcomeScoresLabel, widget.NewSeparator())
conflictExistsColumn := container.NewVBox(conflictExistsLabel, widget.NewSeparator())
viewButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return nil, err }
for _, traitObject := range traitObjectsList{
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
continue
}
traitName := traitObject.TraitName
traitLociList := traitObject.LociList
traitRulesList := traitObject.RulesList
if (len(traitLociList) == 0 && len(traitRulesList) == 0){
// This trait does not have any rules or loci to analyze
if (len(traitRulesList) == 0){
// This trait does not have any rules
// We cannot analyze it yet
// We will add neural network prediction so we can predict these traits
continue
}
traitOutcomeNamesList := traitObject.OutcomesList
traitNameText := getBoldLabelCentered(traitName)
neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, _, quantityOfLociKnown_NeuralNetwork, _, anyRulesExist, rulesAnalysisExists, _, rulesPredictedOutcomeExists, rulesPredictedOutcome, _, quantityOfLociKnown_Rules, conflictExists, err := readGeneticAnalysis.GetPersonDiscreteTraitInfoFromGeneticAnalysis(analysisObject, traitName, mainGenomeIdentifier)
_, anyTraitRuleTested, outcomeScoresMap, _, conflictExists, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisObject, traitName, mainGenomeIdentifier)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false && anyRulesExist == false){
// We can't analyze this trait
continue
}
getPredictionLabel := func()fyne.Widget{
if (neuralNetworkExists == true){
if (neuralNetworkAnalysisExists == false){
result := getItalicLabel("Unknown")
return result
}
result := getBoldLabel(neuralNetworkPredictedOutcome)
return result
}
// anyRulesExist must be true
if (rulesAnalysisExists == false || rulesPredictedOutcomeExists == false){
result := getItalicLabel("Unknown")
return result
}
result := getBoldLabel(rulesPredictedOutcome)
return result
}
predictionLabel := getPredictionLabel()
predictionLabelCentered := getWidgetCentered(predictionLabel)
getQuantityOfLociKnown := func()int{
if (neuralNetworkExists == true){
return quantityOfLociKnown_NeuralNetwork
}
return quantityOfLociKnown_Rules
}
quantityOfLociKnown := getQuantityOfLociKnown()
getTotalQuantityOfLoci := func()int{
if (neuralNetworkExists == true){
totalQuantityOfLoci := len(traitLociList)
return totalQuantityOfLoci
}
traitLociList_Rules := traitObject.LociList_Rules
totalQuantityOfLoci := len(traitLociList_Rules)
return totalQuantityOfLoci
}
totalQuantityOfLoci := getTotalQuantityOfLoci()
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
// We add all of the columns except for the trait outcomes column, which may be multiple rows high
conflictExistsString := helpers.ConvertBoolToYesOrNoString(conflictExists)
conflictExistsLabel := getBoldLabelCentered(conflictExistsString)
viewDetailsButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window, personIdentifier, analysisObject, traitName, currentPage)
setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisObject, traitName, currentPage)
}))
traitNameText := getBoldLabelCentered(traitName)
traitNameColumn.Add(traitNameText)
predictedOutcomeColumn.Add(predictionLabelCentered)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
conflictExistsColumn.Add(conflictExistsLabel)
viewButtonsColumn.Add(viewDetailsButton)
if (anyTraitRuleTested == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
outcomeScoresColumn.Add(unknownLabel)
} else {
// We have to sort outcome names so they always show up in the same order
traitOutcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(traitOutcomeNamesList)
for index, outcomeName := range traitOutcomeNamesListSorted{
outcomeScore, exists := outcomeScoresMap[outcomeName]
if (exists == false){
return nil, errors.New("Outcome name not found in outcomeScoresMap: " + outcomeName)
}
outcomeScoreString := helpers.ConvertIntToString(outcomeScore)
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
outcomeScoresColumn.Add(outcomeRow)
if (index > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
traitNameColumn.Add(emptyLabelA)
conflictExistsColumn.Add(emptyLabelB)
viewButtonsColumn.Add(emptyLabelC)
}
}
}
traitNameColumn.Add(widget.NewSeparator())
predictedOutcomeColumn.Add(widget.NewSeparator())
quantityOfLociKnownColumn.Add(widget.NewSeparator())
outcomeScoresColumn.Add(widget.NewSeparator())
conflictExistsColumn.Add(widget.NewSeparator())
viewButtonsColumn.Add(widget.NewSeparator())
}
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
outcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitOutcomeScoresExplainerPage(window, currentPage)
})
predictedOutcomeColumn.Add(predictedOutcomeHelpButton)
outcomeScoresColumn.Add(outcomeScoresHelpButton)
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
traitsContainer := container.NewHBox(layout.NewSpacer(), traitNameColumn, predictedOutcomeColumn, quantityOfLociKnownColumn)
traitsContainer := container.NewHBox(layout.NewSpacer(), traitNameColumn, outcomeScoresColumn)
if (multipleGenomesExist == true){
@ -1977,15 +1923,16 @@ func setViewPersonGeneticAnalysisDiscreteTraitsPage(window fyne.Window, personId
}
func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, traitName string, previousPage func()){
currentPage := func(){setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window, personIdentifier, analysisObject, traitName, previousPage)}
func setViewPersonGeneticAnalysisTraitDetailsPage(window fyne.Window, personIdentifier string, analysisObject geneticAnalysis.PersonAnalysis, traitName string, previousPage func()){
currentPage := func(){setViewPersonGeneticAnalysisTraitDetailsPage(window, personIdentifier, analysisObject, traitName, previousPage)}
title := getPageTitleCentered("Viewing Genetic Analysis - " + traitName)
backButton := getBackButtonCentered(previousPage)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(analysisObject)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -2014,51 +1961,33 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe
traitInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewTraitDetailsPage(window, traitName, currentPage)
})
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, traitInfoButton, layout.NewSpacer())
neuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, traitInfoButton, layout.NewSpacer())
getGenomesContainer := func()(*fyne.Container, error){
emptyLabel1 := widget.NewLabel("")
traitRulesMap, err := traits.GetTraitRulesMap(traitName)
if (err != nil){ return nil, err }
totalNumberOfRules := len(traitRulesMap)
totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules)
emptyLabelA := widget.NewLabel("")
genomeNameLabel := getItalicLabelCentered("Genome Name")
emptyLabel2 := widget.NewLabel("")
predictedOutcomeLabel := getItalicLabelCentered("Predicted Outcome")
emptyLabel3 := widget.NewLabel("")
predictionConfidenceLabel := getItalicLabelCentered("Prediction Confidence")
quantityOfLabel := getItalicLabelCentered("Quantity Of")
emptyLabelB := widget.NewLabel("")
outcomeScoresLabel := getItalicLabelCentered("Outcome Scores")
numberOfLabel := getItalicLabelCentered("Number of")
rulesTestedLabel := getItalicLabelCentered("Rules Tested")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
genomeNameColumn := container.NewVBox()
predictedOutcomeColumn := container.NewVBox()
neuralNetworkPredictionConfidenceColumn := container.NewVBox()
numberOfRulesTestedColumn := container.NewVBox(quantityOfLabel, rulesTestedLabel, widget.NewSeparator())
viewDetailsButtonsColumn := container.NewVBox()
if (neuralNetworkExists == false){
// We only need an extra header row if the numberOfRulesTested column is shown
genomeNameColumn.Add(emptyLabel1)
predictedOutcomeColumn.Add(emptyLabel2)
neuralNetworkPredictionConfidenceColumn.Add(emptyLabel3)
viewDetailsButtonsColumn.Add(emptyLabel4)
}
genomeNameColumn.Add(genomeNameLabel)
predictedOutcomeColumn.Add(predictedOutcomeLabel)
neuralNetworkPredictionConfidenceColumn.Add(predictionConfidenceLabel)
viewDetailsButtonsColumn.Add(emptyLabel5)
genomeNameColumn.Add(widget.NewSeparator())
predictedOutcomeColumn.Add(widget.NewSeparator())
neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator())
viewDetailsButtonsColumn.Add(widget.NewSeparator())
emptyLabelD := widget.NewLabel("")
emptyLabelE := widget.NewLabel("")
genomeNameColumn := container.NewVBox(emptyLabelA, genomeNameLabel, widget.NewSeparator())
outcomeScoresColumn := container.NewVBox(emptyLabelB, outcomeScoresLabel, widget.NewSeparator())
numberOfRulesTestedColumn := container.NewVBox(numberOfLabel, rulesTestedLabel, widget.NewSeparator())
viewRulesButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator())
addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{
getGenomeNameCell := func()*fyne.Container{
@ -2079,121 +2008,66 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe
genomeNameCell := getGenomeNameCell()
neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, _, _, anyRulesExist, rulesAnalysisExists, _, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, _, _, err := readGeneticAnalysis.GetPersonDiscreteTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier)
_, anyTraitRuleTested, outcomeScoresMap, numberOfRulesTested, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(analysisObject, traitName, genomeIdentifier)
if (err != nil) { return err }
if (neuralNetworkExists == false && anyRulesExist == false){
// This trait is not analyzable
return errors.New("setViewPersonGeneticAnalysisTraitDetailsPage called with trait which cannot be analyzed via neural networks and traits.")
}
// We add all of the columns except for the trait rule column, which may be multiple rows high
getPredictedOutcomeLabel := func()fyne.Widget{
genomeNumberOfRulesTestedString := helpers.ConvertIntToString(numberOfRulesTested)
numberOfRulesTestedLabel := getBoldLabelCentered(genomeNumberOfRulesTestedString + "/" + totalNumberOfRulesString)
if (neuralNetworkExists == true){
if (neuralNetworkAnalysisExists == false){
predictedOutcomeLabel := getItalicLabel("Unknown")
return predictedOutcomeLabel
}
predictedOutcomeLabel := getBoldLabel(neuralNetworkPredictedOutcome)
return predictedOutcomeLabel
}
// anyRulesExist must be true
if (rulesAnalysisExists == false || rulesPredictedOutcomeExists == false){
predictedOutcomeLabel := getItalicLabel("Unknown")
return predictedOutcomeLabel
}
predictedOutcomeLabel := getBoldLabel(rulesPredictedOutcome)
return predictedOutcomeLabel
}
predictedOutcomeLabel := getPredictedOutcomeLabel()
predictedOutcomeLabelCentered := getWidgetCentered(predictedOutcomeLabel)
getNeuralNetworkConfidenceLabel := func()fyne.Widget{
if (neuralNetworkExists == false){
emptyLabel := widget.NewLabel("")
return emptyLabel
}
if (neuralNetworkAnalysisExists == false){
predictedOutcomeConfidenceLabel := getItalicLabel("Unknown")
return predictedOutcomeConfidenceLabel
}
neuralNetworkPredictionConfidenceString := helpers.ConvertIntToString(neuralNetworkPredictionConfidence)
predictedOutcomeConfidenceLabel := getBoldLabel(neuralNetworkPredictionConfidenceString + "%")
return predictedOutcomeConfidenceLabel
}
predictedOutcomeConfidenceLabel := getNeuralNetworkConfidenceLabel()
predictedOutcomeConfidenceLabelCentered := getWidgetCentered(predictedOutcomeConfidenceLabel)
getQuantityOfRulesTestedLabel := func()(fyne.Widget, error){
if (anyRulesExist == false){
emptyLabel := widget.NewLabel("")
return emptyLabel, nil
}
traitRulesMap, err := traits.GetTraitRulesMap(traitName)
if (err != nil){ return nil, err }
totalNumberOfRules := len(traitRulesMap)
totalNumberOfRulesString := helpers.ConvertIntToString(totalNumberOfRules)
if (rulesAnalysisExists == false){
quantityOfRulesTestedLabel := getItalicLabel("0/" + totalNumberOfRulesString)
return quantityOfRulesTestedLabel, nil
}
quantityOfRulesTestedString := helpers.ConvertIntToString(quantityOfRulesTested)
quantityOfRulesTestedLabel := getBoldLabel(quantityOfRulesTestedString + "/" + totalNumberOfRulesString)
return quantityOfRulesTestedLabel, nil
}
quantityOfRulesTestedLabel, err := getQuantityOfRulesTestedLabel()
if (err != nil) { return err }
quantityOfRulesTestedLabelCentered := getWidgetCentered(quantityOfRulesTestedLabel)
viewDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
if (neuralNetworkExists == true){
//TODO
showUnderConstructionDialog(window)
} else {
setViewPersonGenomeDiscreteTraitRulesPage(window, analysisObject, traitName, genomeIdentifier, genomeName, currentPage)
}
viewRulesButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewPersonGenomeTraitRulesPage(window, analysisObject, traitName, genomeIdentifier, genomeName, currentPage)
})
genomeNameColumn.Add(genomeNameCell)
predictedOutcomeColumn.Add(predictedOutcomeLabelCentered)
neuralNetworkPredictionConfidenceColumn.Add(predictedOutcomeConfidenceLabelCentered)
numberOfRulesTestedColumn.Add(quantityOfRulesTestedLabelCentered)
viewDetailsButtonsColumn.Add(viewDetailsButton)
numberOfRulesTestedColumn.Add(numberOfRulesTestedLabel)
viewRulesButtonsColumn.Add(viewRulesButton)
if (anyTraitRuleTested == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
outcomeScoresColumn.Add(unknownLabel)
} else {
// We have to sort the outcome names so they always show up in the same order
outcomeNamesList := helpers.GetListOfMapKeys(outcomeScoresMap)
helpers.SortStringListToUnicodeOrder(outcomeNamesList)
for index, outcomeName := range outcomeNamesList{
outcomeScore, exists := outcomeScoresMap[outcomeName]
if (exists == false){
return errors.New("Outcome name not found in outcome scores map after being found already: " + outcomeName)
}
outcomeScoreString := helpers.ConvertIntToString(outcomeScore)
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
outcomeScoresColumn.Add(outcomeRow)
if (index > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
genomeNameColumn.Add(emptyLabelA)
numberOfRulesTestedColumn.Add(emptyLabelB)
viewRulesButtonsColumn.Add(emptyLabelC)
}
}
}
genomeNameColumn.Add(widget.NewSeparator())
predictedOutcomeColumn.Add(widget.NewSeparator())
neuralNetworkPredictionConfidenceColumn.Add(widget.NewSeparator())
outcomeScoresColumn.Add(widget.NewSeparator())
numberOfRulesTestedColumn.Add(widget.NewSeparator())
viewDetailsButtonsColumn.Add(widget.NewSeparator())
viewRulesButtonsColumn.Add(widget.NewSeparator())
return nil
}
@ -2238,41 +2112,17 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe
if (err != nil){ return nil, err }
}
if (neuralNetworkExists == true){
neuralNetworksHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitNeuralNetworkPredictionExplainerPage(window, currentPage)
})
predictedOutcomeColumn.Add(neuralNetworksHelpButton)
} else {
rulesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitRulesPredictionExplainerPage(window, currentPage)
})
predictedOutcomeColumn.Add(rulesHelpButton)
}
neuralNetworkConfidenceHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
showUnderConstructionDialog(window)
//TODO
outcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitOutcomeScoresExplainerPage(window, currentPage)
})
neuralNetworkPredictionConfidenceColumn.Add(neuralNetworkConfidenceHelpButton)
outcomeScoresColumn.Add(outcomeScoresHelpButton)
numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
})
numberOfRulesTestedColumn.Add(numberOfRulesTestedHelpButton)
genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, predictedOutcomeColumn)
if (neuralNetworkExists == true){
genomesContainer.Add(neuralNetworkPredictionConfidenceColumn)
} else {
genomesContainer.Add(numberOfRulesTestedColumn)
}
genomesContainer.Add(viewDetailsButtonsColumn)
genomesContainer.Add(layout.NewSpacer())
genomesContainer := container.NewHBox(layout.NewSpacer(), genomeNameColumn, outcomeScoresColumn, numberOfRulesTestedColumn, viewRulesButtonsColumn, layout.NewSpacer())
return genomesContainer, nil
}
@ -2290,12 +2140,12 @@ func setViewPersonGeneticAnalysisDiscreteTraitDetailsPage(window fyne.Window, pe
// Ths function provides a page to view the discrete trait rules for a particular genome from a genetic analysis
func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte, genomeName string, previousPage func()){
// Ths function provides a page to view the trait rules for a particular genome from a genetic analysis
func setViewPersonGenomeTraitRulesPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte, genomeName string, previousPage func()){
setLoadingScreen(window, "Loading Trait Rules", "Loading trait rules...")
currentPage := func(){setViewPersonGenomeDiscreteTraitRulesPage(window, geneticAnalysisObject, traitName, genomeIdentifier, genomeName, previousPage)}
currentPage := func(){setViewPersonGenomeTraitRulesPage(window, geneticAnalysisObject, traitName, genomeIdentifier, genomeName, previousPage)}
title := getPageTitleCentered("View Trait Rules - " + traitName)
@ -2303,7 +2153,7 @@ func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalys
description1 := widget.NewLabel("Below are the trait rule results for this genome.")
rulesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitRulesExplainerPage(window, currentPage)
setTraitRulesExplainerPage(window, currentPage)
})
description1Row := container.NewHBox(layout.NewSpacer(), description1, rulesHelpButton, layout.NewSpacer())
@ -2345,7 +2195,7 @@ func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalys
return
}
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
@ -2371,7 +2221,7 @@ func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalys
rulesTestedLabel := widget.NewLabel("Rules Tested:")
rulesTestedText := getBoldLabel(numberOfRulesTestedString + "/" + totalNumberOfRulesString)
rulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
})
rulesTestedRow := container.NewHBox(layout.NewSpacer(), rulesTestedLabel, rulesTestedText, rulesTestedHelpButton, layout.NewSpacer())
@ -2395,7 +2245,7 @@ func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalys
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil){ return err }
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
ruleStatusIsKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil) { return err }
getGenomePassesRuleText := func()string{
@ -2417,7 +2267,7 @@ func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalys
// We do this because the rule effects column may be multiple rows tall
viewRuleButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, currentPage)
setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, currentPage)
})
ruleIdentifierColumn.Add(ruleIdentifierLabel)
@ -2502,12 +2352,12 @@ func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalys
}
ruleEffectsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
setTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
ruleEffectsColumn.Add(ruleEffectsHelpButton)
genomePassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setGenomePassesDiscreteTraitRuleExplainerPage(window, currentPage)
setGenomePassesTraitRuleExplainerPage(window, currentPage)
})
genomePassesRuleColumn.Add(genomePassesRuleHelpButton)
@ -2531,9 +2381,9 @@ func setViewPersonGenomeDiscreteTraitRulesPage(window fyne.Window, geneticAnalys
// This function provides a page to view the details of a specific trait rule from a person genetic analysis
// The page will show the rule details for all of the person's genomes
func setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){
func setViewPersonGeneticAnalysisTraitRuleDetailsPage(window fyne.Window, geneticAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, previousPage func()){
currentPage := func(){setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, previousPage)}
currentPage := func(){setViewPersonGeneticAnalysisTraitRuleDetailsPage(window, geneticAnalysisObject, traitName, ruleIdentifier, previousPage)}
title := getPageTitleCentered("Trait Rule Details - " + traitName)
@ -2546,13 +2396,13 @@ func setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window fyne.Window
ruleIdentifierLabel := widget.NewLabel("Rule Identifier:")
ruleIdentifierText := getBoldLabel(ruleIdentifierHex)
ruleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
})
ruleIdentifierRow := container.NewHBox(layout.NewSpacer(), ruleIdentifierLabel, ruleIdentifierText, ruleInfoButton, layout.NewSpacer())
getGenomesRuleInfoGrid := func()(*fyne.Container, error){
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(geneticAnalysisObject)
if (err != nil) { return nil, err }
genomeNameLabel := getItalicLabelCentered("Genome Name")
@ -2563,7 +2413,7 @@ func setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window fyne.Window
addGenomeRow := func(genomeName string, genomeIdentifier [16]byte, isACombinedGenome bool)error{
genomeRuleStatusKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
genomeRuleStatusKnown, genomePassesRule, err := readGeneticAnalysis.GetPersonTraitRuleInfoFromGeneticAnalysis(geneticAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil) { return err }
getGenomePassesRuleText := func()string{
@ -2651,7 +2501,7 @@ func setViewPersonGeneticAnalysisDiscreteTraitRuleDetailsPage(window fyne.Window
}
genomePassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setGenomePassesDiscreteTraitRuleExplainerPage(window, currentPage)
setGenomePassesTraitRuleExplainerPage(window, currentPage)
})
genomePassesRuleColumn.Add(genomePassesRuleHelpButton)

View file

@ -510,7 +510,7 @@ func setViewTraitDetailsPage(window fyne.Window, traitName string, previousPage
}
traitDescription := traitObject.TraitDescription
traitReferencesMap := traitObject.ReferencesMap
traitReferencesMap := traitObject.References
traitNameLabel := widget.NewLabel("Trait Name:")
traitNameText := getBoldLabel(traitName)
@ -538,9 +538,9 @@ func setViewTraitDetailsPage(window fyne.Window, traitName string, previousPage
setPageContent(page, window)
}
func setViewDiscreteTraitRuleDetailsPage(window fyne.Window, traitName string, ruleIdentifier string, previousPage func()){
func setViewTraitRuleDetailsPage(window fyne.Window, traitName string, ruleIdentifier string, previousPage func()){
currentPage := func(){setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifier, previousPage)}
currentPage := func(){setViewTraitRuleDetailsPage(window, traitName, ruleIdentifier, previousPage)}
title := getPageTitleCentered("Viewing Rule Details")
@ -561,7 +561,7 @@ func setViewDiscreteTraitRuleDetailsPage(window fyne.Window, traitName string, r
}
ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap
ruleReferencesMap := traitRuleObject.ReferencesMap
ruleReferencesMap := traitRuleObject.References
viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){
setViewGeneticAnalysisReferencesPage(window, "Rule", ruleReferencesMap, currentPage)
@ -605,7 +605,7 @@ func setViewDiscreteTraitRuleDetailsPage(window fyne.Window, traitName string, r
}
outcomeEffectHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
setTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
outcomeEffectColumn.Add(outcomeEffectHelpButton)

View file

@ -12,7 +12,6 @@ import "fyne.io/fyne/v2/widget"
import "seekia/resources/worldLanguages"
import "seekia/resources/worldLocations"
import "seekia/resources/geneticPredictionModels"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
import "seekia/resources/geneticReferences/traits"
@ -1271,7 +1270,7 @@ func setViewUserProfilePage_Category(window fyne.Window, profileIsMine bool, cat
})
geneticTraitsButton := widget.NewButton("Genetic Traits", func(){
setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, currentPage)
setViewMateProfilePage_GeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(2, racialSimilarityButton, totalDiseaseRiskButton, ancestryCompositionButton, monogenicDiseasesButton, haplogroupsButton, polygenicDiseasesButton, neanderthalVariantsButton, geneticTraitsButton))
@ -2098,9 +2097,9 @@ func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfi
if (err != nil) { return err }
traitLociList := traitObject.LociList
quantityOfTraitLoci := len(traitLociList)
numberOfTraitLoci := len(traitLociList)
quantityOfTraitLociString := helpers.ConvertIntToString(quantityOfTraitLoci)
numberOfTraitLociString := helpers.ConvertIntToString(numberOfTraitLoci)
geneticSimilarityIsKnown, _, attributeValue, err := getAnyUserProfileAttributeFunction(geneticSimilarityAttributeName)
if (err != nil) { return err }
@ -2109,11 +2108,11 @@ func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfi
geneticSimilarityColumn.Add(unknownLabel)
quantityOfTestedLociText := "0/" + quantityOfTraitLociString
numberOfTestedLociText := "0/" + numberOfTraitLociString
quantityOfTestedLociLabel := getBoldLabelCentered(quantityOfTestedLociText)
numberOfTestedLociLabel := getBoldLabelCentered(numberOfTestedLociText)
numberOfTestedLociColumn.Add(quantityOfTestedLociLabel)
numberOfTestedLociColumn.Add(numberOfTestedLociLabel)
} else {
similarityFormatted := attributeValue + "%"
@ -2148,7 +2147,7 @@ func setViewMateProfilePage_RacialSimilarity(window fyne.Window, getAnyUserProfi
numberOfTestedLociString := helpers.ConvertIntToString(numberOfTestedLoci)
numberOfTestedLociLabelText := numberOfTestedLociString + "/" + quantityOfTraitLociString
numberOfTestedLociLabelText := numberOfTestedLociString + "/" + numberOfTraitLociString
numberOfTestedLociLabel := getBoldLabelCentered(numberOfTestedLociLabelText)
@ -2960,25 +2959,24 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
//Outputs:
// -map[int64]locusValue.LocusValue
// -error
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
getMyDiseaseLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
anyMyLociValuesExist, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, myGenomeIdentifier)
if (err != nil) { return nil, err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
if (anyMyLociValuesExist == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
return myGenomeLocusValuesMap, nil
return myDiseaseLocusValuesMap, nil
}
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
myDiseaseLocusValuesMap, err := getMyDiseaseLocusValuesMap()
if (err != nil) { return nil, err }
// Map Structure: Locus rsID -> Locus Value
@ -3033,7 +3031,7 @@ func setViewMateProfilePage_PolygenicDiseases(window fyne.Window, userOrOffsprin
userDiseaseRiskScoreString, err := getUserDiseaseRiskScoreString()
if (err != nil) { return nil, err }
anyOffspringLociTested, offspringDiseaseRiskScore, offspringNumberOfLociTested, _, offspringSampleRiskScoresList, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLociList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
anyOffspringLociTested, offspringDiseaseRiskScore, offspringNumberOfLociTested, _, offspringSampleRiskScoresList, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLociList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return nil, err }
getOffspringDiseaseRiskScoreFormatted := func()(string, error){
@ -3174,7 +3172,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
// Outputs:
// -map[int64]locusValue.LocusValue: Map Structure: Locus rsID -> Locus Value
// -error
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
getMyDiseaseLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
@ -3185,18 +3183,17 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
return emptyMap, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
anyLocusValuesExist, _, _, myLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myAnalysisObject, diseaseName, myGenomeIdentifier)
if (err != nil) { return nil, err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
if (anyLocusValuesExist == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
return myGenomeLocusValuesMap, nil
return myLocusValuesMap, nil
}
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
myDiseaseLocusValuesMap, err := getMyDiseaseLocusValuesMap()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -3251,7 +3248,7 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
return
}
anyOffspringLociTested, _, offspringNumberOfLociTested, offspringLociInfoMap, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLocusObjectsList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
anyOffspringLociTested, _, offspringNumberOfLociTested, offspringLociInfoMap, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo(diseaseLocusObjectsList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
@ -3516,9 +3513,9 @@ func setViewMateProfilePage_PolygenicDiseaseLoci(window fyne.Window, diseaseName
setPageContent(page, window)
}
func setViewMateProfilePage_GeneticTraits(window fyne.Window, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
func setViewMateProfilePage_GeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_GeneticTraits(window, getAnyUserProfileAttributeFunction, previousPage)}
currentPage := func(){setViewMateProfilePage_GeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
@ -3526,46 +3523,7 @@ func setViewMateProfilePage_GeneticTraits(window fyne.Window, getAnyUserProfileA
subtitle := getPageSubtitleCentered(translate("Genetic Traits"))
description1 := getLabelCentered("Choose if you want to view Discrete traits or Numeric traits.")
description2 := getLabelCentered("Discrete traits are traits which have discrete outcomes, such as Eye Color")
description3 := getLabelCentered("Numeric traits are traits with numeric outcomes, such as Height.")
discreteTraitsButton := widget.NewButton("Discrete Traits", func(){
setViewMateProfilePage_DiscreteGeneticTraits(window, "Offspring", getAnyUserProfileAttributeFunction, currentPage)
})
numericTraitsButton := widget.NewButton("Numeric Traits", func(){
//TODO
showUnderConstructionDialog(window)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, discreteTraitsButton, numericTraitsButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
if (userOrOffspring != "User" && userOrOffspring != "Offspring"){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteGeneticTraits called with invalid userOrOffspring: " + userOrOffspring), previousPage)
return
}
if (userOrOffspring == "Offspring"){
setLoadingScreen(window, "View Profile - Physical", "Computing Genetic Analysis...")
}
currentPage := func(){setViewMateProfilePage_DiscreteGeneticTraits(window, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered(translate("View Profile - Physical"))
backButton := getBackButtonCentered(previousPage)
subtitle := getPageSubtitleCentered(translate("Discrete Genetic Traits"))
description1 := getLabelCentered("Below is the discrete genetic trait analysis for this user.")
description1 := getLabelCentered("Below is the genetic trait analysis for this user.")
description2 := getLabelCentered("You can choose to view the analysis of the user or an offspring between you and the user.")
description3 := getLabelCentered("You must link your genome person in the Build Profile menu to see offspring information.")
@ -3573,66 +3531,43 @@ func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffs
if (userOrOffspring == newUserOrOffspring){
return
}
setViewMateProfilePage_DiscreteGeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
setViewMateProfilePage_GeneticTraits(window, newUserOrOffspring, getAnyUserProfileAttributeFunction, previousPage)
}
userOrOffspringSelector := widget.NewSelect([]string{"User", "Offspring"}, handleSelectButton)
userOrOffspringSelector.Selected = userOrOffspring
userOrOffspringSelector.Selected = userOrOffspring
userOrOffspringSelectorCentered := getWidgetCentered(userOrOffspringSelector)
getTraitsInfoGrid := func()(*fyne.Container, error){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
//Outputs:
// -map[int64]locusValue.LocusValue
// -error
getMyGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil) { return nil, err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which is missing genome with myGenomeIdentifier.")
}
return myGenomeLocusValuesMap, nil
}
myGenomeLocusValuesMap, err := getMyGenomeLocusValuesMap()
if (err != nil) { return nil, err }
emptyLabel1 := widget.NewLabel("")
emptyLabelA := widget.NewLabel("")
traitNameLabel := getItalicLabelCentered("Trait Name")
emptyLabel2 := widget.NewLabel("")
userPredictedOutcomeTitle := getItalicLabelCentered("User Predicted Outcome")
emptyLabelB := widget.NewLabel("")
userOutcomeScoresLabel := getItalicLabelCentered("User Outcome Scores")
emptyLabel3 := widget.NewLabel("")
offspringOutcomeProbabilitiesLabel := getItalicLabelCentered("Offspring Outcome Probabilities")
emptyLabelC := widget.NewLabel("")
offspringOutcomeScoresLabel := getItalicLabelCentered("Offspring Outcome Scores")
quantityOfLabel1 := getItalicLabelCentered("Quantity Of")
lociKnownLabel := getItalicLabelCentered("Loci Known")
numberOfLabelA := getItalicLabelCentered("Number Of")
rulesTestedLabelA := getItalicLabelCentered("Rules Tested")
emptyLabel4 := widget.NewLabel("")
emptyLabel5 := widget.NewLabel("")
numberOfLabelB := getItalicLabelCentered("Number Of")
rulesTestedLabelB := getItalicLabelCentered("Rules Tested")
traitNameColumn := container.NewVBox(emptyLabel1, traitNameLabel, widget.NewSeparator())
userPredictedOutcomeColumn := container.NewVBox(emptyLabel2, userPredictedOutcomeTitle, widget.NewSeparator())
emptyLabelD := widget.NewLabel("")
emptyLabelE := widget.NewLabel("")
offspringOutcomeProbabilitiesColumn := container.NewVBox(emptyLabel3, offspringOutcomeProbabilitiesLabel, widget.NewSeparator())
traitNameColumn := container.NewVBox(emptyLabelA, traitNameLabel, widget.NewSeparator())
userOutcomeScoresColumn := container.NewVBox(emptyLabelB, userOutcomeScoresLabel, widget.NewSeparator())
offspringOutcomeScoresColumn := container.NewVBox(emptyLabelC, offspringOutcomeScoresLabel, widget.NewSeparator())
userNumberOfRulesTestedColumn := container.NewVBox(numberOfLabelA, rulesTestedLabelA, widget.NewSeparator())
offspringNumberOfRulesTestedColumn := container.NewVBox(numberOfLabelB, rulesTestedLabelB, widget.NewSeparator())
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabelD, emptyLabelE, widget.NewSeparator())
quantityOfLociKnownColumn := container.NewVBox(quantityOfLabel1, lociKnownLabel, widget.NewSeparator())
viewTraitDetailsButtonsColumn := container.NewVBox(emptyLabel4, emptyLabel5, widget.NewSeparator())
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return nil, err }
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return nil, err }
@ -3640,39 +3575,29 @@ func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffs
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
continue
}
traitRulesList := traitObject.RulesList
totalNumberOfTraitRules := len(traitRulesList)
traitLociList := traitObject.LociList
numberOfTraitLoci := len(traitLociList)
if (totalNumberOfTraitRules == 0 && numberOfTraitLoci == 0){
if (totalNumberOfTraitRules == 0){
// We are not able to analyze these traits yet
// We will once we add neural network prediction
continue
}
traitNeuralNetworkExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (traitNeuralNetworkExists == false && totalNumberOfTraitRules == 0){
// We are not able to analyze these traits yet
continue
}
totalNumberOfTraitRulesString := helpers.ConvertIntToString(totalNumberOfTraitRules)
traitOutcomeNamesList := traitObject.OutcomesList
// We have to sort outcome names so they always show up in the same order
traitOutcomeNamesListSorted := helpers.CopyAndSortStringListToUnicodeOrder(traitOutcomeNamesList)
traitNameText := getBoldLabelCentered(translate(traitName))
traitNameColumn.Add(traitNameText)
viewTraitDetailsButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
if (traitNeuralNetworkExists == true){
//TODO
showUnderConstructionDialog(window)
} else {
setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage)
}
setViewMateProfilePage_TraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, currentPage)
})
viewTraitDetailsButtonsColumn.Add(viewTraitDetailsButton)
@ -3681,6 +3606,8 @@ func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffs
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
traitLociList := traitObject.LociList
for _, rsID := range traitLociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
@ -3708,244 +3635,200 @@ func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffs
userTraitLocusValuesMap[rsID] = userLocusValue
}
if (userOrOffspring == "User"){
//Outputs:
// -bool: At least 1 rule is known
// -map[string]int: Outcome name -> Outcome score
// -int: Number of rules tested
// -error
getUserTraitOutcomeScoresMap := func()(bool, map[string]int, int, error){
if (traitNeuralNetworkExists == true){
userTraitOutcomeScoresMap := make(map[string]int)
userNumberOfRulesTested := 0
traitNeuralNetworkExists, anyLocusValuesAreKnown, predictedOutcome, _, quantityOfLociKnown, _, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return nil, err }
if (traitNeuralNetworkExists == false){
return nil, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claims neural network doesn't exist for trait, but we already checked.")
}
if (anyLocusValuesAreKnown == false){
unknownLabel := getItalicLabelCentered(translate("Unknown"))
userPredictedOutcomeColumn.Add(unknownLabel)
} else {
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
for _, traitRuleObject := range traitRulesList{
ruleLociList := traitRuleObject.LociList
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return false, nil, 0, err }
if (userRuleStatusIsKnown == false){
continue
}
userNumberOfRulesTested += 1
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
if (userPassesRule == true){
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
for traitOutcome, pointsEffect := range ruleOutcomePointsMap{
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
} else {
// We use the rules-based analysis
anyRulesExist, _, quantityOfLociKnown_Rules, _, predictedOutcomeIsKnown, predictedOutcome, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_Rules(traitObject, userTraitLocusValuesMap, true)
if (err != nil) { return nil, err }
if (anyRulesExist == false){
return nil, errors.New("GetGenomeTraitAnalysis_Rules claims no rules exist when we already checked.")
}
if (predictedOutcomeIsKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
userPredictedOutcomeColumn.Add(unknownLabel)
} else {
predictedOutcomeLabel := getBoldLabelCentered(predictedOutcome)
userPredictedOutcomeColumn.Add(predictedOutcomeLabel)
}
traitLociList_Rules := traitObject.LociList_Rules
totalQuantityOfLoci := len(traitLociList_Rules)
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown_Rules)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
}
} else {
// userOrOffspring == "Offspring"
if (traitNeuralNetworkExists == true){
neuralNetworkExists, anyLociKnown, outcomeProbabilitiesMap, _, quantityOfLociKnown, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject, userTraitLocusValuesMap, myGenomeLocusValuesMap)
if (err != nil) { return nil, err }
if (neuralNetworkExists == false){
return nil, errors.New("GetOffspringTraitInfo_NeuralNetwork claiming that neural network doesn't exist when we already checked.")
}
totalNumberOfLociString := helpers.ConvertIntToString(numberOfTraitLoci)
quantityOfLociKnownString := helpers.ConvertIntToString(quantityOfLociKnown)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalNumberOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
if (anyLociKnown == false){
unknownLabel := getItalicLabelCentered("Unknown")
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
} else {
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
// We sort the outcomes in alphabetical order so they show up the same way each time
slices.Sort(outcomesList)
quantityOfAddedItems := 0
for _, outcomeName := range outcomesList{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
quantityOfAddedItems += 1
if (quantityOfAddedItems > 1){
// We add whitespace for the other columns
traitNameColumn.Add(widget.NewLabel(""))
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
}
userTraitOutcomeScoresMap[traitOutcome] += pointsEffect
}
}
}
if (userNumberOfRulesTested == 0){
return false, nil, 0, nil
}
traitOutcomesList := traitObject.OutcomesList
// We add all outcomes for which there were no points
for _, traitOutcome := range traitOutcomesList{
_, exists := userTraitOutcomeScoresMap[traitOutcome]
if (exists == false){
userTraitOutcomeScoresMap[traitOutcome] = 0
}
}
return true, userTraitOutcomeScoresMap, userNumberOfRulesTested, nil
}
//Outputs:
// -bool: At least 1 rule is known
// -map[string]float64: Outcome name -> Outcome score
// -int: Number of rules tested
// -error
getOffspringTraitOutcomeScoresMap := func()(bool, map[string]float64, int, error){
if (myPersonChosen == false || myGenomesExist == false || myAnalysisIsReady == false){
// Without my genome person chosen, all offspring rules and outcome scores are unknown
return false, nil, 0, nil
}
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, nil, 0, err }
anyRuleTested, offspringNumberOfRulesTested, _, offspringAverageOutcomeScoresMap, err := createCoupleGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, 0, err }
if (anyRuleTested == false){
return false, nil, 0, nil
}
return true, offspringAverageOutcomeScoresMap, offspringNumberOfRulesTested, nil
}
if (userOrOffspring == "User"){
userTraitOutcomeScoresKnown, userTraitOutcomeScoresMap, userNumberOfRulesTested, err := getUserTraitOutcomeScoresMap()
if (err != nil) { return nil, err }
numberOfRulesTestedString := helpers.ConvertIntToString(userNumberOfRulesTested)
numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString
numberOfRulesTestedLabel := getBoldLabelCentered(numberOfRulesTestedFormatted)
userNumberOfRulesTestedColumn.Add(numberOfRulesTestedLabel)
if (userTraitOutcomeScoresKnown == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
userOutcomeScoresColumn.Add(unknownLabel)
} else {
// We use the rules-based analysis
for index, outcomeName := range traitOutcomeNamesListSorted{
anyRulesExist, rulesAnalysisExists, _, offspringQuantityOfLociKnown, _, outcomeProbabilitiesMap, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myGenomeLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return nil, err }
if (anyRulesExist == false){
return nil, errors.New("GetOffspringDiscreteTraitInfo_Rules claiming that no rules exist when we already checked.")
outcomeScore, exists := userTraitOutcomeScoresMap[outcomeName]
if (exists == false){
return nil, errors.New("Outcome not found in userTraitOutcomeScoresMap.")
}
outcomeScoreString := helpers.ConvertIntToString(outcomeScore)
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
userOutcomeScoresColumn.Add(outcomeRow)
if (index > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
traitNameColumn.Add(emptyLabelA)
userNumberOfRulesTestedColumn.Add(emptyLabelB)
viewTraitDetailsButtonsColumn.Add(emptyLabelC)
}
}
}
} else if (userOrOffspring == "Offspring"){
lociList_Rules := traitObject.LociList_Rules
offspringTraitOutcomeScoresKnown, offspringTraitOutcomeScoresMap, offspringNumberOfRulesTested, err := getOffspringTraitOutcomeScoresMap()
if (err != nil) { return nil, err }
totalQuantityOfLoci := len(lociList_Rules)
numberOfRulesTestedString := helpers.ConvertIntToString(offspringNumberOfRulesTested)
numberOfRulesTestedFormatted := numberOfRulesTestedString + "/" + totalNumberOfTraitRulesString
numberOfRulesTestedLabel := getBoldLabelCentered(numberOfRulesTestedFormatted)
offspringNumberOfRulesTestedColumn.Add(numberOfRulesTestedLabel)
quantityOfLociKnownString := helpers.ConvertIntToString(offspringQuantityOfLociKnown)
totalQuantityOfLociString := helpers.ConvertIntToString(totalQuantityOfLoci)
if (offspringTraitOutcomeScoresKnown == false){
unknownTranslated := translate("Unknown")
unknownLabel := getBoldLabelCentered(unknownTranslated)
quantityOfLociKnownFormatted := quantityOfLociKnownString + "/" + totalQuantityOfLociString
quantityOfLociKnownLabel := getBoldLabelCentered(quantityOfLociKnownFormatted)
quantityOfLociKnownColumn.Add(quantityOfLociKnownLabel)
offspringOutcomeScoresColumn.Add(unknownLabel)
} else {
if (rulesAnalysisExists == false){
unknownLabel := getItalicLabelCentered(translate("Unknown"))
for index, outcomeName := range traitOutcomeNamesListSorted{
offspringOutcomeProbabilitiesColumn.Add(unknownLabel)
} else {
outcomeScore, exists := offspringTraitOutcomeScoresMap[outcomeName]
if (exists == false){
return nil, errors.New("Outcome not found in offspringTraitOutcomeScoresMap.")
}
outcomesList := helpers.GetListOfMapKeys(outcomeProbabilitiesMap)
outcomeScoreString := helpers.ConvertFloat64ToStringRounded(outcomeScore, 2)
// We sort the outcomes in alphabetical order so they show up the same way each time
slices.Sort(outcomesList)
outcomeRow := getBoldLabelCentered(outcomeName + ": " + outcomeScoreString)
offspringOutcomeScoresColumn.Add(outcomeRow)
quantityOfAddedItems := 0
if (index > 0){
emptyLabelA := widget.NewLabel("")
emptyLabelB := widget.NewLabel("")
emptyLabelC := widget.NewLabel("")
for _, outcomeName := range outcomesList{
outcomeProbability, exists := outcomeProbabilitiesMap[outcomeName]
if (exists == false){
return nil, errors.New("GetListOfMapKeys returning element which doesn't exist in map.")
}
if (outcomeProbability == 0){
continue
}
outcomeProbabilityString := helpers.ConvertIntToString(outcomeProbability)
outcomeProbabilityLabelText := outcomeName + ": " + outcomeProbabilityString + "%"
outcomeProbabilityLabel := getBoldLabelCentered(outcomeProbabilityLabelText)
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilityLabel)
quantityOfAddedItems += 1
if (quantityOfAddedItems > 1){
// We add whitespace for the other columns
traitNameColumn.Add(widget.NewLabel(""))
quantityOfLociKnownColumn.Add(widget.NewLabel(""))
viewTraitDetailsButtonsColumn.Add(widget.NewLabel(""))
}
traitNameColumn.Add(emptyLabelA)
offspringNumberOfRulesTestedColumn.Add(emptyLabelB)
viewTraitDetailsButtonsColumn.Add(emptyLabelC)
}
}
}
}
traitNameColumn.Add(widget.NewSeparator())
userPredictedOutcomeColumn.Add(widget.NewSeparator())
offspringOutcomeProbabilitiesColumn.Add(widget.NewSeparator())
quantityOfLociKnownColumn.Add(widget.NewSeparator())
userOutcomeScoresColumn.Add(widget.NewSeparator())
offspringOutcomeScoresColumn.Add(widget.NewSeparator())
userNumberOfRulesTestedColumn.Add(widget.NewSeparator())
offspringNumberOfRulesTestedColumn.Add(widget.NewSeparator())
viewTraitDetailsButtonsColumn.Add(widget.NewSeparator())
}
if (userOrOffspring == "User"){
predictedOutcomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
userPredictedOutcomeColumn.Add(predictedOutcomeHelpButton)
} else {
// userOrOffspring == "Offspring"
outcomeProbabilitiesHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
})
offspringOutcomeProbabilitiesColumn.Add(outcomeProbabilitiesHelpButton)
}
quantityOfLociKnownHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
//TODO
showUnderConstructionDialog(window)
userOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitOutcomeScoresExplainerPage(window, currentPage)
})
quantityOfLociKnownColumn.Add(quantityOfLociKnownHelpButton)
offspringOutcomeScoresHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitOutcomeScoresExplainerPage(window, currentPage)
})
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn)
userNumberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
})
offspringNumberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setOffspringTraitNumberOfRulesTestedExplainerPage(window, currentPage)
})
userOutcomeScoresColumn.Add(userOutcomeScoresHelpButton)
offspringOutcomeScoresColumn.Add(offspringOutcomeScoresHelpButton)
userNumberOfRulesTestedColumn.Add(userNumberOfRulesTestedHelpButton)
offspringNumberOfRulesTestedColumn.Add(offspringNumberOfRulesTestedHelpButton)
if (userOrOffspring == "User"){
traitsInfoGrid.Add(userPredictedOutcomeColumn)
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, userOutcomeScoresColumn, userNumberOfRulesTestedColumn, viewTraitDetailsButtonsColumn, layout.NewSpacer())
} else {
// userOrOffspring == "Offspring"
traitsInfoGrid.Add(offspringOutcomeProbabilitiesColumn)
return traitsInfoGrid, nil
}
traitsInfoGrid.Add(quantityOfLociKnownColumn)
traitsInfoGrid.Add(viewTraitDetailsButtonsColumn)
traitsInfoGrid.Add(layout.NewSpacer())
traitsInfoGrid := container.NewHBox(layout.NewSpacer(), traitNameColumn, offspringOutcomeScoresColumn, offspringNumberOfRulesTestedColumn, viewTraitDetailsButtonsColumn, layout.NewSpacer())
return traitsInfoGrid, nil
}
@ -3961,9 +3844,9 @@ func setViewMateProfilePage_DiscreteGeneticTraits(window fyne.Window, userOrOffs
setPageContent(page, window)
}
func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
func setViewMateProfilePage_TraitRules(window fyne.Window, traitName string, userOrOffspring string, getAnyUserProfileAttributeFunction func(string)(bool, int, string, error), previousPage func()){
currentPage := func(){setViewMateProfilePage_DiscreteTraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
currentPage := func(){setViewMateProfilePage_TraitRules(window, traitName, userOrOffspring, getAnyUserProfileAttributeFunction, previousPage)}
title := getPageTitleCentered("View Profile - Physical")
@ -3985,7 +3868,7 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
// -bool: Any trait locus value exists for this myself
// -map[int64]locusValue.LocusValue: My locus values map
// -error
getMyGenomeLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
getMyTraitLocusValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
myPersonChosen, myGenomesExist, myAnalysisIsReady, myAnalysisObject, myGenomeIdentifier, _, err := myChosenAnalysis.GetMyChosenMateGeneticAnalysis()
if (err != nil) { return false, nil, err }
@ -3995,18 +3878,17 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
return false, nil, nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myAnalysisObject)
if (err != nil){ return false, nil, err }
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, nil, err }
myGenomeMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, nil, errors.New("GetMyChosenMateGeneticAnalysis returning analysis which does not contain genome matching myGenomeIdentifier")
if (len(myTraitLocusValuesMap) == 0){
return false, nil, nil
}
return true, myGenomeMap, nil
return true, myTraitLocusValuesMap, nil
}
anyMyLocusValuesExist, myLocusValuesMap, err := getMyGenomeLocusValuesMap()
anyMyTraitLocusValuesExist, myTraitLocusValuesMap, err := getMyTraitLocusValuesMap()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
@ -4018,14 +3900,9 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
return
}
traitLociList_Rules := traitObject.LociList_Rules
traitLociList := traitObject.LociList
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
setErrorEncounteredPage(window, errors.New("setViewMateProfilePage_DiscreteTraitRules called with trait which has no rules."), previousPage)
return
}
//Outputs:
// -bool: Any trait locus value exists for this user
// -map[int64]locusValue.LocusValue: User locus values map
@ -4036,7 +3913,7 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
// Map Structure: Locus rsID -> locusValue.LocusValue
userTraitLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, rsID := range traitLociList_Rules{
for _, rsID := range traitLociList{
rsIDString := helpers.ConvertInt64ToString(rsID)
@ -4082,15 +3959,12 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
// -error
getOffspringProbabilityOfPassingRulesMap := func()(bool, map[[3]byte]int, error){
if (anyMyLocusValuesExist == false || anyUserTraitLocusValueExists == false){
if (anyMyTraitLocusValuesExist == false || anyUserTraitLocusValueExists == false){
return false, nil, nil
}
anyRulesExist, anyOffspringRulesTested, _, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringDiscreteTraitInfo_Rules(traitObject, myLocusValuesMap, userTraitLocusValuesMap)
anyOffspringRulesTested, _, offspringProbabilityOfPassingRulesMap, _, err := createCoupleGeneticAnalysis.GetOffspringTraitInfo(traitObject, myTraitLocusValuesMap, userTraitLocusValuesMap)
if (err != nil) { return false, nil, err }
if (anyRulesExist == false){
return false, nil, errors.New("GetOffspringDiscreteTraitInfo claiming no trait rules exist when we already checked.")
}
if (anyOffspringRulesTested == false){
return false, nil, nil
}
@ -4126,7 +4000,7 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
ruleLociList := ruleObject.LociList
ruleStatusIsKnown, _, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
ruleStatusIsKnown, _, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return 0, err }
if (ruleStatusIsKnown == true){
numberOfRulesTested += 1
@ -4153,9 +4027,9 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
numberOfRulesTestedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
if (userOrOffspring == "User"){
setDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
setTraitNumberOfRulesTestedExplainerPage(window, currentPage)
} else {
setOffspringDiscreteTraitQuantityOfRulesTestedExplainerPage(window, currentPage)
setOffspringTraitNumberOfRulesTestedExplainerPage(window, currentPage)
}
})
@ -4219,7 +4093,7 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
getUserPassesRuleString := func()(string, error){
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
userRuleStatusIsKnown, userPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLociList, userTraitLocusValuesMap, true)
if (err != nil) { return "", err }
if (userRuleStatusIsKnown == false){
@ -4260,7 +4134,7 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
// We do this because the rule effects column may be multiple rows tall
viewRuleInfoButton := widget.NewButtonWithIcon("", theme.InfoIcon(), func(){
setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
setViewTraitRuleDetailsPage(window, traitName, ruleIdentifierHex, currentPage)
})
ruleIdentifierLabel := getBoldLabelCentered(ruleIdentifierHex)
userPassesRuleLabel := getBoldLabelCentered(userPassesRuleString)
@ -4326,11 +4200,11 @@ func setViewMateProfilePage_DiscreteTraitRules(window fyne.Window, traitName str
}
ruleEffectsHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
setTraitRuleOutcomeEffectsExplainerPage(window, currentPage)
})
userPassesRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
setPersonPassesDiscreteTraitRuleExplainerPage(window, currentPage)
setPersonPassesTraitRuleExplainerPage(window, currentPage)
})
offspringProbabilityOfPassingRuleHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
@ -4617,29 +4491,29 @@ func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[strin
// We sort region subregions list
// We sort them from highest to lowest in percentage.
compareSubregionsFunction := func(subregion1Description string, subregion2Description string)int{
compareSubregionsFunction := func(subregionADescription string, subregionBDescription string)int{
if (subregion1Description == subregion2Description){
if (subregionADescription == subregionBDescription){
panic("compareSubregionsFunction called with identical subregion descriptions.")
}
subregion1Percentage, exists := subregionDescriptionPercentagesMap[subregion1Description]
subregionAPercentage, exists := subregionDescriptionPercentagesMap[subregionADescription]
if (exists == false){
panic("subregionPercentagesMap missing subregion during sort.")
}
subregion2Percentage, exists := subregionDescriptionPercentagesMap[subregion2Description]
subregionBPercentage, exists := subregionDescriptionPercentagesMap[subregionBDescription]
if (exists == false){
panic("subregionPercentagesMap missing subregion during sort.")
}
if (subregion1Percentage == subregion2Percentage){
if (subregionAPercentage == subregionBPercentage){
// We sort subregions in unicode order
if (subregion1Description < subregion2Description){
if (subregionADescription < subregionBDescription){
return -1
}
return 1
}
if (subregion1Percentage > subregion2Percentage){
if (subregionAPercentage > subregionBPercentage){
return -1
}
@ -4653,33 +4527,32 @@ func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[strin
// We sort continent regions list by highest to lowest percentage.
compareRegionsFunction := func(region1Description string, region2Description string)int{
compareRegionsFunction := func(regionADescription string, regionBDescription string)int{
if (region1Description == region2Description){
if (regionADescription == regionBDescription){
panic("compareRegionsFunction called with identical regions.")
}
region1Percentage, exists := regionDescriptionPercentagesMap[region1Description]
regionAPercentage, exists := regionDescriptionPercentagesMap[regionADescription]
if (exists == false){
panic("regionPercentagesMap missing subregion during sort.")
}
region2Percentage, exists := regionDescriptionPercentagesMap[region2Description]
regionBPercentage, exists := regionDescriptionPercentagesMap[regionBDescription]
if (exists == false){
panic("regionPercentagesMap missing subregion during sort.")
}
if (region1Percentage == region2Percentage){
if (regionAPercentage == regionBPercentage){
// We sort regions in unicode order
if (region1Description < region2Description){
if (regionADescription < regionBDescription){
return -1
}
return 1
}
if (region1Percentage > region2Percentage){
if (regionAPercentage > regionBPercentage){
return -1
}
return 1
}
@ -4690,29 +4563,29 @@ func get23andMeAncestryCompositionDisplay(inputContinentPercentagesMap map[strin
// We sort root list by highest to lowest proportions
compareContinentsFunction := func(continent1Description string, continent2Description string)int{
compareContinentsFunction := func(continentADescription string, continentBDescription string)int{
if (continent1Description == continent2Description){
if (continentADescription == continentBDescription){
panic("compareContinentsFunction called with identical continents.")
}
continent1Percentage, exists := continentDescriptionPercentagesMap[continent1Description]
continentAPercentage, exists := continentDescriptionPercentagesMap[continentADescription]
if (exists == false){
panic("Continent percentage not found when sorting root list.")
}
continent2Percentage, exists := continentDescriptionPercentagesMap[continent2Description]
continentBPercentage, exists := continentDescriptionPercentagesMap[continentBDescription]
if (exists == false){
panic("Continent percentage not found when sorting root list.")
}
if (continent1Percentage == continent2Percentage){
if (continentAPercentage == continentBPercentage){
// We sort continents in unicode order
if (continent1Description < continent2Description){
if (continentADescription < continentBDescription){
return -1
}
return 1
}
if (continent1Percentage > continent2Percentage){
if (continentAPercentage > continentBPercentage){
return -1
}
return 1

View file

@ -932,7 +932,7 @@ func GetFakeProfile(profileType string, identityPublicKey [32]byte, identityPriv
traitLociList := traitObject.LociList
for _, rsID := range traitLociList{
for _, rsID := range traitLociList{
shareableRSIDsMap[rsID] = struct{}{}
}

View file

@ -9,10 +9,21 @@ package createCoupleGeneticAnalysis
// Disclaimer: I am a novice in the ways of genetics. This package could be flawed in numerous ways.
// TODO: We want to eventually use neural nets for polygenic disease analysis (see geneticPrediction.go)
// TODO: We want to eventually use neural nets for both trait and polygenic disease analysis (see geneticPrediction.go)
// These will be trained on a set of genomes and will output a probability analysis for each trait/disease
// This is only possible once we get access to the necessary training data
//
// This is how offspring trait prediction could work with the neural net model:
// Both users will share all relevant SNPS base pairs that determine the trait on their profile.
// Each location has 4 possible outcomes, so for 1000 SNPs, there are 4^1000 possible offspring outcomes for a given couple. (this
// is actually too high because recombination break points do not occur at each locus, see genetic linkage)
// This is too many options for us to check all of them.
// Seekia will create 100 offspring that would be produced from both users, and run each offspring through the neural net.
// Each offspring would be different. The allele from each parent for each SNP would be randomly chosen.
// The user can choose how many prospective offspring to create in the settings.
// More offspring will take longer, but will yield a more accurate trait probability.
// Seekia will show the the average trait result and a chart showing the trait results for all created offspring.
import "seekia/resources/geneticPredictionModels"
import "seekia/resources/geneticReferences/locusMetadata"
import "seekia/resources/geneticReferences/monogenicDiseases"
import "seekia/resources/geneticReferences/polygenicDiseases"
@ -29,7 +40,6 @@ import "errors"
import mathRand "math/rand/v2"
import "slices"
import "maps"
import "reflect"
//Outputs:
@ -76,30 +86,6 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
return false, "", nil
}
// This map stores each genome's locus values
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
person1GenomesMap := make(map[[16]byte]map[int64]locusValue.LocusValue)
for _, genomeWithMetadata := range person1GenomesWithMetadataList{
genomeIdentifier := genomeWithMetadata.GenomeIdentifier
genomeMap := genomeWithMetadata.GenomeMap
person1GenomesMap[genomeIdentifier] = genomeMap
}
// This map stores each genome's locus values
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
person2GenomesMap := make(map[[16]byte]map[int64]locusValue.LocusValue)
for _, genomeWithMetadata := range person2GenomesWithMetadataList{
genomeIdentifier := genomeWithMetadata.GenomeIdentifier
genomeMap := genomeWithMetadata.GenomeMap
person2GenomesMap[genomeIdentifier] = genomeMap
}
// The analysis will analyze either 1 or 2 genome pairs
// The gui will display the results from each pair
//Outputs:
@ -186,9 +172,9 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
person2DiseaseAnalysisObject, err := createPersonGeneticAnalysis.GetPersonMonogenicDiseaseAnalysis(person2GenomesWithMetadataList, diseaseObject)
if (err != nil) { return false, "", err }
// This map stores the quantity of variants tested in each person's genome
// This map stores the number of variants tested in each person's genome
// Map Structure: Genome Identifier -> Number of variants tested
quantityOfVariantsTestedMap := make(map[[16]byte]int)
numberOfVariantsTestedMap := make(map[[16]byte]int)
// This map stores the offspring disease probabilities for each genome pair.
// A genome pair is a concatenation of two genome identifiers
@ -198,7 +184,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
// This will calculate the probability of monogenic disease for the offspring from the two specified genomes
// It also calculates the probabilities for each monogenic disease variant for the offspring
// It then adds the genome pair disease information to the offspringMonogenicDiseaseInfoMap and quantityOfVariantsTestedMap
// It then adds the genome pair disease information to the offspringMonogenicDiseaseInfoMap and numberOfVariantsTestedMap
addGenomePairInfoToDiseaseMaps := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
//Outputs:
@ -216,9 +202,9 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
personGenomeProbabilityOfPassingADiseaseVariant := personGenomeDiseaseInfoObject.ProbabilityOfPassingADiseaseVariant
personGenomeQuantityOfVariantsTested := personGenomeDiseaseInfoObject.QuantityOfVariantsTested
personGenomeNumberOfVariantsTested := personGenomeDiseaseInfoObject.NumberOfVariantsTested
return true, personGenomeProbabilityOfPassingADiseaseVariant, personGenomeQuantityOfVariantsTested
return true, personGenomeProbabilityOfPassingADiseaseVariant, personGenomeNumberOfVariantsTested
}
person1ProbabilityIsKnown, person1WillPassVariantProbability, person1NumberOfVariantsTested := getPersonWillPassDiseaseVariantProbability(person1DiseaseAnalysisObject, person1GenomeIdentifier)
@ -376,8 +362,8 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
return nil
}
quantityOfVariantsTestedMap[person1GenomeIdentifier] = person1NumberOfVariantsTested
quantityOfVariantsTestedMap[person2GenomeIdentifier] = person2NumberOfVariantsTested
numberOfVariantsTestedMap[person1GenomeIdentifier] = person1NumberOfVariantsTested
numberOfVariantsTestedMap[person2GenomeIdentifier] = person2NumberOfVariantsTested
newOffspringGenomePairMonogenicDiseaseInfoObject := geneticAnalysis.OffspringGenomePairMonogenicDiseaseInfo{
ProbabilityOffspringHasDiseaseIsKnown: offspringHasDiseaseProbabilityIsKnown,
@ -404,7 +390,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
}
newOffspringMonogenicDiseaseInfoObject := geneticAnalysis.OffspringMonogenicDiseaseInfo{
QuantityOfVariantsTestedMap: quantityOfVariantsTestedMap,
NumberOfVariantsTestedMap: numberOfVariantsTestedMap,
MonogenicDiseaseInfoMap: offspringMonogenicDiseaseInfoMap,
}
@ -494,6 +480,12 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
diseaseName := diseaseObject.DiseaseName
diseaseLociList := diseaseObject.LociList
person1DiseaseAnalysisObject, err := createPersonGeneticAnalysis.GetPersonPolygenicDiseaseAnalysis(person1GenomesWithMetadataList, diseaseObject)
if (err != nil) { return false, "", err }
person2DiseaseAnalysisObject, err := createPersonGeneticAnalysis.GetPersonPolygenicDiseaseAnalysis(person2GenomesWithMetadataList, diseaseObject)
if (err != nil) { return false, "", err }
// This map stores the polygenic disease info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairPolygenicDiseaseInfo
offspringPolygenicDiseaseInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo)
@ -502,17 +494,40 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
// It then adds the pair entry to the offspringPolygenicDiseaseInfoMap
addGenomePairDiseaseInfoToDiseaseMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
person1LocusValuesMap, exists := person1GenomesMap[person1GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairDiseaseInfoToDiseaseMap called with unknown person1GenomeIdentifier.")
//Outputs:
// -bool: Any locus values exist
// -map[int64]locusValue.LocusValue
// -error
getPersonGenomeDiseaseLocusValuesMap := func(personGenomeIdentifier [16]byte, personDiseaseAnalysisObject geneticAnalysis.PersonPolygenicDiseaseInfo)(bool, map[int64]locusValue.LocusValue, error){
personPolygenicDiseaseInfoMap := personDiseaseAnalysisObject.PolygenicDiseaseInfoMap
personGenomeDiseaseInfoObject, exists := personPolygenicDiseaseInfoMap[personGenomeIdentifier]
if (exists == false){
// This person's genome has no information about loci related to this disease
return false, nil, nil
}
personGenomeLocusValuesMap := personGenomeDiseaseInfoObject.LocusValuesMap
return true, personGenomeLocusValuesMap, nil
}
person2LocusValuesMap, exists := person2GenomesMap[person2GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairDiseaseInfoToDiseaseMap called with unknown person2GenomeIdentifier.")
anyPerson1LociValuesExist, person1LocusValuesMap, err := getPersonGenomeDiseaseLocusValuesMap(person1GenomeIdentifier, person1DiseaseAnalysisObject)
if (err != nil) { return err }
if (anyPerson1LociValuesExist == false){
// Offspring's disease info for this locus on this genome pair is unknown
return nil
}
anyOffspringLocusTested, genomePairOffspringAverageRiskScore, quantityOfLociTested, genomePairOffspringDiseaseLociInfoMap, genomePairSampleOffspringRiskScoresList, err := GetOffspringPolygenicDiseaseInfo(diseaseLociList, person1LocusValuesMap, person2LocusValuesMap)
anyPerson2LociValuesExist, person2LocusValuesMap, err := getPersonGenomeDiseaseLocusValuesMap(person2GenomeIdentifier, person2DiseaseAnalysisObject)
if (err != nil) { return err }
if (anyPerson2LociValuesExist == false){
// Offspring's disease info for this locus on this genome pair is unknown
return nil
}
anyOffspringLocusTested, genomePairOffspringAverageRiskScore, numberOfLociTested, genomePairOffspringDiseaseLociInfoMap, genomePairSampleOffspringRiskScoresList, err := GetOffspringPolygenicDiseaseInfo(diseaseLociList, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (anyOffspringLocusTested == false){
// We have no information about this genome pair's disease risk
@ -522,7 +537,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
newOffspringGenomePairPolygenicDiseaseInfo := geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo{
QuantityOfLociTested: quantityOfLociTested,
NumberOfLociTested: numberOfLociTested,
OffspringAverageRiskScore: genomePairOffspringAverageRiskScore,
LociInfoMap: genomePairOffspringDiseaseLociInfoMap,
SampleOffspringRiskScoresList: genomePairSampleOffspringRiskScoresList,
@ -555,20 +570,34 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
checkIfConflictExists := func()(bool, error){
currentGenomePairPolygenicDiseaseInfo := geneticAnalysis.OffspringGenomePairPolygenicDiseaseInfo{}
numberOfLociTested := 0
offspringAverageRiskScore := 0
offspringLociInfoMap := make(map[[3]byte]geneticAnalysis.OffspringPolygenicDiseaseLocusInfo)
firstItemReached := false
for _, genomePairDiseaseInfoObject := range offspringPolygenicDiseaseInfoMap{
genomePairNumberOfLociTested := genomePairDiseaseInfoObject.NumberOfLociTested
genomePairOffspringAverageRiskScore := genomePairDiseaseInfoObject.OffspringAverageRiskScore
genomePairLociInfoMap := genomePairDiseaseInfoObject.LociInfoMap
if (firstItemReached == false){
currentGenomePairPolygenicDiseaseInfo = genomePairDiseaseInfoObject
numberOfLociTested = genomePairNumberOfLociTested
offspringAverageRiskScore = genomePairOffspringAverageRiskScore
offspringLociInfoMap = genomePairLociInfoMap
firstItemReached = true
continue
}
areEqual := reflect.DeepEqual(genomePairDiseaseInfoObject, currentGenomePairPolygenicDiseaseInfo)
if (numberOfLociTested != genomePairNumberOfLociTested){
return true, nil
}
if (offspringAverageRiskScore != genomePairOffspringAverageRiskScore){
return true, nil
}
areEqual := maps.Equal(offspringLociInfoMap, genomePairLociInfoMap)
if (areEqual == false){
// A conflict exists
return true, nil
}
}
@ -593,140 +622,128 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
if (err != nil) { return false, "", err }
// Map Structure: Trait Name -> Trait Info Object
offspringDiscreteTraitsMap := make(map[string]geneticAnalysis.OffspringDiscreteTraitInfo)
// Map Structure: Trait Name -> Trait Info Object
offspringNumericTraitsMap := make(map[string]geneticAnalysis.OffspringNumericTraitInfo)
offspringTraitsMap := make(map[string]geneticAnalysis.OffspringTraitInfo)
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric == "Discrete"){
person1TraitAnalysisObject, err := createPersonGeneticAnalysis.GetPersonTraitAnalysis(person1GenomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairDiscreteTraitInfo
offspringTraitInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairDiscreteTraitInfo)
person2TraitAnalysisObject, err := createPersonGeneticAnalysis.GetPersonTraitAnalysis(person2GenomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
// This will add the offspring trait information for the provided genome pair to the offspringTraitInfoMap
addGenomePairTraitInfoToOffspringMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairTraitInfo
offspringTraitInfoMap := make(map[[32]byte]geneticAnalysis.OffspringGenomePairTraitInfo)
person1LocusValuesMap, exists := person1GenomesMap[person1GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairTraitInfoToOffspringMap called with unknown person1GenomeIdentifier.")
}
// This will add the offspring trait information for the provided genome pair to the offspringTraitInfoMap
addGenomePairTraitInfoToOffspringMap := func(person1GenomeIdentifier [16]byte, person2GenomeIdentifier [16]byte)error{
person2LocusValuesMap, exists := person2GenomesMap[person2GenomeIdentifier]
if (exists == false){
return errors.New("addGenomePairTraitInfoToOffspringMap called with unknown person2GenomeIdentifier.")
}
newOffspringGenomePairTraitInfo := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo{}
neuralNetworkExists, neuralNetworkAnalysisExists, outcomeProbabilitiesMap, averagePredictionConfidence, quantityOfLociTested, quantityOfParentalPhasedLoci, err := GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (neuralNetworkExists == true){
newOffspringGenomePairTraitInfo.NeuralNetworkExists = true
if (neuralNetworkAnalysisExists == true){
newOffspringGenomePairTraitInfo.NeuralNetworkAnalysisExists = true
newOffspringGenomePairTraitInfo_NeuralNetwork := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo_NeuralNetwork{
OffspringOutcomeProbabilitiesMap: outcomeProbabilitiesMap,
AverageConfidence: averagePredictionConfidence,
QuantityOfLociKnown: quantityOfLociTested,
QuantityOfParentalPhasedLoci: quantityOfParentalPhasedLoci,
}
newOffspringGenomePairTraitInfo.NeuralNetworkAnalysis = newOffspringGenomePairTraitInfo_NeuralNetwork
}
}
anyRulesExist, rulesAnalysisExists, quantityOfRulesTested, quantityOfLociKnown, offspringProbabilityOfPassingRulesMap, offspringOutcomeProbabilitiesMap, err := GetOffspringDiscreteTraitInfo_Rules(traitObject, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (anyRulesExist == true){
newOffspringGenomePairTraitInfo.RulesExist = true
if (rulesAnalysisExists == true){
newOffspringGenomePairTraitInfo.RulesAnalysisExists = true
newOffspringGenomePairTraitInfo_Rules := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo_Rules{
QuantityOfRulesTested: quantityOfRulesTested,
QuantityOfLociKnown: quantityOfLociKnown,
ProbabilityOfPassingRulesMap: offspringProbabilityOfPassingRulesMap,
OffspringOutcomeProbabilitiesMap: offspringOutcomeProbabilitiesMap,
}
newOffspringGenomePairTraitInfo.RulesAnalysis = newOffspringGenomePairTraitInfo_Rules
}
}
genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier)
offspringTraitInfoMap[genomePairIdentifier] = newOffspringGenomePairTraitInfo
person1TraitInfoMap := person1TraitAnalysisObject.TraitInfoMap
person2TraitInfoMap := person2TraitAnalysisObject.TraitInfoMap
person1GenomeTraitInfoObject, exists := person1TraitInfoMap[person1GenomeIdentifier]
if (exists == false){
// This person has no genome values for any loci for this trait
// No predictions are possible
return nil
}
person2GenomeTraitInfoObject, exists := person2TraitInfoMap[person2GenomeIdentifier]
if (exists == false){
// This person has no genome values for any loci for this trait
// No predictions are possible
return nil
}
err = addGenomePairTraitInfoToOffspringMap(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)
person1LocusValuesMap := person1GenomeTraitInfoObject.LocusValuesMap
person2LocusValuesMap := person2GenomeTraitInfoObject.LocusValuesMap
anyRulesTested, numberOfRulesTested, offspringProbabilityOfPassingRulesMap, offspringAverageOutcomeScoresMap, err := GetOffspringTraitInfo(traitObject, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return err }
if (anyRulesTested == false){
// No rules were tested for this trait
// We will not add anything to the trait info map for this genome pair
return nil
}
newOffspringGenomePairTraitInfoObject := geneticAnalysis.OffspringGenomePairTraitInfo{
NumberOfRulesTested: numberOfRulesTested,
OffspringAverageOutcomeScoresMap: offspringAverageOutcomeScoresMap,
ProbabilityOfPassingRulesMap: offspringProbabilityOfPassingRulesMap,
}
genomePairIdentifier := helpers.JoinTwo16ByteArrays(person1GenomeIdentifier, person2GenomeIdentifier)
offspringTraitInfoMap[genomePairIdentifier] = newOffspringGenomePairTraitInfoObject
return nil
}
err = addGenomePairTraitInfoToOffspringMap(pair1Person1GenomeIdentifier, pair1Person2GenomeIdentifier)
if (err != nil) { return false, "", err }
if (genomePair2Exists == true){
err := addGenomePairTraitInfoToOffspringMap(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier)
if (err != nil) { return false, "", err }
}
if (genomePair2Exists == true){
newOffspringTraitInfoObject := geneticAnalysis.OffspringTraitInfo{
TraitInfoMap: offspringTraitInfoMap,
}
err := addGenomePairTraitInfoToOffspringMap(pair2Person1GenomeIdentifier, pair2Person2GenomeIdentifier)
if (err != nil) { return false, "", err }
}
if (len(offspringTraitInfoMap) >= 2){
newOffspringTraitInfoObject := geneticAnalysis.OffspringDiscreteTraitInfo{
TraitInfoMap: offspringTraitInfoMap,
}
// We check for conflicts
// Conflicts are only possible if two genome pairs exist with information about the trait
if (len(offspringTraitInfoMap) >= 2){
checkIfConflictExists := func()(bool, error){
// We check for conflicts
// Conflicts are only possible if two genome pairs exist with information about the trait
// We check for conflicts between each genome pair's outcome scores and trait rules maps
checkIfConflictExists := func()(bool, error){
offspringAverageOutcomeScoresMap := make(map[string]float64)
offspringProbabilityOfPassingRulesMap := make(map[[3]byte]int)
// We check for conflicts between each genome pair's outcome scores and trait rules maps
firstItemReached := false
genomePairTraitInfoObject := geneticAnalysis.OffspringGenomePairDiscreteTraitInfo{}
for _, genomePairTraitInfoObject := range offspringTraitInfoMap{
firstItemReached := false
currentOffspringAverageOutcomeScoresMap := genomePairTraitInfoObject.OffspringAverageOutcomeScoresMap
currentProbabilityOfPassingRulesMap := genomePairTraitInfoObject.ProbabilityOfPassingRulesMap
for _, currentGenomePairTraitInfoObject := range offspringTraitInfoMap{
if (firstItemReached == false){
offspringAverageOutcomeScoresMap = currentOffspringAverageOutcomeScoresMap
offspringProbabilityOfPassingRulesMap = currentProbabilityOfPassingRulesMap
if (firstItemReached == false){
genomePairTraitInfoObject = currentGenomePairTraitInfoObject
firstItemReached = true
continue
}
areEqual := reflect.DeepEqual(genomePairTraitInfoObject, currentGenomePairTraitInfoObject)
if (areEqual == false){
return true, nil
}
firstItemReached = true
continue
}
return false, nil
areEqual := maps.Equal(offspringAverageOutcomeScoresMap, currentOffspringAverageOutcomeScoresMap)
if (areEqual == false){
return true, nil
}
areEqual = maps.Equal(offspringProbabilityOfPassingRulesMap, currentProbabilityOfPassingRulesMap)
if (areEqual == false){
return true, nil
}
}
conflictExists, err := checkIfConflictExists()
if (err != nil) { return false, "", err }
newOffspringTraitInfoObject.ConflictExists = conflictExists
return false, nil
}
offspringDiscreteTraitsMap[traitName] = newOffspringTraitInfoObject
conflictExists, err := checkIfConflictExists()
if (err != nil) { return false, "", err }
newOffspringTraitInfoObject.ConflictExists = conflictExists
}
offspringTraitsMap[traitName] = newOffspringTraitInfoObject
}
newCoupleAnalysis.DiscreteTraitsMap = offspringDiscreteTraitsMap
newCoupleAnalysis.NumericTraitsMap = offspringNumericTraitsMap
newCoupleAnalysis.TraitsMap = offspringTraitsMap
analysisBytes, err := encoding.EncodeMessagePackBytes(newCoupleAnalysis)
if (err != nil) { return false, "", err }
@ -1119,187 +1136,144 @@ func GetOffspringPolygenicDiseaseInfo(diseaseLociList []polygenicDiseases.Diseas
//Outputs:
// -bool: A neural network exists for this trait
// -bool: Analysis exists (at least 1 locus exists for this analysis from both people's genomes
// -map[string]int: Outcome probabilities map
// Map Structure: Outcome Name -> Offspring probability of outcome
// -int: Average prediction confidence (the average prediction confidence for all prospective offspring)
// -int: Quantity of loci tested
// -int: Quantity of parental phased loci
// -error
func GetOffspringDiscreteTraitInfo_NeuralNetwork(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, map[string]int, int, int, int, error){
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
return false, false, nil, 0, 0, 0, errors.New("GetOffspringDiscreteTraitInfo_NeuralNetwork called with non-discrete trait.")
}
modelExists, _ := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (modelExists == false){
// Neural network prediction is not possible for this trait
return false, false, nil, 0, 0, 0, nil
}
traitLociList := traitObject.LociList
// First we count up the quantity of parental phased loci
// We only count the quantity of phased loci for loci which are known for both parents
quantityOfParentalPhasedLoci := 0
for _, rsID := range traitLociList{
person1LocusValue, exists := person1LocusValuesMap[rsID]
if (exists == false){
continue
}
person2LocusValue, exists := person2LocusValuesMap[rsID]
if (exists == false){
continue
}
person1LocusIsPhased := person1LocusValue.LocusIsPhased
if (person1LocusIsPhased == true){
quantityOfParentalPhasedLoci += 1
}
person2LocusIsPhased := person2LocusValue.LocusIsPhased
if (person2LocusIsPhased == true){
quantityOfParentalPhasedLoci += 1
}
}
// Next, we create 100 prospective offspring genomes.
anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(traitLociList, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return false, false, nil, 0, 0, 0, err }
if (anyLocusValueExists == false){
return true, false, nil, 0, 0, 0, nil
}
// Map Structure: Outcome Name -> Probability of outcome coming true
// Because we are summing from 100 offspring, the count of outcomes is the same as the probability of an offspring having the outcome
outcomeCountsMap := make(map[string]int)
// This is a sum of each prediction's confidence
predictionConfidencesSum := 0
quantityOfLociTested := 0
for index, offspringGenomeMap := range prospectiveOffspringGenomesList{
neuralNetworkExists, predictionIsKnown, predictedOutcome, predictionConfidence, currentQuantityOfLociTested, _, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, offspringGenomeMap, false)
if (err != nil){ return false, false, nil, 0, 0, 0, err }
if (neuralNetworkExists == false){
return false, false, nil, 0, 0, 0, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claiming that neural network doesn't exist when we already checked.")
}
if (predictionIsKnown == false){
return false, false, nil, 0, 0, 0, errors.New("GetGenomeTraitAnalysis_NeuralNetwork claiming that prediction is impossible when we already know at least 1 locus value exists for trait.")
}
outcomeCountsMap[predictedOutcome] += 1
predictionConfidencesSum += predictionConfidence
if (index == 0){
// This value should be the same for each predicted offspring
quantityOfLociTested = currentQuantityOfLociTested
}
}
averagePredictionConfidence := predictionConfidencesSum/100
return true, true, outcomeCountsMap, averagePredictionConfidence, quantityOfLociTested, quantityOfParentalPhasedLoci, nil
}
//Outputs:
// -bool: Any rules exist (if false, rule-based prediction is not possible for this trait)
// -bool: Rule-based analysis exists (if false, no offspring trait information is known, or there is an outcome tie for one of the offspring)
// -int: Quantity of rules tested
// -int: Quantity of loci known
// -bool: Any rules tested (if false, no offspring trait information is known)
// -int: Number of rules tested
// -map[[3]byte]int: Offspring probability of passing rules map
// Map Structure: Rule identifier -> Offspring probability of passing rule (1-100)
// If a rule entry doesn't exist, we don't know the passes-rule probability for any of the offspring
// -map[string]int: Offspring outcome probabilities map
// Map Structure: Outcome Name -> Offspring probability of outcome (0-100)
// -map[string]float64: Offspring average outcome scores map
// Map Structure: Outcome Name -> Offspring average outcome score
// -error
func GetOffspringDiscreteTraitInfo_Rules(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, bool, int, int, map[[3]byte]int, map[string]int, error){
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
return false, false, 0, 0, nil, nil, nil
}
func GetOffspringTraitInfo(traitObject traits.Trait, person1LocusValuesMap map[int64]locusValue.LocusValue, person2LocusValuesMap map[int64]locusValue.LocusValue)(bool, int, map[[3]byte]int, map[string]float64, error){
if (len(person1LocusValuesMap) == 0){
return true, false, 0, 0, nil, nil, nil
return false, 0, nil, nil, nil
}
if (len(person2LocusValuesMap) == 0){
return true, false, 0, 0, nil, nil, nil
return false, 0, nil, nil, nil
}
// First, we create 100 prospective offspring genomes.
traitLociList_Rules := traitObject.LociList_Rules
traitLociList := traitObject.LociList
anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(traitLociList_Rules, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return false, false, 0, 0, nil, nil, err }
anyLocusValueExists, prospectiveOffspringGenomesList, err := getProspectiveOffspringGenomesList(traitLociList, person1LocusValuesMap, person2LocusValuesMap)
if (err != nil) { return false, 0, nil, nil, err }
if (anyLocusValueExists == false){
return true, false, 0, 0, nil, nil, nil
return false, 0, nil, nil, nil
}
traitRulesList := traitObject.RulesList
// Map Structure: Rule Identifier -> Number of offspring who pass the rule (out of 100 prospective offspring)
// Because there are 100 offspring, this also represents the percentage probability that an offspring will pass the rule
offspringPassesRulesCountMap := make(map[[3]byte]int)
// This map stores the quantity of offspring who have each outcome
// The probability an offspring will have this outcome is the same as the
// quantity of offspring who have this outcome in our set of 100 randomly generated offspring
// Map structure: Outcome name -> quantity of offspring who have this outcome
outcomeCountsMap := make(map[string]int)
// We use this map to keep track of the rules for which we know every offspring's passes-rule status
// Map Structure: Rule Identifier -> Rule Object
offspringRulesWithKnownStatusMap := make(map[[3]byte]traits.TraitRule)
quantityOfLociKnown := 0
for offspringIndex, offspringGenomeMap := range prospectiveOffspringGenomesList{
for index, offspringGenomeMap := range prospectiveOffspringGenomesList{
// We iterate through rules to determine genome pair trait info
// Now we get outcome prediction for prospective offspring
for _, ruleObject := range traitRulesList{
anyRulesExist, quantityOfRulesTested, currentQuantityOfLociKnown, offspringPassesRulesMap, predictionOutcomeIsKnown, predictedOutcome, err := createPersonGeneticAnalysis.GetGenomeDiscreteTraitAnalysis_Rules(traitObject, offspringGenomeMap, false)
if (err != nil) { return false, false, 0, 0, nil, nil, err }
if (anyRulesExist == false){
return false, false, 0, 0, nil, nil, errors.New("GetGenomeTraitAnalysis_Rules returning noRulesExists when we already checked and trait rules do in-fact exist.")
}
if (quantityOfRulesTested == 0){
// This will be the same for each of the 100 generated offspring
// No analysis is possible.
return true, false, 0, currentQuantityOfLociKnown, nil, nil, nil
}
if (index == 0){
// currentQuantityOfLociKnown will be the same for each prospective offspring
quantityOfLociKnown = currentQuantityOfLociKnown
}
ruleIdentifierHex := ruleObject.RuleIdentifier
for ruleIdentifier, genomePassesRule := range offspringPassesRulesMap{
if (genomePassesRule == true){
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return false, 0, nil, nil, err }
if (offspringIndex == 0){
offspringRulesWithKnownStatusMap[ruleIdentifier] = ruleObject
} else {
_, exists := offspringRulesWithKnownStatusMap[ruleIdentifier]
if (exists == false){
// We already tried to check a previous offspring's passes-rule status for this rule
// We know that the offspring's passes-rule status will be unknown for every prospective offspring
continue
}
}
// This is a list that describes the locus rsids and their values that must be fulfilled to pass the rule
ruleLocusObjectsList := ruleObject.LociList
offspringPassesRuleIsKnown, offspringPassesRule, err := createPersonGeneticAnalysis.GetGenomePassesTraitRuleStatus(ruleLocusObjectsList, offspringGenomeMap, false)
if (err != nil){ return false, 0, nil, nil, err }
if (offspringPassesRuleIsKnown == false){
continue
}
if (offspringPassesRule == true){
offspringPassesRulesCountMap[ruleIdentifier] += 1
}
}
if (predictionOutcomeIsKnown == false){
// There was a tie between outcomes for this offspring
// We can't predict anything about this trait for this couple using rules
// This is why we need to create rules which make it unlikely for a tie between outcomes to occur.
return true, false, 0, 0, nil, nil, nil
}
outcomeCountsMap[predictedOutcome] += 1
}
quantityOfRulesTested := len(offspringPassesRulesCountMap)
// Map Structure: Rule Identifier -> Offspring Probability Of Passing Rule
// The map value stores the probability that the offspring will pass the rule
// This is a number between 0-100%
offspringProbabilityOfPassingRulesMap := make(map[[3]byte]int)
return true, true, quantityOfRulesTested, quantityOfLociKnown, offspringPassesRulesCountMap, outcomeCountsMap, nil
// Map Structure: Outcome Name -> Outcome Score
// Example: "Intolerant" -> 2.5
offspringAverageOutcomeScoresMap := make(map[string]float64)
for ruleIdentifier, ruleObject := range offspringRulesWithKnownStatusMap{
//Output:
// -int: Offspring probability of passing rule (0-100%)
getOffspringPercentageProbabilityOfPassingRule := func()int{
numberOfOffspringWhoPassRule, exists := offspringPassesRulesCountMap[ruleIdentifier]
if (exists == false){
// None of the offspring passed the rule
return 0
}
// There are 100 tested offspring
// Thus, the percentage of offspring who passed the rule is the same as the number of offspring who passed the rule
// The probability of the offspring passing the rule is the same as the percentage of offspring who passed the rule
return numberOfOffspringWhoPassRule
}
offspringPercentageProbabilityOfPassingRule := getOffspringPercentageProbabilityOfPassingRule()
offspringProbabilityOfPassingRulesMap[ruleIdentifier] = offspringPercentageProbabilityOfPassingRule
// This is the 0 - 1 probability value
offspringProbabilityOfPassingRule := float64(offspringPercentageProbabilityOfPassingRule)/100
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
for outcomeName, outcomePointsEffect := range ruleOutcomePointsMap{
pointsToAdd := float64(outcomePointsEffect) * offspringProbabilityOfPassingRule
offspringAverageOutcomeScoresMap[outcomeName] += pointsToAdd
}
}
numberOfRulesTested := len(offspringProbabilityOfPassingRulesMap)
if (numberOfRulesTested == 0){
return false, 0, nil, nil, nil
}
traitOutcomesList := traitObject.OutcomesList
// We add all outcomes for which there were no points
for _, traitOutcome := range traitOutcomesList{
_, exists := offspringAverageOutcomeScoresMap[traitOutcome]
if (exists == false){
offspringAverageOutcomeScoresMap[traitOutcome] = 0
}
}
return true, numberOfRulesTested, offspringProbabilityOfPassingRulesMap, offspringAverageOutcomeScoresMap, nil
}
@ -1307,10 +1281,6 @@ func GetOffspringDiscreteTraitInfo_Rules(traitObject traits.Trait, person1LocusV
// Each genome represents an equal-probability offspring genome from both people's genomes
// This function takes into account the effects of genetic linkage
// Any locations which do not exist in both people's genomes will not be included
//
// TODO: The user should be able to choose how many prospective offspring to create in the settings.
// More offspring will take longer, but will yield a more accurate trait analysis.
//
//Outputs:
// -bool: Any locus value exists between both users
// -[]map[int64]locusValue.LocusValue
@ -1418,7 +1388,7 @@ func getProspectiveOffspringGenomesList(lociList []int64, person1LociMap map[int
// We step by 1,000,000 each time
// It would be more realistic if we did it in 1 integer increments, but it would be slower
for position := int64(0); position < chromosomeLength; position += 1_000_000{
for position := int64(0); position <= chromosomeLength; position += 1000000{
//From Wikipedia:
// A centimorgan (abbreviated cM) is a unit for measuring genetic linkage.
@ -1471,19 +1441,14 @@ func getProspectiveOffspringGenomesList(lociList []int64, person1LociMap map[int
}
personLocusBase1 := personLocusValue.Base1Value
personLocusBase2 := personLocusValue.Base2Value
personLocusBase2 := personLocusValue.Base1Value
personLocusIsPhased := personLocusValue.LocusIsPhased
if (personLocusBase1 == personLocusBase2){
// Phase doesn't matter
return true, personLocusBase1, nil
}
if (personLocusIsPhased == false){
// Breakpoints are unnecessary
// We either choose base 1 or 2
randomInt := pseudorandomNumberGenerator.IntN(2)
if (randomInt == 1){
randomBool := helpers.GetRandomBool()
if (randomBool == true){
return true, personLocusBase1, nil
}
return true, personLocusBase2, nil
@ -1525,9 +1490,9 @@ func getProspectiveOffspringGenomesList(lociList []int64, person1LociMap map[int
getLocusListIndex := func()int{
for index, breakpointPosition := range personBreakpointsList{
for index, breakpoint := range personBreakpointsList{
if (int64(locusPosition) <= breakpointPosition){
if (int64(locusPosition) <= breakpoint){
return index
}

View file

@ -22,14 +22,13 @@ import "seekia/resources/geneticReferences/traits"
import "seekia/internal/encoding"
import "seekia/internal/genetics/geneticAnalysis"
import "seekia/internal/genetics/geneticPrediction"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/genetics/prepareRawGenomes"
import "seekia/internal/helpers"
import "errors"
import "slices"
import "reflect"
import "maps"
//Outputs:
@ -52,23 +51,10 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe
genomesWithMetadataList, allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(genomesList, prepareRawGenomesUpdatePercentageCompleteFunction)
if (err != nil) { return false, "", err }
// This map stores each genome's locus values
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
genomesMap := make(map[[16]byte]map[int64]locusValue.LocusValue)
for _, genomeWithMetadata := range genomesWithMetadataList{
genomeIdentifier := genomeWithMetadata.GenomeIdentifier
genomeMap := genomeWithMetadata.GenomeMap
genomesMap[genomeIdentifier] = genomeMap
}
newGeneticAnalysisObject := geneticAnalysis.PersonAnalysis{
AnalysisVersion: 1,
CombinedGenomesExist: multipleGenomesExist,
AllRawGenomeIdentifiersList: allRawGenomeIdentifiersList,
GenomesMap: genomesMap,
}
if (multipleGenomesExist == true){
@ -121,30 +107,20 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe
traitObjectsList, err := traits.GetTraitObjectsList()
if (err != nil) { return false, "", err }
// This map will always contain an entry for each discrete trait
// Map Structure: Trait Name -> PersonDiscreteTraitInfo
analysisDiscreteTraitsMap := make(map[string]geneticAnalysis.PersonDiscreteTraitInfo)
// This map will not contain entries for traits which this person's genome has no known loci
// Map Structure: Trait Name -> PersonNumericTraitInfo
analysisNumericTraitsMap := make(map[string]geneticAnalysis.PersonNumericTraitInfo)
// Map Structure: Trait Name -> PersonTraitInfo
analysisTraitsMap := make(map[string]geneticAnalysis.PersonTraitInfo)
for _, traitObject := range traitObjectsList{
personTraitAnalysisObject, err := GetPersonTraitAnalysis(genomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric == "Discrete"){
personTraitAnalysisObject, err := GetPersonDiscreteTraitAnalysis(genomesWithMetadataList, traitObject)
if (err != nil) { return false, "", err }
analysisDiscreteTraitsMap[traitName] = personTraitAnalysisObject
}
analysisTraitsMap[traitName] = personTraitAnalysisObject
}
newGeneticAnalysisObject.DiscreteTraitsMap = analysisDiscreteTraitsMap
newGeneticAnalysisObject.NumericTraitsMap = analysisNumericTraitsMap
newGeneticAnalysisObject.TraitsMap = analysisTraitsMap
analysisBytes, err := encoding.EncodeMessagePackBytes(newGeneticAnalysisObject)
if (err != nil) { return false, "", err }
@ -529,9 +505,9 @@ func GetPersonMonogenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
diseaseAnalysisObject := geneticAnalysis.PersonGenomeMonogenicDiseaseInfo{
PersonHasDisease: personHasDisease,
QuantityOfVariantsTested: numberOfVariantsTested,
QuantityOfLociTested: numberOfLociTested,
QuantityOfPhasedLoci: numberOfPhasedLoci,
NumberOfVariantsTested: numberOfVariantsTested,
NumberOfLociTested: numberOfLociTested,
NumberOfPhasedLoci: numberOfPhasedLoci,
ProbabilityOfPassingADiseaseVariant: percentageProbabilityPersonWillPassADiseaseVariant,
VariantsInfoMap: variantsInfoMap,
}
@ -761,15 +737,16 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
genomeLocusValuesMap[locusRSID] = locusValueObject
}
anyLociTested, personDiseaseRiskScore, genomeNumberOfLociTested, genomeLociInfoMap, err := GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, genomeLocusValuesMap, true)
anyLociTested, personDiseaseRiskScore, genomeNumberOfLociTested, genomeLociInfoMap, err := GetPersonGenomePolygenicDiseaseInfo(diseaseLociList, genomeLocusValuesMap, false)
if (err != nil) { return emptyDiseaseInfoObject, err }
if (anyLociTested == false){
continue
}
newDiseaseInfoObject := geneticAnalysis.PersonGenomePolygenicDiseaseInfo{
QuantityOfLociTested: genomeNumberOfLociTested,
NumberOfLociTested: genomeNumberOfLociTested,
RiskScore: personDiseaseRiskScore,
LocusValuesMap: genomeLocusValuesMap,
LociInfoMap: genomeLociInfoMap,
}
@ -800,7 +777,7 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
for _, personGenomeDiseaseInfoObject := range personPolygenicDiseaseInfoMap{
currentGenomeRiskScore := personGenomeDiseaseInfoObject.RiskScore
currentGenomeNumberOfLociTested := personGenomeDiseaseInfoObject.QuantityOfLociTested
currentGenomeNumberOfLociTested := personGenomeDiseaseInfoObject.NumberOfLociTested
if (firstItemReached == false){
genomeRiskScore = currentGenomeRiskScore
@ -878,68 +855,106 @@ func GetPersonPolygenicDiseaseAnalysis(inputGenomesWithMetadataList []prepareRaw
//Outputs:
// -geneticAnalysis.PersonDiscreteTraitInfo: Trait analysis object
// -geneticAnalysis.PersonTraitInfo: Trait analysis object
// -error
func GetPersonDiscreteTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, traitObject traits.Trait)(geneticAnalysis.PersonDiscreteTraitInfo, error){
func GetPersonTraitAnalysis(inputGenomesWithMetadataList []prepareRawGenomes.GenomeWithMetadata, traitObject traits.Trait)(geneticAnalysis.PersonTraitInfo, error){
// Map Structure: Genome Identifier -> PersonGenomeDiscreteTraitInfo
newPersonTraitInfoMap := make(map[[16]byte]geneticAnalysis.PersonGenomeDiscreteTraitInfo)
// We use this when returning errors
emptyPersonTraitInfo := geneticAnalysis.PersonTraitInfo{}
traitLociList := traitObject.LociList
traitRulesList := traitObject.RulesList
// Map Structure: Genome Identifier -> PersonGenomeTraitInfo
newPersonTraitInfoMap := make(map[[16]byte]geneticAnalysis.PersonGenomeTraitInfo)
for _, genomeWithMetadataObject := range inputGenomesWithMetadataList{
genomeIdentifier := genomeWithMetadataObject.GenomeIdentifier
genomeMap := genomeWithMetadataObject.GenomeMap
newPersonGenomeTraitInfo := geneticAnalysis.PersonGenomeDiscreteTraitInfo{}
// This map contains the locus values for the genome
// If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
genomeLocusValuesMap := make(map[int64]locusValue.LocusValue)
neuralNetworkExists, neuralNetworkOutcomeIsKnown, predictedOutcome, predictionConfidence, quantityOfLociTested, quantityOfPhasedLoci, err := GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject, genomeMap, true)
if (err != nil) { return geneticAnalysis.PersonDiscreteTraitInfo{}, err }
if (neuralNetworkExists == true){
for _, locusRSID := range traitLociList{
newPersonGenomeTraitInfo.NeuralNetworkExists = true
locusBasePairKnown, _, _, _, locusValueObject, err := GetLocusValueFromGenomeMap(true, genomeMap, locusRSID)
if (err != nil) { return emptyPersonTraitInfo, err }
if (locusBasePairKnown == false){
continue
}
if (neuralNetworkOutcomeIsKnown == true){
genomeLocusValuesMap[locusRSID] = locusValueObject
}
newPersonGenomeTraitInfo.NeuralNetworkAnalysisExists = true
// This map contains the trait outcome scores for the genome
// Map Structure: Outcome Name -> Score
// Example: "Intolerant" -> 5
traitOutcomeScoresMap := make(map[string]int)
newPersonGenomeTraitInfo_NeuralNetwork := geneticAnalysis.PersonGenomeDiscreteTraitInfo_NeuralNetwork{
// Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule)
personPassesRulesMap := make(map[[3]byte]bool)
PredictedOutcome: predictedOutcome,
PredictionConfidence: predictionConfidence,
QuantityOfLociKnown: quantityOfLociTested,
QuantityOfPhasedLoci: quantityOfPhasedLoci,
if (len(traitRulesList) != 0){
// At least 1 rule exists for this trait
for _, ruleObject := range traitRulesList{
ruleIdentifierHex := ruleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return emptyPersonTraitInfo, err }
ruleLociList := ruleObject.LociList
genomePassesRuleIsKnown, genomePassesRule, err := GetGenomePassesTraitRuleStatus(ruleLociList, genomeMap, false)
if (err != nil) { return emptyPersonTraitInfo, err }
if (genomePassesRuleIsKnown == false){
continue
}
newPersonGenomeTraitInfo.NeuralNetworkAnalysis = newPersonGenomeTraitInfo_NeuralNetwork
personPassesRulesMap[ruleIdentifier] = genomePassesRule
// The rule has been passed by this genome
// We add the outcome points for the rule to the traitOutcomeScoresMap
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
for traitOutcome, pointsChange := range ruleOutcomePointsMap{
traitOutcomeScoresMap[traitOutcome] += pointsChange
}
}
}
anyRulesExist, quantityOfRulesTested, quantityOfLociKnown, genomePassesRulesMap, predictedOutcomeExists, predictedOutcome, err := GetGenomeDiscreteTraitAnalysis_Rules(traitObject, genomeMap, true)
if (err != nil) { return geneticAnalysis.PersonDiscreteTraitInfo{}, err }
if (anyRulesExist == true){
newPersonGenomeTraitInfo.AnyRulesExist = true
traitOutcomesList := traitObject.OutcomesList
if (quantityOfRulesTested != 0){
// We add all outcomes for which there were no points
newPersonGenomeTraitInfo.RulesAnalysisExists = true
for _, traitOutcome := range traitOutcomesList{
newPersonGenomeTraitInfo_Rules := geneticAnalysis.PersonGenomeDiscreteTraitInfo_Rules{
GenomePassesRulesMap: genomePassesRulesMap,
PredictedOutcomeExists: predictedOutcomeExists,
PredictedOutcome: predictedOutcome,
QuantityOfRulesTested: quantityOfRulesTested,
QuantityOfLociKnown: quantityOfLociKnown,
}
newPersonGenomeTraitInfo.RulesAnalysis = newPersonGenomeTraitInfo_Rules
_, exists := traitOutcomeScoresMap[traitOutcome]
if (exists == false){
traitOutcomeScoresMap[traitOutcome] = 0
}
}
numberOfRulesTested := len(personPassesRulesMap)
newPersonGenomeTraitInfo := geneticAnalysis.PersonGenomeTraitInfo{
NumberOfRulesTested: numberOfRulesTested,
LocusValuesMap: genomeLocusValuesMap,
OutcomeScoresMap: traitOutcomeScoresMap,
GenomePassesRulesMap: personPassesRulesMap,
}
newPersonTraitInfoMap[genomeIdentifier] = newPersonGenomeTraitInfo
}
newPersonTraitInfoObject := geneticAnalysis.PersonDiscreteTraitInfo{
newPersonTraitInfoObject := geneticAnalysis.PersonTraitInfo{
TraitInfoMap: newPersonTraitInfoMap,
}
@ -953,20 +968,40 @@ func GetPersonDiscreteTraitAnalysis(inputGenomesWithMetadataList []prepareRawGen
getConflictExistsBool := func()(bool, error){
// We check to see if the analysis results are the same for all genomes
//TODO: Check for locus value conflicts once locus values are used in neural network prediction.
if (len(traitRulesList) == 0){
return false, nil
}
// We check to see if the outcome scores are the same for all genomes
// We also check each rule result
firstItemReached := false
personGenomeTraitInfoObject := geneticAnalysis.PersonGenomeDiscreteTraitInfo{}
outcomeScoresMap := make(map[string]int)
passesRulesMap := make(map[[3]byte]bool)
for _, genomeTraitInfoObject := range newPersonTraitInfoMap{
currentGenomeOutcomeScoresMap := genomeTraitInfoObject.OutcomeScoresMap
currentGenomePassesRulesMap := genomeTraitInfoObject.GenomePassesRulesMap
if (firstItemReached == false){
personGenomeTraitInfoObject = genomeTraitInfoObject
outcomeScoresMap = currentGenomeOutcomeScoresMap
passesRulesMap = currentGenomePassesRulesMap
firstItemReached = true
continue
}
areEqual := reflect.DeepEqual(personGenomeTraitInfoObject, genomeTraitInfoObject)
areEqual := maps.Equal(currentGenomeOutcomeScoresMap, outcomeScoresMap)
if (areEqual == false){
// A conflict exists
return true, nil
}
areEqual = maps.Equal(currentGenomePassesRulesMap, passesRulesMap)
if (areEqual == false){
// A conflict exists
return true, nil
}
}
@ -975,7 +1010,7 @@ func GetPersonDiscreteTraitAnalysis(inputGenomesWithMetadataList []prepareRawGen
}
conflictExists, err := getConflictExistsBool()
if (err != nil) { return geneticAnalysis.PersonDiscreteTraitInfo{}, err }
if (err != nil) { return emptyPersonTraitInfo, err }
newPersonTraitInfoObject.ConflictExists = conflictExists
@ -1011,205 +1046,12 @@ func GetGenomePolygenicDiseaseLocusRiskInfo(locusRiskWeightsMap map[string]int,
return riskWeight, true, oddsRatio, nil
}
// We use this to generate trait predictions using a neural network
// The alternative prediction method is to use Rules (see GetGenomeTraitAnalysis_Rules)
//Outputs:
// -bool: Trait Neural network analysis available (if false, we can't predict this trait using a neural network)
// -bool: Neural network outcome is known (at least 1 locus value is known which is needed for the neural network
// -string: The predicted outcome (Example: "Blue")
// -int: Probability (0-100) that the outcome from neural network is true (confidence)
// -int: Quantity of loci tested
// -int: Quantity of phased loci
// -error
func GetGenomeDiscreteTraitAnalysis_NeuralNetwork(traitObject traits.Trait, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, string, int, int, int, error){
getGenomeLocusValuesMap := func()(map[int64]locusValue.LocusValue, error){
if (checkForAliases == false){
// We don't need to check for rsID aliases.
return genomeMap, nil
}
traitLociList := traitObject.LociList
// This map contains the locus values for the genome
// If a locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
genomeLocusValuesMap := make(map[int64]locusValue.LocusValue)
for _, locusRSID := range traitLociList{
locusBasePairKnown, _, _, _, locusValueObject, err := GetLocusValueFromGenomeMap(checkForAliases, genomeMap, locusRSID)
if (err != nil) { return nil, err }
if (locusBasePairKnown == false){
continue
}
genomeLocusValuesMap[locusRSID] = locusValueObject
}
return genomeLocusValuesMap, nil
}
genomeLocusValuesMap, err := getGenomeLocusValuesMap()
if (err != nil) { return false, false, "", 0, 0, 0, err }
traitName := traitObject.TraitName
neuralNetworkModelExists, traitPredictionIsPossible, predictedOutcome, predictionConfidence, quantityOfLociKnown, quantityOfPhasedLoci, err := geneticPrediction.GetNeuralNetworkTraitPredictionFromGenomeMap(traitName, genomeLocusValuesMap)
if (err != nil) { return false, false, "", 0, 0, 0, err }
if (neuralNetworkModelExists == false){
return false, false, "", 0, 0, 0, nil
}
if (traitPredictionIsPossible == false){
return true, false, "", 0, 0, 0, nil
}
return true, true, predictedOutcome, predictionConfidence, quantityOfLociKnown, quantityOfPhasedLoci, nil
}
//Outputs:
// -bool: Rule-based trait prediction is available
// -int: Quantity of trait rules tested
// -int: Quantity of loci known
// -map[[3]byte]bool: Passed rules map (Rule Identifier -> Genome passes rule)
// -bool: Rule-based prediction outcome is known (at least 1 rule has been tested and there is no outcome tie)
// -string: The predicted outcome (Example: "Tolerant")
// -error
func GetGenomeDiscreteTraitAnalysis_Rules(traitObject traits.Trait, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, int, int, map[[3]byte]bool, bool, string, error){
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric != "Discrete"){
return false, 0, 0, nil, false, "", errors.New("GetGenomeDiscreteTraitAnalysis_Rules called with non-discrete trait.")
}
traitRulesList := traitObject.RulesList
if (len(traitRulesList) == 0){
// We can't predict this trait using rules
// This means that neural network prediction is the only alternative potential way to predict this trait
return false, 0, 0, nil, false, "", nil
}
// This map contains the trait outcome scores for the genome
// Map Structure: Outcome Name -> Score
// Example: "Intolerant" -> 5
traitOutcomeScoresMap := make(map[string]int)
// Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule)
personPassesRulesMap := make(map[[3]byte]bool)
// This map stores each known loci
// Multiple rules can use the same loci, so we need a map to avoid duplicates
// Map Structure: Locus RSID -> Locus is known
lociAreKnownMap := make(map[int64]bool)
for _, ruleObject := range traitRulesList{
ruleIdentifierHex := ruleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return false, 0, 0, nil, false, "", err }
ruleLociList := ruleObject.LociList
for _, locusObject := range ruleLociList{
locusRSID := locusObject.LocusRSID
_, exists := lociAreKnownMap[locusRSID]
if (exists == true){
// We already know if this locus is known
continue
}
locusIsKnown, _, _, _, _, err := GetLocusValueFromGenomeMap(checkForAliases, genomeMap, locusRSID)
if (err != nil) { return false, 0, 0, nil, false, "", err }
lociAreKnownMap[locusRSID] = locusIsKnown
}
genomePassesRuleIsKnown, genomePassesRule, err := GetGenomePassesDiscreteTraitRuleStatus(ruleLociList, genomeMap, checkForAliases)
if (err != nil) { return false, 0, 0, nil, false, "", err }
if (genomePassesRuleIsKnown == false){
continue
}
personPassesRulesMap[ruleIdentifier] = genomePassesRule
// The rule has been passed by this genome
// We add the outcome points for the rule to the traitOutcomeScoresMap
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
for traitOutcome, pointsChange := range ruleOutcomePointsMap{
traitOutcomeScoresMap[traitOutcome] += pointsChange
}
}
quantityOfLociKnown := 0
for _, locusIsKnown := range lociAreKnownMap{
if (locusIsKnown == true){
quantityOfLociKnown += 1
}
}
quantityOfRulesTested := len(personPassesRulesMap)
if (quantityOfRulesTested == 0){
return true, 0, quantityOfLociKnown, personPassesRulesMap, false, "", nil
}
// -bool: Outcome is known (will be false if there is a tie
// -string:
// -error
getOutcome := func()(bool, string, error){
largestOutcome := ""
largestOutcomePoints := 0
tieExists := false
for outcomeName, outcomePoints := range traitOutcomeScoresMap{
if (outcomePoints < 1){
// This should never happen, because outcomes points should only be increased by integers which are at least 1 or greater
return false, "", errors.New("traitOutcomeScoresMap contains outcomePoints < 1.")
}
if (outcomePoints > largestOutcomePoints){
largestOutcome = outcomeName
largestOutcomePoints = outcomePoints
tieExists = false
continue
} else if (outcomePoints == largestOutcomePoints){
tieExists = true
}
}
if (tieExists == true){
return false, "", nil
}
return true, largestOutcome, nil
}
outcomeIsKnown, outcomeName, err := getOutcome()
if (err != nil) { return false, 0, 0, nil, false, "", err }
if (outcomeIsKnown == false){
return true, quantityOfRulesTested, quantityOfLociKnown, personPassesRulesMap, false, "", nil
}
return true, quantityOfRulesTested, quantityOfLociKnown, personPassesRulesMap, true, outcomeName, nil
}
// This function checks to see if a genome will pass a trait rule
// Outputs:
// -bool: Genome passes trait rule status is known
// -bool: Genome passes trait rule
// -error
func GetGenomePassesDiscreteTraitRuleStatus(ruleLociList []traits.RuleLocus, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, error){
func GetGenomePassesTraitRuleStatus(ruleLociList []traits.RuleLocus, genomeMap map[int64]locusValue.LocusValue, checkForAliases bool)(bool, bool, error){
// We check to see if genome passes all rule loci
// To pass a rule, all of the rule's loci must be passed by the provided genome

View file

@ -22,27 +22,14 @@ type PersonAnalysis struct{
OnlyIncludeSharedGenomeIdentifier [16]byte
OnlyExcludeConflictsGenomeIdentifier [16]byte
// This map stores each genome's locus values
// Only the loci that belong in the locusMetadata package are inside of this map
// This is necessary, otherwise genetic analyses would be too large by containing each analyzed raw genome.
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
GenomesMap map[[16]byte]map[int64]locusValue.LocusValue
// Map Structure: Disease Name -> PersonMonogenicDiseaseInfo
MonogenicDiseasesMap map[string]PersonMonogenicDiseaseInfo
// Map Structure: Disease Name -> PersonPolygenicDiseaseInfo
PolygenicDiseasesMap map[string]PersonPolygenicDiseaseInfo
// These are traits which have discrete outcomes, rather than numeric outcomes
// For example: Eye color
// Map Structure: Trait Name -> Trait Info Object
DiscreteTraitsMap map[string]PersonDiscreteTraitInfo
// These are traits which have numeric outcomes, rather than discrete outcomes
// For example: Height
// Map Structure: Trait Name -> Trait Info Object
NumericTraitsMap map[string]PersonNumericTraitInfo
TraitsMap map[string]PersonTraitInfo
}
@ -63,15 +50,15 @@ type PersonGenomeMonogenicDiseaseInfo struct{
PersonHasDisease bool
// This describes the number of variants that were tested for this disease
QuantityOfVariantsTested int
NumberOfVariantsTested int
// This describes the number of loci that were tested for this disease
// 1 locus can have multiple potential variants
QuantityOfLociTested int
NumberOfLociTested int
// This describes the number of loci which are phased
// This number will always be <= QuantityOfLociTested
QuantityOfPhasedLoci int
// This number will always be <= NumberOfLociTested
NumberOfPhasedLoci int
// This describes the probability that the person will pass a disease variant
// It is a value that represents a percentage between 0-100
@ -107,9 +94,14 @@ type PersonPolygenicDiseaseInfo struct{
type PersonGenomePolygenicDiseaseInfo struct{
// This describes the quantity of loci tested for this disease
// This should be len(LociInfoMap)
QuantityOfLociTested int
// This describes the number of loci tested for this disease
// This should be len(LociInfoList)
NumberOfLociTested int
// This map contains the locus values for the genome for this trait
// If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
LocusValuesMap map[int64]locusValue.LocusValue
// This is total risk score for this disease for the person's genome
// This is a number between 1-10
@ -136,102 +128,34 @@ type PersonGenomePolygenicDiseaseLocusInfo struct{
}
type PersonDiscreteTraitInfo struct{
type PersonTraitInfo struct{
// This map contains the person's trait info for each genome
// If no map entries exist, then no trait info is known
// Map Structure: Genome Identifier -> PersonGenomeDiscreteTraitInfo
TraitInfoMap map[[16]byte]PersonGenomeDiscreteTraitInfo
// Map Structure: Genome Identifier -> PersonGenomeTraitInfo
TraitInfoMap map[[16]byte]PersonGenomeTraitInfo
// This is true if there are multiple genomes and the results from each genome differ
ConflictExists bool
}
// For a trait analysis, both analysis methods may exist in the results
// However, the GUI will only display the results from one of the methods.
// The neural network prediction is always prioritized over the rule-based prediction
type PersonGenomeDiscreteTraitInfo struct{
type PersonGenomeTraitInfo struct{
// This is true if it is possible to analyze this trait using a neural network
NeuralNetworkExists bool
// This should be len(GenomePassesRulesMap)
NumberOfRulesTested int
// This is true if a neural network analysis was performed for this genome
// This means that at least 1 locus for this trait was contained in the genome
NeuralNetworkAnalysisExists bool
// This map contains the locus values for the genome for this trait
// If an locus's entry doesn't exist, its value is unknown
// Map Structure: Locus rsID -> Locus Value
LocusValuesMap map[int64]locusValue.LocusValue
NeuralNetworkAnalysis PersonGenomeDiscreteTraitInfo_NeuralNetwork
// This is true if it is possible to analyze this trait using rules
AnyRulesExist bool
// This is true if a rules-based analysis was performed for this genome
// This means that all of the loci for at least 1 rule for this trait was contained in the genome
RulesAnalysisExists bool
RulesAnalysis PersonGenomeDiscreteTraitInfo_Rules
}
type PersonGenomeDiscreteTraitInfo_NeuralNetwork struct{
// The predicted outcome (Example: "Blue")
PredictedOutcome string
// Probability (0-100) that the outcome from the neural network is true
PredictionConfidence int
QuantityOfLociKnown int
QuantityOfPhasedLoci int
}
type PersonGenomeDiscreteTraitInfo_Rules struct{
// This map contains the outcome scores for the genome
// Map Structure: Outcome Name -> Score
// Example: "Intolerant" -> 5
OutcomeScoresMap map[string]int
// Map Structure: Rule Identifier -> Genome Passes rule (true if the genome passes the rule)
GenomePassesRulesMap map[[3]byte]bool
// This is true if there was not a tie between summed rule outcome scores
// It is possible to have some tested rules without a known outcome
PredictedOutcomeExists bool
// This is the outcome that was predicted
// Example: "Intolerant"
PredictedOutcome string
// This should be len(GenomePassesRulesMap)
QuantityOfRulesTested int
// This only counts the loci which are used for rules
// For example, loci that are only used in neural-network-based prediction are not counted
QuantityOfLociKnown int
}
type PersonNumericTraitInfo struct{
// This map contains the person's trait info for each genome
// If no map entries exist, then no trait info is known
// Map Structure: Genome Identifier -> PersonGenomeNumericTraitInfo
TraitInfoMap map[[16]byte]PersonGenomeNumericTraitInfo
// This is true if there are multiple genomes and the results from each genome differ
ConflictExists bool
}
type PersonGenomeNumericTraitInfo struct{
// The predicted outcome (Example: The predicted height for this person, in centimeters)
PredictedOutcome float64
// This map stores the confidence ranges for the predicted value
// If we want to know how accurate the prediction is with a X% accuracy, how far would we have to expand the
// predicted value's range to be accurate, X% of the time?
// For example: 50% accuracy requires a +/-5 point range, 80% accuracy requires a +-15 point range
// Map Structure: Accuracy probability (0-100) -> Amount to add to value in both +/- directions so prediction is that accurate
ConfidenceRangesMap map[int]float64
QuantityOfLociKnown int
QuantityOfPhasedLoci int
}
@ -266,21 +190,16 @@ type CoupleAnalysis struct{
// Map Structure: Disease Name -> OffspringPolygenicDiseaseInfo
PolygenicDiseasesMap map[string]OffspringPolygenicDiseaseInfo
// Discrete traits are traits with discrete outcomes, such as Eye Color
// Map Structure: Trait Name -> Trait Info Object
DiscreteTraitsMap map[string]OffspringDiscreteTraitInfo
// Numeric traits are traits with numeric outcomes, such as Height
// Map Structure: Trait Name -> Trait Info Object
NumericTraitsMap map[string]OffspringNumericTraitInfo
TraitsMap map[string]OffspringTraitInfo
}
type OffspringMonogenicDiseaseInfo struct{
// This map stores the quantity of variants tested in each person's genome
// This map stores the number of variants tested in each person's genome
// Map Structure: Genome Identifier -> Number of variants tested
QuantityOfVariantsTestedMap map[[16]byte]int
NumberOfVariantsTestedMap map[[16]byte]int
// This map stores the offspring disease probabilities for each genome pair.
// A genome pair is a concatenation of two genome identifiers
@ -296,6 +215,7 @@ type OffspringMonogenicDiseaseInfo struct{
type OffspringGenomePairMonogenicDiseaseInfo struct{
// At least 1 variant's information is needed from either person to include the diseaseInfo object in the MonogenicDiseaseInfoMap
ProbabilityOffspringHasDiseaseIsKnown bool
// This is the probability that the offspring will have the disease
@ -339,8 +259,8 @@ type OffspringPolygenicDiseaseInfo struct{
type OffspringGenomePairPolygenicDiseaseInfo struct{
// This should be len(LociInfoMap)
QuantityOfLociTested int
// This should be len(DiseaseLociList)
NumberOfLociTested int
// A number between 1-10 representing the offspring's average risk score
// 1 == lowest risk, 10 == highest risk
@ -377,110 +297,27 @@ type OffspringPolygenicDiseaseLocusInfo struct{
}
type OffspringDiscreteTraitInfo struct{
type OffspringTraitInfo struct{
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairTraitInfo
TraitInfoMap map[[32]byte]OffspringGenomePairDiscreteTraitInfo
TraitInfoMap map[[32]byte]OffspringGenomePairTraitInfo
ConflictExists bool
}
// For a trait analysis, both analysis methods may exist in the results
// However, the GUI will only display the results from one of the methods.
// The neural network prediction is always prioritized over the rule-based prediction
type OffspringGenomePairDiscreteTraitInfo struct{
type OffspringGenomePairTraitInfo struct{
// This is true if it is possible to analyze this trait using a neural network
NeuralNetworkExists bool
// This should be len(TraitRulesList)
NumberOfRulesTested int
// This is true if a neural network analysis was performed for this genome
// This means that at least 1 locus for this trait was contained in both of the genomes in the pair
NeuralNetworkAnalysisExists bool
NeuralNetworkAnalysis OffspringGenomePairDiscreteTraitInfo_NeuralNetwork
// This is true if it is possible to analyze this trait using rules
RulesExist bool
// This is true if a rules-based analysis was performed for this genome
// This means that all of the loci for at least 1 rule for this trait was contained in both of the genomes in the pair
// Also, none of the offspring have an unknown outcome caused by an outcome score tie
RulesAnalysisExists bool
RulesAnalysis OffspringGenomePairDiscreteTraitInfo_Rules
}
type OffspringGenomePairDiscreteTraitInfo_NeuralNetwork struct{
// Map Structure: Outcome Name -> Outcome Probability (0-100)
// Example: "Intolerant" -> 5
OffspringOutcomeProbabilitiesMap map[string]int
// Probability (0-100) that each outcome from the neural network is true
// This is an average of the confidence for each of the calculated 100 outcome probabilities
AverageConfidence int
QuantityOfLociKnown int
// This describes the quantity of loci from both parents that are phased
// For example, if there are 10 loci for this trait, and one parent has 10 phased loci and the other has 5,
// this variable will have a value of 15
QuantityOfParentalPhasedLoci int
}
type OffspringGenomePairDiscreteTraitInfo_Rules struct{
// Map Structure: Outcome Name -> Outcome Probability (0-100)
// Example: "Intolerant" -> 5
OffspringOutcomeProbabilitiesMap map[string]int
// Map Structure: Outcome Name -> Outcome Score
// Example: "Intolerant" -> 2.5
OffspringAverageOutcomeScoresMap map[string]float64
// Map Structure: Rule Identifier -> Offspring Probability Of Passing Rule
// The value stores the probability that the offspring will pass the rule
// This is a number between 0-100%
ProbabilityOfPassingRulesMap map[[3]byte]int
// This should be len(ProbabilityOfPassingRulesMap)
QuantityOfRulesTested int
// This only counts the loci which are used for rules
// For example, loci that are only used in neural-network-based prediction are not counted
QuantityOfLociKnown int
}
type OffspringNumericTraitInfo struct{
// This map stores the trait info for each genome pair
// Map Structure: Genome Pair Identifier -> OffspringGenomePairNumericTraitInfo
TraitInfoMap map[[32]byte]OffspringGenomePairNumericTraitInfo
ConflictExists bool
}
type OffspringGenomePairNumericTraitInfo struct{
// The average outcome for the offspring
// For example, the average height for an offspring between these 2 people
OffspringAverageOutcome float64
// This map stores the confidence ranges for the predicted value
// If we want to know how accurate the prediction is with a X% accuracy, how far would we have to expand the
// predicted value's range to be accurate, X% of the time?
// For example: 50% accuracy requires a +/-5 point range, 80% accuracy requires a +-15 point range
// Map Structure: Accuracy probability (0-100) -> Amount to add to value in both +/- directions so prediction is that accurate
AverageConfidenceRangesMap map[int]float64
// This describes the quantity of loci from both parents that are phased
// For example, if there are 10 loci for this trait, and one parent has 10 phased loci and the other has 5,
// this variable will have a value of 15
QuantityOfParentalPhasedLoci int
QuantityOfLociKnown int
// A list of 100 offspring outcomes for 100 prospective offspring from the genome pair
// Example: A list of heights for 100 prospective offspring
SampleOffspringOutcomesList []float64
}

View file

@ -11,7 +11,6 @@ package geneticPrediction
// We could create slower models that provide more accurate predictions
import "seekia/resources/geneticReferences/traits"
import "seekia/resources/geneticPredictionModels"
import "seekia/internal/genetics/locusValue"
import "seekia/internal/genetics/readBiobankData"
@ -212,252 +211,6 @@ func DecodeBytesToNeuralNetworkObject(inputNeuralNetwork []byte)(NeuralNetwork,
return newNeuralNetworkObject, nil
}
// This map is used to store information about how accurate genetic prediction models are
// Map Structure: Trait Outcome Info -> Trait Prediction Accuracy Info
type TraitPredictionAccuracyInfoMap map[TraitOutcomeInfo]TraitPredictionAccuracyInfo
type TraitOutcomeInfo struct{
// This is the outcome which was found
// Example: "Blue"
OutcomeName string
// This is a value between 0-100 which describes the percentage of the loci which were tested for the input for the prediction
PercentageOfLociTested int
// This is a value between 0-100 which describes the percentage of the tested loci which were phased for the input for the prediction
PercentageOfPhasedLoci int
}
type TraitPredictionAccuracyInfo struct{
// This contains the quantity of examples for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
QuantityOfExamples int
// This contains the quantity of predictions for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
// Prediction = our model predicted this outcome
QuantityOfPredictions int
// This stores the probability (0-100) that our model will accurately predict this outcome for a genome which has
// the specified percentageOfLociTested and percentageOfPhasedLoci
// In other words: What is the probability that if you give Seekia a blue-eyed genome, it will give you a correct Blue prediction?
// This value is only accurate is QuantityOfExamples > 0
ProbabilityOfCorrectGenomePrediction int
// This stores the probability (0-100) that our model is correct if our model predicts that a genome
// with the specified percentageOfLociTested and percentageOfPhasedLoci has this outcome
// In other words: What is the probability that if Seekia says a genome will have blue eyes, it is correct?
// This value is only accurate is QuantityOfPredictions > 0
ProbabilityOfCorrectOutcomePrediction int
}
func EncodeTraitPredictionAccuracyInfoMapToBytes(inputMap TraitPredictionAccuracyInfoMap)([]byte, error){
buffer := new(bytes.Buffer)
encoder := gob.NewEncoder(buffer)
err := encoder.Encode(inputMap)
if (err != nil) { return nil, err }
inputMapBytes := buffer.Bytes()
return inputMapBytes, nil
}
func DecodeBytesToTraitPredictionAccuracyInfoMap(inputBytes []byte)(TraitPredictionAccuracyInfoMap, error){
if (inputBytes == nil){
return nil, errors.New("DecodeBytesToTraitPredictionAccuracyInfoMap called with nil inputBytes.")
}
buffer := bytes.NewBuffer(inputBytes)
decoder := gob.NewDecoder(buffer)
var newTraitPredictionAccuracyInfoMap TraitPredictionAccuracyInfoMap
err := decoder.Decode(&newTraitPredictionAccuracyInfoMap)
if (err != nil){ return nil, err }
return newTraitPredictionAccuracyInfoMap, nil
}
//Outputs:
// -bool: Neural network model exists for this trait (trait prediction is possible for this trait)
// -bool: Trait prediction is possible for this user (User has at least 1 known trait locus value)
// -string: Predicted trait outcome (Example: "Blue")
// -int: Confidence: Probability (0-100) that the prediction is accurate
// -int: Quantity of loci known
// -int: Quantity of phased loci
// -error
func GetNeuralNetworkTraitPredictionFromGenomeMap(traitName string, genomeMap map[int64]locusValue.LocusValue)(bool, bool, string, int, int, int, error){
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, false, "", 0, 0, 0, err }
// This is a map of rsIDs which influence this trait
traitRSIDsList := traitObject.LociList
if (len(traitRSIDsList) == 0){
// Neural network trait prediction is not possible for this trait
return false, false, "", 0, 0, 0, nil
}
predictionModelExists, predictionModelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (predictionModelExists == false){
// Neural network trait prediction is not possible for this trait
return false, false, "", 0, 0, 0, nil
}
traitRSIDsListCopy := slices.Clone(traitRSIDsList)
slices.Sort(traitRSIDsListCopy)
// In the inputLayer, each locus value is represented by 3 neurons:
// 1. LocusExists/LocusIsPhased
// -0 = Locus value is unknown
// -0.5 = Locus Is known, phase is unknown
// -1 = Locus Is Known, phase is known
// 2. Allele1 Locus Value (Value between 0-1)
// -0 = Value is unknown
// 3. Allele2 Locus Value (Value between 0-1)
// -0 = Value is unknown
//
neuralNetworkInput := make([]float32, 0)
quantityOfLociKnown := 0
quantityOfPhasedLoci := 0
for _, rsID := range traitRSIDsListCopy{
userLocusValue, exists := genomeMap[rsID]
if (exists == false){
neuralNetworkInput = append(neuralNetworkInput, 0, 0, 0)
continue
}
quantityOfLociKnown += 1
locusAllele1 := userLocusValue.Base1Value
locusAllele2 := userLocusValue.Base2Value
locusIsPhased := userLocusValue.LocusIsPhased
getNeuron1 := func()float32{
if (locusIsPhased == false){
return 0.5
}
quantityOfPhasedLoci += 1
return 1
}
neuron1 := getNeuron1()
neuron2, err := convertAlleleToNeuron(locusAllele1)
if (err != nil) { return false, false, "", 0, 0, 0, err }
neuron3, err := convertAlleleToNeuron(locusAllele2)
if (err != nil) { return false, false, "", 0, 0, 0, err }
neuralNetworkInput = append(neuralNetworkInput, neuron1, neuron2, neuron3)
}
if (quantityOfLociKnown == 0){
// We can't predict anything about this trait for this genome
return true, false, "", 0, 0, 0, nil
}
neuralNetworkObject, err := DecodeBytesToNeuralNetworkObject(predictionModelBytes)
if (err != nil) { return false, false, "", 0, 0, 0, err }
outputLayer, err := GetNeuralNetworkRawPrediction(&neuralNetworkObject, neuralNetworkInput)
if (err != nil) { return false, false, "", 0, 0, 0, err }
predictedOutcomeName, err := GetOutcomeNameFromOutputLayer(traitName, false, outputLayer)
if (err != nil) { return false, false, "", 0, 0, 0, err }
modelTraitAccuracyInfoFile, err := geneticPredictionModels.GetPredictionModelTraitAccuracyInfoBytes(traitName)
if (err != nil) { return false, false, "", 0, 0, 0, err }
modelTraitAccuracyInfoMap, err := DecodeBytesToTraitPredictionAccuracyInfoMap(modelTraitAccuracyInfoFile)
if (err != nil) { return false, false, "", 0, 0, 0, err }
// We find the model trait accuracy info object that is the most similar to our predicted outcome
getPredictionAccuracy := func()int{
totalNumberOfTraitLoci := len(traitRSIDsList)
proportionOfLociTested := float64(quantityOfLociKnown)/float64(totalNumberOfTraitLoci)
percentageOfLociTested := int(proportionOfLociTested * 100)
proportionOfPhasedLoci := float64(quantityOfPhasedLoci)/float64(totalNumberOfTraitLoci)
percentageOfPhasedLoci := int(proportionOfPhasedLoci * 100)
// This is a value between 0 and 100 that represents the most likely accuracy probability for this prediction
closestPredictionAccuracy := 0
// This is a value that represents the distance our closest prediction accuracy has from the current prediction
// Consider each prediction accuracy value on an (X,Y) coordinate plane
// X = Number of loci tested
// Y = Number of phased loci
closestPredictionAccuracyDistance := float64(0)
anyOutcomeAccuracyFound := false
for traitOutcomeInfo, traitPredictionAccuracyInfo := range modelTraitAccuracyInfoMap{
outcomeName := traitOutcomeInfo.OutcomeName
if (outcomeName != predictedOutcomeName){
continue
}
probabilityOfCorrectOutcomePrediction := traitPredictionAccuracyInfo.ProbabilityOfCorrectOutcomePrediction
currentPercentageOfLociTested := traitOutcomeInfo.PercentageOfLociTested
currentPercentageOfPhasedLoci := traitOutcomeInfo.PercentageOfPhasedLoci
// Distance Formula for 2 coordinates (x1, y1) and (x2, y2):
// distance = √((x2 - x1)^2 + (y2 - y1)^2)
differenceInX := float64(currentPercentageOfLociTested - percentageOfLociTested)
differenceInY := float64(currentPercentageOfPhasedLoci - percentageOfPhasedLoci)
distance := math.Sqrt(math.Pow(differenceInX, 2) + math.Pow(differenceInY, 2))
if (distance == 0){
// We found the exact prediction accuracy
return probabilityOfCorrectOutcomePrediction
}
if (anyOutcomeAccuracyFound == false){
closestPredictionAccuracyDistance = distance
closestPredictionAccuracy = probabilityOfCorrectOutcomePrediction
anyOutcomeAccuracyFound = true
continue
} else {
if (distance < closestPredictionAccuracyDistance){
closestPredictionAccuracyDistance = distance
closestPredictionAccuracy = probabilityOfCorrectOutcomePrediction
}
}
}
if (anyOutcomeAccuracyFound == false){
// This means that our model has never actually predicted this outcome
// This shouldn't happen unless our model is really bad, or our training set has very few people with this outcome.
// We return a 0% accuracy rating
return 0
}
return closestPredictionAccuracy
}
predictionAccuracy := getPredictionAccuracy()
return true, true, predictedOutcomeName, predictionAccuracy, quantityOfLociKnown, quantityOfPhasedLoci, nil
}
//Outputs:
// -int: Number of loci values that are known
// -int: Number of loci values that are known and phased
@ -684,9 +437,9 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
if (err != nil) { return false, nil, err }
// This is a list of rsIDs which influence this trait
traitRSIDsList := traitObject.LociList
traitRSIDs := traitObject.LociList
if (len(traitRSIDsList) == 0){
if (len(traitRSIDs) == 0){
return false, nil, errors.New("traitObject contains no rsIDs.")
}
@ -704,7 +457,7 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
// -0 = Locus value is unknown
// -0.5 = Locus Is known, phase is unknown
// -1 = Locus Is Known, phase is known
expectedNumberOfInputLayerRows := len(traitRSIDsList) * 3
expectedNumberOfInputLayerRows := len(traitRSIDs) * 3
if (numberOfInputLayerRows != expectedNumberOfInputLayerRows){
@ -715,7 +468,7 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
checkIfAnyTraitLocusValuesExist := func()bool{
for _, rsID := range traitRSIDsList{
for _, rsID := range traitRSIDs{
_, exists := userLocusValuesMap[rsID]
if (exists == true){
@ -734,9 +487,11 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
}
// We sort rsIDs in ascending order
// We copy list so we don't change the original
traitRSIDsListCopy := slices.Clone(traitRSIDsList)
slices.Sort(traitRSIDsListCopy)
traitRSIDsList := slices.Clone(traitRSIDs)
slices.Sort(traitRSIDsList)
// This function returns the outputLayer for all trainingDatas for this user
// Each outputLayer represents the user's trait value (Example: "Blue" for Eye Color)
@ -882,11 +637,11 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
anyLocusExists := false
inputLayerLength := len(traitRSIDsListCopy) * 3
inputLayerLength := len(traitRSIDsList) * 3
inputLayer := make([]float32, 0, inputLayerLength)
for _, rsID := range traitRSIDsListCopy{
for _, rsID := range traitRSIDsList{
randomFloat := pseudorandomNumberGenerator.Float64()
if (randomFloat > probabilityOfUsingLoci){
@ -1241,7 +996,7 @@ func (inputNetwork *NeuralNetwork)buildNeuralNetwork(inputLayer *gorgonia.Node)e
inputLayerCopy := inputLayer
// We multiply weights at each layer and perform ReLU (Rectification) after each multiplication
// We multiply weights at each layer and perform sigmoid after each multiplication
weights1 := inputNetwork.weights1

View file

@ -27,6 +27,7 @@ var myCacheChosenGeneticAnalysisIdentifier string
// We use this variable to store the analysis in memory
// This prevents us from having to read and unmarshal the messagepack file each time we want to retrieve the analysis
// TODO: Read attributes from the analysis into maps for faster retrieval
var myCacheChosenGeneticAnalysis geneticAnalysis.PersonAnalysis
// These variables store metadata about the cache genetic analysis
@ -35,7 +36,6 @@ var myCacheChosenGeneticAnalysis_GenomeIdentifierToUse [16]byte
// This variable tells us if the current cache chosen genetic analysis contains multiple genomes
var myCacheChosenGeneticAnalysis_MultipleGenomesExist bool
// This function is used to retrieve the user's chosen person genetic analysis
// The user must choose a person to link to their mate profile/identity
// This genetic analysis is not shared on the person's profile publicly.
@ -101,7 +101,7 @@ func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, geneticAnalysis.PersonAn
return false, false, false, emptyPersonAnalysis, [16]byte{}, false, errors.New("CheckIfPersonAnalysisIsReady returning missing genetic analysis.")
}
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err }
getGenomeIdentifierToUse := func()([16]byte, error){

View file

@ -6,7 +6,6 @@ package myGenomes
import "seekia/internal/cryptography/blake3"
import "seekia/internal/encoding"
import "seekia/internal/genetics/prepareRawGenomes"
import "seekia/internal/genetics/readRawGenomes"
import "seekia/internal/helpers"
import "seekia/internal/localFilesystem"
@ -107,18 +106,11 @@ func AddRawGenome(personIdentifier string, rawGenomeString string)(bool, bool, e
rawGenomeReader := strings.NewReader(rawGenomeString)
companyName, importVersion, timeFileWasGenerated, snpCount, genomeIsPhased, rawGenomeMap, err := readRawGenomes.ReadRawGenomeFile(rawGenomeReader)
companyName, importVersion, timeFileWasGenerated, snpCount, genomeIsPhased, _, err := readRawGenomes.ReadRawGenomeFile(rawGenomeReader)
if (err != nil){
return false, false, nil
}
genomeHasUsefulLocations, _, err := prepareRawGenomes.ConvertRawGenomeToGenomeMap(rawGenomeMap, genomeIsPhased)
if (err != nil) { return false, false, err }
if (genomeHasUsefulLocations == false){
//TODO: Explain this to the user rather than just telling the user that the file is invalid
return false, false, nil
}
genomeIdentifier, err := helpers.GetNewRandomHexString(16)
if (err != nil) { return false, false, err }

View file

@ -76,10 +76,6 @@ func CreateRawGenomeWithMetadataObject(genomeIdentifier [16]byte, rawGenomeStrin
// -error
func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error)([]GenomeWithMetadata, [][16]byte, bool, [16]byte, [16]byte, error){
if (len(inputGenomesList) == 0){
return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("GetGenomesWithMetadataListFromRawGenomesList called with empty inputGenomesList")
}
// The reading of genomes will take up the first 20% of the percentage range
// The creation of multiple genomes will take up the last 80% of the percentage range
@ -106,13 +102,103 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
// Now we convert rawGenomeMap to a genomeMap
anyValuesExist, genomeMap, err := ConvertRawGenomeToGenomeMap(rawGenomeMap, genomeIsPhased)
if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err }
if (anyValuesExist == false){
// We have to make sure this never happens so the user isn't confused as to why genomes
// that were imported were not included in the analysis
// We make sure this doesn't happen by verifying the genome at the time of importing
return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("Genome supplied to GetGenomesWithMetadataListFromRawGenomesList has no valid locations.")
// Map Structure: RSID -> Locus Value
genomeMap := make(map[int64]locusValue.LocusValue)
// We use this list to check for alias collisions later
allRSIDsList := make([]int64, 0)
for rsID, locusBasePairValue := range rawGenomeMap{
locusAllele2Exists := locusBasePairValue.Allele2Exists
if (locusAllele2Exists == false){
// This SNP contains less than 2 bases
// We don't support reading these kinds of SNP values yet
continue
}
locusAllele1 := locusBasePairValue.Allele1
locusAllele2 := locusBasePairValue.Allele2
getLocusIsPhasedBool := func()bool{
if (locusAllele1 == locusAllele2){
// Locus has to be phased, because phase flip does not change value
return true
}
return genomeIsPhased
}
locusIsPhased := getLocusIsPhasedBool()
locusValueObject := locusValue.LocusValue{
LocusIsPhased: locusIsPhased,
Base1Value: locusAllele1,
Base2Value: locusAllele2,
}
genomeMap[rsID] = locusValueObject
allRSIDsList = append(allRSIDsList, rsID)
}
// Now we check for rsID aliases
// rsID aliases are multiple rsids that refer to the same location
// If a single genome file contained two identical rsids whose value conflicted, the company must have made an error in reporting
for _, rsID := range allRSIDsList{
rsidLocusValue, exists := genomeMap[rsID]
if (exists == false){
// This must have been an alias that was deleted
continue
}
aliasExists, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID)
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
if (aliasExists == false){
continue
}
checkIfRSIDCollisionExists := func()bool{
for _, rsidAlias := range rsidAliasesList{
aliasLocusValue, exists := genomeMap[rsidAlias]
if (exists == false){
continue
}
if (aliasLocusValue != rsidLocusValue){
// A collision exists with an alias rsID
// The company must be creating invalid results, or our alias list is invalid
return true
}
}
return false
}
rsidCollisionExists := checkIfRSIDCollisionExists()
if (rsidCollisionExists == true){
// We will delete this rsID
// We cannot trust any of the results
delete(genomeMap, rsID)
}
// We delete all aliases from the genome map
// We do this to save space
for _, rsidAlias := range rsidAliasesList{
delete(genomeMap, rsidAlias)
}
}
if (len(genomeMap) == 0){
// No valid locations exist
continue
}
genomeWithMetadataObject := GenomeWithMetadata{
@ -135,9 +221,9 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
err := updatePercentageCompleteFunction(20)
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
if (len(genomesWithMetadataList) <= 1){
if (len(genomesWithMetadataList) == 1){
// <=1 genome exists.
// Only 1 genome exists.
// No genome combining is needed.
err = updatePercentageCompleteFunction(100)
@ -461,112 +547,4 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
return genomesWithMetadataList, allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil
}
//Outputs:
// -bool: Genome has any useful locations
// -map[int64]locusValue.LocusValue
// -error
func ConvertRawGenomeToGenomeMap(rawGenomeMap map[int64]readRawGenomes.RawGenomeLocusValue, genomeIsPhased bool)(bool, map[int64]locusValue.LocusValue, error){
// Map Structure: RSID -> Locus Value
genomeMap := make(map[int64]locusValue.LocusValue)
// We use this list to check for alias collisions later
allRSIDsList := make([]int64, 0)
for rsID, locusBasePairValue := range rawGenomeMap{
locusAllele2Exists := locusBasePairValue.Allele2Exists
if (locusAllele2Exists == false){
// This SNP contains less than 2 bases
// We don't support reading these kinds of SNP values yet
continue
}
locusAllele1 := locusBasePairValue.Allele1
locusAllele2 := locusBasePairValue.Allele2
getLocusIsPhasedBool := func()bool{
if (locusAllele1 == locusAllele2){
// Locus has to be phased, because phase flip does not change value
return true
}
return genomeIsPhased
}
locusIsPhased := getLocusIsPhasedBool()
locusValueObject := locusValue.LocusValue{
Base1Value: locusAllele1,
Base2Value: locusAllele2,
LocusIsPhased: locusIsPhased,
}
genomeMap[rsID] = locusValueObject
allRSIDsList = append(allRSIDsList, rsID)
}
// Now we check for rsID aliases
// rsID aliases are multiple rsids that refer to the same location
// If a single genome file contained two identical rsids whose value conflicted, the company must have made an error in reporting
for _, rsID := range allRSIDsList{
rsidLocusValue, exists := genomeMap[rsID]
if (exists == false){
// This must have been an alias that was deleted
continue
}
aliasExists, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID)
if (err != nil){ return false, nil, err }
if (aliasExists == false){
continue
}
checkIfRSIDCollisionExists := func()bool{
for _, rsidAlias := range rsidAliasesList{
aliasLocusValue, exists := genomeMap[rsidAlias]
if (exists == false){
continue
}
if (aliasLocusValue != rsidLocusValue){
// A collision exists with an alias rsID
// The company must be creating invalid results, or our alias list is invalid
return true
}
}
return false
}
rsidCollisionExists := checkIfRSIDCollisionExists()
if (rsidCollisionExists == true){
// We will delete this rsID
// We cannot trust any of the results
delete(genomeMap, rsID)
}
// We delete all aliases from the genome map
// We do this to save space
for _, rsidAlias := range rsidAliasesList{
delete(genomeMap, rsidAlias)
}
}
if (len(genomeMap) == 0){
// No valid locations exist
return false, nil, nil
}
return true, genomeMap, nil
}

View file

@ -47,30 +47,27 @@ func ReadCoupleGeneticAnalysisString(inputAnalysisString string)(geneticAnalysis
// -bool: Multiple genomes exist
// -[16]byte: OnlyExcludeConflicts GenomeIdentifier
// -[16]byte: OnlyIncludeShared GenomeIdentifier
// -map[[16]byte]map[int64]locusValue.LocusValue: Genomes locus values map
// -error
func GetMetadataFromPersonGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.PersonAnalysis)([][16]byte, bool, [16]byte, [16]byte, map[[16]byte]map[int64]locusValue.LocusValue, error){
func GetMetadataFromPersonGeneticAnalysis(inputGeneticAnalysis geneticAnalysis.PersonAnalysis)([][16]byte, bool, [16]byte, [16]byte, error){
analysisVersion := inputGeneticAnalysis.AnalysisVersion
if (analysisVersion != 1){
// This analysis must have been created by a newer version of Seekia
// We cannot read it
return nil, false, [16]byte{}, [16]byte{}, nil, errors.New("Cannot read analysis: Is a newer analysis version.")
return nil, false, [16]byte{}, [16]byte{}, errors.New("Cannot read analysis: Is a newer analysis version.")
}
allRawGenomeIdentifiersList := inputGeneticAnalysis.AllRawGenomeIdentifiersList
genomesMap := inputGeneticAnalysis.GenomesMap
combinedGenomesExist := inputGeneticAnalysis.CombinedGenomesExist
if (combinedGenomesExist == false){
return allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, genomesMap, nil
return allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, nil
}
onlyExcludeConflictsGenomeIdentifier := inputGeneticAnalysis.OnlyExcludeConflictsGenomeIdentifier
onlyIncludeSharedGenomeIdentifier := inputGeneticAnalysis.OnlyIncludeSharedGenomeIdentifier
return allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, genomesMap, nil
return allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil
}
//Outputs:
@ -181,7 +178,7 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool,
return inputGenomeIdentifier, true, false, "", nil
}
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(person1AnalysisObject)
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(person1AnalysisObject)
if (err != nil) { return [16]byte{}, false, false, "", err }
if (multipleGenomesExist == false){
return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.")
@ -207,7 +204,7 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool,
return inputGenomeIdentifier, true, false, "", nil
}
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(person2AnalysisObject)
_, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(person2AnalysisObject)
if (err != nil) { return [16]byte{}, false, false, "", err }
if (multipleGenomesExist == false){
return [16]byte{}, false, false, "", errors.New("Couple analysis says person has multiple genomes, person analysis does not.")
@ -229,9 +226,9 @@ func GetMatchingPersonAnalysisGenomeIdentifierFromCoupleAnalysis(isPerson1 bool,
// -bool: Person has disease
// -int: Probability of passing a disease variant
// -string: Probability of passing a disease variant formatted (with % suffix)
// -int: Quantity of variants tested
// -int: Quantity of loci tested
// -int: Quantity of phased loci
// -int: Number of variants tested
// -int: Number of loci tested
// -int: Number of phased loci
// -bool: Conflict exists
// -error
func GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, bool, int, string, int, int, int, bool, error){
@ -253,16 +250,16 @@ func GetPersonMonogenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject genet
conflictExists := personMonogenicDiseaseInfo.ConflictExists
personHasDisease := genomeMonogenicDiseaseInfo.PersonHasDisease
quantityOfVariantsTested := genomeMonogenicDiseaseInfo.QuantityOfVariantsTested
quantityOfLociTested := genomeMonogenicDiseaseInfo.QuantityOfLociTested
quantityOfPhasedLoci := genomeMonogenicDiseaseInfo.QuantityOfPhasedLoci
numberOfVariantsTested := genomeMonogenicDiseaseInfo.NumberOfVariantsTested
numberOfLociTested := genomeMonogenicDiseaseInfo.NumberOfLociTested
numberOfPhasedLoci := genomeMonogenicDiseaseInfo.NumberOfPhasedLoci
probabilityOfPassingAVariant := genomeMonogenicDiseaseInfo.ProbabilityOfPassingADiseaseVariant
probabilityOfPassingAVariantString := helpers.ConvertIntToString(probabilityOfPassingAVariant)
probabilityOfPassingAVariantFormatted := probabilityOfPassingAVariantString + "%"
return true, personHasDisease, probabilityOfPassingAVariant, probabilityOfPassingAVariantFormatted, quantityOfVariantsTested, quantityOfLociTested, quantityOfPhasedLoci, conflictExists, nil
return true, personHasDisease, probabilityOfPassingAVariant, probabilityOfPassingAVariantFormatted, numberOfVariantsTested, numberOfLociTested, numberOfPhasedLoci, conflictExists, nil
}
@ -488,23 +485,24 @@ func GetOffspringMonogenicDiseaseVariantInfoFromGeneticAnalysis(coupleAnalysisOb
// -bool: Polygenic Disease Risk Score known (any loci values exist)
// -int: Person Disease risk score
// -string: Person Disease risk score formatted (has "/10" suffix)
// -int: Quantity of loci tested
// -map[int]locusValue.LocusValue: Person locus values map
// -int: Number of loci tested
// -bool: Conflict exists
// -error
func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, string, int, bool, error){
func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, diseaseName string, genomeIdentifier [16]byte)(bool, int, string, map[int64]locusValue.LocusValue, int, bool, error){
personPolygenicDiseasesMap := personAnalysisObject.PolygenicDiseasesMap
personPolygenicDiseaseInfo, exists := personPolygenicDiseasesMap[diseaseName]
if (exists == false){
return false, 0, "", 0, false, nil
return false, 0, "", nil, 0, false, nil
}
personPolygenicDiseaseInfoMap := personPolygenicDiseaseInfo.PolygenicDiseaseInfoMap
genomePolygenicDiseaseInfo, exists := personPolygenicDiseaseInfoMap[genomeIdentifier]
if (exists == false){
return false, 0, "", 0, false, nil
return false, 0, "", nil, 0, false, nil
}
conflictExists := personPolygenicDiseaseInfo.ConflictExists
@ -515,9 +513,11 @@ func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject genet
personDiseaseRiskScoreFormatted := personDiseaseRiskScoreString + "/10"
quantityOfLociTested := genomePolygenicDiseaseInfo.QuantityOfLociTested
personLocusValuesMap := genomePolygenicDiseaseInfo.LocusValuesMap
return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, quantityOfLociTested, conflictExists, nil
numberOfLociTested := genomePolygenicDiseaseInfo.NumberOfLociTested
return true, personDiseaseRiskScore, personDiseaseRiskScoreFormatted, personLocusValuesMap, numberOfLociTested, conflictExists, nil
}
@ -526,7 +526,7 @@ func GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject genet
// -int: Offspring average disease risk score
// -string: Offspring Disease average risk score formatted (has "/10" suffix)
// -[]int: Sample Offspring Risk Scores List
// -int: Quantity of loci tested
// -int: Number of loci tested
// -bool: Conflict exists
// -error
func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, diseaseName string, genomePairIdentifier [32]byte)(bool, int, string, []int, int, bool, error){
@ -547,7 +547,7 @@ func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject ge
conflictExists := couplePolygenicDiseaseInfo.ConflictExists
quantityOfLociTested := genomePairPolygenicDiseaseInfo.QuantityOfLociTested
numberOfLociTested := genomePairPolygenicDiseaseInfo.NumberOfLociTested
offspringAverageRiskScore := genomePairPolygenicDiseaseInfo.OffspringAverageRiskScore
@ -557,7 +557,7 @@ func GetOffspringPolygenicDiseaseInfoFromGeneticAnalysis(coupleAnalysisObject ge
sampleOffspringRiskScoresList := genomePairPolygenicDiseaseInfo.SampleOffspringRiskScoresList
return true, offspringAverageRiskScore, offspringAverageRiskScoreFormatted, sampleOffspringRiskScoresList, quantityOfLociTested, conflictExists, nil
return true, offspringAverageRiskScore, offspringAverageRiskScoreFormatted, sampleOffspringRiskScoresList, numberOfLociTested, conflictExists, nil
}
//Outputs:
@ -673,169 +673,71 @@ func GetOffspringPolygenicDiseaseLocusInfoFromGeneticAnalysis(coupleAnalysisObje
}
//Outputs:
// -bool: Neural network exists
// -bool: Any neural network analysis exists
// -string: Neural network predicted outcome
// -int: Prediction confidence
// -int: Quantity of loci known (Neural network)
// -int: Quantity of phased loci (Neural network)
// -bool: Any trait rules exist (Rule-based analysis is possible)
// -map[int64]locusValue.LocusValue (rsID -> Base pair) (missing rsIDs represent unknown values)
// -bool: Any Trait Rule known/tested
// -map[[3]byte]bool: Rule Identifier -> Person passes rule
// -bool: Predicted outcome exists
// -string: Predicted outcome
// -int: Quantity of rules tested
// -int: Quantity Of Loci Known (Rules)
// -bool: Conflict exists (between any of these results for each genome)
// -map[string]int: Trait outcomes scores map (Outcome -> Number of points)
// -int: Number of rules tested
// -bool: Conflict exists
// -error
func GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte)(bool, bool, string, int, int, int, bool, bool, map[[3]byte]bool, bool, string, int, int, bool, error){
func GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, genomeIdentifier [16]byte)(map[int64]locusValue.LocusValue, bool, map[string]int, int, bool, error){
personTraitsMap := personAnalysisObject.DiscreteTraitsMap
personTraitsMap := personAnalysisObject.TraitsMap
personTraitInfoObject, exists := personTraitsMap[traitName]
if (exists == false){
return false, false, "", 0, 0, 0, false, false, nil, false, "", 0, 0, false, errors.New("Person trait analysis is missing trait: " + traitName)
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, false, nil, 0, false, nil
}
personTraitInfoMap := personTraitInfoObject.TraitInfoMap
conflictExists := personTraitInfoObject.ConflictExists
personGenomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier]
if (exists == false){
return false, false, "", 0, 0, 0, false, false, nil, false, "", 0, 0, false, errors.New("personTraitInfoMap in Person analysis is missing map for genome identifier.")
emptyMap := make(map[int64]locusValue.LocusValue)
return emptyMap, false, nil, 0, false, nil
}
neuralNetworkExists := personGenomeTraitInfoObject.NeuralNetworkExists
neuralNetworkAnalysisExists := personGenomeTraitInfoObject.NeuralNetworkAnalysisExists
conflictExists := personTraitInfoObject.ConflictExists
getNeuralNetworkAnalysisInfo := func()(string, int, int, int){
genomeNumberOfRulesTested := personGenomeTraitInfoObject.NumberOfRulesTested
if (neuralNetworkExists == false || neuralNetworkAnalysisExists == false){
return "", 0, 0, 0
}
genomeLocusValuesMap := personGenomeTraitInfoObject.LocusValuesMap
neuralNetworkAnalysis := personGenomeTraitInfoObject.NeuralNetworkAnalysis
genomeOutcomeScoresMap := personGenomeTraitInfoObject.OutcomeScoresMap
predictedOutcome := neuralNetworkAnalysis.PredictedOutcome
predictionConfidence := neuralNetworkAnalysis.PredictionConfidence
quantityOfLociKnown := neuralNetworkAnalysis.QuantityOfLociKnown
quantityOfPhasedLoci := neuralNetworkAnalysis.QuantityOfPhasedLoci
return predictedOutcome, predictionConfidence, quantityOfLociKnown, quantityOfPhasedLoci
}
neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, quantityOfPhasedLoci_NeuralNetwork :=
getNeuralNetworkAnalysisInfo()
anyRulesExist := personGenomeTraitInfoObject.AnyRulesExist
rulesAnalysisExists := personGenomeTraitInfoObject.RulesAnalysisExists
getTraitAnalysisInfo := func()(map[[3]byte]bool, bool, string, int, int){
if (anyRulesExist == false || rulesAnalysisExists == false){
return nil, false, "", 0, 0
}
rulesAnalysisObject := personGenomeTraitInfoObject.RulesAnalysis
genomePassesRulesMap := rulesAnalysisObject.GenomePassesRulesMap
predictedOutcomeExists := rulesAnalysisObject.PredictedOutcomeExists
predictedOutcome := rulesAnalysisObject.PredictedOutcome
quantityOfRulesTested := rulesAnalysisObject.QuantityOfRulesTested
quantityOfLociKnown := rulesAnalysisObject.QuantityOfLociKnown
return genomePassesRulesMap, predictedOutcomeExists, predictedOutcome, quantityOfRulesTested, quantityOfLociKnown
}
genomePassesRulesMap, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, quantityOfLociKnown_Rules := getTraitAnalysisInfo()
return neuralNetworkExists, neuralNetworkAnalysisExists, neuralNetworkPredictedOutcome, neuralNetworkPredictionConfidence, quantityOfLociKnown_NeuralNetwork, quantityOfPhasedLoci_NeuralNetwork, anyRulesExist, rulesAnalysisExists, genomePassesRulesMap, rulesPredictedOutcomeExists, rulesPredictedOutcome, quantityOfRulesTested, quantityOfLociKnown_Rules, conflictExists, nil
return genomeLocusValuesMap, true, genomeOutcomeScoresMap, genomeNumberOfRulesTested, conflictExists, nil
}
//Outputs:
// -bool: Neural network exists
// -bool: Neural network analysis exists
// -map[string]int: Offspring outcome probabilities map for neural network prediction
// -Map Structure: Outcome name -> Probability of outcome (0-100)
// -int: Average confidence (for neural network prediction)
// -int: Quantity of loci known (for neural network)
// -int: Quantity of Parental phased loci
// -bool: Any Rules exist
// -bool: Rules analysis exists
// -map[string]int: Offspring outcome probabilities map for rules-based prediction
// -Map Structure: Outcome name -> Probability of outcome (0-100)
// -map[[3]byte]int: Offspring probability of passing rules map
// -Map Structure: Rule Identifier -> Probability of passing rule (0-100)
// -int: Quantity of rules tested
// -int: Quantity of loci known (For Rules)
// -bool: Conflict exists (Between this genome pair and other genome pairs)
// -error
func GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte)(bool, bool, map[string]int, int, int, int, bool, bool, map[string]int, map[[3]byte]int, int, int, bool, error){
offspringTraitsMap := coupleAnalysisObject.DiscreteTraitsMap
//Outputs:
// -bool: Trait Outcome Scores known
// -map[string]float64: Trait average outcome scores map (OutcomeName -> AverageScore)
// -int: Number of rules tested
// -bool: Conflict exists
// -error
func GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, genomePairIdentifier [32]byte)(bool, map[string]float64, int, bool, error){
offspringTraitsMap := coupleAnalysisObject.TraitsMap
traitInfoObject, exists := offspringTraitsMap[traitName]
if (exists == false){
return false, false, nil, 0, 0, 0, false, false, nil, nil, 0, 0, false, errors.New("offspringTraitsMap missing trait when reading couple genetic analysis: " + traitName)
return false, nil, 0, false, nil
}
traitInfoMap := traitInfoObject.TraitInfoMap
conflictExists := traitInfoObject.ConflictExists
genomePairTraitInfoObject, exists := traitInfoMap[genomePairIdentifier]
if (exists == false){
return false, false, nil, 0, 0, 0, false, false, nil, nil, 0, 0, false, errors.New("traitInfoMap missing trait info for genome pair when reading from genetic analysis.")
return false, nil, 0, false, nil
}
neuralNetworkExists := genomePairTraitInfoObject.NeuralNetworkExists
conflictExists := traitInfoObject.ConflictExists
neuralNetworkAnalysisExists := genomePairTraitInfoObject.NeuralNetworkAnalysisExists
numberOfRulesTested := genomePairTraitInfoObject.NumberOfRulesTested
offspringAverageOutcomeScoresMap := genomePairTraitInfoObject.OffspringAverageOutcomeScoresMap
getGenomePairTraitNeuralNetworkAnalysisInfo := func()(map[string]int, int, int, int){
if (neuralNetworkExists == false || neuralNetworkAnalysisExists == false){
return nil, 0, 0, 0
}
genomePairTraitNeuralNetworkInfo := genomePairTraitInfoObject.NeuralNetworkAnalysis
offspringOutcomeProbabilitiesMap := genomePairTraitNeuralNetworkInfo.OffspringOutcomeProbabilitiesMap
averageConfidence := genomePairTraitNeuralNetworkInfo.AverageConfidence
quantityOfLociKnown := genomePairTraitNeuralNetworkInfo.QuantityOfLociKnown
quantityOfParentalPhasedLoci := genomePairTraitNeuralNetworkInfo.QuantityOfParentalPhasedLoci
return offspringOutcomeProbabilitiesMap, averageConfidence, quantityOfLociKnown, quantityOfParentalPhasedLoci
}
offspringOutcomeProbabilitiesMap_NeuralNetwork, averageConfidence_NeuralNetwork, quantityOfLociKnown_NeuralNetwork, quantityOfParentalPhasedLoci_NeuralNetwork := getGenomePairTraitNeuralNetworkAnalysisInfo()
anyRulesExist := genomePairTraitInfoObject.RulesExist
rulesAnalysisExists := genomePairTraitInfoObject.RulesAnalysisExists
getGenomePairTraitRulesAnalysisInfo := func()(map[string]int, map[[3]byte]int, int, int){
if (anyRulesExist == false || rulesAnalysisExists == false){
return nil, nil, 0, 0
}
genomePairTraitInfo_Rules := genomePairTraitInfoObject.RulesAnalysis
offspringOutcomeProbabilitiesMap := genomePairTraitInfo_Rules.OffspringOutcomeProbabilitiesMap
probabilityOfPassingRulesMap := genomePairTraitInfo_Rules.ProbabilityOfPassingRulesMap
quantityOfRulesTested := genomePairTraitInfo_Rules.QuantityOfRulesTested
quantityOfLociKnown := genomePairTraitInfo_Rules.QuantityOfLociKnown
return offspringOutcomeProbabilitiesMap, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown
}
offspringOutcomeProbabilitiesMap_Rules, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown_Rules := getGenomePairTraitRulesAnalysisInfo()
return neuralNetworkExists, neuralNetworkAnalysisExists, offspringOutcomeProbabilitiesMap_NeuralNetwork, averageConfidence_NeuralNetwork, quantityOfLociKnown_NeuralNetwork, quantityOfParentalPhasedLoci_NeuralNetwork, anyRulesExist, rulesAnalysisExists, offspringOutcomeProbabilitiesMap_Rules, probabilityOfPassingRulesMap, quantityOfRulesTested, quantityOfLociKnown_Rules, conflictExists, nil
return true, offspringAverageOutcomeScoresMap, numberOfRulesTested, conflictExists, nil
}
@ -843,17 +745,24 @@ func GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject genet
// -bool: Rule status is known (we know if the rule is passed or not)
// -bool: Genome passes rule
// -error
func GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, genomeIdentifier [16]byte)(bool, bool, error){
func GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis, traitName string, ruleIdentifier [3]byte, genomeIdentifier [16]byte)(bool, bool, error){
_, _, _, _, _, _, anyRulesExist, rulesAnalysisExists, genomePassesRulesMap, _, _, _, _, _, err := GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
if (err != nil) { return false, false, err }
if (anyRulesExist == false){
return false, false, errors.New("GetPersonTraitRuleInfoFromGeneticAnalysis called when no trait rules exist.")
}
if (rulesAnalysisExists == false){
personTraitsMap := personAnalysisObject.TraitsMap
traitInfoObject, exists := personTraitsMap[traitName]
if (exists == false){
return false, false, nil
}
personTraitInfoMap := traitInfoObject.TraitInfoMap
genomeTraitInfoObject, exists := personTraitInfoMap[genomeIdentifier]
if (exists == false){
return false, false, nil
}
genomePassesRulesMap := genomeTraitInfoObject.GenomePassesRulesMap
genomePassesRule, statusIsKnown := genomePassesRulesMap[ruleIdentifier]
if (statusIsKnown == false){
return false, false, nil
@ -868,17 +777,24 @@ func GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(personAnalysisObject gene
// -int: Offspring probability of passing rule (0 - 100)
// -string: Offspring probability of passing rule formatted (with % suffix)
// -error
func GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, string, error){
func GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnalysis, traitName string, ruleIdentifier [3]byte, genomePairIdentifier [32]byte)(bool, int, string, error){
_, _, _, _, _, _, anyRulesExist, rulesAnalysisExists, _, offspringProbabilityOfPassingRulesMap, _, _, _, err := GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
if (err != nil) { return false, 0, "", err }
if (anyRulesExist == false){
return false, 0, "", errors.New("GetOffspringTraitRuleInfoFromGeneticAnalysis called for trait which has no rules: " + traitName)
}
if (rulesAnalysisExists == false){
offspringTraitsMap := coupleAnalysisObject.TraitsMap
offspringTraitInfo, exists := offspringTraitsMap[traitName]
if (exists == false){
return false, 0, "", nil
}
offspringTraitInfoMap := offspringTraitInfo.TraitInfoMap
offspringTraitInfoObject, exists := offspringTraitInfoMap[genomePairIdentifier]
if (exists == false){
return false, 0, "", nil
}
offspringProbabilityOfPassingRulesMap := offspringTraitInfoObject.ProbabilityOfPassingRulesMap
offspringProbabilityOfPassingRule, exists := offspringProbabilityOfPassingRulesMap[ruleIdentifier]
if (exists == false){
return false, 0, "", nil
@ -896,7 +812,7 @@ func GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject g
//TODO: Perform sanity checks on data
func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnalysis)error{
allRawGenomeIdentifiersList, personHasMultipleGenomes, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := GetMetadataFromPersonGeneticAnalysis(personAnalysisObject)
allRawGenomeIdentifiersList, personHasMultipleGenomes, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := GetMetadataFromPersonGeneticAnalysis(personAnalysisObject)
if (err != nil) { return err }
allGenomeIdentifiersList := allRawGenomeIdentifiersList
@ -943,7 +859,7 @@ func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnal
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier)
_, _, _, _, _, _, err := GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(personAnalysisObject, diseaseName, genomeIdentifier)
if (err != nil) { return err }
}
@ -970,31 +886,27 @@ func VerifyPersonGeneticAnalysis(personAnalysisObject geneticAnalysis.PersonAnal
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric == "Discrete"){
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, _, _, _, err := GetPersonTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
if (err != nil) { return err }
}
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, _, _, _, _, _, _, _, _, _, _, _, _, err := GetPersonDiscreteTraitInfoFromGeneticAnalysis(personAnalysisObject, traitName, genomeIdentifier)
_, _, err := GetPersonTraitRuleInfoFromGeneticAnalysis(personAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil) { return err }
}
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
for _, genomeIdentifier := range allGenomeIdentifiersList{
_, _, err := GetPersonDiscreteTraitRuleInfoFromGeneticAnalysis(personAnalysisObject, traitName, ruleIdentifier, genomeIdentifier)
if (err != nil) { return err }
}
}
}
}
@ -1086,31 +998,27 @@ func VerifyCoupleGeneticAnalysis(coupleAnalysisObject geneticAnalysis.CoupleAnal
for _, traitObject := range traitObjectsList{
traitName := traitObject.TraitName
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
if (traitIsDiscreteOrNumeric == "Discrete"){
for _, genomePairIdentifier := range allGenomePairIdentifiersList{
for _, genomePairIdentifier := range allGenomePairIdentifiersList{
_, _, _, _, err := GetOffspringTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
if (err != nil) { return err }
}
_, _, _, _, _, _, _, _, _, _, _, _, _, err := GetOffspringDiscreteTraitInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, genomePairIdentifier)
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
for _, genomePairIdentifier := range allGenomePairIdentifiersList{
_, _, _, err := GetOffspringTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
if (err != nil) { return err }
}
traitRulesList := traitObject.RulesList
for _, traitRuleObject := range traitRulesList{
ruleIdentifierHex := traitRuleObject.RuleIdentifier
ruleIdentifier, err := encoding.DecodeHexStringTo3ByteArray(ruleIdentifierHex)
if (err != nil) { return err }
for _, genomePairIdentifier := range allGenomePairIdentifiersList{
_, _, _, err := GetOffspringDiscreteTraitRuleInfoFromGeneticAnalysis(coupleAnalysisObject, traitName, ruleIdentifier, genomePairIdentifier)
if (err != nil) { return err }
}
}
}
}

View file

@ -128,44 +128,41 @@ func ReadRawGenomeFile(fileReader io.Reader) (string, int, int64, int64, bool, m
}
getMonthObject := func()(time.Month, error){
switch monthString{
case "01":{
return time.January, nil
}
case "02":{
return time.February, nil
}
case "03":{
return time.March, nil
}
case "04":{
return time.April, nil
}
case "05":{
return time.May, nil
}
case "06":{
return time.June, nil
}
case "07":{
return time.July, nil
}
case "08":{
return time.August, nil
}
case "09":{
return time.September, nil
}
case "10":{
return time.October, nil
}
case "11":{
return time.November, nil
}
case "12":{
return time.December, nil
}
if (monthString == "01"){
return time.January, nil
}
if (monthString == "02"){
return time.February, nil
}
if (monthString == "03"){
return time.March, nil
}
if (monthString == "04"){
return time.April, nil
}
if (monthString == "05"){
return time.May, nil
}
if (monthString == "06"){
return time.June, nil
}
if (monthString == "07"){
return time.July, nil
}
if (monthString == "08"){
return time.August, nil
}
if (monthString == "09"){
return time.September, nil
}
if (monthString == "10"){
return time.October, nil
}
if (monthString == "11"){
return time.November, nil
}
if (monthString == "12"){
return time.December, nil
}
return time.January, errors.New("Malformed AncestryDNA genome file: Invalid month: " + monthString)
}
@ -441,22 +438,15 @@ func ReadRawGenomeFile(fileReader io.Reader) (string, int, int64, int64, bool, m
snpIdentifier := rowSlice[0]
snpValueRaw := rowSlice[3]
if (len(snpValueRaw) < 2){
return "", 0, 0, 0, false, nil, errors.New("Malformed 23andMe genome data: Invalid SNP row snp value: " + fileLineString)
if (snpValueRaw[0] != byte('-')){
// Locus value is not "--"
// Locus value exists
numberOfLoci += 1
}
if (snpValueRaw[0] == '-'){
// Locus value is "--"
// Locus value does not exist
continue
}
numberOfLoci += 1
//Outputs:
// -bool: rsID found
// -int64: rsID value
// -error
getRSIDIdentifier := func()(bool, int64, error){
isRSID, rsidInt64 := readRSIDString(snpIdentifier)
@ -491,50 +481,75 @@ func ReadRawGenomeFile(fileReader io.Reader) (string, int, int64, int64, bool, m
continue
}
getLocusBasesList := func()([]rune, error){
// This will return either a base pair or a single base
// Base pair can be "--"
getLocusValueString := func()(string, error){
locusBasesList := make([]rune, 0)
// This value has a control character suffix
// Final index is always a control character
// We remove the control character suffix
finalIndex := len(snpValueRaw) - 1
if (len(snpValueRaw) == 2){
for index, character := range snpValueRaw{
baseIsValid := verifyBase(string(character))
if (baseIsValid == false){
if (index == finalIndex){
// The final index of snpValueRaw is sometimes a control character
return locusBasesList, nil
}
return nil, errors.New("Malformed 23andMe genome file: Invalid SNP base: " + string(character))
}
locusBasesList = append(locusBasesList, character)
singleBase := string(snpValueRaw[0])
return singleBase, nil
}
return locusBasesList, nil
if (len(snpValueRaw) == 3){
basePair := snpValueRaw[:2]
return basePair, nil
}
return "", errors.New("Malformed 23andMe genome file: Invalid SNP value: " + snpValueRaw)
}
locusBasesList, err := getLocusBasesList()
if (err != nil){ return "", 0, 0, 0, false, nil, err }
basesString, err := getLocusValueString()
if (err != nil) { return "", 0, 0, 0, false, nil, err }
allele1 := string(locusBasesList[0])
locusValueObject := RawGenomeLocusValue{
Allele1: allele1,
if (basesString == "--"){
// No data exists, skip.
continue
}
if (len(locusBasesList) > 1){
for _, baseRune := range basesString{
allele2 := string(locusBasesList[1])
locusValueObject.Allele2Exists = true
locusValueObject.Allele2 = allele2
baseIsValid := verifyBase(string(baseRune))
if (baseIsValid == false){
return "", 0, 0, 0, false, nil, errors.New("Malformed 23andMe genome file: Invalid SNP base: " + string(baseRune))
}
}
genomeMap[locusRSID] = locusValueObject
getMapEntryValue := func()RawGenomeLocusValue{
if (len(basesString) == 1){
locusValueObject := RawGenomeLocusValue{
Allele1: basesString,
Allele2Exists: false,
Allele2: "",
}
return locusValueObject
}
baseAString := string(basesString[0])
baseBString := string(basesString[1])
locusValueObject := RawGenomeLocusValue{
Allele1: baseAString,
Allele2Exists: true,
Allele2: baseBString,
}
return locusValueObject
}
mapEntryValue := getMapEntryValue()
genomeMap[locusRSID] = mapEntryValue
}
return "23andMe", 1, fileTimeUnix, numberOfLoci, false, genomeMap, nil

View file

@ -769,14 +769,6 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
return false, profileVersion, "", nil
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return false, 0, "", err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, 0, "", errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
}
polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList()
if (err != nil) { return false, 0, "", err }
@ -788,8 +780,12 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
for _, diseaseObject := range polygenicDiseaseObjectsList{
diseaseName := diseaseObject.DiseaseName
diseaseLociList := diseaseObject.LociList
_, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, myGenomeIdentifier)
if (err != nil) { return false, 0, "", err }
// Map Structure: rsID -> Locus Value
userDiseaseLocusValuesMap := make(map[int64]locusValue.LocusValue)
@ -822,7 +818,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
userDiseaseLocusValuesMap[locusRSID] = newLocusValue
}
anyLocusValuesTested, offspringAverageRiskScore, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList, myGenomeLocusValuesMap, userDiseaseLocusValuesMap)
anyLocusValuesTested, offspringAverageRiskScore, _, err := createCoupleGeneticAnalysis.GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList, myDiseaseLocusValuesMap, userDiseaseLocusValuesMap)
if (err != nil) { return false, 0, "", err }
if (anyLocusValuesTested == false){
continue
@ -1403,14 +1399,9 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
traitName := getTraitName()
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
myTraitLociMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisObject, traitName, myGenomeIdentifier)
if (err != nil) { return false, 0, "", err }
myGenomeLocusValuesMap, exists := myGenomesMap[myGenomeIdentifier]
if (exists == false){
return false, 0, "", errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis with a GenomesMap which is missing my genome identifier.")
}
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return false, 0, "", err }
@ -1424,7 +1415,7 @@ func GetAnyProfileAttributeIncludingCalculated(attributeName string, getProfileA
for _, rsID := range traitLociList{
myLocusValue, myLocusValueExists := myGenomeLocusValuesMap[rsID]
myLocusValue, myLocusValueExists := myTraitLociMap[rsID]
if (myLocusValueExists == false){
continue
}

View file

@ -150,14 +150,6 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
return errors.New("UpdateMyExportedProfile called when profile genetic analysis is not ready.")
}
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
if (err != nil) { return err }
myGenomeLocusValuesMap, exists := myGenomesMap[genomeIdentifierToShare]
if (exists == false){
return errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
}
monogenicDiseaseNamesList, err := monogenicDiseases.GetMonogenicDiseaseNamesList()
if (err != nil) { return err }
@ -213,24 +205,19 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
continue
}
lociList := diseaseObject.LociList
_, _, _, myDiseaseLocusValuesMap, _, _, err := readGeneticAnalysis.GetPersonPolygenicDiseaseInfoFromGeneticAnalysis(myGeneticAnalysisObject, diseaseName, genomeIdentifierToShare)
if (err != nil) { return err }
for _, locusObject := range lociList{
for rsID, locusValueObject := range myDiseaseLocusValuesMap{
locusRSID := locusObject.LocusRSID
rsIDString := helpers.ConvertInt64ToString(rsID)
locusValueObject, exists := myGenomeLocusValuesMap[locusRSID]
if (exists == true){
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
rsIDString := helpers.ConvertInt64ToString(locusRSID)
basePairValue := locusBase1 + ";" + locusBase2
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
basePairValue := locusBase1 + ";" + locusBase2
profileMap["LocusValue_rs" + rsIDString] = basePairValue
}
profileMap["LocusValue_rs" + rsIDString] = basePairValue
}
}
@ -252,22 +239,19 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
continue
}
lociList := traitObject.LociList
myTraitLocusValuesMap, _, _, _, _, err := readGeneticAnalysis.GetPersonTraitInfoFromGeneticAnalysis(myGeneticAnalysisObject, traitName, genomeIdentifierToShare)
if (err != nil) { return err }
for _, rsID := range lociList{
for rsID, locusValueObject := range myTraitLocusValuesMap{
locusValueObject, exists := myGenomeLocusValuesMap[rsID]
if (exists == true){
rsIDString := helpers.ConvertInt64ToString(rsID)
rsIDString := helpers.ConvertInt64ToString(rsID)
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
locusBase1 := locusValueObject.Base1Value
locusBase2 := locusValueObject.Base2Value
basePairValue := locusBase1 + ";" + locusBase2
basePairValue := locusBase1 + ";" + locusBase2
profileMap["LocusValue_rs" + rsIDString] = basePairValue
}
profileMap["LocusValue_rs" + rsIDString] = basePairValue
}
}

View file

@ -1,58 +0,0 @@
// geneticPredictionModels contains genetic prediction neural network models for predicting genetic traits
// These are .gob encoded files of []float32 weights
// This package also contains prediction accuracy information for each model
// Prediction accuracy models describe information about how accurate the predictions made by the models are
// All of the files in this package are created by the Create Genetic Models utility.
// This utility is located in /utilities/createGeneticModels/createGeneticModels.go
package geneticPredictionModels
import _ "embed"
import "errors"
//Outputs:
// -bool: Model exists
// -[]byte
func GetGeneticPredictionModelBytes(traitName string)(bool, []byte){
switch traitName{
case "Eye Color":{
return true, predictionModel_EyeColor
}
case "Lactose Tolerance":{
return true, predictionModel_LactoseTolerance
}
}
return false, nil
}
//go:embed predictionModels/EyeColorModel.gob
var predictionModel_EyeColor []byte
//go:embed predictionModels/LactoseToleranceModel.gob
var predictionModel_LactoseTolerance []byte
// The files returned by this function are .gob encoded geneticPrediction.TraitPredictionAccuracyInfoMap objects
func GetPredictionModelTraitAccuracyInfoBytes(traitName string)([]byte, error){
switch traitName{
case "Eye Color":{
return predictionAccuracy_EyeColor, nil
}
case "Lactose Tolerance":{
return predictionAccuracy_LactoseTolerance, nil
}
}
return nil, errors.New("GetPredictionModelTraitAccuracyInfoFile called with unknown traitName: " + traitName)
}
//go:embed predictionModelAccuracies/EyeColorModelAccuracy.gob
var predictionAccuracy_EyeColor []byte
//go:embed predictionModelAccuracies/LactoseToleranceModelAccuracy.gob
var predictionAccuracy_LactoseTolerance []byte

View file

@ -1,48 +0,0 @@
package geneticPredictionModels_test
import "seekia/resources/geneticPredictionModels"
import "testing"
import "seekia/internal/genetics/geneticPrediction"
func TestGeneticPredictionModels(t *testing.T){
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
for _, traitName := range traitNamesList{
modelFound, modelBytes := geneticPredictionModels.GetGeneticPredictionModelBytes(traitName)
if (modelFound == false){
t.Fatalf("GetGeneticPredictionModelBytes failed to find model for trait: " + traitName)
}
_, err := geneticPrediction.DecodeBytesToNeuralNetworkObject(modelBytes)
if (err != nil){
t.Fatalf("DecodeBytesToNeuralNetworkObject failed: " + err.Error())
}
}
}
func TestGeneticPredictionModelAccuracies(t *testing.T){
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
for _, traitName := range traitNamesList{
accuracyInfoBytes, err := geneticPredictionModels.GetPredictionModelTraitAccuracyInfoBytes(traitName)
if (err != nil){
t.Fatalf("GetGeneticPredictionModelBytes failed: " + err.Error())
}
_, err = geneticPrediction.DecodeBytesToTraitPredictionAccuracyInfoMap(accuracyInfoBytes)
if (err != nil){
t.Fatalf("DecodeBytesToTraitPredictionAccuracyInfoMap failed: " + err.Error())
}
}
}

View file

@ -309,13 +309,10 @@ func TestGeneticReferences(t *testing.T){
traitName := traitObject.TraitName
traitDescription := traitObject.TraitDescription
traitDiscreteOrNumeric := traitObject.DiscreteOrNumeric
traitLocusReferencesMap := traitObject.LocusReferencesMap
traitLociList := traitObject.LociList
traitLociList_Rules := traitObject.LociList_Rules
traitRulesList := traitObject.RulesList
traitOutcomesList := traitObject.OutcomesList
traitReferencesMap := traitObject.ReferencesMap
traitReferencesMap := traitObject.References
if (traitName == ""){
t.Fatalf("Empty trait name exists.")
@ -329,9 +326,6 @@ func TestGeneticReferences(t *testing.T){
if (traitDescription == ""){
t.Fatalf("Empty trait description exists for trait: " + traitName)
}
if (traitDiscreteOrNumeric != "Discrete" && traitDiscreteOrNumeric != "Numeric"){
t.Fatalf("Invalid DiscreteOrNumeric for trait: " + traitDiscreteOrNumeric)
}
if (len(traitOutcomesList) != 0){
if (len(traitOutcomesList) < 2){
@ -355,41 +349,18 @@ func TestGeneticReferences(t *testing.T){
t.Fatalf("Invalid references exist for trait: " + traitName)
}
if (len(traitLocusReferencesMap) == 0){
t.Fatalf("No trait locus references exist for trait: " + traitName)
}
for locusRSID, locusReferences := range traitLocusReferencesMap{
allRSIDsMap[locusRSID] = struct{}{}
if (locusReferences == nil){
t.Fatalf("A trait locus has no references map: " + traitName)
}
if (len(locusReferences) == 0){
t.Fatalf("A trait locus has no references: " + traitName)
}
locusExists := slices.Contains(traitLociList, locusRSID)
if (locusExists == false){
t.Fatalf("traitLocusReferencesMap contains rsID which does not exist in traitLociList")
}
}
if (len(traitLociList) == 0){
t.Fatalf("No trait loci exist for trait: " + traitName)
}
for _, rsID := range traitLociList{
allRSIDsMap[rsID] = struct{}{}
for _, locusRSID := range traitLociList{
allRSIDsMap[locusRSID] = struct{}{}
}
for _, rsID := range traitLociList_Rules{
locusExists := slices.Contains(traitLociList, rsID)
if (locusExists == false){
t.Fatalf("traitLociList_Rules contains locus not present in traitLociList")
}
containsDuplicates, duplicateLocus := helpers.CheckIfListContainsDuplicates(traitLociList)
if (containsDuplicates == true){
duplicateLocusString := helpers.ConvertInt64ToString(duplicateLocus)
t.Fatalf("traitLociList contains duplicates for trait: " + traitName + ". RSID: " + duplicateLocusString)
}
if (len(traitRulesList) == 0){
@ -402,7 +373,7 @@ func TestGeneticReferences(t *testing.T){
ruleIdentifier := ruleObject.RuleIdentifier
ruleLociList := ruleObject.LociList
ruleOutcomePointsMap := ruleObject.OutcomePointsMap
ruleReferencesMap := ruleObject.ReferencesMap
ruleReferences := ruleObject.References
identifierIsValid := verifyIdentifier(ruleIdentifier)
if (identifierIsValid == false){
@ -442,21 +413,11 @@ func TestGeneticReferences(t *testing.T){
t.Fatalf("Trait rule Locus identifier is invalid: " + locusIdentifier)
}
_, mapContainsItem := traitLocusReferencesMap[locusRSID]
if (mapContainsItem == false){
t.Fatalf("Rule locus contains rsid which is not contained within LocusReferencesMap.")
}
sliceContainsItem := slices.Contains(traitLociList, locusRSID)
if (sliceContainsItem == false){
listContainsItem := slices.Contains(traitLociList, locusRSID)
if (listContainsItem == false){
t.Fatalf("Rule locus contains rsid which is not contained within traitLociList.")
}
sliceContainsItem = slices.Contains(traitLociList_Rules, locusRSID)
if (sliceContainsItem == false){
t.Fatalf("Rule locus contains rsid which is not contained within traitLociList_Rules.")
}
if (len(locusBasePairsList) == 0){
t.Fatalf("Trait rule locus base pairs list is empty: " + locusIdentifier)
}
@ -469,7 +430,7 @@ func TestGeneticReferences(t *testing.T){
}
}
referencesAreValid := verifyReferencesMap(ruleReferencesMap)
referencesAreValid := verifyReferencesMap(ruleReferences)
if (referencesAreValid == false){
t.Fatalf("Invalid references map for trait rule locus: " + ruleIdentifier)
}

View file

@ -8,8 +8,9 @@ package polygenicDiseases
// Polygenic disease probabilities are less accurate, because individual base pair changes only cause comparatively small changes in the disease risk.
// Polygenic diseases are also more influenced by environmental factors, further decreasing risk accuracy.
//TODO: Eventually we want to use neural networks for polygenic disease prediction.
// This package is currently a less accurate solution until we get access to the necessary training data.
//TODO: Eventually we want to use neural networks for both polygenic disease and trait prediction.
// This package is currently a temporary, less accurate solution until we get access to the necessary training data.
// It may still be worth keeping this method in place for polygenic diseases and using neural networks in tandem.
import "errors"

View file

@ -1,20 +1,14 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getEyeColorTraitObject()Trait{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
eyeColorLociList := []int64{
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Eye Color"] = "https://www.snpedia.com/index.php/Eye_color"
//TODO: Add more SNPs.
// These SNPs are taken from https://www.snpedia.com/index.php/Eye_color
// These SNPs are taken from https://www.snpedia.com/index.php/Eye_color
lociList_1 := []int64{
2733832,
1800401,
1800407,
@ -57,39 +51,16 @@ func getEyeColorTraitObject()Trait{
989869,
4778138,
12906280,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
referencesMap_List2 := make(map[string]string)
referencesMap_List2["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/20546537/
lociList_2 := []int64{
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/20546537/
12203592,
1408799,
1126809,
12896399,
7495174,
1667394,
}
for _, rsID := range lociList_2{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List2)
}
referencesMap_List3 := make(map[string]string)
referencesMap_List3["Genome-wide association study in almost 195,000 individuals identifies 50 previously unidentified genetic loci for eye color."] = "https://pubmed.ncbi.nlm.nih.gov/33692100/"
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/33692100/
lociList_3 := []int64{
// These SNPs are taken from https://pubmed.ncbi.nlm.nih.gov/33692100/
6693258,
351385,
2385028,
@ -144,12 +115,6 @@ func getEyeColorTraitObject()Trait{
// 5957354,
}
for _, rsID := range lociList_3{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List3)
}
eyeColorLociList := helpers.GetListOfMapKeys(locusReferencesMap)
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Eye Color"] = "https://www.snpedia.com/index.php/Eye_color"
referencesMap["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
@ -158,13 +123,10 @@ func getEyeColorTraitObject()Trait{
eyeColorObject := Trait{
TraitName: "Eye Color",
TraitDescription: "The color of a person's eyes.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: eyeColorLociList,
LociList_Rules: []int64{},
RulesList: []TraitRule{},
OutcomesList: []string{"Blue", "Green", "Hazel", "Brown"},
ReferencesMap: referencesMap,
References: referencesMap,
}
return eyeColorObject

View file

@ -1,19 +1,9 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getFacialStructureTraitObject()Trait{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap_List1["A Genome-Wide Association Study Identifies Five Loci Influencing Facial Morphology in Europeans"] = "https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.1002932"
lociList_1 := []int64{
facialStructureLociList := []int64{
//TODO: Add more SNPs.
@ -122,13 +112,6 @@ func getFacialStructureTraitObject()Trait{
397723,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
facialStructureLociList := helpers.GetListOfMapKeys(locusReferencesMap)
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap["A Genome-Wide Association Study Identifies Five Loci Influencing Facial Morphology in Europeans"] = "https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.1002932"
@ -136,13 +119,10 @@ func getFacialStructureTraitObject()Trait{
facialStructureObject := Trait{
TraitName: "Facial Structure",
TraitDescription: "The structure of a person's face.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: facialStructureLociList,
LociList_Rules: []int64{},
RulesList: []TraitRule{},
OutcomesList: []string{},
ReferencesMap: referencesMap,
References: referencesMap,
}
return facialStructureObject

View file

@ -3,20 +3,10 @@ package traits
// Hair color is influenced by thousands of genes
// We only have a few listed here
import "seekia/internal/helpers"
import "maps"
func getHairColorTraitObject()Trait{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap_List1["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
lociList_1 := []int64{
hairColorLociList := []int64{
//These loci were taken from https://pubmed.ncbi.nlm.nih.gov/20546537/
@ -42,13 +32,6 @@ func getHairColorTraitObject()Trait{
1805008,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
hairColorLociList := helpers.GetListOfMapKeys(locusReferencesMap)
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
@ -56,13 +39,10 @@ func getHairColorTraitObject()Trait{
hairColorObject := Trait{
TraitName: "Hair Color",
TraitDescription: "The color of a person's hair.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: hairColorLociList,
LociList_Rules: []int64{},
RulesList: []TraitRule{},
OutcomesList: []string{},
ReferencesMap: referencesMap,
References: referencesMap,
}
return hairColorObject

View file

@ -1,53 +1,11 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getHairTextureTraitObject()Trait{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
lociList_1 := []int64{
7349332,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
referencesMap_List2 := make(map[string]string)
referencesMap_List2["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
lociList_2 := []int64{
11803731,
}
for _, rsID := range lociList_2{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List2)
}
referencesMap_List3 := make(map[string]string)
referencesMap_List3["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
lociList_3 := []int64{
17646946,
}
for _, rsID := range lociList_3{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List3)
}
referencesMap_rs7349332 := make(map[string]string)
referencesMap_rs7349332["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
rule1_ReferencesMap := make(map[string]string)
rule1_ReferencesMap["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
rule1_Locus1Object := RuleLocus{
@ -64,10 +22,12 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "fde405",
LociList: rule1_LociList,
OutcomePointsMap: rule1_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs7349332),
References: rule1_ReferencesMap,
}
//TODO: Make sure this is true, that a heterozygote has a higher likelihood of curly hair
rule2_ReferencesMap := make(map[string]string)
rule2_ReferencesMap["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
rule2_Locus1Object := RuleLocus{
@ -84,8 +44,11 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "6bd1da",
LociList: rule2_LociList,
OutcomePointsMap: rule2_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs7349332),
References: rule2_ReferencesMap,
}
rule3_ReferencesMap := make(map[string]string)
rule3_ReferencesMap["SNPedia.com - rs7349332"] = "https://www.snpedia.com/index.php/Rs7349332"
rule3_Locus1Object := RuleLocus{
@ -102,13 +65,11 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "32e377",
LociList: rule3_LociList,
OutcomePointsMap: rule3_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs7349332),
References: rule3_ReferencesMap,
}
referencesMap_rs11803731 := make(map[string]string)
referencesMap_rs11803731["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
rule4_ReferencesMap := make(map[string]string)
rule4_ReferencesMap["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
rule4_Locus1Object := RuleLocus{
@ -125,10 +86,12 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "34e6d2",
LociList: rule4_LociList,
OutcomePointsMap: rule4_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs11803731),
References: rule4_ReferencesMap,
}
//TODO: Make sure this is true, that a heterozygote has a higher likelihood of curly hair
rule5_ReferencesMap := make(map[string]string)
rule5_ReferencesMap["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
rule5_Locus1Object := RuleLocus{
@ -145,8 +108,11 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "cf6cb5",
LociList: rule5_LociList,
OutcomePointsMap: rule5_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs11803731),
References: rule5_ReferencesMap,
}
rule6_ReferencesMap := make(map[string]string)
rule6_ReferencesMap["SNPedia.com - rs11803731"] = "https://www.snpedia.com/index.php/Rs11803731"
rule6_Locus1Object := RuleLocus{
@ -163,11 +129,11 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "2ba65b",
LociList: rule6_LociList,
OutcomePointsMap: rule6_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs11803731),
References: rule6_ReferencesMap,
}
referencesMap_rs17646946 := make(map[string]string)
referencesMap_rs17646946["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
rule7_ReferencesMap := make(map[string]string)
rule7_ReferencesMap["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
rule7_Locus1Object := RuleLocus{
@ -184,9 +150,12 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "ae3274",
LociList: rule7_LociList,
OutcomePointsMap: rule7_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs17646946),
References: rule7_ReferencesMap,
}
rule8_ReferencesMap := make(map[string]string)
rule8_ReferencesMap["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
rule8_Locus1Object := RuleLocus{
LocusIdentifier: "f1144a",
@ -202,9 +171,12 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "a546bf",
LociList: rule8_LociList,
OutcomePointsMap: rule8_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs17646946),
References: rule8_ReferencesMap,
}
rule9_ReferencesMap := make(map[string]string)
rule9_ReferencesMap["SNPedia.com - rs17646946"] = "https://www.snpedia.com/index.php/Rs17646946"
rule9_Locus1Object := RuleLocus{
LocusIdentifier: "468bb3",
@ -220,32 +192,26 @@ func getHairTextureTraitObject()Trait{
RuleIdentifier: "b8dc0a",
LociList: rule9_LociList,
OutcomePointsMap: rule9_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs17646946),
References: rule9_ReferencesMap,
}
hairTextureRulesList := []TraitRule{rule1_Object, rule2_Object, rule3_Object, rule4_Object, rule5_Object, rule6_Object, rule7_Object, rule8_Object, rule9_Object}
lociList_Rules := []int64{7349332, 11803731, 17646946}
hairTextureLociList := []int64{17646946, 11803731, 7349332}
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Hair Curliness"] = "https://www.snpedia.com/index.php/Hair_curliness"
outcomesList := []string{"Straight", "Curly"}
hairTextureLociList := helpers.GetListOfMapKeys(locusReferencesMap)
hairTextureObject := Trait{
TraitName: "Hair Texture",
TraitDescription: "The texture of a person's head hair.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: hairTextureLociList,
LociList_Rules: lociList_Rules,
RulesList: hairTextureRulesList,
OutcomesList: outcomesList,
ReferencesMap: referencesMap,
References: referencesMap,
}
return hairTextureObject

View file

@ -1,30 +1,11 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getLactoseToleranceTraitObject()Trait{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_1 := make(map[string]string)
referencesMap_1["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
locusReferencesMap[182549] = referencesMap_1
referencesMap_2 := make(map[string]string)
referencesMap_2["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
locusReferencesMap[4988235] = referencesMap_2
referencesMap_rs182549 := make(map[string]string)
referencesMap_rs182549["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
rule1_ReferencesMap := make(map[string]string)
rule1_ReferencesMap["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
rule1_Locus1Object := RuleLocus{
@ -41,9 +22,12 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "f4e02c",
LociList: rule1_LociList,
OutcomePointsMap: rule1_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs182549),
References: rule1_ReferencesMap,
}
rule2_ReferencesMap := make(map[string]string)
rule2_ReferencesMap["SNPedia.com - rs182549"] = "https://www.snpedia.com/index.php/Rs182549"
rule2_Locus1Object := RuleLocus{
LocusIdentifier: "a7feff",
@ -59,11 +43,11 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "cc3df0",
LociList: rule2_LociList,
OutcomePointsMap: rule2_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs182549),
References: rule2_ReferencesMap,
}
referencesMap_rs4988235 := make(map[string]string)
referencesMap_rs4988235["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
rule3_ReferencesMap := make(map[string]string)
rule3_ReferencesMap["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
rule3_Locus1Object := RuleLocus{
@ -80,9 +64,12 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "8170ee",
LociList: rule3_LociList,
OutcomePointsMap: rule3_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs4988235),
References: rule3_ReferencesMap,
}
rule4_ReferencesMap := make(map[string]string)
rule4_ReferencesMap["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
rule4_Locus1Object := RuleLocus{
LocusIdentifier: "176dde",
@ -98,9 +85,12 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "52425f",
LociList: rule4_LociList,
OutcomePointsMap: rule4_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs4988235),
References: rule4_ReferencesMap,
}
rule5_ReferencesMap := make(map[string]string)
rule5_ReferencesMap["SNPedia.com - rs4988235"] = "https://www.snpedia.com/index.php/Rs4988235"
rule5_Locus1Object := RuleLocus{
LocusIdentifier: "164acb",
@ -116,31 +106,25 @@ func getLactoseToleranceTraitObject()Trait{
RuleIdentifier: "4b5c35",
LociList: rule5_LociList,
OutcomePointsMap: rule5_OutcomePointsMap,
ReferencesMap: maps.Clone(referencesMap_rs4988235),
References: rule5_ReferencesMap,
}
lactoseToleranceRulesList := []TraitRule{rule1_Object, rule2_Object, rule3_Object, rule4_Object, rule5_Object}
lactoseToleranceLociList := []int64{4988235, 182549}
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Lactose Intolerance"] = "https://www.snpedia.com/index.php/Lactose_intolerance"
outcomesList := []string{"Tolerant", "Intolerant"}
lactoseToleranceLociList := helpers.GetListOfMapKeys(locusReferencesMap)
lociList_Rules := []int64{182549, 4988235}
lactoseToleranceObject := Trait{
TraitName: "Lactose Tolerance",
TraitDescription: "The ability to tolerate lactose.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: lactoseToleranceLociList,
LociList_Rules: lociList_Rules,
RulesList: lactoseToleranceRulesList,
OutcomesList: outcomesList,
ReferencesMap: referencesMap,
References: referencesMap,
}
return lactoseToleranceObject

View file

@ -1,18 +1,10 @@
package traits
import "seekia/internal/helpers"
import "maps"
func getSkinColorTraitObject()Trait{
// Map Structure: rsID -> References Map
locusReferencesMap := make(map[int64]map[string]string)
referencesMap_List1 := make(map[string]string)
referencesMap_List1["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
lociList_1 := []int64{
skinColorLociList := []int64{
//TODO: Add more SNPs.
@ -22,32 +14,12 @@ func getSkinColorTraitObject()Trait{
26722,
1426654,
642742,
}
for _, rsID := range lociList_1{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List1)
}
referencesMap_List2 := make(map[string]string)
referencesMap_List2["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
lociList_2 := []int64{
// These SNPs are from https://pubmed.ncbi.nlm.nih.gov/20546537/
16891982,
12203592,
1042602,
1834640,
}
for _, rsID := range lociList_2{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List2)
}
referencesMap_List3 := make(map[string]string)
referencesMap_List3["Meta-analysis and prioritization of human skin pigmentation-associated GWAS-SNPs using ENCODE data-based web-tools"] = "https://link.springer.com/article/10.1007/s00403-019-01891-3"
lociList_3 := []int64{
// These SNPs are from https://link.springer.com/article/10.1007/s00403-019-01891-3
7182710,
@ -61,28 +33,18 @@ func getSkinColorTraitObject()Trait{
3212368,
}
for _, rsID := range lociList_3{
locusReferencesMap[rsID] = maps.Clone(referencesMap_List3)
}
referencesMap := make(map[string]string)
referencesMap["SNPedia.com - Appearance"] = "https://www.snpedia.com/index.php/Appearance"
referencesMap["Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis"] = "https://pubmed.ncbi.nlm.nih.gov/20546537/"
referencesMap["Meta-analysis and prioritization of human skin pigmentation-associated GWAS-SNPs using ENCODE data-based web-tools"] = "https://link.springer.com/article/10.1007/s00403-019-01891-3"
lociList := helpers.GetListOfMapKeys(locusReferencesMap)
skinColorObject := Trait{
TraitName: "Skin Color",
TraitDescription: "The color of a person's skin.",
DiscreteOrNumeric: "Discrete",
LocusReferencesMap: locusReferencesMap,
LociList: lociList,
LociList_Rules: []int64{},
LociList: skinColorLociList,
RulesList: []TraitRule{},
OutcomesList: []string{},
ReferencesMap: referencesMap,
References: referencesMap,
}
return skinColorObject

View file

@ -3,51 +3,30 @@
package traits
// TODO: We want to eventually use neural nets for both trait and polygenic disease analysis
// These will be trained on a set of genomes and will output a probability analysis for each trait
// This is only possible once we get access to the necessary training data
//
// See geneticPrediction.go for a non-working attempt to predict traits with neural nets
import "errors"
type Trait struct{
type RuleLocus struct{
// Example: "Eye Color"
TraitName string
// 3 byte hex encoded string
LocusIdentifier string
TraitDescription string
// RSID that represents this locus
// If multiple RSIDs represent the same locus, use the first rsid for the locus in the locusMetadata package
LocusRSID int64
// This describes if the trait is discrete or numeric
// Discrete traits have a set of outcomes (Example: Eye Color: Blue, Green...)
// Numeric traits have a numeric outcome (Example: Height)
// The value of this variable is either "Discrete" or "Numeric"
DiscreteOrNumeric string
// This is a list of rsIDs which are known to have an effect on this trait
// These loci may not have any associated rules
// We use these loci to predict trait outcomes with neural networks.
// We also use these loci to calculate Racial Similarity.
// Map Structure: rsID -> (map[ReferenceName]Reference Link)
LocusReferencesMap map[int64]map[string]string
// This is a list of all loci used to predict this trait
// If a neural network exists, all of these will be used as input into the network for prediction
LociList []int64
// This is a list of all loci used to predict this trait using rules
// It is sometimes a subset of LociList
LociList_Rules []int64
// This list can be empty if no rules exist
// An empty list means we are relying on LociList and neural networks for trait prediction.
RulesList []TraitRule
// List of outcomes
// Example: "Lactose Intolerant", "Lactore Tolerant"
// This list can be empty if outcomes are not text descriptions (Example: Facial structure)
// If the trait is Numeric, or their or no rules nor a neural network, then this list will be empty.
OutcomesList []string
// This map contains scientific resources about this trait
// Map structure: Reference name -> Reference link
ReferencesMap map[string]string
// List of base pair values that this RSID must fulfill to pass the rule
// As long as the value matches any base pair value in the list, the genome has passed this rule locus
// The genome must pass every rule locus within a rule to pass the rule
BasePairsList []string
}
type TraitRule struct{
// 3 byte identifier encoded hex
@ -65,26 +44,36 @@ type TraitRule struct{
OutcomePointsMap map[string]int
// Map structure: Reference name -> Reference link
ReferencesMap map[string]string
References map[string]string
}
type RuleLocus struct{
type Trait struct{
// 3 byte hex encoded string
LocusIdentifier string
TraitName string
// RSID that represents this locus
// If multiple RSIDs represent the same locus, use the first rsid for the locus in the locusMetadata package
LocusRSID int64
TraitDescription string
// List of base pair values that this RSID must fulfill to pass the rule
// As long as the value matches any base pair value in the list, the genome has passed this rule locus
// The genome must pass every rule locus within a rule to pass the rule
BasePairsList []string
// This is a list of rsIDs which are known to have an effect on this trait
// These loci may not have any associated rules
// We use these loci to calculate Racial Similarity.
// We will also use neural networks to predict trait outcome scores using these loci
LociList []int64
// This list can be empty if no rules exist
// An empty list means we are relying on LociList and neural networks for trait prediction.
RulesList []TraitRule
// List of outcomes
// Example: "Lactose Intolerant", "Lactore Tolerant"
// This list can be empty if outcomes are not text descriptions (Example: Facial structure)
// If there are no outcomes, then no rules can exist
OutcomesList []string
// Map structure: Reference name -> Reference link
References map[string]string
}
var traitNamesList []string
var traitObjectsList []Trait

View file

@ -1,4 +1,3 @@
OpenSNPDataArchiveFolderpath.txt
TrainingData
TrainedModels
ModelAccuracies
TrainedModels

View file

@ -3,7 +3,6 @@
// These are neural networks which predict traits such as eye color from raw genome files
// The OpenSNP.org dataset is used, and more datasets will be added in the future.
// You must download the dataset and extract it. The instructions are described in the utility.
// The trained models are saved in the /resources/geneticPredictionModels package for use in the Seekia app.
package main
@ -1137,16 +1136,10 @@ func setStartAndMonitorTrainModelPage(window fyne.Window, traitName string, prev
_, err := localFilesystem.CreateFolder("./TrainedModels")
if (err != nil) { return false, err }
trainingSetFilepathsList, _, err := getTrainingAndTestingDataFilepathLists(traitName)
if (err != nil) { return false, err }
// Now we deterministically randomize the order of the trainingSetFilepathsList
pseudorandomNumberGenerator := mathRand.New(mathRand.NewPCG(1, 2))
pseudorandomNumberGenerator.Shuffle(len(trainingSetFilepathsList), func(i int, j int){
trainingSetFilepathsList[i], trainingSetFilepathsList[j] = trainingSetFilepathsList[j], trainingSetFilepathsList[i]
})
// We create a new neural network object to train
neuralNetworkObject, err := geneticPrediction.GetNewUntrainedNeuralNetworkObject(traitName)
if (err != nil) { return false, err }
@ -1284,8 +1277,7 @@ func setTestModelsPage(window fyne.Window, previousPage func()){
description2 := getLabelCentered("This will test each neural network using user training data examples.")
description3 := getLabelCentered("The testing data is not used to train the models.")
description4 := getLabelCentered("The results of the testing will be displayed at the end.")
description5 := getLabelCentered("The results will also be saved in the ModelAccuracies folder.")
description6 := getLabelCentered("You must select a trait model to test.")
description5 := getLabelCentered("You must select a trait model to test.")
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
@ -1309,12 +1301,50 @@ func setTestModelsPage(window fyne.Window, previousPage func()){
traitNameSelectorCentered := getWidgetCentered(traitNameSelector)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, widget.NewSeparator(), traitNameSelectorCentered, widget.NewSeparator(), beginTestingButton)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, widget.NewSeparator(), traitNameSelectorCentered, widget.NewSeparator(), beginTestingButton)
window.SetContent(page)
}
type TraitOutcomeInfo struct{
// This is the outcome which was found
// Example: "Blue"
OutcomeName string
// This is a value between 0-100 which describes the percentage of the loci which were tested for the input for the prediction
PercentageOfLociTested int
// This is a value between 0-100 which describes the percentage of the tested loci which were phased for the input for the prediction
PercentageOfPhasedLoci int
}
type TraitPredictionAccuracyInfo struct{
// This contains the quantity of examples for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
QuantityOfExamples int
// This contains the quantity of predictions for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
// Prediction = our model predicted this outcome
QuantityOfPredictions int
// This stores the probability (0-100) that our model will accurately predict this outcome for a genome which has
// the specified percentageOfLociTested and percentageOfPhasedLoci
// In other words: What is the probability that if you give Seekia a blue-eyed genome, it will give you a correct Blue prediction?
// This value is only accurate is QuantityOfExamples > 0
ProbabilityOfCorrectGenomePrediction int
// This stores the probability (0-100) that our model is correct if our model predicts that a genome
// with the specified percentageOfLociTested and percentageOfPhasedLoci has this outcome
// In other words: What is the probability that if Seekia says a genome will have blue eyes, it is correct?
// This value is only accurate is QuantityOfPredictions > 0
ProbabilityOfCorrectOutcomePrediction int
}
// Map Structure: Trait Outcome Info -> Trait Prediction Accuracy Info
type TraitAccuracyInfoMap map[TraitOutcomeInfo]TraitPredictionAccuracyInfo
func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previousPage func()){
title := getBoldLabelCentered("Testing Model")
@ -1356,9 +1386,9 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
//Outputs:
// -bool: Process completed (true == was not stopped mid-way)
// -geneticPrediction.TraitPredictionAccuracyInfoMap
// -TraitAccuracyInfoMap
// -error
testModel := func()(bool, geneticPrediction.TraitPredictionAccuracyInfoMap, error){
testModel := func()(bool, TraitAccuracyInfoMap, error){
type TraitAccuracyStatisticsValue struct{
@ -1378,7 +1408,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
// We use this map to count up the information about predictions
// We use information from this map to construct the final accuracy information map
traitPredictionInfoMap := make(map[geneticPrediction.TraitOutcomeInfo]TraitAccuracyStatisticsValue)
traitPredictionInfoMap := make(map[TraitOutcomeInfo]TraitAccuracyStatisticsValue)
_, testingSetFilepathsList, err := getTrainingAndTestingDataFilepathLists(traitName)
@ -1464,7 +1494,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
{
// We first add the information to the map for the correct outcome
newTraitOutcomeInfo_CorrectOutcome := geneticPrediction.TraitOutcomeInfo{
newTraitOutcomeInfo_CorrectOutcome := TraitOutcomeInfo{
OutcomeName: correctOutcomeName,
PercentageOfLociTested: percentageOfLociTested,
@ -1495,7 +1525,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
{
// We now add the information to the map for the predicted outcome
newTraitOutcomeInfo_PredictedOutcome := geneticPrediction.TraitOutcomeInfo{
newTraitOutcomeInfo_PredictedOutcome := TraitOutcomeInfo{
OutcomeName: predictedOutcomeName,
PercentageOfLociTested: percentageOfLociTested,
@ -1536,7 +1566,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
// Now we construct the TraitAccuracyInfoMap
// This map stores the accuracy for each outcome
traitPredictionAccuracyInfoMap := make(map[geneticPrediction.TraitOutcomeInfo]geneticPrediction.TraitPredictionAccuracyInfo)
traitAccuracyInfoMap := make(map[TraitOutcomeInfo]TraitPredictionAccuracyInfo)
for traitAccuracyData, value := range traitPredictionInfoMap{
@ -1553,7 +1583,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
return false, nil, errors.New("traitPredictionInfoMap contains quantityOfCorrectOutcomePredictions > quantityOfPredictions")
}
newTraitPredictionAccuracyInfo := geneticPrediction.TraitPredictionAccuracyInfo{
newTraitPredictionAccuracyInfo := TraitPredictionAccuracyInfo{
QuantityOfExamples: quantityOfExamples,
QuantityOfPredictions: quantityOfPredictions,
}
@ -1574,30 +1604,17 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
newTraitPredictionAccuracyInfo.ProbabilityOfCorrectOutcomePrediction = percentageOfCorrectOutcomePredictions
}
traitPredictionAccuracyInfoMap[traitAccuracyData] = newTraitPredictionAccuracyInfo
traitAccuracyInfoMap[traitAccuracyData] = newTraitPredictionAccuracyInfo
}
// Testing is complete.
// We save the info map as a file in the ModelAccuracies folder
fileBytes, err := geneticPrediction.EncodeTraitPredictionAccuracyInfoMapToBytes(traitPredictionAccuracyInfoMap)
if (err != nil) { return false, nil, err }
_, err = localFilesystem.CreateFolder("./ModelAccuracies")
if (err != nil) { return false, nil, err }
modelAccuracyFilename := traitNameWithoutWhitespaces + "ModelAccuracy.gob"
err = localFilesystem.CreateOrOverwriteFile(fileBytes, "./ModelAccuracies/", modelAccuracyFilename)
if (err != nil) { return false, nil, err }
progressPercentageBinding.Set(1)
return true, traitPredictionAccuracyInfoMap, nil
return true, traitAccuracyInfoMap, nil
}
processIsComplete, traitPredictionAccuracyInfoMap, err := testModel()
processIsComplete, traitAccuracyInfoMap, err := testModel()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
@ -1607,14 +1624,14 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
return
}
setViewModelTestingTraitResultsPage(window, traitName, traitPredictionAccuracyInfoMap, previousPage)
setViewModelTestingTraitResultsPage(window, traitName, traitAccuracyInfoMap, previousPage)
}
go testModelFunction()
}
// This is a page to view the details of testing for a specific trait's model
func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, traitAccuracyInfoMap geneticPrediction.TraitPredictionAccuracyInfoMap, exitPage func()){
func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, traitAccuracyInfoMap TraitAccuracyInfoMap, exitPage func()){
title := getBoldLabelCentered("Trait Prediction Accuracy Details")
@ -1644,10 +1661,14 @@ func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, t
predictionAccuracyTitle3 := getItalicLabelCentered("Prediction Accuracy")
knownLociLabel_67to100 := getItalicLabelCentered("67-100% Known Loci")
emptyLabel2 := widget.NewLabel("")
emptyLabel3 := widget.NewLabel("")
outcomeNameColumn := container.NewVBox(outcomeNameTitle, emptyLabel1, widget.NewSeparator())
predictionAccuracyColumn_0to33 := container.NewVBox(predictionAccuracyTitle1, knownLociLabel_0to33, widget.NewSeparator())
predictionAccuracyColumn_34to66 := container.NewVBox(predictionAccuracyTitle2, knownLociLabel_34to66, widget.NewSeparator())
predictionAccuracyColumn_67to100 := container.NewVBox(predictionAccuracyTitle3, knownLociLabel_67to100, widget.NewSeparator())
viewTraitAccuracyDetailsColumn := container.NewVBox(emptyLabel2, emptyLabel3, widget.NewSeparator())
traitObject, err := traits.GetTraitObject(traitName)
if (err != nil) { return nil, err }
@ -1756,7 +1777,7 @@ func setViewModelTestingTraitResultsPage(window fyne.Window, traitName string, t
predictionAccuracyColumn_67to100.Add(widget.NewSeparator())
}
resultsGrid := container.NewHBox(layout.NewSpacer(), outcomeNameColumn, predictionAccuracyColumn_0to33, predictionAccuracyColumn_34to66, predictionAccuracyColumn_67to100, layout.NewSpacer())
resultsGrid := container.NewHBox(layout.NewSpacer(), outcomeNameColumn, predictionAccuracyColumn_0to33, predictionAccuracyColumn_34to66, predictionAccuracyColumn_67to100, viewTraitAccuracyDetailsColumn, layout.NewSpacer())
return resultsGrid, nil
}