mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-16 01:27:03 +02:00
Merge branch 'main' into za/1456-sorting-not-working-correctly
This commit is contained in:
commit
fc814fdb43
28 changed files with 840 additions and 862 deletions
|
@ -13,7 +13,9 @@
|
||||||
It is necessary to use `bash -c` because `run pipenv requirements` will not recognize that it is running non-interactively and will include garbage formatting characters.
|
It is necessary to use `bash -c` because `run pipenv requirements` will not recognize that it is running non-interactively and will include garbage formatting characters.
|
||||||
|
|
||||||
The requirements.txt is used by Cloud.gov. It is needed to work around a bug in the CloudFoundry buildpack version of Pipenv that breaks on installing from a git repository.
|
The requirements.txt is used by Cloud.gov. It is needed to work around a bug in the CloudFoundry buildpack version of Pipenv that breaks on installing from a git repository.
|
||||||
|
3. Change geventconnpool back to what it was originally within the Pipfile.lock and requirements.txt.
|
||||||
3. (optional) Run `docker-compose stop` and `docker-compose build` to build a new image for local development with the updated dependencies.
|
This is done by either saving what it was originally or opening a PR and using that as a reference to undo changes to any mention of geventconnpool.
|
||||||
|
Geventconnpool, when set as a requirement without the reference portion, is defaulting to get a commit from 2014 which then breaks the code, as we want the newest version from them.
|
||||||
|
4. (optional) Run `docker-compose stop` and `docker-compose build` to build a new image for local development with the updated dependencies.
|
||||||
|
|
||||||
The reason for de-coupling the `build` and `lock` steps is to increase consistency between builds--a run of `build` will always get exactly the dependencies listed in `Pipfile.lock`, nothing more, nothing less.
|
The reason for de-coupling the `build` and `lock` steps is to increase consistency between builds--a run of `build` will always get exactly the dependencies listed in `Pipfile.lock`, nothing more, nothing less.
|
499
src/Pipfile.lock
generated
499
src/Pipfile.lock
generated
|
@ -32,20 +32,20 @@
|
||||||
},
|
},
|
||||||
"boto3": {
|
"boto3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:02ce7dcad2d3b054cd99e7ca6df7a708e016a31b1c98b46d8df3b3891070c121",
|
"sha256:d12467fb3a64d359b0bda0570a8163a5859fcac13e786f2a3db0392523178556",
|
||||||
"sha256:b8acb57a124434284d6ab69c61d32d70e84e13e2c27c33b4ad3c32f15ad407d3"
|
"sha256:eed0f7df91066b6ac63a53d16459ac082458d57061bedf766135d9e1c2b75a6b"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==1.28.79"
|
"version": "==1.33.7"
|
||||||
},
|
},
|
||||||
"botocore": {
|
"botocore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:07ecb93833475dde68e5c0e02a7ccf8ca22caf68cdc892651c300529894133e1",
|
"sha256:71ec0e85b996cf9def3dd8f4ca6cb4a9fd3a614aa4c9c7cbf33f2f68e1d0649a",
|
||||||
"sha256:6f1fc49e9e12f9772b4fef577837670bc84d772a7c946b4d08fe2890e34a4305"
|
"sha256:b2299bc13bb8c0928edc98bf4594deb14cba2357536120f63772027a16ce7374"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==1.31.79"
|
"version": "==1.33.7"
|
||||||
},
|
},
|
||||||
"cachetools": {
|
"cachetools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -58,11 +58,11 @@
|
||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082",
|
"sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1",
|
||||||
"sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"
|
"sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.6'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==2023.7.22"
|
"version": "==2023.11.17"
|
||||||
},
|
},
|
||||||
"cfenv": {
|
"cfenv": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -228,32 +228,32 @@
|
||||||
},
|
},
|
||||||
"cryptography": {
|
"cryptography": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf",
|
"sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960",
|
||||||
"sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84",
|
"sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a",
|
||||||
"sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e",
|
"sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc",
|
||||||
"sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8",
|
"sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a",
|
||||||
"sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7",
|
"sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf",
|
||||||
"sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1",
|
"sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1",
|
||||||
"sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88",
|
"sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39",
|
||||||
"sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86",
|
"sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406",
|
||||||
"sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179",
|
"sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a",
|
||||||
"sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81",
|
"sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a",
|
||||||
"sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20",
|
"sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c",
|
||||||
"sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548",
|
"sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be",
|
||||||
"sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d",
|
"sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15",
|
||||||
"sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d",
|
"sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2",
|
||||||
"sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5",
|
"sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d",
|
||||||
"sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1",
|
"sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157",
|
||||||
"sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147",
|
"sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003",
|
||||||
"sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936",
|
"sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248",
|
||||||
"sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797",
|
"sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a",
|
||||||
"sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696",
|
"sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec",
|
||||||
"sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72",
|
"sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309",
|
||||||
"sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da",
|
"sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7",
|
||||||
"sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723"
|
"sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==41.0.5"
|
"version": "==41.0.7"
|
||||||
},
|
},
|
||||||
"defusedxml": {
|
"defusedxml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -305,19 +305,19 @@
|
||||||
},
|
},
|
||||||
"django-cache-url": {
|
"django-cache-url": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:5ca4760b4580b80e41279bc60d1e5c16a822e4e462265faab0a330701bb0ef9a",
|
"sha256:5f350759978483ab85dc0e3e17b3d53eed3394a28148f6bf0f53d11d0feb5b3c",
|
||||||
"sha256:ef2cfacea361ee22e9b67d6ca941db22e0a9eaf892b67ca71cad52c62a17fd36"
|
"sha256:eb9fb194717524348c95cad9905b70b647452741c1d9e481fac6d2125f0ad917"
|
||||||
],
|
],
|
||||||
"version": "==3.4.4"
|
"version": "==3.4.5"
|
||||||
},
|
},
|
||||||
"django-cors-headers": {
|
"django-cors-headers": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:25aabc94d4837678c1edf442c7f68a5f5fd151f6767b0e0b01c61a2179d02711",
|
"sha256:0b1fd19297e37417fc9f835d39e45c8c642938ddba1acce0c1753d3edef04f36",
|
||||||
"sha256:bd36c7aea0d070e462f3383f0dc9ef717e5fdc2b10a99c98c285f16da84ffba2"
|
"sha256:0bf65ef45e606aff1994d35503e6b677c0b26cafff6506f8fd7187f3be840207"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==4.3.0"
|
"version": "==4.3.1"
|
||||||
},
|
},
|
||||||
"django-csp": {
|
"django-csp": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -375,11 +375,11 @@
|
||||||
},
|
},
|
||||||
"faker": {
|
"faker": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:14ccb0aec342d33aa3889a864a56e5b3c2d56bce1b89f9189f4fbc128b9afc1e",
|
"sha256:562a3a09c3ed3a1a7b20e13d79f904dfdfc5e740f72813ecf95e4cf71e5a2f52",
|
||||||
"sha256:da880a76322db7a879c848a0771e129338e0a680a9f695fd9a3e7a6ac82b45e1"
|
"sha256:aeb3e26742863d1e387f9d156f1c36e14af63bf5e6f36fb39b8c27f6a903be38"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==19.13.0"
|
"version": "==20.1.0"
|
||||||
},
|
},
|
||||||
"fred-epplib": {
|
"fred-epplib": {
|
||||||
"git": "https://github.com/cisagov/epplib.git",
|
"git": "https://github.com/cisagov/epplib.git",
|
||||||
|
@ -525,11 +525,11 @@
|
||||||
},
|
},
|
||||||
"idna": {
|
"idna": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
"sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca",
|
||||||
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
"sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.5'",
|
"markers": "python_version >= '3.5'",
|
||||||
"version": "==3.4"
|
"version": "==3.6"
|
||||||
},
|
},
|
||||||
"jmespath": {
|
"jmespath": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -639,11 +639,11 @@
|
||||||
},
|
},
|
||||||
"mako": {
|
"mako": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818",
|
"sha256:57d4e997349f1a92035aa25c17ace371a4213f2ca42f99bee9a602500cfd54d9",
|
||||||
"sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"
|
"sha256:e3a9d388fd00e87043edbe8792f45880ac0114e9c4adc69f6e9bfb2c55e3b11b"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.2.4"
|
"version": "==1.3.0"
|
||||||
},
|
},
|
||||||
"markupsafe": {
|
"markupsafe": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -745,10 +745,10 @@
|
||||||
},
|
},
|
||||||
"phonenumberslite": {
|
"phonenumberslite": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1e03f7076ab2f010088b1b8041ebdc42acd3b797e8f45997ab1861cdaea76851",
|
"sha256:305736b1b489e2bc6831710a2f34a9324f2bf96a1e77c8e0b3136dfaf9ca7753",
|
||||||
"sha256:adce353ee15b75f2deccf0eff77bada2a3d036f49ccfb30b8c172dd814fd51e9"
|
"sha256:6356f2728fa1d2c2bc9e79c3bfcfedc91a36537df7a134f150731a821a469a96"
|
||||||
],
|
],
|
||||||
"version": "==8.13.24"
|
"version": "==8.13.26"
|
||||||
},
|
},
|
||||||
"psycopg2-binary": {
|
"psycopg2-binary": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -877,131 +877,130 @@
|
||||||
},
|
},
|
||||||
"pydantic": {
|
"pydantic": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7",
|
"sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0",
|
||||||
"sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"
|
"sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==2.4.2"
|
"version": "==2.5.2"
|
||||||
},
|
},
|
||||||
"pydantic-core": {
|
"pydantic-core": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e",
|
"sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b",
|
||||||
"sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33",
|
"sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b",
|
||||||
"sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7",
|
"sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d",
|
||||||
"sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7",
|
"sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8",
|
||||||
"sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea",
|
"sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124",
|
||||||
"sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4",
|
"sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189",
|
||||||
"sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0",
|
"sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c",
|
||||||
"sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7",
|
"sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d",
|
||||||
"sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94",
|
"sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f",
|
||||||
"sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff",
|
"sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520",
|
||||||
"sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82",
|
"sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4",
|
||||||
"sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd",
|
"sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6",
|
||||||
"sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893",
|
"sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955",
|
||||||
"sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e",
|
"sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3",
|
||||||
"sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d",
|
"sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b",
|
||||||
"sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901",
|
"sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a",
|
||||||
"sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9",
|
"sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68",
|
||||||
"sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c",
|
"sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3",
|
||||||
"sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7",
|
"sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd",
|
||||||
"sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891",
|
"sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de",
|
||||||
"sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f",
|
"sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b",
|
||||||
"sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a",
|
"sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634",
|
||||||
"sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9",
|
"sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7",
|
||||||
"sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5",
|
"sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459",
|
||||||
"sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e",
|
"sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7",
|
||||||
"sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a",
|
"sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3",
|
||||||
"sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c",
|
"sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331",
|
||||||
"sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f",
|
"sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf",
|
||||||
"sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514",
|
"sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d",
|
||||||
"sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b",
|
"sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36",
|
||||||
"sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302",
|
"sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59",
|
||||||
"sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096",
|
"sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937",
|
||||||
"sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0",
|
"sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc",
|
||||||
"sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27",
|
"sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093",
|
||||||
"sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884",
|
"sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753",
|
||||||
"sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a",
|
"sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706",
|
||||||
"sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357",
|
"sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca",
|
||||||
"sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430",
|
"sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260",
|
||||||
"sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221",
|
"sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997",
|
||||||
"sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325",
|
"sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588",
|
||||||
"sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4",
|
"sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71",
|
||||||
"sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05",
|
"sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb",
|
||||||
"sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55",
|
"sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e",
|
||||||
"sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875",
|
"sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69",
|
||||||
"sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970",
|
"sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5",
|
||||||
"sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc",
|
"sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07",
|
||||||
"sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6",
|
"sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1",
|
||||||
"sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f",
|
"sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0",
|
||||||
"sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b",
|
"sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd",
|
||||||
"sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d",
|
"sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8",
|
||||||
"sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15",
|
"sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944",
|
||||||
"sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118",
|
"sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26",
|
||||||
"sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee",
|
"sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda",
|
||||||
"sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e",
|
"sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4",
|
||||||
"sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6",
|
"sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9",
|
||||||
"sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208",
|
"sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00",
|
||||||
"sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede",
|
"sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe",
|
||||||
"sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3",
|
"sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6",
|
||||||
"sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e",
|
"sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada",
|
||||||
"sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada",
|
"sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4",
|
||||||
"sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175",
|
"sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7",
|
||||||
"sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a",
|
"sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325",
|
||||||
"sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c",
|
"sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4",
|
||||||
"sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f",
|
"sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b",
|
||||||
"sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58",
|
"sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88",
|
||||||
"sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f",
|
"sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04",
|
||||||
"sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a",
|
"sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863",
|
||||||
"sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a",
|
"sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0",
|
||||||
"sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921",
|
"sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911",
|
||||||
"sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e",
|
"sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b",
|
||||||
"sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904",
|
"sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e",
|
||||||
"sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776",
|
"sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144",
|
||||||
"sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52",
|
"sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5",
|
||||||
"sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf",
|
"sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720",
|
||||||
"sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8",
|
"sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab",
|
||||||
"sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f",
|
"sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d",
|
||||||
"sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b",
|
"sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789",
|
||||||
"sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63",
|
"sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec",
|
||||||
"sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c",
|
"sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2",
|
||||||
"sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f",
|
"sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db",
|
||||||
"sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468",
|
"sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f",
|
||||||
"sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e",
|
"sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef",
|
||||||
"sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab",
|
"sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3",
|
||||||
"sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2",
|
"sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209",
|
||||||
"sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb",
|
"sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc",
|
||||||
"sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb",
|
"sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651",
|
||||||
"sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132",
|
"sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8",
|
||||||
"sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b",
|
"sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e",
|
||||||
"sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607",
|
"sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66",
|
||||||
"sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934",
|
"sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7",
|
||||||
"sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698",
|
"sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550",
|
||||||
"sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e",
|
"sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd",
|
||||||
"sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561",
|
"sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405",
|
||||||
"sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de",
|
"sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27",
|
||||||
"sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b",
|
"sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093",
|
||||||
"sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a",
|
"sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077",
|
||||||
"sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595",
|
"sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113",
|
||||||
"sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402",
|
"sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3",
|
||||||
"sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881",
|
"sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6",
|
||||||
"sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429",
|
"sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf",
|
||||||
"sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5",
|
"sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed",
|
||||||
"sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7",
|
"sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88",
|
||||||
"sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c",
|
"sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe",
|
||||||
"sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531",
|
"sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18",
|
||||||
"sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6",
|
"sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"
|
||||||
"sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"
|
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==2.10.1"
|
"version": "==2.14.5"
|
||||||
},
|
},
|
||||||
"pydantic-settings": {
|
"pydantic-settings": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:962dc3672495aad6ae96a4390fac7e593591e144625e5112d359f8f67fb75945",
|
"sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c",
|
||||||
"sha256:ddd907b066622bd67603b75e2ff791875540dc485b7307c4fffc015719da8625"
|
"sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==2.0.3"
|
"version": "==2.1.0"
|
||||||
},
|
},
|
||||||
"pyjwkest": {
|
"pyjwkest": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1037,19 +1036,19 @@
|
||||||
},
|
},
|
||||||
"s3transfer": {
|
"s3transfer": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a",
|
"sha256:368ac6876a9e9ed91f6bc86581e319be08188dc60d50e0d56308ed5765446283",
|
||||||
"sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e"
|
"sha256:c9e56cbe88b28d8e197cf841f1f0c130f246595e77ae5b5a05b69fe7cb83de76"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==0.7.0"
|
"version": "==0.8.2"
|
||||||
},
|
},
|
||||||
"setuptools": {
|
"setuptools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87",
|
"sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2",
|
||||||
"sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"
|
"sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==68.2.2"
|
"version": "==69.0.2"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1172,28 +1171,28 @@
|
||||||
},
|
},
|
||||||
"black": {
|
"black": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:037e9b4664cafda5f025a1728c50a9e9aedb99a759c89f760bd83730e76ba884",
|
"sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4",
|
||||||
"sha256:1b917a2aa020ca600483a7b340c165970b26e9029067f019e3755b56e8dd5916",
|
"sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b",
|
||||||
"sha256:1f8ce316753428ff68749c65a5f7844631aa18c8679dfd3ca9dc1a289979c258",
|
"sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f",
|
||||||
"sha256:33d40f5b06be80c1bbce17b173cda17994fbad096ce60eb22054da021bf933d1",
|
"sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07",
|
||||||
"sha256:3f157a8945a7b2d424da3335f7ace89c14a3b0625e6593d21139c2d8214d55ce",
|
"sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187",
|
||||||
"sha256:5ed45ac9a613fb52dad3b61c8dea2ec9510bf3108d4db88422bacc7d1ba1243d",
|
"sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6",
|
||||||
"sha256:6d23d7822140e3fef190734216cefb262521789367fbdc0b3f22af6744058982",
|
"sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05",
|
||||||
"sha256:7670242e90dc129c539e9ca17665e39a146a761e681805c54fbd86015c7c84f7",
|
"sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06",
|
||||||
"sha256:7b4d10b0f016616a0d93d24a448100adf1699712fb7a4efd0e2c32bbb219b173",
|
"sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e",
|
||||||
"sha256:7cb5936e686e782fddb1c73f8aa6f459e1ad38a6a7b0e54b403f1f05a1507ee9",
|
"sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5",
|
||||||
"sha256:7d56124b7a61d092cb52cce34182a5280e160e6aff3137172a68c2c2c4b76bcb",
|
"sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244",
|
||||||
"sha256:840015166dbdfbc47992871325799fd2dc0dcf9395e401ada6d88fe11498abad",
|
"sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f",
|
||||||
"sha256:9c74de4c77b849e6359c6f01987e94873c707098322b91490d24296f66d067dc",
|
"sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221",
|
||||||
"sha256:b15b75fc53a2fbcac8a87d3e20f69874d161beef13954747e053bca7a1ce53a0",
|
"sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055",
|
||||||
"sha256:cfcce6f0a384d0da692119f2d72d79ed07c7159879d0bb1bb32d2e443382bf3a",
|
"sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479",
|
||||||
"sha256:d431e6739f727bb2e0495df64a6c7a5310758e87505f5f8cde9ff6c0f2d7e4fe",
|
"sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394",
|
||||||
"sha256:e293e4c2f4a992b980032bbd62df07c1bcff82d6964d6c9496f2cd726e246ace",
|
"sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911",
|
||||||
"sha256:ec3f8e6234c4e46ff9e16d9ae96f4ef69fa328bb4ad08198c8cee45bb1f08c69"
|
"sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==23.10.1"
|
"version": "==23.11.0"
|
||||||
},
|
},
|
||||||
"blinker": {
|
"blinker": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1205,12 +1204,12 @@
|
||||||
},
|
},
|
||||||
"boto3": {
|
"boto3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:02ce7dcad2d3b054cd99e7ca6df7a708e016a31b1c98b46d8df3b3891070c121",
|
"sha256:d12467fb3a64d359b0bda0570a8163a5859fcac13e786f2a3db0392523178556",
|
||||||
"sha256:b8acb57a124434284d6ab69c61d32d70e84e13e2c27c33b4ad3c32f15ad407d3"
|
"sha256:eed0f7df91066b6ac63a53d16459ac082458d57061bedf766135d9e1c2b75a6b"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==1.28.79"
|
"version": "==1.33.7"
|
||||||
},
|
},
|
||||||
"boto3-mocking": {
|
"boto3-mocking": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1223,28 +1222,28 @@
|
||||||
},
|
},
|
||||||
"boto3-stubs": {
|
"boto3-stubs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:621e229ef9b394cd1f6cd5caa58a17347440b14423b01435d9f2a50031a427fc",
|
"sha256:0461f6fec92d96aa2ea3a207329bd020a62a7aaa86f284e5cf054d9b0c7f03c2",
|
||||||
"sha256:f5986d1b09d516f58780100a3a86bfa75114370dd5dd0bdea67bfe8cda255723"
|
"sha256:449b91060cd953e08980d76a3b67d7eb4246e663b37ecba4ec625b54619e1c22"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==1.28.79"
|
"version": "==1.33.7"
|
||||||
},
|
},
|
||||||
"botocore": {
|
"botocore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:07ecb93833475dde68e5c0e02a7ccf8ca22caf68cdc892651c300529894133e1",
|
"sha256:71ec0e85b996cf9def3dd8f4ca6cb4a9fd3a614aa4c9c7cbf33f2f68e1d0649a",
|
||||||
"sha256:6f1fc49e9e12f9772b4fef577837670bc84d772a7c946b4d08fe2890e34a4305"
|
"sha256:b2299bc13bb8c0928edc98bf4594deb14cba2357536120f63772027a16ce7374"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==1.31.79"
|
"version": "==1.33.7"
|
||||||
},
|
},
|
||||||
"botocore-stubs": {
|
"botocore-stubs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:64488b9f38905f8a60041998f9dc945754222d900a3345b449059667890c2c17",
|
"sha256:ca5de1ad4dc384f919387bb96eececb70900fda9219acfcf4b473b35a4834ec9",
|
||||||
"sha256:e4d8e782d774f45dbfc36d922a0a0edfffbacca2ce66bccaba02a893a38359f2"
|
"sha256:f73e4728a4a391f0407cd9403f6935343aac5687fb1e0eab7c3351d3419e853b"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
||||||
"version": "==1.31.79"
|
"version": "==1.33.7"
|
||||||
},
|
},
|
||||||
"click": {
|
"click": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1363,37 +1362,37 @@
|
||||||
},
|
},
|
||||||
"mypy": {
|
"mypy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7",
|
"sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340",
|
||||||
"sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e",
|
"sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49",
|
||||||
"sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c",
|
"sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82",
|
||||||
"sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169",
|
"sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce",
|
||||||
"sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208",
|
"sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb",
|
||||||
"sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0",
|
"sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51",
|
||||||
"sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1",
|
"sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5",
|
||||||
"sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1",
|
"sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e",
|
||||||
"sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7",
|
"sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7",
|
||||||
"sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45",
|
"sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33",
|
||||||
"sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143",
|
"sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9",
|
||||||
"sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5",
|
"sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1",
|
||||||
"sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f",
|
"sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6",
|
||||||
"sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd",
|
"sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a",
|
||||||
"sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245",
|
"sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe",
|
||||||
"sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f",
|
"sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7",
|
||||||
"sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332",
|
"sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200",
|
||||||
"sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30",
|
"sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7",
|
||||||
"sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183",
|
"sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a",
|
||||||
"sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f",
|
"sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28",
|
||||||
"sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85",
|
"sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea",
|
||||||
"sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46",
|
"sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120",
|
||||||
"sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71",
|
"sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d",
|
||||||
"sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660",
|
"sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42",
|
||||||
"sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb",
|
"sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea",
|
||||||
"sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c",
|
"sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2",
|
||||||
"sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"
|
"sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.6.1"
|
"version": "==1.7.1"
|
||||||
},
|
},
|
||||||
"mypy-extensions": {
|
"mypy-extensions": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1437,11 +1436,11 @@
|
||||||
},
|
},
|
||||||
"platformdirs": {
|
"platformdirs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3",
|
"sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380",
|
||||||
"sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"
|
"sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==3.11.0"
|
"version": "==4.1.0"
|
||||||
},
|
},
|
||||||
"pycodestyle": {
|
"pycodestyle": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1461,11 +1460,11 @@
|
||||||
},
|
},
|
||||||
"pygments": {
|
"pygments": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692",
|
"sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c",
|
||||||
"sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"
|
"sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==2.16.1"
|
"version": "==2.17.2"
|
||||||
},
|
},
|
||||||
"python-dateutil": {
|
"python-dateutil": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1533,19 +1532,19 @@
|
||||||
},
|
},
|
||||||
"rich": {
|
"rich": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245",
|
"sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa",
|
||||||
"sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"
|
"sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.7.0'",
|
"markers": "python_full_version >= '3.7.0'",
|
||||||
"version": "==13.6.0"
|
"version": "==13.7.0"
|
||||||
},
|
},
|
||||||
"s3transfer": {
|
"s3transfer": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a",
|
"sha256:368ac6876a9e9ed91f6bc86581e319be08188dc60d50e0d56308ed5765446283",
|
||||||
"sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e"
|
"sha256:c9e56cbe88b28d8e197cf841f1f0c130f246595e77ae5b5a05b69fe7cb83de76"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==0.7.0"
|
"version": "==0.8.2"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1597,11 +1596,11 @@
|
||||||
},
|
},
|
||||||
"types-awscrt": {
|
"types-awscrt": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4eb4f3bd0c41a2710cacda13098374da9faa76c5a0fb901aa5659e0fd48ceda1",
|
"sha256:850d5ad95d8f337b15fb154790f39af077faf5c08d43758fd750f379a87d5f73",
|
||||||
"sha256:a2d534b7017c3476ee69a44bd8aeaf3b588c42baa8322473d100a45ee67510d7"
|
"sha256:a577c4d60a7fb7e21b436a73207a66f6ba50329d578b347934c5d99d4d612901"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
||||||
"version": "==0.19.8"
|
"version": "==0.19.19"
|
||||||
},
|
},
|
||||||
"types-cachetools": {
|
"types-cachetools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1637,11 +1636,11 @@
|
||||||
},
|
},
|
||||||
"types-s3transfer": {
|
"types-s3transfer": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:aca0f2486d0a3a5037cd5b8f3e20a4522a29579a8dd183281ff0aa1c4e2c8aa7",
|
"sha256:2e41756fcf94775a9949afa856489ac4570308609b0493dfbd7b4d333eb423e6",
|
||||||
"sha256:ae9ed9273465d9f43da8b96307383da410c6b59c3b2464c88d20b578768e97c6"
|
"sha256:5e084ebcf2704281c71b19d5da6e1544b50859367d034b50080d5316a76a9418"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
||||||
"version": "==0.7.0"
|
"version": "==0.8.2"
|
||||||
},
|
},
|
||||||
"typing-extensions": {
|
"typing-extensions": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.db.models.functions import Concat
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django_fsm import get_available_FIELD_transitions
|
from django_fsm import get_available_FIELD_transitions
|
||||||
|
@ -11,7 +12,6 @@ from django.http.response import HttpResponseRedirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from epplibwrapper.errors import ErrorCode, RegistryError
|
from epplibwrapper.errors import ErrorCode, RegistryError
|
||||||
from registrar.models.domain import Domain
|
from registrar.models.domain import Domain
|
||||||
from registrar.models.utility.admin_sort_fields import AdminSortFields
|
|
||||||
from registrar.utility import csv_export
|
from registrar.utility import csv_export
|
||||||
from registrar.views.utility.mixins import OrderableFieldsMixin
|
from registrar.views.utility.mixins import OrderableFieldsMixin
|
||||||
from django.contrib.admin.views.main import ORDER_VAR
|
from django.contrib.admin.views.main import ORDER_VAR
|
||||||
|
@ -114,7 +114,44 @@ class CustomLogEntryAdmin(LogEntryAdmin):
|
||||||
add_form_template = "admin/change_form_no_submit.html"
|
add_form_template = "admin/change_form_no_submit.html"
|
||||||
|
|
||||||
|
|
||||||
class AuditedAdmin(admin.ModelAdmin, AdminSortFields):
|
class AdminSortFields:
|
||||||
|
def get_queryset(db_field):
|
||||||
|
"""This is a helper function for formfield_for_manytomany and formfield_for_foreignkey"""
|
||||||
|
# customize sorting
|
||||||
|
if db_field.name in (
|
||||||
|
"other_contacts",
|
||||||
|
"authorizing_official",
|
||||||
|
"submitter",
|
||||||
|
):
|
||||||
|
# Sort contacts by first_name, then last_name, then email
|
||||||
|
return models.Contact.objects.all().order_by(Concat("first_name", "last_name", "email"))
|
||||||
|
elif db_field.name in ("current_websites", "alternative_domains"):
|
||||||
|
# sort web sites
|
||||||
|
return models.Website.objects.all().order_by("website")
|
||||||
|
elif db_field.name in (
|
||||||
|
"creator",
|
||||||
|
"user",
|
||||||
|
"investigator",
|
||||||
|
):
|
||||||
|
# Sort users by first_name, then last_name, then email
|
||||||
|
return models.User.objects.all().order_by(Concat("first_name", "last_name", "email"))
|
||||||
|
elif db_field.name in (
|
||||||
|
"domain",
|
||||||
|
"approved_domain",
|
||||||
|
):
|
||||||
|
# Sort domains by name
|
||||||
|
return models.Domain.objects.all().order_by("name")
|
||||||
|
elif db_field.name in ("requested_domain",):
|
||||||
|
# Sort draft domains by name
|
||||||
|
return models.DraftDomain.objects.all().order_by("name")
|
||||||
|
elif db_field.name in ("domain_application",):
|
||||||
|
# Sort domain applications by name
|
||||||
|
return models.DomainApplication.objects.all().order_by("requested_domain__name")
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class AuditedAdmin(admin.ModelAdmin):
|
||||||
"""Custom admin to make auditing easier."""
|
"""Custom admin to make auditing easier."""
|
||||||
|
|
||||||
def history_view(self, request, object_id, extra_context=None):
|
def history_view(self, request, object_id, extra_context=None):
|
||||||
|
@ -127,10 +164,27 @@ class AuditedAdmin(admin.ModelAdmin, AdminSortFields):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
"""customize the behavior of formfields with manytomany relationships. the customized
|
||||||
|
behavior includes sorting of objects in lists as well as customizing helper text"""
|
||||||
|
queryset = AdminSortFields.get_queryset(db_field)
|
||||||
|
if queryset:
|
||||||
|
kwargs["queryset"] = queryset
|
||||||
|
formfield = super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
# customize the help text for all formfields for manytomany
|
||||||
|
formfield.help_text = (
|
||||||
|
formfield.help_text
|
||||||
|
+ " If more than one value is selected, the change/delete/view actions will be disabled."
|
||||||
|
)
|
||||||
|
return formfield
|
||||||
|
|
||||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||||
"""Used to sort dropdown fields alphabetically but can be expanded upon"""
|
"""customize the behavior of formfields with foreign key relationships. this will customize
|
||||||
form_field = super().formfield_for_foreignkey(db_field, request, **kwargs)
|
the behavior of selects. customized behavior includes sorting of objects in list"""
|
||||||
return self.form_field_order_helper(form_field, db_field)
|
queryset = AdminSortFields.get_queryset(db_field)
|
||||||
|
if queryset:
|
||||||
|
kwargs["queryset"] = queryset
|
||||||
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ListHeaderAdmin(AuditedAdmin, OrderableFieldsMixin):
|
class ListHeaderAdmin(AuditedAdmin, OrderableFieldsMixin):
|
||||||
|
@ -198,15 +252,6 @@ class ListHeaderAdmin(AuditedAdmin, OrderableFieldsMixin):
|
||||||
)
|
)
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
# customize the help_text for all formfields for manytomany
|
|
||||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
|
||||||
formfield = super().formfield_for_manytomany(db_field, request, **kwargs)
|
|
||||||
formfield.help_text = (
|
|
||||||
formfield.help_text
|
|
||||||
+ " If more than one value is selected, the change/delete/view actions will be disabled."
|
|
||||||
)
|
|
||||||
return formfield
|
|
||||||
|
|
||||||
|
|
||||||
class UserContactInline(admin.StackedInline):
|
class UserContactInline(admin.StackedInline):
|
||||||
"""Edit a user's profile on the user page."""
|
"""Edit a user's profile on the user page."""
|
||||||
|
@ -301,6 +346,10 @@ class MyUserAdmin(BaseUserAdmin):
|
||||||
"groups",
|
"groups",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# this ordering effects the ordering of results
|
||||||
|
# in autocomplete_fields for user
|
||||||
|
ordering = ["first_name", "last_name", "email"]
|
||||||
|
|
||||||
# Let's define First group
|
# Let's define First group
|
||||||
# (which should in theory be the ONLY group)
|
# (which should in theory be the ONLY group)
|
||||||
def group(self, obj):
|
def group(self, obj):
|
||||||
|
@ -579,13 +628,6 @@ class DomainInformationAdmin(ListHeaderAdmin):
|
||||||
# to activate the edit/delete/view buttons
|
# to activate the edit/delete/view buttons
|
||||||
filter_horizontal = ("other_contacts",)
|
filter_horizontal = ("other_contacts",)
|
||||||
|
|
||||||
# lists in filter_horizontal are not sorted properly, sort them
|
|
||||||
# by first_name
|
|
||||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
|
||||||
if db_field.name in ("other_contacts",):
|
|
||||||
kwargs["queryset"] = models.Contact.objects.all().order_by("first_name") # Sort contacts
|
|
||||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
"""Set the read-only state on form elements.
|
"""Set the read-only state on form elements.
|
||||||
We have 1 conditions that determine which fields are read-only:
|
We have 1 conditions that determine which fields are read-only:
|
||||||
|
@ -870,12 +912,27 @@ class DomainInformationInline(admin.StackedInline):
|
||||||
# to activate the edit/delete/view buttons
|
# to activate the edit/delete/view buttons
|
||||||
filter_horizontal = ("other_contacts",)
|
filter_horizontal = ("other_contacts",)
|
||||||
|
|
||||||
# lists in filter_horizontal are not sorted properly, sort them
|
|
||||||
# by first_name
|
|
||||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
if db_field.name in ("other_contacts",):
|
"""customize the behavior of formfields with manytomany relationships. the customized
|
||||||
kwargs["queryset"] = models.Contact.objects.all().order_by("first_name") # Sort contacts
|
behavior includes sorting of objects in lists as well as customizing helper text"""
|
||||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
queryset = AdminSortFields.get_queryset(db_field)
|
||||||
|
if queryset:
|
||||||
|
kwargs["queryset"] = queryset
|
||||||
|
formfield = super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
# customize the help text for all formfields for manytomany
|
||||||
|
formfield.help_text = (
|
||||||
|
formfield.help_text
|
||||||
|
+ " If more than one value is selected, the change/delete/view actions will be disabled."
|
||||||
|
)
|
||||||
|
return formfield
|
||||||
|
|
||||||
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||||
|
"""customize the behavior of formfields with foreign key relationships. this will customize
|
||||||
|
the behavior of selects. customized behavior includes sorting of objects in list"""
|
||||||
|
queryset = AdminSortFields.get_queryset(db_field)
|
||||||
|
if queryset:
|
||||||
|
kwargs["queryset"] = queryset
|
||||||
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
return DomainInformationAdmin.get_readonly_fields(self, request, obj=None)
|
return DomainInformationAdmin.get_readonly_fields(self, request, obj=None)
|
||||||
|
@ -893,6 +950,10 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
"state",
|
"state",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# this ordering effects the ordering of results
|
||||||
|
# in autocomplete_fields for domain
|
||||||
|
ordering = ["name"]
|
||||||
|
|
||||||
def organization_type(self, obj):
|
def organization_type(self, obj):
|
||||||
return obj.domain_info.get_organization_type_display()
|
return obj.domain_info.get_organization_type_display()
|
||||||
|
|
||||||
|
|
|
@ -329,10 +329,6 @@ class AuthorizingOfficialForm(RegistrarForm):
|
||||||
label="First name / given name",
|
label="First name / given name",
|
||||||
error_messages={"required": ("Enter the first name / given name of your authorizing official.")},
|
error_messages={"required": ("Enter the first name / given name of your authorizing official.")},
|
||||||
)
|
)
|
||||||
middle_name = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label="Middle name (optional)",
|
|
||||||
)
|
|
||||||
last_name = forms.CharField(
|
last_name = forms.CharField(
|
||||||
label="Last name / family name",
|
label="Last name / family name",
|
||||||
error_messages={"required": ("Enter the last name / family name of your authorizing official.")},
|
error_messages={"required": ("Enter the last name / family name of your authorizing official.")},
|
||||||
|
@ -350,10 +346,6 @@ class AuthorizingOfficialForm(RegistrarForm):
|
||||||
label="Email",
|
label="Email",
|
||||||
error_messages={"invalid": ("Enter an email address in the required format, like name@example.com.")},
|
error_messages={"invalid": ("Enter an email address in the required format, like name@example.com.")},
|
||||||
)
|
)
|
||||||
phone = PhoneNumberField(
|
|
||||||
label="Phone",
|
|
||||||
error_messages={"required": "Enter the phone number for your authorizing official."},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CurrentSitesForm(RegistrarForm):
|
class CurrentSitesForm(RegistrarForm):
|
||||||
|
@ -618,6 +610,12 @@ class NoOtherContactsForm(RegistrarForm):
|
||||||
"we can contact to help us assess your eligibility for a .gov domain."
|
"we can contact to help us assess your eligibility for a .gov domain."
|
||||||
),
|
),
|
||||||
widget=forms.Textarea(),
|
widget=forms.Textarea(),
|
||||||
|
validators=[
|
||||||
|
MaxLengthValidator(
|
||||||
|
1000,
|
||||||
|
message="Response must be less than 1000 characters.",
|
||||||
|
)
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,9 @@ class AuthorizingOfficialContactForm(ContactForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Overriding bc phone not required in this form
|
||||||
|
self.fields["phone"] = forms.IntegerField(required=False)
|
||||||
|
|
||||||
# Set custom error messages
|
# Set custom error messages
|
||||||
self.fields["first_name"].error_messages = {
|
self.fields["first_name"].error_messages = {
|
||||||
"required": "Enter the first name / given name of your authorizing official."
|
"required": "Enter the first name / given name of your authorizing official."
|
||||||
|
@ -227,7 +230,6 @@ class AuthorizingOfficialContactForm(ContactForm):
|
||||||
self.fields["email"].error_messages = {
|
self.fields["email"].error_messages = {
|
||||||
"required": "Enter an email address in the required format, like name@example.com."
|
"required": "Enter an email address in the required format, like name@example.com."
|
||||||
}
|
}
|
||||||
self.fields["phone"].error_messages["required"] = "Enter a phone number for your authorizing official."
|
|
||||||
|
|
||||||
|
|
||||||
class DomainSecurityEmailForm(forms.Form):
|
class DomainSecurityEmailForm(forms.Form):
|
||||||
|
|
|
@ -585,7 +585,12 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[ApplicationStatus.STARTED, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.WITHDRAWN],
|
source=[
|
||||||
|
ApplicationStatus.STARTED,
|
||||||
|
ApplicationStatus.IN_REVIEW,
|
||||||
|
ApplicationStatus.ACTION_NEEDED,
|
||||||
|
ApplicationStatus.WITHDRAWN,
|
||||||
|
],
|
||||||
target=ApplicationStatus.SUBMITTED,
|
target=ApplicationStatus.SUBMITTED,
|
||||||
)
|
)
|
||||||
def submit(self):
|
def submit(self):
|
||||||
|
@ -613,7 +618,17 @@ class DomainApplication(TimeStampedModel):
|
||||||
"emails/submission_confirmation_subject.txt",
|
"emails/submission_confirmation_subject.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
@transition(field="status", source=ApplicationStatus.SUBMITTED, target=ApplicationStatus.IN_REVIEW)
|
@transition(
|
||||||
|
field="status",
|
||||||
|
source=[
|
||||||
|
ApplicationStatus.SUBMITTED,
|
||||||
|
ApplicationStatus.ACTION_NEEDED,
|
||||||
|
ApplicationStatus.APPROVED,
|
||||||
|
ApplicationStatus.REJECTED,
|
||||||
|
ApplicationStatus.INELIGIBLE,
|
||||||
|
],
|
||||||
|
target=ApplicationStatus.IN_REVIEW,
|
||||||
|
)
|
||||||
def in_review(self):
|
def in_review(self):
|
||||||
"""Investigate an application that has been submitted.
|
"""Investigate an application that has been submitted.
|
||||||
|
|
||||||
|
@ -627,7 +642,12 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED],
|
source=[
|
||||||
|
ApplicationStatus.IN_REVIEW,
|
||||||
|
ApplicationStatus.APPROVED,
|
||||||
|
ApplicationStatus.REJECTED,
|
||||||
|
ApplicationStatus.INELIGIBLE,
|
||||||
|
],
|
||||||
target=ApplicationStatus.ACTION_NEEDED,
|
target=ApplicationStatus.ACTION_NEEDED,
|
||||||
)
|
)
|
||||||
def action_needed(self):
|
def action_needed(self):
|
||||||
|
@ -646,8 +666,8 @@ class DomainApplication(TimeStampedModel):
|
||||||
source=[
|
source=[
|
||||||
ApplicationStatus.SUBMITTED,
|
ApplicationStatus.SUBMITTED,
|
||||||
ApplicationStatus.IN_REVIEW,
|
ApplicationStatus.IN_REVIEW,
|
||||||
|
ApplicationStatus.ACTION_NEEDED,
|
||||||
ApplicationStatus.REJECTED,
|
ApplicationStatus.REJECTED,
|
||||||
ApplicationStatus.INELIGIBLE,
|
|
||||||
],
|
],
|
||||||
target=ApplicationStatus.APPROVED,
|
target=ApplicationStatus.APPROVED,
|
||||||
)
|
)
|
||||||
|
@ -684,7 +704,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW],
|
source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED],
|
||||||
target=ApplicationStatus.WITHDRAWN,
|
target=ApplicationStatus.WITHDRAWN,
|
||||||
)
|
)
|
||||||
def withdraw(self):
|
def withdraw(self):
|
||||||
|
@ -697,7 +717,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED],
|
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.APPROVED],
|
||||||
target=ApplicationStatus.REJECTED,
|
target=ApplicationStatus.REJECTED,
|
||||||
conditions=[domain_is_not_active],
|
conditions=[domain_is_not_active],
|
||||||
)
|
)
|
||||||
|
@ -707,12 +727,16 @@ class DomainApplication(TimeStampedModel):
|
||||||
As side effects this will delete the domain and domain_information
|
As side effects this will delete the domain and domain_information
|
||||||
(will cascade), and send an email notification."""
|
(will cascade), and send an email notification."""
|
||||||
if self.status == self.ApplicationStatus.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
domain_state = self.approved_domain.state
|
try:
|
||||||
# Only reject if it exists on EPP
|
domain_state = self.approved_domain.state
|
||||||
if domain_state != Domain.State.UNKNOWN:
|
# Only reject if it exists on EPP
|
||||||
self.approved_domain.deletedInEpp()
|
if domain_state != Domain.State.UNKNOWN:
|
||||||
self.approved_domain.delete()
|
self.approved_domain.deletedInEpp()
|
||||||
self.approved_domain = None
|
self.approved_domain.delete()
|
||||||
|
self.approved_domain = None
|
||||||
|
except Exception as err:
|
||||||
|
logger.error(err)
|
||||||
|
logger.error("Can't query an approved domain while attempting a DA reject()")
|
||||||
|
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"action needed",
|
"action needed",
|
||||||
|
@ -722,7 +746,12 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED],
|
source=[
|
||||||
|
ApplicationStatus.IN_REVIEW,
|
||||||
|
ApplicationStatus.ACTION_NEEDED,
|
||||||
|
ApplicationStatus.APPROVED,
|
||||||
|
ApplicationStatus.REJECTED,
|
||||||
|
],
|
||||||
target=ApplicationStatus.INELIGIBLE,
|
target=ApplicationStatus.INELIGIBLE,
|
||||||
conditions=[domain_is_not_active],
|
conditions=[domain_is_not_active],
|
||||||
)
|
)
|
||||||
|
@ -736,12 +765,16 @@ class DomainApplication(TimeStampedModel):
|
||||||
and domain_information (will cascade) when they exist."""
|
and domain_information (will cascade) when they exist."""
|
||||||
|
|
||||||
if self.status == self.ApplicationStatus.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
domain_state = self.approved_domain.state
|
try:
|
||||||
# Only reject if it exists on EPP
|
domain_state = self.approved_domain.state
|
||||||
if domain_state != Domain.State.UNKNOWN:
|
# Only reject if it exists on EPP
|
||||||
self.approved_domain.deletedInEpp()
|
if domain_state != Domain.State.UNKNOWN:
|
||||||
self.approved_domain.delete()
|
self.approved_domain.deletedInEpp()
|
||||||
self.approved_domain = None
|
self.approved_domain.delete()
|
||||||
|
self.approved_domain = None
|
||||||
|
except Exception as err:
|
||||||
|
logger.error(err)
|
||||||
|
logger.error("Can't query an approved domain while attempting a DA reject_with_prejudice()")
|
||||||
|
|
||||||
self.creator.restrict_user()
|
self.creator.restrict_user()
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ class UserGroup(Group):
|
||||||
)
|
)
|
||||||
|
|
||||||
cisa_analysts_group.save()
|
cisa_analysts_group.save()
|
||||||
logger.debug("CISA Analyt permissions added to group " + cisa_analysts_group.name)
|
logger.debug("CISA Analyst permissions added to group " + cisa_analysts_group.name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error creating analyst permissions group: {e}")
|
logger.error(f"Error creating analyst permissions group: {e}")
|
||||||
|
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
import logging
|
|
||||||
from typing import Dict
|
|
||||||
from django.forms import ModelChoiceField
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SortingDict:
|
|
||||||
"""Stores a sorting dictionary object"""
|
|
||||||
|
|
||||||
_sorting_dict: Dict[type, type] = {}
|
|
||||||
|
|
||||||
def __init__(self, model_list, sort_list):
|
|
||||||
self._sorting_dict = {
|
|
||||||
"dropDownSelected": self.convert_list_to_dict(model_list),
|
|
||||||
"sortBy": sort_list,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Used in __init__ for model_list for performance reasons
|
|
||||||
def convert_list_to_dict(self, value_list):
|
|
||||||
"""Used internally to convert model_list to a dictionary"""
|
|
||||||
return {item: item for item in value_list}
|
|
||||||
|
|
||||||
def get_dict(self):
|
|
||||||
"""Grabs the associated dictionary item,
|
|
||||||
has two fields: 'dropDownSelected': model_list and 'sortBy': sort_list"""
|
|
||||||
# This should never happen so we need to log this
|
|
||||||
if self._sorting_dict is None:
|
|
||||||
raise ValueError("_sorting_dict was None")
|
|
||||||
return self._sorting_dict
|
|
||||||
|
|
||||||
|
|
||||||
class AdminFormOrderHelper:
|
|
||||||
"""A helper class to order a dropdown field in Django Admin,
|
|
||||||
takes the fields you want to order by as an array"""
|
|
||||||
|
|
||||||
# Used to keep track of how we want to order_by certain FKs
|
|
||||||
_sorting_list: list[SortingDict] = []
|
|
||||||
|
|
||||||
def __init__(self, sort: list[SortingDict]):
|
|
||||||
self._sorting_list = sort
|
|
||||||
|
|
||||||
def get_ordered_form_field(self, form_field, db_field) -> ModelChoiceField | None:
|
|
||||||
"""Orders the queryset for a ModelChoiceField
|
|
||||||
based on the order_by_dict dictionary"""
|
|
||||||
_order_by_list = []
|
|
||||||
|
|
||||||
for item in self._sorting_list:
|
|
||||||
item_dict = item.get_dict()
|
|
||||||
drop_down_selected = item_dict.get("dropDownSelected")
|
|
||||||
sort_by = item_dict.get("sortBy")
|
|
||||||
|
|
||||||
if db_field.name in drop_down_selected:
|
|
||||||
_order_by_list = sort_by
|
|
||||||
# Exit loop when order_by_list is found
|
|
||||||
break
|
|
||||||
|
|
||||||
# Only order if we choose to do so
|
|
||||||
# noqa for the linter... reduces readability otherwise
|
|
||||||
if _order_by_list is not None and _order_by_list != []: # noqa
|
|
||||||
form_field.queryset = form_field.queryset.order_by(*_order_by_list)
|
|
||||||
|
|
||||||
return form_field
|
|
|
@ -1,27 +0,0 @@
|
||||||
from registrar.models.utility.admin_form_order_helper import (
|
|
||||||
AdminFormOrderHelper,
|
|
||||||
SortingDict,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AdminSortFields:
|
|
||||||
# Used to keep track of how we want to order_by certain FKs
|
|
||||||
foreignkey_orderby_dict: list[SortingDict] = [
|
|
||||||
# foreign_key - order_by
|
|
||||||
# Handles fields that are sorted by 'first_name / last_name
|
|
||||||
SortingDict(
|
|
||||||
["submitter", "authorizing_official", "investigator", "creator", "user"],
|
|
||||||
["first_name", "last_name"],
|
|
||||||
),
|
|
||||||
# Handles fields that are sorted by 'name'
|
|
||||||
SortingDict(["domain", "requested_domain"], ["name"]),
|
|
||||||
SortingDict(["domain_application"], ["requested_domain__name"]),
|
|
||||||
]
|
|
||||||
|
|
||||||
# For readability purposes, but can be replaced with a one liner
|
|
||||||
def form_field_order_helper(self, form_field, db_field):
|
|
||||||
"""A shorthand for AdminFormOrderHelper(foreignkey_orderby_dict)
|
|
||||||
.get_ordered_form_field(form_field, db_field)"""
|
|
||||||
|
|
||||||
form = AdminFormOrderHelper(self.foreignkey_orderby_dict)
|
|
||||||
return form.get_ordered_form_field(form_field, db_field)
|
|
|
@ -23,7 +23,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>
|
<p>
|
||||||
You must be an authorized user and need to be signed in to view this page.
|
You must be an authorized user and need to be signed in to view this page.
|
||||||
Would you like to <a href="{% url 'login' %}"> try logging in again</a>?
|
<a href="{% url 'login' %}"> Try logging in again</a>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If you'd like help with this error <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'contact' %}">contact us</a>.
|
If you'd like help with this error <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'contact' %}">contact us</a>.
|
||||||
|
|
|
@ -25,17 +25,11 @@
|
||||||
|
|
||||||
{% input_with_errors forms.0.first_name %}
|
{% input_with_errors forms.0.first_name %}
|
||||||
|
|
||||||
{% input_with_errors forms.0.middle_name %}
|
|
||||||
|
|
||||||
{% input_with_errors forms.0.last_name %}
|
{% input_with_errors forms.0.last_name %}
|
||||||
|
|
||||||
{% input_with_errors forms.0.title %}
|
{% input_with_errors forms.0.title %}
|
||||||
|
|
||||||
{% input_with_errors forms.0.email %}
|
{% input_with_errors forms.0.email %}
|
||||||
|
|
||||||
{% with add_class="usa-input--medium" %}
|
|
||||||
{% input_with_errors forms.0.phone %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load url_helpers %}
|
||||||
|
|
||||||
{% block title %}Thanks for your domain request! | {% endblock %}
|
{% block title %}Thanks for your domain request! | {% endblock %}
|
||||||
|
|
||||||
|
@ -14,21 +15,22 @@
|
||||||
/>
|
/>
|
||||||
<h1>Thanks for your domain request!</h1>
|
<h1>Thanks for your domain request!</h1>
|
||||||
</span>
|
</span>
|
||||||
<p>We'll email a copy of your request to you.</p>
|
<p>We’ll email a copy of your request to you.</p>
|
||||||
|
|
||||||
<h2>Next steps in this process</h2>
|
<h2>Next steps in this process</h2>
|
||||||
|
|
||||||
<p> We'll review your request. This usually takes 20 business days. During
|
<p> We’ll review your request. This usually takes 20 business days. During
|
||||||
this review we'll verify that your:</p>
|
this review we’ll verify that:</p>
|
||||||
<ul class="usa-list">
|
<ul class="usa-list">
|
||||||
<li>Organization is eligible for a .gov domain</li>
|
<li>Your organization is eligible for a .gov domain.</li>
|
||||||
<li>Authorizing official approves your request</li>
|
<li>You work at the organization and/or can make requests on its behalf.</li>
|
||||||
<li>Domain meets our naming requirements</li>
|
<li>Your requested domain meets our naming requirements.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p> You can <a href="{% url 'home' %}">check the status</a>
|
<p> We’ll email you if we have questions and when we complete our review. You can <a href="{% url 'home' %}">check the status</a>
|
||||||
of your request at any time. We'll email you with any questions or when we
|
of your request at any time on the registrar homepage.</p>
|
||||||
complete our review.</p>
|
|
||||||
|
<p> <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'contact' %}">Contact us if you need help during this process</a>.</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
aria-describedby="Are you sure you want to submit a domain request?"
|
aria-describedby="Are you sure you want to submit a domain request?"
|
||||||
data-force-action
|
data-force-action
|
||||||
>
|
>
|
||||||
{% include 'includes/modal.html' with modal_heading=modal_heading|safe modal_description="Once you submit this request, you won’t be able to make further edits until it’s reviewed by our staff. You’ll only be able to withdraw your request." modal_button=modal_button|safe %}
|
{% include 'includes/modal.html' with modal_heading=modal_heading|safe modal_description="Once you submit this request, you won’t be able to edit it until we review it. You’ll only be able to withdraw your request." modal_button=modal_button|safe %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block after_form_content %}{% endblock %}
|
{% block after_form_content %}{% endblock %}
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
{% load static field_helpers %}
|
{% load static field_helpers %}
|
||||||
|
|
||||||
{% block form_fields %}
|
{% block form_fields %}
|
||||||
{% input_with_errors forms.0.no_other_contacts_rationale %}
|
{% with attr_maxlength=1000 %}
|
||||||
|
{% input_with_errors forms.0.no_other_contacts_rationale %}
|
||||||
|
{% endwith %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -19,18 +19,12 @@
|
||||||
|
|
||||||
{% input_with_errors form.first_name %}
|
{% input_with_errors form.first_name %}
|
||||||
|
|
||||||
{% input_with_errors form.middle_name %}
|
|
||||||
|
|
||||||
{% input_with_errors form.last_name %}
|
{% input_with_errors form.last_name %}
|
||||||
|
|
||||||
{% input_with_errors form.title %}
|
{% input_with_errors form.title %}
|
||||||
|
|
||||||
{% input_with_errors form.email %}
|
{% input_with_errors form.email %}
|
||||||
|
|
||||||
{% input_with_errors form.phone %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="usa-button"
|
class="usa-button"
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
{% load url_helpers %}
|
||||||
|
|
||||||
<h2 class="margin-top-0 margin-bottom-2 text-primary-darker text-semibold" >
|
<h2 class="margin-top-0 margin-bottom-2 text-primary-darker text-semibold" >
|
||||||
Next steps
|
Next steps in this process
|
||||||
</h2>
|
</h2>
|
||||||
<p>We received your .gov domain request. Our next step is to review your request. This usually takes two weeks. We’ll email you with questions or when we complete our review. Contact us with any questions.</p>
|
<p>We received your .gov domain request. Our next step is to review your request. This usually takes 20 business days. We’ll email you if we have questions and when we complete our review. <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'contact' %}">Contact us with any questions</a>.</p>
|
||||||
|
|
||||||
<h2 class="margin-top-0 margin-bottom-2 text-primary-darker text-semibold">
|
<h2 class="margin-top-0 margin-bottom-2 text-primary-darker text-semibold">
|
||||||
Need to make changes?
|
Need to make changes?
|
||||||
|
|
|
@ -530,7 +530,7 @@ def completed_application(
|
||||||
):
|
):
|
||||||
"""A completed domain application."""
|
"""A completed domain application."""
|
||||||
if not user:
|
if not user:
|
||||||
user = get_user_model().objects.create(username="username")
|
user = get_user_model().objects.create(username="username" + str(uuid.uuid4())[:8])
|
||||||
ao, _ = Contact.objects.get_or_create(
|
ao, _ = Contact.objects.get_or_create(
|
||||||
first_name="Testy",
|
first_name="Testy",
|
||||||
last_name="Tester",
|
last_name="Tester",
|
||||||
|
|
|
@ -1371,11 +1371,10 @@ class AuditedAdminTest(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_alphabetically_sorted_fk_fields_domain_information(self):
|
def test_alphabetically_sorted_fk_fields_domain_information(self):
|
||||||
self.maxDiff = None
|
|
||||||
tested_fields = [
|
tested_fields = [
|
||||||
DomainInformation.authorizing_official.field,
|
DomainInformation.authorizing_official.field,
|
||||||
DomainInformation.submitter.field,
|
DomainInformation.submitter.field,
|
||||||
#DomainInformation.creator.field,
|
# DomainInformation.creator.field,
|
||||||
(DomainInformation.domain.field, ["name"]),
|
(DomainInformation.domain.field, ["name"]),
|
||||||
(DomainInformation.domain_application.field, ["requested_domain__name"]),
|
(DomainInformation.domain_application.field, ["requested_domain__name"]),
|
||||||
]
|
]
|
||||||
|
|
|
@ -196,11 +196,6 @@ class TestFormValidation(MockEppLib):
|
||||||
["Response must be less than 1000 characters."],
|
["Response must be less than 1000 characters."],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_authorizing_official_phone_invalid(self):
|
|
||||||
"""Must be a valid phone number."""
|
|
||||||
form = AuthorizingOfficialForm(data={"phone": "boss@boss"})
|
|
||||||
self.assertTrue(form.errors["phone"][0].startswith("Enter a valid phone number "))
|
|
||||||
|
|
||||||
def test_your_contact_email_invalid(self):
|
def test_your_contact_email_invalid(self):
|
||||||
"""must be a valid email address."""
|
"""must be a valid email address."""
|
||||||
form = YourContactForm(data={"email": "boss@boss"})
|
form = YourContactForm(data={"email": "boss@boss"})
|
||||||
|
|
|
@ -26,20 +26,52 @@ boto3_mocking.clients.register_handler("sesv2", MockSESClient)
|
||||||
# with AWS SES, so mock that out in all of these test cases
|
# with AWS SES, so mock that out in all of these test cases
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
class TestDomainApplication(TestCase):
|
class TestDomainApplication(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.started_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.STARTED, name="started.gov"
|
||||||
|
)
|
||||||
|
self.submitted_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.SUBMITTED, name="submitted.gov"
|
||||||
|
)
|
||||||
|
self.in_review_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.IN_REVIEW, name="in-review.gov"
|
||||||
|
)
|
||||||
|
self.action_needed_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.ACTION_NEEDED, name="action-needed.gov"
|
||||||
|
)
|
||||||
|
self.approved_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.APPROVED, name="approved.gov"
|
||||||
|
)
|
||||||
|
self.withdrawn_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.WITHDRAWN, name="withdrawn.gov"
|
||||||
|
)
|
||||||
|
self.rejected_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.REJECTED, name="rejected.gov"
|
||||||
|
)
|
||||||
|
self.ineligible_application = completed_application(
|
||||||
|
status=DomainApplication.ApplicationStatus.INELIGIBLE, name="ineligible.gov"
|
||||||
|
)
|
||||||
|
|
||||||
|
def assertNotRaises(self, exception_type):
|
||||||
|
"""Helper method for testing allowed transitions."""
|
||||||
|
return self.assertRaises(Exception, None, exception_type)
|
||||||
|
|
||||||
def test_empty_create_fails(self):
|
def test_empty_create_fails(self):
|
||||||
"""Can't create a completely empty domain application."""
|
"""Can't create a completely empty domain application.
|
||||||
|
NOTE: something about theexception this test raises messes up with the
|
||||||
|
atomic block in a custom tearDown method for the parent test class."""
|
||||||
with self.assertRaisesRegex(IntegrityError, "creator"):
|
with self.assertRaisesRegex(IntegrityError, "creator"):
|
||||||
DomainApplication.objects.create()
|
DomainApplication.objects.create()
|
||||||
|
|
||||||
def test_minimal_create(self):
|
def test_minimal_create(self):
|
||||||
"""Can create with just a creator."""
|
"""Can create with just a creator."""
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create(username="testy")
|
||||||
application = DomainApplication.objects.create(creator=user)
|
application = DomainApplication.objects.create(creator=user)
|
||||||
self.assertEqual(application.status, DomainApplication.ApplicationStatus.STARTED)
|
self.assertEqual(application.status, DomainApplication.ApplicationStatus.STARTED)
|
||||||
|
|
||||||
def test_full_create(self):
|
def test_full_create(self):
|
||||||
"""Can create with all fields."""
|
"""Can create with all fields."""
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create(username="testy")
|
||||||
contact = Contact.objects.create()
|
contact = Contact.objects.create()
|
||||||
com_website, _ = Website.objects.get_or_create(website="igorville.com")
|
com_website, _ = Website.objects.get_or_create(website="igorville.com")
|
||||||
gov_website, _ = Website.objects.get_or_create(website="igorville.gov")
|
gov_website, _ = Website.objects.get_or_create(website="igorville.gov")
|
||||||
|
@ -69,7 +101,7 @@ class TestDomainApplication(TestCase):
|
||||||
|
|
||||||
def test_domain_info(self):
|
def test_domain_info(self):
|
||||||
"""Can create domain info with all fields."""
|
"""Can create domain info with all fields."""
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create(username="testy")
|
||||||
contact = Contact.objects.create()
|
contact = Contact.objects.create()
|
||||||
domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
||||||
information = DomainInformation.objects.create(
|
information = DomainInformation.objects.create(
|
||||||
|
@ -95,14 +127,14 @@ class TestDomainApplication(TestCase):
|
||||||
self.assertEqual(information.id, domain.domain_info.id)
|
self.assertEqual(information.id, domain.domain_info.id)
|
||||||
|
|
||||||
def test_status_fsm_submit_fail(self):
|
def test_status_fsm_submit_fail(self):
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create(username="testy")
|
||||||
application = DomainApplication.objects.create(creator=user)
|
application = DomainApplication.objects.create(creator=user)
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
# can't submit an application with a null domain name
|
# can't submit an application with a null domain name
|
||||||
application.submit()
|
application.submit()
|
||||||
|
|
||||||
def test_status_fsm_submit_succeed(self):
|
def test_status_fsm_submit_succeed(self):
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create(username="testy")
|
||||||
site = DraftDomain.objects.create(name="igorville.gov")
|
site = DraftDomain.objects.create(name="igorville.gov")
|
||||||
application = DomainApplication.objects.create(creator=user, requested_domain=site)
|
application = DomainApplication.objects.create(creator=user, requested_domain=site)
|
||||||
# no submitter email so this emits a log warning
|
# no submitter email so this emits a log warning
|
||||||
|
@ -112,7 +144,7 @@ class TestDomainApplication(TestCase):
|
||||||
|
|
||||||
def test_submit_sends_email(self):
|
def test_submit_sends_email(self):
|
||||||
"""Create an application and submit it and see if email was sent."""
|
"""Create an application and submit it and see if email was sent."""
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create(username="testy")
|
||||||
contact = Contact.objects.create(email="test@test.gov")
|
contact = Contact.objects.create(email="test@test.gov")
|
||||||
domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
||||||
application = DomainApplication.objects.create(
|
application = DomainApplication.objects.create(
|
||||||
|
@ -135,320 +167,251 @@ class TestDomainApplication(TestCase):
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_transition_not_allowed_submitted_submitted(self):
|
def test_submit_transition_allowed(self):
|
||||||
"""Create an application with status submitted and call submit
|
"""
|
||||||
against transition rules"""
|
Test that calling submit from allowable statuses does raises TransitionNotAllowed.
|
||||||
|
"""
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
test_cases = [
|
||||||
|
(self.started_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.in_review_application, TransitionNotAllowed),
|
||||||
application.submit()
|
(self.action_needed_application, TransitionNotAllowed),
|
||||||
|
(self.withdrawn_application, TransitionNotAllowed),
|
||||||
def test_transition_not_allowed_in_review_submitted(self):
|
]
|
||||||
"""Create an application with status in review and call submit
|
|
||||||
against transition rules"""
|
for application, exception_type in test_cases:
|
||||||
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
try:
|
||||||
|
application.submit()
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
except TransitionNotAllowed:
|
||||||
application.submit()
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||||
|
|
||||||
def test_transition_not_allowed_approved_submitted(self):
|
def test_submit_transition_not_allowed(self):
|
||||||
"""Create an application with status approved and call submit
|
"""
|
||||||
against transition rules"""
|
Test that calling submit against transition rules raises TransitionNotAllowed.
|
||||||
|
"""
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
test_cases = [
|
||||||
|
(self.submitted_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.approved_application, TransitionNotAllowed),
|
||||||
application.submit()
|
(self.rejected_application, TransitionNotAllowed),
|
||||||
|
(self.ineligible_application, TransitionNotAllowed),
|
||||||
def test_transition_not_allowed_rejected_submitted(self):
|
]
|
||||||
"""Create an application with status rejected and call submit
|
|
||||||
against transition rules"""
|
for application, exception_type in test_cases:
|
||||||
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
with self.assertRaises(exception_type):
|
||||||
|
application.submit()
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.submit()
|
def test_in_review_transition_allowed(self):
|
||||||
|
"""
|
||||||
def test_transition_not_allowed_ineligible_submitted(self):
|
Test that calling in_review from allowable statuses does raises TransitionNotAllowed.
|
||||||
"""Create an application with status ineligible and call submit
|
"""
|
||||||
against transition rules"""
|
test_cases = [
|
||||||
|
(self.submitted_application, TransitionNotAllowed),
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
(self.action_needed_application, TransitionNotAllowed),
|
||||||
|
(self.approved_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.rejected_application, TransitionNotAllowed),
|
||||||
application.submit()
|
(self.ineligible_application, TransitionNotAllowed),
|
||||||
|
]
|
||||||
def test_transition_not_allowed_started_in_review(self):
|
|
||||||
"""Create an application with status started and call in_review
|
for application, exception_type in test_cases:
|
||||||
against transition rules"""
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
|
try:
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
application.in_review()
|
||||||
|
except TransitionNotAllowed:
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||||
application.in_review()
|
|
||||||
|
def test_in_review_transition_not_allowed(self):
|
||||||
def test_transition_not_allowed_in_review_in_review(self):
|
"""
|
||||||
"""Create an application with status in review and call in_review
|
Test that calling in_review against transition rules raises TransitionNotAllowed.
|
||||||
against transition rules"""
|
"""
|
||||||
|
test_cases = [
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
(self.started_application, TransitionNotAllowed),
|
||||||
|
(self.in_review_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.withdrawn_application, TransitionNotAllowed),
|
||||||
application.in_review()
|
]
|
||||||
|
|
||||||
def test_transition_not_allowed_approved_in_review(self):
|
for application, exception_type in test_cases:
|
||||||
"""Create an application with status approved and call in_review
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
against transition rules"""
|
with self.assertRaises(exception_type):
|
||||||
|
application.in_review()
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
|
||||||
|
def test_action_needed_transition_allowed(self):
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
"""
|
||||||
application.in_review()
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
||||||
|
"""
|
||||||
def test_transition_not_allowed_action_needed_in_review(self):
|
test_cases = [
|
||||||
"""Create an application with status action needed and call in_review
|
(self.in_review_application, TransitionNotAllowed),
|
||||||
against transition rules"""
|
(self.approved_application, TransitionNotAllowed),
|
||||||
|
(self.rejected_application, TransitionNotAllowed),
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
(self.ineligible_application, TransitionNotAllowed),
|
||||||
|
]
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.in_review()
|
for application, exception_type in test_cases:
|
||||||
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
def test_transition_not_allowed_rejected_in_review(self):
|
try:
|
||||||
"""Create an application with status rejected and call in_review
|
application.action_needed()
|
||||||
against transition rules"""
|
except TransitionNotAllowed:
|
||||||
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
|
||||||
|
def test_action_needed_transition_not_allowed(self):
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
"""
|
||||||
application.in_review()
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
||||||
|
"""
|
||||||
def test_transition_not_allowed_withdrawn_in_review(self):
|
test_cases = [
|
||||||
"""Create an application with status withdrawn and call in_review
|
(self.started_application, TransitionNotAllowed),
|
||||||
against transition rules"""
|
(self.submitted_application, TransitionNotAllowed),
|
||||||
|
(self.action_needed_application, TransitionNotAllowed),
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
(self.withdrawn_application, TransitionNotAllowed),
|
||||||
|
]
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.in_review()
|
for application, exception_type in test_cases:
|
||||||
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
def test_transition_not_allowed_ineligible_in_review(self):
|
with self.assertRaises(exception_type):
|
||||||
"""Create an application with status ineligible and call in_review
|
application.action_needed()
|
||||||
against transition rules"""
|
|
||||||
|
def test_approved_transition_allowed(self):
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
"""
|
||||||
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
"""
|
||||||
application.in_review()
|
test_cases = [
|
||||||
|
(self.submitted_application, TransitionNotAllowed),
|
||||||
def test_transition_not_allowed_started_action_needed(self):
|
(self.in_review_application, TransitionNotAllowed),
|
||||||
"""Create an application with status started and call action_needed
|
(self.action_needed_application, TransitionNotAllowed),
|
||||||
against transition rules"""
|
(self.rejected_application, TransitionNotAllowed),
|
||||||
|
]
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
|
||||||
|
for application, exception_type in test_cases:
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
application.action_needed()
|
try:
|
||||||
|
application.approve()
|
||||||
def test_transition_not_allowed_submitted_action_needed(self):
|
except TransitionNotAllowed:
|
||||||
"""Create an application with status submitted and call action_needed
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||||
against transition rules"""
|
|
||||||
|
def test_approved_transition_not_allowed(self):
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
"""
|
||||||
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
"""
|
||||||
application.action_needed()
|
test_cases = [
|
||||||
|
(self.started_application, TransitionNotAllowed),
|
||||||
def test_transition_not_allowed_action_needed_action_needed(self):
|
(self.approved_application, TransitionNotAllowed),
|
||||||
"""Create an application with status action needed and call action_needed
|
(self.withdrawn_application, TransitionNotAllowed),
|
||||||
against transition rules"""
|
(self.ineligible_application, TransitionNotAllowed),
|
||||||
|
]
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
|
||||||
|
for application, exception_type in test_cases:
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
application.action_needed()
|
with self.assertRaises(exception_type):
|
||||||
|
application.approve()
|
||||||
def test_transition_not_allowed_approved_action_needed(self):
|
|
||||||
"""Create an application with status approved and call action_needed
|
def test_withdraw_transition_allowed(self):
|
||||||
against transition rules"""
|
"""
|
||||||
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
"""
|
||||||
|
test_cases = [
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.submitted_application, TransitionNotAllowed),
|
||||||
application.action_needed()
|
(self.in_review_application, TransitionNotAllowed),
|
||||||
|
(self.action_needed_application, TransitionNotAllowed),
|
||||||
def test_transition_not_allowed_withdrawn_action_needed(self):
|
]
|
||||||
"""Create an application with status withdrawn and call action_needed
|
|
||||||
against transition rules"""
|
for application, exception_type in test_cases:
|
||||||
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
try:
|
||||||
|
application.withdraw()
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
except TransitionNotAllowed:
|
||||||
application.action_needed()
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||||
|
|
||||||
def test_transition_not_allowed_ineligible_action_needed(self):
|
def test_withdraw_transition_not_allowed(self):
|
||||||
"""Create an application with status ineligible and call action_needed
|
"""
|
||||||
against transition rules"""
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
||||||
|
"""
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
test_cases = [
|
||||||
|
(self.started_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.approved_application, TransitionNotAllowed),
|
||||||
application.action_needed()
|
(self.withdrawn_application, TransitionNotAllowed),
|
||||||
|
(self.rejected_application, TransitionNotAllowed),
|
||||||
def test_transition_not_allowed_started_approved(self):
|
(self.ineligible_application, TransitionNotAllowed),
|
||||||
"""Create an application with status started and call approve
|
]
|
||||||
against transition rules"""
|
|
||||||
|
for application, exception_type in test_cases:
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
|
with self.assertRaises(exception_type):
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
application.withdraw()
|
||||||
application.approve()
|
|
||||||
|
def test_reject_transition_allowed(self):
|
||||||
def test_transition_not_allowed_approved_approved(self):
|
"""
|
||||||
"""Create an application with status approved and call approve
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
||||||
against transition rules"""
|
"""
|
||||||
|
test_cases = [
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
(self.in_review_application, TransitionNotAllowed),
|
||||||
|
(self.action_needed_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.approved_application, TransitionNotAllowed),
|
||||||
application.approve()
|
]
|
||||||
|
|
||||||
def test_transition_not_allowed_action_needed_approved(self):
|
for application, exception_type in test_cases:
|
||||||
"""Create an application with status action needed and call approve
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
against transition rules"""
|
try:
|
||||||
|
application.reject()
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
except TransitionNotAllowed:
|
||||||
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.approve()
|
def test_reject_transition_not_allowed(self):
|
||||||
|
"""
|
||||||
def test_transition_not_allowed_withdrawn_approved(self):
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
||||||
"""Create an application with status withdrawn and call approve
|
"""
|
||||||
against transition rules"""
|
test_cases = [
|
||||||
|
(self.started_application, TransitionNotAllowed),
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
(self.submitted_application, TransitionNotAllowed),
|
||||||
|
(self.withdrawn_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
(self.rejected_application, TransitionNotAllowed),
|
||||||
application.approve()
|
(self.ineligible_application, TransitionNotAllowed),
|
||||||
|
]
|
||||||
def test_transition_not_allowed_started_withdrawn(self):
|
|
||||||
"""Create an application with status started and call withdraw
|
for application, exception_type in test_cases:
|
||||||
against transition rules"""
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
|
with self.assertRaises(exception_type):
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
application.reject()
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
def test_reject_with_prejudice_transition_allowed(self):
|
||||||
application.withdraw()
|
"""
|
||||||
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
||||||
def test_transition_not_allowed_approved_withdrawn(self):
|
"""
|
||||||
"""Create an application with status approved and call withdraw
|
test_cases = [
|
||||||
against transition rules"""
|
(self.in_review_application, TransitionNotAllowed),
|
||||||
|
(self.action_needed_application, TransitionNotAllowed),
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
(self.approved_application, TransitionNotAllowed),
|
||||||
|
(self.rejected_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
]
|
||||||
application.withdraw()
|
|
||||||
|
for application, exception_type in test_cases:
|
||||||
def test_transition_not_allowed_action_needed_withdrawn(self):
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
"""Create an application with status action needed and call withdraw
|
try:
|
||||||
against transition rules"""
|
application.reject_with_prejudice()
|
||||||
|
except TransitionNotAllowed:
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
def test_reject_with_prejudice_transition_not_allowed(self):
|
||||||
application.withdraw()
|
"""
|
||||||
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
||||||
def test_transition_not_allowed_rejected_withdrawn(self):
|
"""
|
||||||
"""Create an application with status rejected and call withdraw
|
test_cases = [
|
||||||
against transition rules"""
|
(self.started_application, TransitionNotAllowed),
|
||||||
|
(self.submitted_application, TransitionNotAllowed),
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
(self.withdrawn_application, TransitionNotAllowed),
|
||||||
|
(self.ineligible_application, TransitionNotAllowed),
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
]
|
||||||
application.withdraw()
|
|
||||||
|
for application, exception_type in test_cases:
|
||||||
def test_transition_not_allowed_withdrawn_withdrawn(self):
|
with self.subTest(application=application, exception_type=exception_type):
|
||||||
"""Create an application with status withdrawn and call withdraw
|
with self.assertRaises(exception_type):
|
||||||
against transition rules"""
|
application.reject_with_prejudice()
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.withdraw()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_ineligible_withdrawn(self):
|
|
||||||
"""Create an application with status ineligible and call withdraw
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.withdraw()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_started_rejected(self):
|
|
||||||
"""Create an application with status started and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_submitted_rejected(self):
|
|
||||||
"""Create an application with status submitted and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_action_needed_rejected(self):
|
|
||||||
"""Create an application with status action needed and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_withdrawn_rejected(self):
|
|
||||||
"""Create an application with status withdrawn and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_rejected_rejected(self):
|
|
||||||
"""Create an application with status rejected and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_ineligible_rejected(self):
|
|
||||||
"""Create an application with status ineligible and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_approved_rejected_when_domain_is_active(self):
|
def test_transition_not_allowed_approved_rejected_when_domain_is_active(self):
|
||||||
"""Create an application with status approved, create a matching domain that
|
"""Create an application with status approved, create a matching domain that
|
||||||
is active, and call reject against transition rules"""
|
is active, and call reject against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
domain = Domain.objects.create(name=self.approved_application.requested_domain.name)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
self.approved_application.approved_domain = domain
|
||||||
application.approved_domain = domain
|
self.approved_application.save()
|
||||||
application.save()
|
|
||||||
|
|
||||||
# Define a custom implementation for is_active
|
# Define a custom implementation for is_active
|
||||||
def custom_is_active(self):
|
def custom_is_active(self):
|
||||||
|
@ -458,70 +421,15 @@ class TestDomainApplication(TestCase):
|
||||||
with patch.object(Domain, "is_active", custom_is_active):
|
with patch.object(Domain, "is_active", custom_is_active):
|
||||||
# Now, when you call is_active on Domain, it will return True
|
# Now, when you call is_active on Domain, it will return True
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject()
|
self.approved_application.reject()
|
||||||
|
|
||||||
def test_transition_not_allowed_started_ineligible(self):
|
|
||||||
"""Create an application with status started and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject_with_prejudice()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_submitted_ineligible(self):
|
|
||||||
"""Create an application with status submitted and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject_with_prejudice()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_action_needed_ineligible(self):
|
|
||||||
"""Create an application with status action needed and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject_with_prejudice()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_withdrawn_ineligible(self):
|
|
||||||
"""Create an application with status withdrawn and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject_with_prejudice()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_rejected_ineligible(self):
|
|
||||||
"""Create an application with status rejected and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject_with_prejudice()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_ineligible_ineligible(self):
|
|
||||||
"""Create an application with status ineligible and call reject
|
|
||||||
against transition rules"""
|
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
|
||||||
|
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
|
||||||
application.reject_with_prejudice()
|
|
||||||
|
|
||||||
def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self):
|
def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self):
|
||||||
"""Create an application with status approved, create a matching domain that
|
"""Create an application with status approved, create a matching domain that
|
||||||
is active, and call reject_with_prejudice against transition rules"""
|
is active, and call reject_with_prejudice against transition rules"""
|
||||||
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
domain = Domain.objects.create(name=self.approved_application.requested_domain.name)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
self.approved_application.approved_domain = domain
|
||||||
application.approved_domain = domain
|
self.approved_application.save()
|
||||||
application.save()
|
|
||||||
|
|
||||||
# Define a custom implementation for is_active
|
# Define a custom implementation for is_active
|
||||||
def custom_is_active(self):
|
def custom_is_active(self):
|
||||||
|
@ -531,7 +439,7 @@ class TestDomainApplication(TestCase):
|
||||||
with patch.object(Domain, "is_active", custom_is_active):
|
with patch.object(Domain, "is_active", custom_is_active):
|
||||||
# Now, when you call is_active on Domain, it will return True
|
# Now, when you call is_active on Domain, it will return True
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
application.reject_with_prejudice()
|
self.approved_application.reject_with_prejudice()
|
||||||
|
|
||||||
|
|
||||||
class TestPermissions(TestCase):
|
class TestPermissions(TestCase):
|
||||||
|
@ -674,12 +582,13 @@ class TestContact(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.email_for_invalid = "intern@igorville.gov"
|
self.email_for_invalid = "intern@igorville.gov"
|
||||||
self.invalid_user, _ = User.objects.get_or_create(
|
self.invalid_user, _ = User.objects.get_or_create(
|
||||||
username=self.email_for_invalid, email=self.email_for_invalid, first_name="", last_name="")
|
username=self.email_for_invalid, email=self.email_for_invalid, first_name="", last_name=""
|
||||||
|
)
|
||||||
self.invalid_contact, _ = Contact.objects.get_or_create(user=self.invalid_user)
|
self.invalid_contact, _ = Contact.objects.get_or_create(user=self.invalid_user)
|
||||||
|
|
||||||
self.email = "mayor@igorville.gov"
|
self.email = "mayor@igorville.gov"
|
||||||
self.existing_user, _ = User.objects.get_or_create(email=self.email, first_name="Jeff", last_name="Lebowski")
|
self.user, _ = User.objects.get_or_create(email=self.email, first_name="Jeff", last_name="Lebowski")
|
||||||
self.existing_contact, _ = Contact.objects.get_or_create(user=self.existing_user)
|
self.contact, _ = Contact.objects.get_or_create(user=self.user)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
@ -689,13 +598,14 @@ class TestContact(TestCase):
|
||||||
def test_saving_contact_updates_user_first_last_names(self):
|
def test_saving_contact_updates_user_first_last_names(self):
|
||||||
"""When a contact is updated, we propagate the changes to the linked user if it exists."""
|
"""When a contact is updated, we propagate the changes to the linked user if it exists."""
|
||||||
|
|
||||||
# User and Contact are created and linked as expected
|
# User and Contact are created and linked as expected.
|
||||||
|
# An empty User object should create an empty contact.
|
||||||
self.assertEqual(self.invalid_contact.first_name, "")
|
self.assertEqual(self.invalid_contact.first_name, "")
|
||||||
self.assertEqual(self.invalid_contact.last_name, "")
|
self.assertEqual(self.invalid_contact.last_name, "")
|
||||||
self.assertEqual(self.invalid_user.first_name, "")
|
self.assertEqual(self.invalid_user.first_name, "")
|
||||||
self.assertEqual(self.invalid_user.last_name, "")
|
self.assertEqual(self.invalid_user.last_name, "")
|
||||||
|
|
||||||
# Manually update the contact - mimicking production
|
# Manually update the contact - mimicking production (pre-existing data)
|
||||||
self.invalid_contact.first_name = "Joey"
|
self.invalid_contact.first_name = "Joey"
|
||||||
self.invalid_contact.last_name = "Baloney"
|
self.invalid_contact.last_name = "Baloney"
|
||||||
self.invalid_contact.save()
|
self.invalid_contact.save()
|
||||||
|
@ -708,43 +618,44 @@ class TestContact(TestCase):
|
||||||
self.assertEqual(self.invalid_contact.last_name, "Baloney")
|
self.assertEqual(self.invalid_contact.last_name, "Baloney")
|
||||||
self.assertEqual(self.invalid_user.first_name, "Joey")
|
self.assertEqual(self.invalid_user.first_name, "Joey")
|
||||||
self.assertEqual(self.invalid_user.last_name, "Baloney")
|
self.assertEqual(self.invalid_user.last_name, "Baloney")
|
||||||
|
|
||||||
def test_saving_contact_does_not_update_user_first_last_names(self):
|
def test_saving_contact_does_not_update_user_first_last_names(self):
|
||||||
"""When a contact is updated, we avoid propagating the changes to the linked user if it already has a value"""
|
"""When a contact is updated, we avoid propagating the changes to the linked user if it already has a value"""
|
||||||
|
|
||||||
# User and Contact are created and linked as expected
|
# User and Contact are created and linked as expected
|
||||||
self.assertEqual(self.existing_contact.first_name, "Jeff")
|
self.assertEqual(self.contact.first_name, "Jeff")
|
||||||
self.assertEqual(self.existing_contact.last_name, "Lebowski")
|
self.assertEqual(self.contact.last_name, "Lebowski")
|
||||||
self.assertEqual(self.existing_user.first_name, "Jeff")
|
self.assertEqual(self.user.first_name, "Jeff")
|
||||||
self.assertEqual(self.existing_user.last_name, "Lebowski")
|
self.assertEqual(self.user.last_name, "Lebowski")
|
||||||
|
|
||||||
self.existing_contact.first_name = "Joey"
|
self.contact.first_name = "Joey"
|
||||||
self.existing_contact.last_name = "Baloney"
|
self.contact.last_name = "Baloney"
|
||||||
self.existing_contact.save()
|
self.contact.save()
|
||||||
|
|
||||||
# Refresh the user object to reflect the changes made in the database
|
# Refresh the user object to reflect the changes made in the database
|
||||||
self.existing_user.refresh_from_db()
|
self.user.refresh_from_db()
|
||||||
|
|
||||||
# Updating the contact's first and last names propagate to the user
|
# Updating the contact's first and last names propagate to the user
|
||||||
self.assertEqual(self.existing_contact.first_name, "Joey")
|
self.assertEqual(self.contact.first_name, "Joey")
|
||||||
self.assertEqual(self.existing_contact.last_name, "Baloney")
|
self.assertEqual(self.contact.last_name, "Baloney")
|
||||||
self.assertEqual(self.existing_user.first_name, "Jeff")
|
self.assertEqual(self.user.first_name, "Jeff")
|
||||||
self.assertEqual(self.existing_user.last_name, "Lebowski")
|
self.assertEqual(self.user.last_name, "Lebowski")
|
||||||
|
|
||||||
def test_saving_contact_does_not_update_user_email(self):
|
def test_saving_contact_does_not_update_user_email(self):
|
||||||
"""When a contact's email is updated, the change is not propagated to the lined user."""
|
"""When a contact's email is updated, the change is not propagated to the user."""
|
||||||
self.existing_contact.email = "joey.baloney@diaperville.com"
|
self.contact.email = "joey.baloney@diaperville.com"
|
||||||
self.existing_contact.save()
|
self.contact.save()
|
||||||
|
|
||||||
# Refresh the user object to reflect the changes made in the database
|
# Refresh the user object to reflect the changes made in the database
|
||||||
self.existing_user.refresh_from_db()
|
self.user.refresh_from_db()
|
||||||
|
|
||||||
# Updating the contact's email does not propagate
|
# Updating the contact's email does not propagate
|
||||||
self.assertEqual(self.existing_contact.email, "joey.baloney@diaperville.com")
|
self.assertEqual(self.contact.email, "joey.baloney@diaperville.com")
|
||||||
self.assertEqual(self.existing_user.email, "mayor@igorville.gov")
|
self.assertEqual(self.user.email, "mayor@igorville.gov")
|
||||||
|
|
||||||
def test_saving_contact_does_not_update_user_email_when_none(self):
|
def test_saving_contact_does_not_update_user_email_when_none(self):
|
||||||
"""When a contact's email is updated, the change is not propagated to the lined user."""
|
"""When a contact's email is updated, and the first/last name is none,
|
||||||
|
the change is not propagated to the user."""
|
||||||
self.invalid_contact.email = "joey.baloney@diaperville.com"
|
self.invalid_contact.email = "joey.baloney@diaperville.com"
|
||||||
self.invalid_contact.save()
|
self.invalid_contact.save()
|
||||||
|
|
||||||
|
|
|
@ -761,7 +761,6 @@ class TestRegistrantContacts(MockEppLib):
|
||||||
self.assertEqual(expected_contact.email, actual_contact.email)
|
self.assertEqual(expected_contact.email, actual_contact.email)
|
||||||
|
|
||||||
def test_convert_public_contact_to_epp(self):
|
def test_convert_public_contact_to_epp(self):
|
||||||
self.maxDiff = None
|
|
||||||
domain, _ = Domain.objects.get_or_create(name="freeman.gov")
|
domain, _ = Domain.objects.get_or_create(name="freeman.gov")
|
||||||
dummy_contact = domain.get_default_security_contact()
|
dummy_contact = domain.get_default_security_contact()
|
||||||
test_disclose = self._convertPublicContactToEpp(dummy_contact, disclose_email=True).__dict__
|
test_disclose = self._convertPublicContactToEpp(dummy_contact, disclose_email=True).__dict__
|
||||||
|
|
|
@ -152,7 +152,6 @@ class CsvReportsTest(TestCase):
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_load_federal_report(self):
|
def test_load_federal_report(self):
|
||||||
"""Tests the get_current_federal api endpoint"""
|
"""Tests the get_current_federal api endpoint"""
|
||||||
self.maxDiff = None
|
|
||||||
mock_client = MagicMock()
|
mock_client = MagicMock()
|
||||||
mock_client_instance = mock_client.return_value
|
mock_client_instance = mock_client.return_value
|
||||||
|
|
||||||
|
|
|
@ -257,7 +257,6 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
||||||
ao_form["authorizing_official-last_name"] = "Tester ATO"
|
ao_form["authorizing_official-last_name"] = "Tester ATO"
|
||||||
ao_form["authorizing_official-title"] = "Chief Tester"
|
ao_form["authorizing_official-title"] = "Chief Tester"
|
||||||
ao_form["authorizing_official-email"] = "testy@town.com"
|
ao_form["authorizing_official-email"] = "testy@town.com"
|
||||||
ao_form["authorizing_official-phone"] = "(201) 555 5555"
|
|
||||||
|
|
||||||
# test next button
|
# test next button
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
@ -268,7 +267,6 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
||||||
self.assertEqual(application.authorizing_official.last_name, "Tester ATO")
|
self.assertEqual(application.authorizing_official.last_name, "Tester ATO")
|
||||||
self.assertEqual(application.authorizing_official.title, "Chief Tester")
|
self.assertEqual(application.authorizing_official.title, "Chief Tester")
|
||||||
self.assertEqual(application.authorizing_official.email, "testy@town.com")
|
self.assertEqual(application.authorizing_official.email, "testy@town.com")
|
||||||
self.assertEqual(application.authorizing_official.phone, "(201) 555 5555")
|
|
||||||
# the post request should return a redirect to the next form in
|
# the post request should return a redirect to the next form in
|
||||||
# the application
|
# the application
|
||||||
self.assertEqual(ao_result.status_code, 302)
|
self.assertEqual(ao_result.status_code, 302)
|
||||||
|
@ -458,7 +456,6 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
||||||
self.assertContains(review_page, "Tester ATO")
|
self.assertContains(review_page, "Tester ATO")
|
||||||
self.assertContains(review_page, "Chief Tester")
|
self.assertContains(review_page, "Chief Tester")
|
||||||
self.assertContains(review_page, "testy@town.com")
|
self.assertContains(review_page, "testy@town.com")
|
||||||
self.assertContains(review_page, "(201) 555-5555")
|
|
||||||
self.assertContains(review_page, "city.com")
|
self.assertContains(review_page, "city.com")
|
||||||
self.assertContains(review_page, "city.gov")
|
self.assertContains(review_page, "city.gov")
|
||||||
self.assertContains(review_page, "city1.gov")
|
self.assertContains(review_page, "city1.gov")
|
||||||
|
@ -886,7 +883,6 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
||||||
ao_form["authorizing_official-last_name"] = "Tester ATO"
|
ao_form["authorizing_official-last_name"] = "Tester ATO"
|
||||||
ao_form["authorizing_official-title"] = "Chief Tester"
|
ao_form["authorizing_official-title"] = "Chief Tester"
|
||||||
ao_form["authorizing_official-email"] = "testy@town.com"
|
ao_form["authorizing_official-email"] = "testy@town.com"
|
||||||
ao_form["authorizing_official-phone"] = "(201) 555 5555"
|
|
||||||
|
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
ao_result = ao_form.submit()
|
ao_result = ao_form.submit()
|
||||||
|
@ -2229,6 +2225,33 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
||||||
home_page = self.app.get("/")
|
home_page = self.app.get("/")
|
||||||
self.assertContains(home_page, "Withdrawn")
|
self.assertContains(home_page, "Withdrawn")
|
||||||
|
|
||||||
|
def test_application_withdraw_no_permissions(self):
|
||||||
|
"""Can't withdraw applications as a restricted user."""
|
||||||
|
self.user.status = User.RESTRICTED
|
||||||
|
self.user.save()
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||||
|
application.save()
|
||||||
|
|
||||||
|
home_page = self.app.get("/")
|
||||||
|
self.assertContains(home_page, "city.gov")
|
||||||
|
# click the "Manage" link
|
||||||
|
detail_page = home_page.click("Manage", index=0)
|
||||||
|
self.assertContains(detail_page, "city.gov")
|
||||||
|
self.assertContains(detail_page, "city1.gov")
|
||||||
|
self.assertContains(detail_page, "Chief Tester")
|
||||||
|
self.assertContains(detail_page, "testy@town.com")
|
||||||
|
self.assertContains(detail_page, "Admin Tester")
|
||||||
|
self.assertContains(detail_page, "Status:")
|
||||||
|
# Restricted user trying to withdraw results in 403 error
|
||||||
|
with less_console_noise():
|
||||||
|
for url_name in [
|
||||||
|
"application-withdraw-confirmation",
|
||||||
|
"application-withdrawn",
|
||||||
|
]:
|
||||||
|
with self.subTest(url_name=url_name):
|
||||||
|
page = self.client.get(reverse(url_name, kwargs={"pk": application.pk}))
|
||||||
|
self.assertEqual(page.status_code, 403)
|
||||||
|
|
||||||
def test_application_status_no_permissions(self):
|
def test_application_status_no_permissions(self):
|
||||||
"""Can't access applications without being the creator."""
|
"""Can't access applications without being the creator."""
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||||
|
|
|
@ -13,7 +13,11 @@ from registrar.models import DomainApplication
|
||||||
from registrar.utility import StrEnum
|
from registrar.utility import StrEnum
|
||||||
from registrar.views.utility import StepsHelper
|
from registrar.views.utility import StepsHelper
|
||||||
|
|
||||||
from .utility import DomainApplicationPermissionView, ApplicationWizardPermissionView
|
from .utility import (
|
||||||
|
DomainApplicationPermissionView,
|
||||||
|
DomainApplicationPermissionWithdrawView,
|
||||||
|
ApplicationWizardPermissionView,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -544,7 +548,7 @@ class ApplicationStatus(DomainApplicationPermissionView):
|
||||||
template_name = "application_status.html"
|
template_name = "application_status.html"
|
||||||
|
|
||||||
|
|
||||||
class ApplicationWithdrawConfirmation(DomainApplicationPermissionView):
|
class ApplicationWithdrawConfirmation(DomainApplicationPermissionWithdrawView):
|
||||||
"""This page will ask user to confirm if they want to withdraw
|
"""This page will ask user to confirm if they want to withdraw
|
||||||
|
|
||||||
The DomainApplicationPermissionView restricts access so that only the
|
The DomainApplicationPermissionView restricts access so that only the
|
||||||
|
@ -554,7 +558,7 @@ class ApplicationWithdrawConfirmation(DomainApplicationPermissionView):
|
||||||
template_name = "application_withdraw_confirmation.html"
|
template_name = "application_withdraw_confirmation.html"
|
||||||
|
|
||||||
|
|
||||||
class ApplicationWithdrawn(DomainApplicationPermissionView):
|
class ApplicationWithdrawn(DomainApplicationPermissionWithdrawView):
|
||||||
# this view renders no template
|
# this view renders no template
|
||||||
template_name = ""
|
template_name = ""
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from .always_404 import always_404
|
||||||
from .permission_views import (
|
from .permission_views import (
|
||||||
DomainPermissionView,
|
DomainPermissionView,
|
||||||
DomainApplicationPermissionView,
|
DomainApplicationPermissionView,
|
||||||
|
DomainApplicationPermissionWithdrawView,
|
||||||
DomainInvitationPermissionDeleteView,
|
DomainInvitationPermissionDeleteView,
|
||||||
ApplicationWizardPermissionView,
|
ApplicationWizardPermissionView,
|
||||||
)
|
)
|
||||||
|
|
|
@ -149,7 +149,8 @@ class PermissionsLoginMixin(PermissionRequiredMixin):
|
||||||
|
|
||||||
class DomainPermission(PermissionsLoginMixin):
|
class DomainPermission(PermissionsLoginMixin):
|
||||||
|
|
||||||
"""Does the logged-in user have access to this domain?"""
|
"""Permission mixin that redirects to domain if user has access,
|
||||||
|
otherwise 403"""
|
||||||
|
|
||||||
def has_permission(self):
|
def has_permission(self):
|
||||||
"""Check if this user has access to this domain.
|
"""Check if this user has access to this domain.
|
||||||
|
@ -257,7 +258,8 @@ class DomainPermission(PermissionsLoginMixin):
|
||||||
|
|
||||||
class DomainApplicationPermission(PermissionsLoginMixin):
|
class DomainApplicationPermission(PermissionsLoginMixin):
|
||||||
|
|
||||||
"""Does the logged-in user have access to this domain application?"""
|
"""Permission mixin that redirects to domain application if user
|
||||||
|
has access, otherwise 403"""
|
||||||
|
|
||||||
def has_permission(self):
|
def has_permission(self):
|
||||||
"""Check if this user has access to this domain application.
|
"""Check if this user has access to this domain application.
|
||||||
|
@ -277,9 +279,33 @@ class DomainApplicationPermission(PermissionsLoginMixin):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class DomainApplicationPermissionWithdraw(PermissionsLoginMixin):
|
||||||
|
|
||||||
|
"""Permission mixin that redirects to withdraw action on domain application
|
||||||
|
if user has access, otherwise 403"""
|
||||||
|
|
||||||
|
def has_permission(self):
|
||||||
|
"""Check if this user has access to withdraw this domain application."""
|
||||||
|
if not self.request.user.is_authenticated:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# user needs to be the creator of the application
|
||||||
|
# this query is empty if there isn't a domain application with this
|
||||||
|
# id and this user as creator
|
||||||
|
if not DomainApplication.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Restricted users should not be able to withdraw domain requests
|
||||||
|
if self.request.user.is_restricted():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ApplicationWizardPermission(PermissionsLoginMixin):
|
class ApplicationWizardPermission(PermissionsLoginMixin):
|
||||||
|
|
||||||
"""Does the logged-in user have permission to start or edit an application?"""
|
"""Permission mixin that redirects to start or edit domain application if
|
||||||
|
user has access, otherwise 403"""
|
||||||
|
|
||||||
def has_permission(self):
|
def has_permission(self):
|
||||||
"""Check if this user has permission to start or edit an application.
|
"""Check if this user has permission to start or edit an application.
|
||||||
|
@ -296,7 +322,8 @@ class ApplicationWizardPermission(PermissionsLoginMixin):
|
||||||
|
|
||||||
class DomainInvitationPermission(PermissionsLoginMixin):
|
class DomainInvitationPermission(PermissionsLoginMixin):
|
||||||
|
|
||||||
"""Does the logged-in user have access to this domain invitation?
|
"""Permission mixin that redirects to domain invitation if user has
|
||||||
|
access, otherwise 403"
|
||||||
|
|
||||||
A user has access to a domain invitation if they have a role on the
|
A user has access to a domain invitation if they have a role on the
|
||||||
associated domain.
|
associated domain.
|
||||||
|
|
|
@ -8,6 +8,7 @@ from registrar.models import Domain, DomainApplication, DomainInvitation
|
||||||
from .mixins import (
|
from .mixins import (
|
||||||
DomainPermission,
|
DomainPermission,
|
||||||
DomainApplicationPermission,
|
DomainApplicationPermission,
|
||||||
|
DomainApplicationPermissionWithdraw,
|
||||||
DomainInvitationPermission,
|
DomainInvitationPermission,
|
||||||
ApplicationWizardPermission,
|
ApplicationWizardPermission,
|
||||||
)
|
)
|
||||||
|
@ -74,6 +75,26 @@ class DomainApplicationPermissionView(DomainApplicationPermission, DetailView, a
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class DomainApplicationPermissionWithdrawView(DomainApplicationPermissionWithdraw, DetailView, abc.ABC):
|
||||||
|
|
||||||
|
"""Abstract base view for domain application withdraw function
|
||||||
|
|
||||||
|
This abstract view cannot be instantiated. Actual views must specify
|
||||||
|
`template_name`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# DetailView property for what model this is viewing
|
||||||
|
model = DomainApplication
|
||||||
|
# variable name in template context for the model object
|
||||||
|
context_object_name = "domainapplication"
|
||||||
|
|
||||||
|
# Abstract property enforces NotImplementedError on an attribute.
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def template_name(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class ApplicationWizardPermissionView(ApplicationWizardPermission, TemplateView, abc.ABC):
|
class ApplicationWizardPermissionView(ApplicationWizardPermission, TemplateView, abc.ABC):
|
||||||
|
|
||||||
"""Abstract base view for the application form that enforces permissions
|
"""Abstract base view for the application form that enforces permissions
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
-i https://pypi.python.org/simple
|
-i https://pypi.python.org/simple
|
||||||
annotated-types==0.6.0; python_version >= '3.8'
|
annotated-types==0.6.0; python_version >= '3.8'
|
||||||
asgiref==3.7.2; python_version >= '3.7'
|
asgiref==3.7.2; python_version >= '3.7'
|
||||||
boto3==1.28.79; python_version >= '3.7'
|
boto3==1.33.7; python_version >= '3.7'
|
||||||
botocore==1.31.79; python_version >= '3.7'
|
botocore==1.33.7; python_version >= '3.7'
|
||||||
cachetools==5.3.2; python_version >= '3.7'
|
cachetools==5.3.2; python_version >= '3.7'
|
||||||
certifi==2023.7.22; python_version >= '3.6'
|
certifi==2023.11.17; python_version >= '3.6'
|
||||||
cfenv==0.5.3
|
cfenv==0.5.3
|
||||||
cffi==1.16.0; python_version >= '3.8'
|
cffi==1.16.0; python_version >= '3.8'
|
||||||
charset-normalizer==3.3.2; python_full_version >= '3.7.0'
|
charset-normalizer==3.3.2; python_full_version >= '3.7.0'
|
||||||
cryptography==41.0.6; python_version >= '3.7'
|
cryptography==41.0.7; python_version >= '3.7'
|
||||||
defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||||
dj-database-url==2.1.0
|
dj-database-url==2.1.0
|
||||||
dj-email-url==1.0.6
|
dj-email-url==1.0.6
|
||||||
django==4.2.7; python_version >= '3.8'
|
django==4.2.7; python_version >= '3.8'
|
||||||
django-allow-cidr==0.7.1
|
django-allow-cidr==0.7.1
|
||||||
django-auditlog==2.3.0; python_version >= '3.7'
|
django-auditlog==2.3.0; python_version >= '3.7'
|
||||||
django-cache-url==3.4.4
|
django-cache-url==3.4.5
|
||||||
django-cors-headers==4.3.0; python_version >= '3.8'
|
django-cors-headers==4.3.1; python_version >= '3.8'
|
||||||
django-csp==3.7
|
django-csp==3.7
|
||||||
django-fsm==2.8.1
|
django-fsm==2.8.1
|
||||||
django-login-required-middleware==0.9.0
|
django-login-required-middleware==0.9.0
|
||||||
django-phonenumber-field[phonenumberslite]==7.2.0; python_version >= '3.8'
|
django-phonenumber-field[phonenumberslite]==7.2.0; python_version >= '3.8'
|
||||||
django-widget-tweaks==1.5.0; python_version >= '3.8'
|
django-widget-tweaks==1.5.0; python_version >= '3.8'
|
||||||
environs[django]==9.5.0; python_version >= '3.6'
|
environs[django]==9.5.0; python_version >= '3.6'
|
||||||
faker==19.13.0; python_version >= '3.8'
|
faker==20.1.0; python_version >= '3.8'
|
||||||
fred-epplib@ git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c
|
fred-epplib@ git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c
|
||||||
furl==2.1.3
|
furl==2.1.3
|
||||||
future==0.18.3; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
future==0.18.3; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||||
|
@ -31,28 +31,28 @@ gevent==23.9.1; python_version >= '3.8'
|
||||||
geventconnpool@ git+https://github.com/rasky/geventconnpool.git@1bbb93a714a331a069adf27265fe582d9ba7ecd4
|
geventconnpool@ git+https://github.com/rasky/geventconnpool.git@1bbb93a714a331a069adf27265fe582d9ba7ecd4
|
||||||
greenlet==3.0.1; python_version >= '3.7'
|
greenlet==3.0.1; python_version >= '3.7'
|
||||||
gunicorn==21.2.0; python_version >= '3.5'
|
gunicorn==21.2.0; python_version >= '3.5'
|
||||||
idna==3.4; python_version >= '3.5'
|
idna==3.6; python_version >= '3.5'
|
||||||
jmespath==1.0.1; python_version >= '3.7'
|
jmespath==1.0.1; python_version >= '3.7'
|
||||||
lxml==4.9.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
lxml==4.9.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||||
mako==1.2.4; python_version >= '3.7'
|
mako==1.3.0; python_version >= '3.8'
|
||||||
markupsafe==2.1.3; python_version >= '3.7'
|
markupsafe==2.1.3; python_version >= '3.7'
|
||||||
marshmallow==3.20.1; python_version >= '3.8'
|
marshmallow==3.20.1; python_version >= '3.8'
|
||||||
oic==1.6.1; python_version ~= '3.7'
|
oic==1.6.1; python_version ~= '3.7'
|
||||||
orderedmultidict==1.0.1
|
orderedmultidict==1.0.1
|
||||||
packaging==23.2; python_version >= '3.7'
|
packaging==23.2; python_version >= '3.7'
|
||||||
phonenumberslite==8.13.24
|
phonenumberslite==8.13.26
|
||||||
psycopg2-binary==2.9.9; python_version >= '3.7'
|
psycopg2-binary==2.9.9; python_version >= '3.7'
|
||||||
pycparser==2.21
|
pycparser==2.21
|
||||||
pycryptodomex==3.19.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
pycryptodomex==3.19.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||||
pydantic==2.4.2; python_version >= '3.7'
|
pydantic==2.5.2; python_version >= '3.7'
|
||||||
pydantic-core==2.10.1; python_version >= '3.7'
|
pydantic-core==2.14.5; python_version >= '3.7'
|
||||||
pydantic-settings==2.0.3; python_version >= '3.7'
|
pydantic-settings==2.1.0; python_version >= '3.8'
|
||||||
pyjwkest==1.4.2
|
pyjwkest==1.4.2
|
||||||
python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||||
python-dotenv==1.0.0; python_version >= '3.8'
|
python-dotenv==1.0.0; python_version >= '3.8'
|
||||||
requests==2.31.0; python_version >= '3.7'
|
requests==2.31.0; python_version >= '3.7'
|
||||||
s3transfer==0.7.0; python_version >= '3.7'
|
s3transfer==0.8.2; python_version >= '3.7'
|
||||||
setuptools==68.2.2; python_version >= '3.8'
|
setuptools==69.0.2; python_version >= '3.8'
|
||||||
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||||
sqlparse==0.4.4; python_version >= '3.5'
|
sqlparse==0.4.4; python_version >= '3.5'
|
||||||
typing-extensions==4.8.0; python_version >= '3.8'
|
typing-extensions==4.8.0; python_version >= '3.8'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue