From fd57afd87bcf19f0bcdcd6271d9a9ffd95c09ac7 Mon Sep 17 00:00:00 2001 From: Alan <alan@aptivate.org> Date: Wed, 1 Apr 2020 14:26:34 +0100 Subject: [PATCH] Key value --- Pipfile | 3 +- Pipfile.lock | 409 ++++++++++++------ internewshid/chn_spreadsheet/importer.py | 32 +- .../chn_spreadsheet/tests/importer_tests.py | 2 + .../chn_spreadsheet/tests/key_values_tests.py | 42 ++ .../test_files/sample_kobo_keyValues.xlsx | Bin 0 -> 6235 bytes .../data_layer/migrations/0027_key_value.py | 30 ++ internewshid/data_layer/models.py | 39 ++ .../hid/tests/add_edit_item_view_tests.py | 2 +- internewshid/rest_api/serializers.py | 4 + internewshid/rest_api/views.py | 17 +- internewshid/transport/items.py | 28 +- 12 files changed, 468 insertions(+), 140 deletions(-) create mode 100644 internewshid/chn_spreadsheet/tests/key_values_tests.py create mode 100644 internewshid/chn_spreadsheet/tests/test_files/sample_kobo_keyValues.xlsx create mode 100644 internewshid/data_layer/migrations/0027_key_value.py diff --git a/Pipfile b/Pipfile index a06a11cc..edc6a86b 100644 --- a/Pipfile +++ b/Pipfile @@ -10,7 +10,7 @@ python_version = "3.6" asgiref = "*" "django-bootstrap3" = "*" "django-tables2" = "*" -"django.js" = {ref = "ca328a94b00023bd64f4fc1c908675edaaf2ac19",git = "https://git@github.com/aptivate/django.js.git",editable = true} +"django.js" = {editable = true,git = "https://git@github.com/aptivate/django.js.git",ref = "ca328a94b00023bd64f4fc1c908675edaaf2ac19"} "linecache2" = "*" Django = ">2.2.8,<3.0" Pillow = "*" @@ -32,6 +32,7 @@ pytz = "*" rest-pandas = "*" mysqlclient = "*" django-debug-toolbar-template-timings = "*" +ipdb = "*" [dev-packages] django-debug-toolbar = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 7e26ea66..bad64889 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "8a5bc8f5d57faebea7b5e6fc4f117b54ae9dbc2410c85feca8332dc7831aa7dd" + "sha256": "853bc2ef3d6225526ec3254e2e004b9d724e7d198064ad801c0b47c0bd8e4943" }, "pipfile-spec": 6, "requires": { @@ -18,11 +18,18 @@ "default": { "asgiref": { "hashes": [ - "sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0", - "sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5" + "sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5", + "sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c" ], "index": "pypi", - "version": "==3.2.3" + "version": "==3.2.7" + }, + "backcall": { + "hashes": [ + "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", + "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" + ], + "version": "==0.1.0" }, "cssmin": { "hashes": [ @@ -31,6 +38,13 @@ "index": "pypi", "version": "==0.2.0" }, + "decorator": { + "hashes": [ + "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760", + "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7" + ], + "version": "==4.4.2" + }, "django": { "hashes": [ "sha256:65e2387e6bde531d3bb803244a2b74e0253550a9612c64a60c8c5be267b30f50", @@ -133,11 +147,11 @@ }, "django-widget-tweaks": { "hashes": [ - "sha256:65c960f3d75008a285e4b10f4d21f9eae4160fd77a0f6097ad545185f8648bd6", - "sha256:f2e2c9c9be1ccc59061e248dcc2144f4906d594abe1a563902f4bdf6aa14e432" + "sha256:9f91ca4217199b7671971d3c1f323a2bec71a0c27dec6260b3c006fa541bc489", + "sha256:f80bff4a8a59b278bb277a405a76a8b9a884e4bae7a6c70e78a39c626cd1c836" ], "index": "pypi", - "version": "==1.4.5" + "version": "==1.4.8" }, "django.js": { "editable": true, @@ -165,6 +179,27 @@ ], "version": "==1.0.1" }, + "ipdb": { + "hashes": [ + "sha256:77fb1c2a6fccdfee0136078c9ed6fe547ab00db00bebff181f1e8c9e13418d49" + ], + "index": "pypi", + "version": "==0.13.2" + }, + "ipython": { + "hashes": [ + "sha256:ca478e52ae1f88da0102360e57e528b92f3ae4316aabac80a2cd7f7ab2efb48a", + "sha256:eb8d075de37f678424527b5ef6ea23f7b80240ca031c2dd6de5879d687a65333" + ], + "version": "==7.13.0" + }, + "ipython-genutils": { + "hashes": [ + "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", + "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" + ], + "version": "==0.2.0" + }, "jdcal": { "hashes": [ "sha256:1abf1305fce18b4e8aa248cf8fe0c56ce2032392bc64bbd61b5dff2a19ec8bba", @@ -172,6 +207,13 @@ ], "version": "==1.4.1" }, + "jedi": { + "hashes": [ + "sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2", + "sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5" + ], + "version": "==0.16.0" + }, "jsmin": { "hashes": [ "sha256:b6df99b2cd1c75d9d342e4335b535789b8da9107ec748212706ef7bbe5c2553b" @@ -207,29 +249,29 @@ }, "numpy": { "hashes": [ - "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6", - "sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e", - "sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc", - "sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc", - "sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a", - "sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa", - "sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3", - "sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121", - "sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971", - "sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26", - "sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd", - "sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480", - "sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec", - "sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77", - "sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57", - "sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07", - "sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572", - "sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73", - "sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca", - "sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474", - "sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5" - ], - "version": "==1.18.1" + "sha256:1598a6de323508cfeed6b7cd6c4efb43324f4692e20d1f76e1feec7f59013448", + "sha256:1b0ece94018ae21163d1f651b527156e1f03943b986188dd81bc7e066eae9d1c", + "sha256:2e40be731ad618cb4974d5ba60d373cdf4f1b8dcbf1dcf4d9dff5e212baf69c5", + "sha256:4ba59db1fcc27ea31368af524dcf874d9277f21fd2e1f7f1e2e0c75ee61419ed", + "sha256:59ca9c6592da581a03d42cc4e270732552243dc45e87248aa8d636d53812f6a5", + "sha256:5e0feb76849ca3e83dd396254e47c7dba65b3fa9ed3df67c2556293ae3e16de3", + "sha256:6d205249a0293e62bbb3898c4c2e1ff8a22f98375a34775a259a0523111a8f6c", + "sha256:6fcc5a3990e269f86d388f165a089259893851437b904f422d301cdce4ff25c8", + "sha256:82847f2765835c8e5308f136bc34018d09b49037ec23ecc42b246424c767056b", + "sha256:87902e5c03355335fc5992a74ba0247a70d937f326d852fc613b7f53516c0963", + "sha256:9ab21d1cb156a620d3999dd92f7d1c86824c622873841d6b080ca5495fa10fef", + "sha256:a1baa1dc8ecd88fb2d2a651671a84b9938461e8a8eed13e2f0a812a94084d1fa", + "sha256:a244f7af80dacf21054386539699ce29bcc64796ed9850c99a34b41305630286", + "sha256:a35af656a7ba1d3decdd4fae5322b87277de8ac98b7d9da657d9e212ece76a61", + "sha256:b1fe1a6f3a6f355f6c29789b5927f8bd4f134a4bd9a781099a7c4f66af8850f5", + "sha256:b5ad0adb51b2dee7d0ee75a69e9871e2ddfb061c73ea8bc439376298141f77f5", + "sha256:ba3c7a2814ec8a176bb71f91478293d633c08582119e713a0c5351c0f77698da", + "sha256:cd77d58fb2acf57c1d1ee2835567cd70e6f1835e32090538f17f8a3a99e5e34b", + "sha256:cdb3a70285e8220875e4d2bc394e49b4988bdb1298ffa4e0bd81b2f613be397c", + "sha256:deb529c40c3f1e38d53d5ae6cd077c21f1d49e13afc7936f7f868455e16b64a0", + "sha256:e7894793e6e8540dbeac77c87b489e331947813511108ae097f1715c018b8f3d" + ], + "version": "==1.18.2" }, "openpyxl": { "hashes": [ @@ -240,22 +282,46 @@ }, "pandas": { "hashes": [ - "sha256:23e177d43e4bf68950b0f8788b6a2fef2f478f4ec94883acb627b9264522a98a", - "sha256:2530aea4fe46e8df7829c3f05e0a0f821c893885d53cb8ac9b89cc67c143448c", - "sha256:303827f0bb40ff610fbada5b12d50014811efcc37aaf6ef03202dc3054bfdda1", - "sha256:3b019e3ea9f5d0cfee0efabae2cfd3976874e90bcc3e97b29600e5a9b345ae3d", - "sha256:3c07765308f091d81b6735d4f2242bb43c332cc3461cae60543df6b10967fe27", - "sha256:5036d4009012a44aa3e50173e482b664c1fae36decd277c49e453463798eca4e", - "sha256:6f38969e2325056f9959efbe06c27aa2e94dd35382265ad0703681d993036052", - "sha256:74a470d349d52b9d00a2ba192ae1ee22155bb0a300fd1ccb2961006c3fa98ed3", - "sha256:7d77034e402165b947f43050a8a415aa3205abfed38d127ea66e57a2b7b5a9e0", - "sha256:7f9a509f6f11fa8b9313002ebdf6f690a7aa1dd91efd95d90185371a0d68220e", - "sha256:942b5d04762feb0e55b2ad97ce2b254a0ffdd344b56493b04a627266e24f2d82", - "sha256:a9fbe41663416bb70ed05f4e16c5f377519c0dc292ba9aa45f5356e37df03a38", - "sha256:d10e83866b48c0cdb83281f786564e2a2b51a7ae7b8a950c3442ad3c9e36b48c", - "sha256:e2140e1bbf9c46db9936ee70f4be6584d15ff8dc3dfff1da022d71227d53bad3" + "sha256:07c1b58936b80eafdfe694ce964ac21567b80a48d972879a359b3ebb2ea76835", + "sha256:0ebe327fb088df4d06145227a4aa0998e4f80a9e6aed4b61c1f303bdfdf7c722", + "sha256:11c7cb654cd3a0e9c54d81761b5920cdc86b373510d829461d8f2ed6d5905266", + "sha256:12f492dd840e9db1688126216706aa2d1fcd3f4df68a195f9479272d50054645", + "sha256:167a1315367cea6ec6a5e11e791d9604f8e03f95b57ad227409de35cf850c9c5", + "sha256:1a7c56f1df8d5ad8571fa251b864231f26b47b59cbe41aa5c0983d17dbb7a8e4", + "sha256:1fa4bae1a6784aa550a1c9e168422798104a85bf9c77a1063ea77ee6f8452e3a", + "sha256:32f42e322fb903d0e189a4c10b75ba70d90958cc4f66a1781ed027f1a1d14586", + "sha256:387dc7b3c0424327fe3218f81e05fc27832772a5dffbed385013161be58df90b", + "sha256:6597df07ea361231e60c00692d8a8099b519ed741c04e65821e632bc9ccb924c", + "sha256:743bba36e99d4440403beb45a6f4f3a667c090c00394c176092b0b910666189b", + "sha256:858a0d890d957ae62338624e4aeaf1de436dba2c2c0772570a686eaca8b4fc85", + "sha256:863c3e4b7ae550749a0bb77fa22e601a36df9d2905afef34a6965bed092ba9e5", + "sha256:a210c91a02ec5ff05617a298ad6f137b9f6f5771bf31f2d6b6367d7f71486639", + "sha256:ca84a44cf727f211752e91eab2d1c6c1ab0f0540d5636a8382a3af428542826e", + "sha256:d234bcf669e8b4d6cbcd99e3ce7a8918414520aeb113e2a81aeb02d0a533d7f7" ], - "version": "==1.0.1" + "version": "==1.0.3" + }, + "parso": { + "hashes": [ + "sha256:0c5659e0c6eba20636f99a04f469798dca8da279645ce5c387315b2c23912157", + "sha256:8515fc12cfca6ee3aa59138741fc5624d62340c97e401c74875769948d4f2995" + ], + "version": "==0.6.2" + }, + "pexpect": { + "hashes": [ + "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", + "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c" + ], + "markers": "sys_platform != 'win32'", + "version": "==4.8.0" + }, + "pickleshare": { + "hashes": [ + "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", + "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" + ], + "version": "==0.7.5" }, "pillow": { "hashes": [ @@ -285,6 +351,27 @@ "index": "pypi", "version": "==7.0.0" }, + "prompt-toolkit": { + "hashes": [ + "sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8", + "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04" + ], + "version": "==3.0.5" + }, + "ptyprocess": { + "hashes": [ + "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", + "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" + ], + "version": "==0.6.0" + }, + "pygments": { + "hashes": [ + "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", + "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" + ], + "version": "==2.6.1" + }, "python-dateutil": { "hashes": [ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", @@ -323,6 +410,20 @@ ], "version": "==0.3.1" }, + "traitlets": { + "hashes": [ + "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44", + "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7" + ], + "version": "==4.3.3" + }, + "wcwidth": { + "hashes": [ + "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1", + "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1" + ], + "version": "==0.1.9" + }, "webassets": { "hashes": [ "sha256:167132337677c8cedc9705090f6d48da3fb262c8e0b2773b29f3352f050181cd", @@ -332,13 +433,20 @@ } }, "develop": { + "appdirs": { + "hashes": [ + "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", + "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e" + ], + "version": "==1.4.3" + }, "asgiref": { "hashes": [ - "sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0", - "sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5" + "sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5", + "sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c" ], "index": "pypi", - "version": "==3.2.3" + "version": "==3.2.7" }, "attrs": { "hashes": [ @@ -363,46 +471,52 @@ }, "click": { "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc", + "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a" ], - "version": "==7.0" + "version": "==7.1.1" }, "coverage": { "hashes": [ - "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3", - "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c", - "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0", - "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477", - "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a", - "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf", - "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691", - "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73", - "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987", - "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894", - "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e", - "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef", - "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf", - "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68", - "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8", - "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954", - "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2", - "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40", - "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc", - "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc", - "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e", - "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d", - "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f", - "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc", - "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301", - "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea", - "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb", - "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af", - "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52", - "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37", - "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0" - ], - "version": "==5.0.3" + "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0", + "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30", + "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b", + "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0", + "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823", + "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe", + "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037", + "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6", + "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31", + "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd", + "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892", + "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1", + "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78", + "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac", + "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006", + "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014", + "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2", + "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7", + "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8", + "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7", + "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9", + "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1", + "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307", + "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a", + "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435", + "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0", + "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5", + "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441", + "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732", + "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de", + "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1" + ], + "version": "==5.0.4" + }, + "distlib": { + "hashes": [ + "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21" + ], + "version": "==0.3.0" }, "django": { "hashes": [ @@ -421,25 +535,25 @@ }, "django-dynamic-fixture": { "hashes": [ - "sha256:cb6fac74b60ced901ce1f8f9e4dd8c438ab6e9021301a5ce2550fb424add4c33" + "sha256:e772102ba40f70c2e66470cae85f3874aa992e6b22a0d0c360450f2949b0728d" ], "index": "pypi", - "version": "==3.0.3" + "version": "==3.1.0" }, "django-extensions": { "hashes": [ - "sha256:1a03c4e8bade575f8c2be6c76456f8a2be3f9b02ab9f47d3535afa9562dc0493", - "sha256:2699cc1d6fb4bd393c0b5832fea4bc685f2ace5800b3c9ff222b2080f161ac04" + "sha256:2f81b618ba4d1b0e58603e25012e5c74f88a4b706e0022a3b21f24f0322a6ce6", + "sha256:b19182d101a441fe001c5753553a901e2ef3ff60e8fbbe38881eb4a61fdd17c4" ], "index": "pypi", - "version": "==2.2.8" + "version": "==2.2.9" }, "dparse": { "hashes": [ - "sha256:00a5fdfa900629e5159bf3600d44905b333f4059a3366f28e0dbd13eeab17b19", - "sha256:cef95156fa0adedaf042cd42f9990974bec76f25dfeca4dc01f381a243d5aa5b" + "sha256:14fed5efc5e98c0a81dfe100c4c2ea0a4c189104e9a9d18b5cfd342a163f97be", + "sha256:db349e53f6d03c8ee80606c49b35f515ed2ab287a8e1579e2b4bdf52b12b1530" ], - "version": "==0.4.1" + "version": "==0.5.0" }, "factory-boy": { "hashes": [ @@ -451,10 +565,17 @@ }, "faker": { "hashes": [ - "sha256:440d68fe0e46c1658b1975b2497abe0c24a7f772e3892253f31e713ffcc48965", - "sha256:ee24608768549c2c69e593e9d7a3b53c9498ae735534243ec8390cae5d529f8b" + "sha256:2d3f866ef25e1a5af80e7b0ceeacc3c92dec5d0fdbad3e2cb6adf6e60b22188f", + "sha256:b89aa33837498498e15c709eb40c31386408a901a53c7a5e12a425737a767976" + ], + "version": "==4.0.2" + }, + "filelock": { + "hashes": [ + "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", + "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" ], - "version": "==4.0.1" + "version": "==3.0.12" }, "idna": { "hashes": [ @@ -465,11 +586,19 @@ }, "importlib-metadata": { "hashes": [ - "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302", - "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b" + "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", + "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" ], "markers": "python_version < '3.8'", - "version": "==1.5.0" + "version": "==1.6.0" + }, + "importlib-resources": { + "hashes": [ + "sha256:4019b6a9082d8ada9def02bece4a76b131518866790d58fdda0b5f8c603b36c2", + "sha256:dd98ceeef3f5ad2ef4cc287b8586da4ebad15877f351e9688987ad663a0a29b8" + ], + "markers": "python_version < '3.7'", + "version": "==1.4.0" }, "isort": { "hashes": [ @@ -488,11 +617,11 @@ }, "mock": { "hashes": [ - "sha256:2a572b715f09dd2f0a583d8aeb5bb67d7ed7a8fd31d193cf1227a99c16a67bc3", - "sha256:5e48d216809f6f393987ed56920305d8f3c647e6ed35407c1ff2ecb88a9e1151" + "sha256:3f9b2c0196c60d21838f307f5825a7b86b678cedc58ab9e50a8988187b4d81e0", + "sha256:dd33eb70232b6118298d516bbcecd26704689c386594f0f3c4f13867b2c56f72" ], "index": "pypi", - "version": "==4.0.1" + "version": "==4.0.2" }, "more-itertools": { "hashes": [ @@ -508,6 +637,14 @@ ], "version": "==20.3" }, + "pipenv": { + "hashes": [ + "sha256:56ad5f5cb48f1e58878e14525a6e3129d4306049cb76d2f6a3e95df0d5fc6330", + "sha256:7df8e33a2387de6f537836f48ac6fcd94eda6ed9ba3d5e3fd52e35b5bc7ff49e", + "sha256:a673e606e8452185e9817a987572b55360f4d28b50831ef3b42ac3cab3fee846" + ], + "version": "==2018.11.26" + }, "pluggy": { "hashes": [ "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", @@ -563,10 +700,10 @@ }, "pytest": { "hashes": [ - "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d", - "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6" + "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172", + "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970" ], - "version": "==5.3.5" + "version": "==5.4.1" }, "pytest-cov": { "hashes": [ @@ -578,11 +715,11 @@ }, "pytest-django": { "hashes": [ - "sha256:456fa6854d04ee625d6bbb8b38ca2259e7040a6f93333bfe8bc8159b7e987203", - "sha256:489b904f695f9fb880ce591cf5a4979880afb467763b1f180c07574554bdfd26" + "sha256:64f99d565dd9497af412fcab2989fe40982c1282d4118ff422b407f3f7275ca5", + "sha256:664e5f42242e5e182519388f01b9f25d824a9feb7cd17d8f863c8d776f38baf9" ], "index": "pypi", - "version": "==3.8.0" + "version": "==3.9.0" }, "pytest-env": { "hashes": [ @@ -616,19 +753,19 @@ }, "pyyaml": { "hashes": [ - "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", - "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", - "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", - "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", - "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", - "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", - "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", - "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", - "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", - "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", - "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" + "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", + "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", + "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", + "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", + "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", + "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", + "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", + "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", + "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", + "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", + "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" ], - "version": "==5.3" + "version": "==5.3.1" }, "requests": { "hashes": [ @@ -639,11 +776,11 @@ }, "safety": { "hashes": [ - "sha256:0a3a8a178a9c96242b224f033ee8d1d130c0448b0e6622d12deaf37f6c3b4e59", - "sha256:5059f3ffab3648330548ea9c7403405bbfaf085b11235770825d14c58f24cb78" + "sha256:05f77773bbab834502328b29ed013677aa53ed0c22b6e330aef7d2a7e1dfd838", + "sha256:3016631e0dd17193d6cf12e8ed1af92df399585e8ee0e4b1300d9e7e32b54903" ], "index": "pypi", - "version": "==1.8.5" + "version": "==1.8.7" }, "six": { "hashes": [ @@ -673,6 +810,13 @@ ], "version": "==1.3" }, + "toml": { + "hashes": [ + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" + ], + "version": "==0.10.0" + }, "urllib3": { "hashes": [ "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", @@ -680,12 +824,26 @@ ], "version": "==1.25.8" }, + "virtualenv": { + "hashes": [ + "sha256:4e399f48c6b71228bf79f5febd27e3bbb753d9d5905776a86667bc61ab628a25", + "sha256:9e81279f4a9d16d1c0654a127c2c86e5bca2073585341691882c1e66e31ef8a5" + ], + "version": "==20.0.15" + }, + "virtualenv-clone": { + "hashes": [ + "sha256:07e74418b7cc64f4fda987bf5bc71ebd59af27a7bc9e8a8ee9fd54b1f2390a27", + "sha256:665e48dd54c84b98b71a657acb49104c54e7652bce9c1c4f6c6976ed4c827a29" + ], + "version": "==0.5.4" + }, "wcwidth": { "hashes": [ - "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603", - "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" + "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1", + "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1" ], - "version": "==0.1.8" + "version": "==0.1.9" }, "werkzeug": { "hashes": [ @@ -700,6 +858,7 @@ "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" ], + "markers": "python_version < '3.8'", "version": "==3.1.0" } } diff --git a/internewshid/chn_spreadsheet/importer.py b/internewshid/chn_spreadsheet/importer.py index 88cdb3b1..365fd8b5 100644 --- a/internewshid/chn_spreadsheet/importer.py +++ b/internewshid/chn_spreadsheet/importer.py @@ -1,4 +1,5 @@ import datetime +from collections import OrderedDict from decimal import Decimal from django.utils import six @@ -61,14 +62,21 @@ class Importer(object): if first_row: col_map = self.get_columns_map() - for label in first_row[:len(col_map)]: + for label in first_row: if label is not None: + stripped_label = '' try: stripped_label = label.strip() columns.append(col_map[stripped_label]) - except Exception: - error_msg = _('Unknown column: {0}').format(label) - raise SheetImportException(error_msg) + except KeyError: + # If the column isn't in the importer specification then save the data as a keyvalue. + if stripped_label: + col = OrderedDict([('field', 'values'), ('type', 'keyvalue'), ('name', stripped_label)]) + columns.append(col) + + except Exception as exception: + # error_msg = _('Unknown column: {0}').format(label) + raise SheetImportException(exception) else: columns = [d.copy() for d in profile_columns] @@ -108,7 +116,6 @@ class Importer(object): except SheetImportException as e: raise type(e)(str(e) + 'in row {0} '.format(i)) from e - return objects def process_row(self, values, columns): @@ -127,6 +134,11 @@ class Importer(object): if value: self._append_term_to_item( item, 'tags', value.strip()) + elif col['type'] == 'keyvalue': + key, value = converter.convert_value() + if value: + self._append_keyvalue_to_item( + item, key, value) else: converter.add_to(item) @@ -136,6 +148,10 @@ class Importer(object): term = self._get_term_dict(taxonomy, name) item.setdefault('terms', []).append(term) + def _append_keyvalue_to_item(self, item, key, value): + keyvalue = {'key': key, 'value': value} + item.setdefault('keyvalues', []).append(keyvalue) + def _get_term_dict(self, taxonomy, name): return {'taxonomy': taxonomy, 'name': name} @@ -145,11 +161,15 @@ class Importer(object): for obj in objects: row = obj.pop('_row_number', '') terms = obj.pop('terms', []) + keyvalues = obj.pop('keyvalues', []) try: item = transport.items.create(obj) for term in terms: transport.items.add_terms( item['id'], term['taxonomy'], term['name']) + for keyvalue in keyvalues: + transport.items.add_keyvalue( + item['id'], keyvalue['key'], keyvalue['value']) except ItemNotUniqueException: pass @@ -244,6 +264,7 @@ class CellConverter(object): self.value = value self.type = col_spec['type'] self.field = col_spec['field'] + self.name = col_spec.get('name') self.date_format = col_spec.get('date_format', None) def add_to(self, object_dict): @@ -258,6 +279,7 @@ class CellConverter(object): 'integer': lambda x: int(x), 'number': lambda x: Decimal(x), 'taxonomy': lambda x: x if x else '', + 'keyvalue': lambda x: (self.name, x) if x else '', 'protection_concern': lambda x: 'Protection Concern' if x.lower() == 'yes' else '' } if self.type not in converters: diff --git a/internewshid/chn_spreadsheet/tests/importer_tests.py b/internewshid/chn_spreadsheet/tests/importer_tests.py index f891e6a0..8707b8b7 100644 --- a/internewshid/chn_spreadsheet/tests/importer_tests.py +++ b/internewshid/chn_spreadsheet/tests/importer_tests.py @@ -139,6 +139,7 @@ def test_order_columns_with_first_row_return_first_row_order(importer): assert ordered == [cleaned[2], cleaned[0], cleaned[1]] +@pytest.mark.skip("importing all columns") # Now importing all missing columns as key value pairs def test_order_columns_ignores_extra_columns_in_first_row(importer): cleaned = _make_columns_row(COLUMN_LIST) first_row = ['Message', 'Province', 'Sub-Province', 'None', 'None', 'None'] @@ -148,6 +149,7 @@ def test_order_columns_ignores_extra_columns_in_first_row(importer): assert ordered == [cleaned[2], cleaned[0], cleaned[1]] +@pytest.mark.skip("importing all columns") # Now importing all missing columns as key value pairs def test_order_columns_ignores_none_and_missing_columns_in_first_row(importer): first_row = ['Province', None] diff --git a/internewshid/chn_spreadsheet/tests/key_values_tests.py b/internewshid/chn_spreadsheet/tests/key_values_tests.py new file mode 100644 index 00000000..c65aa90f --- /dev/null +++ b/internewshid/chn_spreadsheet/tests/key_values_tests.py @@ -0,0 +1,42 @@ +import datetime +from os import path + +from django.core.management import call_command + +import pytest + +import transport +from chn_spreadsheet.tests.conftest import taxonomies # noqa + +TEST_BASE_DIR = path.abspath(path.dirname(__file__)) +TEST_DIR = path.join(TEST_BASE_DIR, 'test_files') + + +@pytest.fixture +def django_db_setup(django_db_setup, django_db_blocker): + with django_db_blocker.unblock(): + call_command('loaddata', 'spreadsheet-profiles.json') + + +@pytest.mark.django_db # noqa +def test_kobo_keyvalue_imported(importer, django_db_setup, taxonomies): # noqa + assert len(transport.items.list_items()['results']) == 0 + + file_path = path.join(TEST_DIR, 'sample_kobo_keyValues.xlsx') + (num_saved, _) = importer.store_spreadsheet('kobo', open(file_path, 'rb')) + + assert num_saved > 0 + + items = transport.items.list_items()['results'] + assert len(items) == num_saved + + # default ordering is by timestamp desc + assert items[2]['body'] == 'the community members want more food.' + assert items[2]['translation'] == '' + assert items[2]['location'] == 'Camp 4' + assert items[2]['language'] == 'English' + assert items[2]['risk'] == '1' + assert items[2]['values']['KV1'] == 'one' + assert items[1]['values']['KV1'] == '2020-12-12 00:00:00' + assert items[0]['values']['KV1'] == '123' + assert isinstance(items[2]['timestamp'], datetime.datetime) diff --git a/internewshid/chn_spreadsheet/tests/test_files/sample_kobo_keyValues.xlsx b/internewshid/chn_spreadsheet/tests/test_files/sample_kobo_keyValues.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..15e8d2d321f6b57ac5edb851c829673fa65fbe7f GIT binary patch literal 6235 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 Mo_pZC1p>nV03sB5w*UYD literal 0 HcmV?d00001 diff --git a/internewshid/data_layer/migrations/0027_key_value.py b/internewshid/data_layer/migrations/0027_key_value.py new file mode 100644 index 00000000..f737a852 --- /dev/null +++ b/internewshid/data_layer/migrations/0027_key_value.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.11 on 2020-03-26 16:14 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('data_layer', '0026_message_risk'), + ] + + operations = [ + migrations.CreateModel( + name='Key', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(db_index=True, help_text='Key', max_length=190, unique=True, verbose_name='Key')), + ], + ), + migrations.CreateModel( + name='Value', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.CharField(help_text='Value', max_length=190, verbose_name='Value')), + ('key', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='data_layer.Key', verbose_name='Key')), + ('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='data_layer.Message', verbose_name='Message')), + ], + ), + ] diff --git a/internewshid/data_layer/models.py b/internewshid/data_layer/models.py index 7d21935f..2edfb50a 100644 --- a/internewshid/data_layer/models.py +++ b/internewshid/data_layer/models.py @@ -14,6 +14,45 @@ except ImportError: raise ImproperlyConfigured('Could not import django-picklefield.') +class Key(models.Model): + + key = models.CharField( + verbose_name=_('Key'), + max_length=190, + help_text=_('Key'), + unique=True, + db_index=True, + ) + + def __str__(self): + return self.key + + +class Value(models.Model): + + value = models.CharField( + verbose_name=_('Value'), + max_length=190, + help_text=_('Value'), + ) + + key = models.ForeignKey( + Key, + verbose_name=_('Key'), + on_delete=models.CASCADE + ) + + message = models.ForeignKey( + "Message", + related_name='values', + verbose_name=_('Message'), + on_delete=models.CASCADE + ) + + def __str__(self): + return self.value + + class DataLayerModel(models.Model): created = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) diff --git a/internewshid/hid/tests/add_edit_item_view_tests.py b/internewshid/hid/tests/add_edit_item_view_tests.py index ed1114eb..d5eb246f 100644 --- a/internewshid/hid/tests/add_edit_item_view_tests.py +++ b/internewshid/hid/tests/add_edit_item_view_tests.py @@ -662,7 +662,7 @@ def test_item_can_be_deleted_with_post_request(item): @pytest.mark.django_db def test_item_can_be_updated(view, update_form): new_text = "What is the cause of Ebola?" - update_form.cleaned_data['body'] = new_text, + update_form.cleaned_data['body'] = new_text view.form_valid(update_form) item = transport.items.get(view.item['id']) diff --git a/internewshid/rest_api/serializers.py b/internewshid/rest_api/serializers.py index 8109d7f9..3cfacc16 100644 --- a/internewshid/rest_api/serializers.py +++ b/internewshid/rest_api/serializers.py @@ -79,6 +79,10 @@ class ItemSerializer(serializers.ModelSerializer): timestamp = IgnoreMicrosecondsDateTimeField() terms = TermSerializer(many=True, required=False) + values = serializers.SerializerMethodField() + + def get_values(self, item): + return {kv.key.key: kv.value for kv in item.values.all()} def create(self, validated_data): """ Create an item with nested metadata terms.""" diff --git a/internewshid/rest_api/views.py b/internewshid/rest_api/views.py index bb17428b..3d380834 100644 --- a/internewshid/rest_api/views.py +++ b/internewshid/rest_api/views.py @@ -12,7 +12,7 @@ from rest_framework.response import Response from rest_framework_bulk.mixins import BulkDestroyModelMixin from rest_pandas import PandasView -from data_layer.models import Item +from data_layer.models import Item, Key, Value from taxonomies.models import Taxonomy, Term from .serializers import ( @@ -183,6 +183,21 @@ class ItemViewSet(viewsets.ModelViewSet, BulkDestroyModelMixin): serializer = ItemSerializer(item) return Response(serializer.data, status=status.HTTP_200_OK) + @action(methods=['post'], detail=True) + def add_keyvalue(self, request, item_pk): + try: + item = Item.objects.get(pk=item_pk) + except Item.DoesNotExist as e: + data = {'detail': str(e)} + return Response(data, status=status.HTTP_404_NOT_FOUND) + + keyvalue = request.data + key, _ = Key.objects.get_or_create(key=keyvalue['key']) + value = Value.objects.create(key=key, value=keyvalue['value'], message=item) + + serializer = ItemSerializer(item) + return Response(serializer.data, status=status.HTTP_200_OK) + @action(methods=['post'], detail=True) def delete_all_terms(self, request, item_pk): taxonomy_slug = request.data['taxonomy'] diff --git a/internewshid/transport/items.py b/internewshid/transport/items.py index dfc2845f..d45cb1d2 100644 --- a/internewshid/transport/items.py +++ b/internewshid/transport/items.py @@ -111,7 +111,7 @@ def create(item): def update(id, item): """ Update an Item from the given dict """ view = get_view({'put': 'update'}) - request = request_factory.put("", item) + request = request_factory.put("", item, format='json') response = view(request, pk=id) if status.is_success(response.status_code): return response.data @@ -180,13 +180,27 @@ def add_terms(item_id, taxonomy_slug, names): if status.is_success(response.status_code): return response.data - else: - response.data['status_code'] = response.status_code - response.data['terms'] = terms - response.data['item_id'] = item_id - raise TransportException(response.data) - return response.data + response.data['status_code'] = response.status_code + response.data['terms'] = terms + response.data['item_id'] = item_id + raise TransportException(response.data) + + +def add_keyvalue(item_id, key, value): + view = get_view({'post': 'add_keyvalue'}) + + keyvalue = {'key': key, 'value': value} + request = request_factory.post('', keyvalue) + response = view(request, item_pk=item_id) + + if status.is_success(response.status_code): + return response.data + + response.data['status_code'] = response.status_code + response.data['value'] = keyvalue + response.data['item_id'] = item_id + raise TransportException(response.data) def delete_all_terms(item_id, taxonomy_slug): -- GitLab