├── .coverage ├── .coveragerc ├── .coveralls.yml ├── .env_sample ├── .gitignore ├── .travis.yml ├── Pipfile ├── Pipfile.lock ├── Procfile ├── README.md ├── app.json ├── app ├── __init__.py ├── auth │ └── decorator.py ├── database │ └── database.py ├── doc │ ├── cancel_parcell.yml │ ├── change_present_locationn.yml │ ├── changedestination.yml │ ├── get_a_parcel.yml │ ├── get_all_parcels.yml │ ├── login.yml │ ├── new_parcel.yml │ ├── signup.yml │ ├── status.yml │ └── user_parcels.yml ├── model │ └── models.py ├── util │ ├── __init__.py │ └── helper.py └── views │ ├── parcels.py │ ├── search.py │ └── users.py ├── manage.py ├── run.py ├── runtime.txt └── tests ├── __init__.py ├── test_base.py ├── test_parcels.py └── test_users.py /.coverage: -------------------------------------------------------------------------------- 1 | !coverage.py: This is a private format, don't read it directly!{"lines":{"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytest_cov/engine.py":[44,45,48,49,50,53,54,166,60,61,62,63,167],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/config/__init__.py":[297,305,261,262,264,267,268,271,275,276,278,279,280,281,306,307,308,311,313,713,357,360,361,363,364,365,366,367,377,378,381,390,393,396,398,407,408,409,411,412,415,416,383,384,385,386,872,873,874,875,879,880,883,924,928,936,884,885,886,887,888,889,876,621,622,623,947,948,949,950,952,863,890,892,864,865,891,964,1025,1026,1028,1030,326,327,328,331,332,333,336,910,911,419,420,425,912,913,394,492,495,542,543,545,550,496],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pluggy/manager.py":[73,75,85,88,89,90,91,107,108,110,111,114,118,127,92,93,94,95,96,97,98,102,103,99,100,214,220,223,224,101,104,67,58,59,60,61,201,282,241,242,243,244,245,246],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pluggy/hooks.py":[120,121,122,123,338,339,145,146,147,148,149,151,156,162,163,129,167,168,172,175,176,179,181,182,183,186,340,341,342,343,201,202,203,204,205,206,207,208,209,243,246,248,250,254,255,257,259,214,223,329,244,256,296,304,306,238,307,308,271,273,274,276,278,284,251,249,330,331,332],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pluggy/callers.py":[163,164,165,166,167,168,169,195,198,33,34,201,208,74,75,76,171,38,170,179,187,188,180,181,182,183,189,190,202,203,205,206,191,196],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_path/local.py":[148,149,161,372,374,375,377,378,588,151,152,156,158,314,315,316,317,319,164,165,166,169,175,327,328,329,330,339,340,341,342,346,347,348,349,350,376,386,389,390,395,396,397,398,399,364,365,366,400,284,285,287,288,289,290,292,295,296,297,310,401,402,403,599,331,332,333,334,335,336,373,387,388,17,379,181,299,300,303,304,306,307,301,293,178,651,654,655,656,618,619,620,621,623,992,993,994,625,622,626,657,658,659,253,254,257,258,291,305,259,263,264,265,269,271,273,276,277,278,279,280,660,662,667,629,630,631,635,636,668,411,539,540,26,27,23,407,669,670,673,674,676,678,681,682,189,190,192,193,685,689,357,359,361,167,168,661,671],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_error.py":[64,65,66],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_path/common.py":[313,319,320,321,322,323,324,395,326,325,327,328,329,434,437,439,440,447,448,135,453,381,310,377,399,401,403,406,407,408,409,410,413,414,417,418,419,233,235,51,86,87,88,89,90,98,101,102,103,80,254,104,120,420,424,425,426,378,307,150,427,428,429,430,260,262,263,264,267,268,272,273,140,274,170,171,286,287,289,335,336,337,339,340,290,292,293,294,297,298,299,300,301,302],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_vendored_packages/apipkg.py":[136,137,140,141,148,69,70,73,74,75,76,77,149,150,151,154,142,143,146],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/capture.py":[68,123,124,125,459,460,562,622,563,461,462,463,69,188,199,189,120,468,469,566,625,567,470,471,472,190,191,192,128,490,491,585,546,424,547,548,549,550,586,587,588,589,492,193,194,196,410,411,412,195,203,204,209,172,173,136,137,174,175,210,177,142,143,178,180,181,182,214,215,219,220,205],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/config/argparsing.py":[100,75,77,83,85,323,325,326,327,328,329,330,334,86,87,88,89,90,91,92,223,93,227,228,229,230,231,232,233,234,235,236,237,239,240,94,96,97,78,79,80,348,349,357,101,102,103],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/terminal.py":[39,40,41,42,43,44,45,147,225,227,228,229,230,231,232,233,235,236,237,239,241,242,243,244,159,160,161,163,165,171,245,246,247,248,254,257,259,249,148,354,149,469,470,533,534,535,537,318,286,319,538,539,540,543,544,545,546,549,550,551,554,294,296,297,555,556,566,567,569,571,572,574,879,880,882,884,885,888,889,890,575,558,561,562,563,476,477,478,291,479,484,486,488,489,490,491,494,497,499,500,501,502,504,505,506,507,510,511,512,514,516,518,520,521,308,309,310,311,314,315,522,529,508,523,578,586,587,589,341,343,344,345,347,348,349,351,367,370,371,372,266,267,268,270,271,272,273,274,375,376,377,378,381,382,383,384,386,175,176,183,387,388,389,390,397,398,399,423,427,429,430,432,434,437,438,439,269,461,444,446,447,455,456,457,462,463,464,435],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/helpconfig.py":[26,27,28,29,30,31,32,93,94,122,132,200,201,208,217],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/_argcomplete.py":[107],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/setupplan.py":[29],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/setuponly.py":[87],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/mark/__init__.py":[90,92,157,158,161,163,152,110,111,113,114,118,119,120,121,124,126,128,153,134,135,136],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/cacheprovider.py":[292,300,43,51,44,47,301,138,139,140,141,92,79,93,94,95,142,143,302,209,210,211,323,163,164,165,214,226,172,146,157,159,158],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/python.py":[116,119,137,138,139,148,149,150,171,172,173,174,175,184,177,178,179,188,189,512,513,514,242,516,517,518,519,559,560,561,562,565,566,522,524,537,538,539,540,545,546,525,527,528,530,531,532,523,568,569,570,572,573,574,575,579,582,549,550,556,551,553,580,583,577,190,443,246,247,248,440,448,449,450,493,494,251,252,253,444,354,359,360,361,362,363,364,365,366,368,369,384,195,196,197,200,204,322,307,341,342,347,351,316,331,370,371,201,334,319,198,372,373,374,367,375,290,263,291,292,299,300,267,268,269,270,271,273,274,280,275,276,277,279,281,282,283,301,302,376,1354,1355,1356,1357,256,1359,1360,1361,1371,1374,1375,1376,52,53,54,1421,1378,1379,1380,1389,1390,1395,1401,1386,497,594,595,611,612,613,596,498,499,500,503,504,505,506,641,642,646,647],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/main.py":[218,176,377,378,380,381,382,383,384,385,386,387,389,391,177,178,179,180,181,171,182,183,416,417,418,419,424,425,184,185,224,234,428,429,430,441,442,443,444,445,446,447,448,449,450,626,627,629,630,631,637,638,451,452,453,454,395,397,473,474,475,476,477,478,488,490,491,492,494,498,499,500,501,504,505,506,519,520,522,529,532,533,534,591,593,594,272,273,274,275,278,281,282,257,258,259,285,596,597,599,600,601,598,261,268,283,595,530,536,537,538,539,540,545,546,574,575,411,576,580,581,582,583,586,588,592,541,542,479,543,485,455,402,456,457,464,467,468,469,689,690,694,695,696,697,698,700,691,692,699,470,431,432,433,289,290,291,436,437,438,225,238,241,244,245,246,247,249,251],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/nodes.py":[455,456,457,462,464,466,474,475,83,86,89,92,95,98,101,104,107,109,110,240,237,64,65,118,458,459,460,461,467,469,471,112,113,251,252,253,254,255,256,257,343,344,345,346,491,492,496,285,294,295,522,523,524,525,527,528,529,530,531,532,533,534,535,243,338,514,318,319,515,246],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/mark/structures.py":[408,409,410,381,382,256,257,259,269,304,305,427,431,432,433,434,428],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pluggy/_tracing.py":[14,54,55,64,58,33,36,37,38,39],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/pathlib.py":[275,276,277,278,279,282,302,303,305,311,312,315,319,320],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytest_cov/plugin.py":[316,179,184,185,174,189,219,277,289,290,295,283,221,224,226,227,228],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/logging.py":[374,387,390,427,428,429,395,75,76,77,78,79,80,396,397,398,400,351,352,353,354,355,358,402,403,419,421,516,538,539,571,517,518,520,524,435,437,438,441,445,529,530,534,493,495,450,451,200,201,170,172,173,174,179,181,182,183,186,187,452,453,456,457,496,458,189,191,192,478,460,461,462,463,464,465,479,467,471,473,474,483,484,488,489,468,469,500,501],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/warnings.py":[55,56,57,143,144,145,71,72,73,75,77,78,82,85,88,93,147,95,96,97,106,107,108,117,118,119,120,121,122,124,130,135,136,89,138],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/stepwise.py":[23,28,29,30,32,37,65,40,41,72,73],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/resultlog.py":[26,28],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/junitxml.py":[329,331],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/tmpdir.py":[130,131,38,39,132,133,134,135,136],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/monkeypatch.py":[106,107,108,109,148,149,151,161,162,166,168,169],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/skipping.py":[33,46,47,48,52,53,54,60,61,62,75,76,77,81,90,91,105,106,107,126,127,128,130,142,144,147,165,181],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/debugging.py":[38,43,45,47,50,52,57,58,59,60,61],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/assertion/rewrite.py":[81,83,84,184,194,195,199,201,204,206,209,237,238,239,240,241,246,247,212,213,85,73,74,185,186,189,191,207,86,87,88,89,90,93,94,95,96,118,120,121,217,218,222,223,229,230,231,232,124,134,135,136,137,138,139,140,141,145,155,156,159,426,427,430,431,432,433,434,440,441,442,446,447,451,454,160,173,174,175,302,303,78,304,305,276,277,286,287,288,290,291,293,294,299,444,445,161,162,367,368,369,370,373,404,405,410,459,649,650,651,655,661,662,664,665,666,668,669,670,672,673,681,682,683,685,686,691,693,695,696,697,698,699,700,701,702,706,707,708,709,711,714,716,411,412,418,163,166,167,168,169,342,343,344,345,346,347,348,355,171],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_io/__init__.py":[1],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_io/terminalwriter.py":[5,8,9,10,11,12,14,15,16,27,39,60,40,41,42,28,29,30,31,49,56,58,63,64,65,66,67,68,72,78,131,140,141,142,143,144,145,148,166,172,176,189,199,205,214,245,258,270,275,283,288,289,321,322,326,331,335,399,149,154,157,159,160,161,132,134,136,137,138,162,163,164,168,170,197,215,216,219,225,230,231,232,240,241,243,271,246,247,250,259,260,261,263,267,268,73,74,252,253,206,207,208,210,211,212,200,202,201,203,256,402,406,421,272,284,285,273,264,265,255],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/pastebin.py":[29],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/compat.py":[171,331,332,296,297,298,267,268,272,273,274,275,286,288,301,303,137,138,145,146,147,149,152,157,158,162,164,339,340,308,276,277,278,309,311,312,313,317,318,319,320],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/fixtures.py":[51,52,54,56,57,58,59,60,63,1163,1164,1165,1166,1167,1168,1169,1187,1188,1189,1190,1191,1201,1296,1298,1299,1303,1306,1308,1309,1310,1313,1314,1317,1318,176,177,1321,1322,1323,1378,1196,1336,1341,1343,1348,1352,1354,1355,1356,1357,1358,1359,1360,1361,1362,859,860,861,862,863,864,865,866,867,868,721,722,870,871,872,873,874,1365,1366,1373,1374,1375,1316,1301,1302,1172,1175,1176,1177,1179,1180,1181,1182,1224,1225,1205,1206,1207,1208,1213,1214,1227,1232,1228,1237,1239,1240,1241,1242,1243,1251,1259,1260,1184,352,354,356,357,358,359,360,361,1304,1293,217,218,219,220,221,222,223,187,188,189,190,191,224,228,229,239,241,242,243,244,245,246,247,248,249,250,251,253,254,256,257,268,269,270,240,272,273,274,275,466,467,366,367,368,468],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_xmlgen.py":[24,25],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/runner.py":[68,301,302,388,389,390,272,209,210,211,212,221,273,274,275,290,291,293,294,391,392,183,394,76,77,83,84,86,172,191,192,193,194,195,196,114,146,147,148,150,151,115,371,372,354,355,365,375,378,379,380,381,309,310,312,173,174,232,233,234,235,236,237,238,239,256,258,259,260,261,262,263,264,265,266,267,175,176,157,158,161,164,177,179,87,88,89,91,92,119,120,121,122,257,93,135,136,350,351,356,358,359,315,316,334,319,320,321,330,335,336,337,339,357,137,153,96,97,98,99,78,79,376,322,323,324],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pkg_resources/__init__.py":[2671,2672],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/more_itertools/more.py":[879,898,881,882,883,888,889,894,895,885,896,899,886],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/assertion/__init__.py":[98,99,100,101,113,140,144],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/nose.py":[57,32,64,65,24],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_code/code.py":[783,784],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/doctest.py":[86,87,90,102,104,105,106,108,88],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/reports.py":[173,174,175,176,177,178,91,92,90,118,123,127,130,133,136,140,146,149,151],"/Users/crycetruly/Desktop/SendITApi-v2/tests/__init__.py":[1],"/Users/crycetruly/Desktop/SendITApi-v2/tests/test_base.py":[1,2,3,4,5,6,7,10,11,15,21,27,31,35,39,44,12,13,14,32,33,40,41,36,37,16,17,18,19,22,23,24,25,28,29],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/__init__.py":[11,13,17,18,19,21,22,23,26,28,30,31,34,41,45,48,49],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/__init__.py":[16,17,18,20,22,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,101,104,105,106,107,110,112,114,124,133,137,138,139,140,141,142,143,144,145,151,115,120,122],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/_compat.py":[3,4,5,6,7,9,10,15,16,18,20,120,121,122,123,125,126,127,129,130,132,133,135,140,141,142,143,144,145,146,147,148,150,151,153,155,160,172,173,175,178,183,192,198,199,184,186,187,188,189,193,195,156,157,200,202,203,194,164,165,166,167,170,179,180,181,176,204,206],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/exceptions.py":[59,60,64,65,67,68,71,74,75,81,83,84,86,92,93,106,111,115,128,132,148,158,162,167,173,174,176,181,194,197,203,206,211,214,220,221,223,230,236,237,239,244,249,250,252,258,268,269,270,272,278,285,291,292,295,302,307,308,310,315,323,324,326,331,336,337,339,345,351,352,354,359,365,366,368,373,379,380,382,386,391,392,394,399,405,406,408,413,420,421,423,426,434,442,449,450,452,456,464,465,467,471,477,478,480,485,490,491,493,497,508,509,511,516,525,526,528,532,539,540,542,546,552,553,555,559,565,566,568,574,580,581,583,588,595,596,598,603,608,609,611,617,623,624,626,630,635,636,638,643,644,647,660,648,649,650,651,652,653,654,655,656,657,659,658,661,664,673,675,682,690,709,676,677,678,679,714,97,99,102,103,104,718,719,100,87,88,90,101],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/_internal.py":[10,11,12,13,14,15,16,18,22,23,24,25,26,30,31,34,35,36,37,39,40,43,44,45,46,55,58,60,63,66,69,76,91,159,175,177,178,181,190,204,211,216,223,240,276,295,314,337,354,182,183,184,185,186,188,187,297,302,303,70,71,73,191,193,194,196,197,198,199,202,318,319,320,327,328,329,330,334],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/wrappers.py":[22,23,24,25,27,36,37,38,40,42,47,48,53,62,74,81,89,99,143,146,149,160,171,182,189,197,201,213,219,221,227,243,252,280,316,342,351,364,395,407,418,421,424,445,450,452,467,485,521,540,551,574,582,589,599,604,611,619,627,636,644,651,652,653,654,655,656,657,659,671,676,679,681,684,686,704,705,706,708,709,711,712,715,718,787,790,793,796,806,812,818,828,831,866,877,888,889,925,926,941,944,950,951,952,954,957,970,971,973,992,1009,1012,1014,1022,1045,1062,1077,1119,1130,1146,1156,1168,1171,1174,1187,1274,1300,1317,1330,1336,1338,1345,1353,1361,1374,1379,1381,1390,1398,1406,1411,1416,1426,1437,1442,1444,1451,1456,1458,1465,1473,1475,1476,1479,1489,1491,1506,1511,1523,1559,1620,1625,1629,1635,1644,1650,1652,1667,1674,1680,1681,1684,1689,1691,1693,1697,1705,1709,1712,1716,1721,1725,1730,1735,1737,1743,1750,1752,1756,1758,1767,1774,1775,1782,1783,1787,1788,1791,1792,1795,1797,1802,1812,1821,1832,1837,1839,1844,1847,1854,1855,1856,1862,1863,1866,1867,1873,1874,1878,1879,1883,1884,1888,1889,1894,1895,1901,1902,1905,1906,1909,1910,1911,1913,1915,1923,1934,1939,1941,1959,1963,1942,1950,1957,1964,1968,1969,1974,1976,1977,1980,1982,1984,1996,1997,1998,2007,2010,2015,2018,2019,2020,2028,222,223,224,225,587,832,834,835,839,840,842,843,844,845,846,847,848,849,850,945,946,947,854,855,859,861,862,1001,1002,1005,1006,1007,878,1154,879,1069,66,1073,82,83,86,882,883,884,885,955,595,596,597,1325,1313,1211,1212,1213,1214,1215,942,1220,1221,1222,1224,1226,1227,1230,1246,1250,1258,1259,1272,1314,1288,1289,1290,1292,1297,1298,1315,1326,1327,414,415,833,852,958,959,963,964,864,986,1029,1034,1038,1043,1053,1057,1058,1059,1060,874,875,987,988,990,1809,1798,1800,1810,510,511,512,514,442,75,443,515,516,517,519,633,634],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/http.py":[18,19,20,21,22,23,26,27,28,32,33,34,36,38,43,45,60,61,63,64,65,88,89,91,92,93,94,96,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,162,171,179,199,225,242,273,303,344,398,431,455,485,520,541,556,608,653,667,690,719,724,757,774,789,803,825,847,912,930,942,953,964,1010,1131,1148,1155,1158,362,365,367,368,369,370,372,373,375,376,390,391,392,800,759,761,763,765,766,767,768,769,770],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/datastructures.py":[10,11,12,13,14,15,16,18,19,22,25,28,32,51,78,85,87,89,95,98,101,103,105,108,110,112,115,118,121,124,128,135,137,144,151,152,154,155,160,163,166,172,175,178,181,184,187,190,194,201,203,206,209,212,215,218,221,225,232,234,236,245,252,262,237,242,243,263,264,265,266,267,270,277,279,312,318,320,327,331,333,340,343,346,350,52,53,351,391,393,413,416,420,433,441,451,477,495,509,533,548,555,558,560,565,576,580,584,598,619,645,657,667,674,677,680,684,690,691,693,705,716,717,732,734,740,765,767,770,773,776,781,786,790,793,796,798,801,815,824,828,831,846,851,855,859,865,876,885,895,900,908,909,941,943,951,970,974,976,979,1018,1048,1056,1062,1066,1070,1085,1096,1103,1125,1129,1137,1139,1143,1146,1168,1175,1183,1187,1221,1234,1248,1255,1267,1270,1273,1281,1288,1297,1299,1302,1304,1306,1308,1310,1313,1316,1319,1322,1326,1336,1338,1341,1344,1346,1356,1361,1371,1375,1376,1398,1400,1403,1406,1411,1417,1428,1434,1446,1449,1451,1461,1465,1472,1475,1479,1494,1497,1503,1505,1509,1516,1518,1542,1547,1549,1555,1562,1566,1571,1573,1580,1584,1589,1591,1594,1601,1605,1606,1635,1637,1650,1654,1658,1667,1679,1685,1691,1707,1717,1722,1731,1734,1740,1766,1773,1777,1779,1782,1808,1817,1825,1831,1833,1835,1841,1843,1845,1854,1863,1894,1896,1857,1858,1859,1860,1897,1898,1899,1901,1906,1921,1936,1941,1945,1948,1957,1970,1972,1973,1974,1975,1978,1992,1994,1995,1996,1997,1998,2003,2006,2010,2012,2016,2023,2035,2037,2042,2046,2067,2082,2092,2103,2114,2121,2134,2138,2141,2147,2155,2158,2161,2164,2167,2170,2177,2181,2183,2188,2196,2200,2204,2208,2216,2225,2234,2244,2247,2249,2252,2255,2258,2262,2269,2271,2278,2286,2289,2293,2300,2302,2309,2324,2332,2342,2353,2356,2360,2365,2367,2373,2384,2374,2377,2381,2386,2389,2391,2393,2404,2410,2426,2429,2431,2434,2438,2450,2452,2456,2458,2459,2461,2462,2463,2464,2467,2468,2471,2472,2474,2475,2478,2479,2481,2482,2485,2486,2489,2492,2494,2497,2499,2505,2513,2532,2542,2545,2551,2569,2579,2581,2562,2567,2582,2586,2587,2590,2570,2577,2591,2594,2595,2599,2600,2604,2605,2606,2608,2613,2618,2620,2621,2625,2626,2629,2636,2640,2670,2675,2680,2685,2697,2708,2733,2740,2742,2744,2747,2750,2759,2762,1560,394,396,408,409,411,944,945,1112,1114,1116,1117,952,953,955,957,959,960,966,968,1119,1120,1121,1005,1006,967,1007,1008,36,37,541,1263,1265,1141,1144,562,2013,2014,1339,1131,1132,1349,1351,1352,1354,1133,1134,1236,1246,1202,1204,901,903,905,1205,1169,1171,1206,1207,1208,1209,1210,1211,1212,1217,1218,961,449,439,427,428,429,430,542,546,410,681,543,544,1162,1164,1165,1166,946,947,39,47,962,1135,1009,1011,1013,1014,1012,949,1074,1075,1076,1080],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/six.py":[185,187],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pkg_resources/extern/__init__.py":[28,29,30],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pkg_resources/_vendor/six.py":[185,187],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/filesystem.py":[10,12,13,14,18,21,35,37,40,43],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/urls.py":[17,18,19,20,24,25,26,30,33,36,37,38,39,42,46,47,48,52,54,55,57,62,70,85,97,104,113,120,129,136,143,150,157,174,184,195,253,258,266,287,288,293,294,295,296,297,298,300,303,319,332,334,335,336,337,338,339,341,344,348,361,382,396,404,452,481,492,523,539,559,590,622,688,739,777,797,823,852,920,970,972,980,988,417,418,420,421,422,423,424,432,443,445,448,449,664,667,676,678,305,77,68,267,254,256,268,269,78,83,306,308,90,91,94,95,309,311,312,118,259,260,261,463,465,466,467,468,469,470,471,472,473,478,313,134,315,317,679,474,475,680,681,683,684,500,501,502,508,512,513,514,516,518,520,427,428,430,433,434,435,436,437,438,439,440,271,272,274,79,80,509,511,515,575,579,582,583,584,489,585,586,587,818,819,383,384,386,533,362,363,364,365,366,367,368,369,379,534,535,536,612,614,615,616,617,618,159,161,163,164,166,167,397,398,399,168,170,172,619],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/formparser.py":[11,12,13,17,18,23,24,26,27,28,30,31,35,38,42,46,59,105,123,150,155,168,171,183,212,108,120,225,237,238,239,243,248,259,289,290,291,292,295,298,314,323,336,339,345,353,366,369,381,482,526,534],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/wsgi.py":[10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,27,30,31,32,34,35,38,52,102,145,174,194,230,247,264,281,333,361,448,490,492,505,588,598,653,656,679,686,693,696,722,733,742,797,806,808,812,829,830,850,852,866,869,872,877,896,897,914,916,920,924,931,935,940,943,950,951,966,968,979,982,991,1005,1019,1026,1031,1052,1138,1209,1210,1243,1245,1251,1254,1259,1268,1277,1292,1311,1328,1350,1357,1363,853,854,855,857,858,861,862,863,864,867,87,158,160,161,167,171,88,89,91,92,93,94,95,96,240,244,97,99,870,211,212,183,186,187,188,189,217,223,227,1246,1247,1248,1249,1297,1299,1300,1301,1302,1303,1306,1308,1309],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/utils.py":[12,13,14,15,16,17,18,22,24,28,29,30,31,35,51,59,65,68,78,98,100,102,106,108,110,114,139,141,142,143,144,145,146,147,149,150,151,153,154,156,159,162,204,211,157,212,215,233,253,298,323,344,384,399,446,478,531,564,566,568,579,581,584,586,588,618,626,628,60,61,62,63,103,69,71,72,73,74,75,225,226,227,228,230,111],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/__init__.py":[28,29,30,33,36,41,45,49,54,56,61,62,63,64,65,66,67,68,69,70,71,75,82,76,77,78,79,83],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/environment.py":[10,11,12,13,14,15,16,22,23,24,25,26,27,29,31,37,41,44,60,69,78,90,100,113,234,240,243,246,250,253,254,258,262,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,340,347,356,357,358,359,360,361,362,363,364,401,403,408,425,439,469,479,480,495,499,516,524,536,545,553,555,593,640,733,760,782,794,809,810,832,833,859,861,874,882,889,917,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,947,948,961,970,993,1010,1023,1029,1047,1055,1066,1075,1085,1092,1108,1117,1124,1130,1138,1139,1143,1145,1157,1160,1163,1171,1175,1177,1181,1190,1191,1200,1202,1206,1235,1240,1259,1267,1270,1276],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/nodes.py":[14,15,16,18,19,20,24,28,29,30,31,32,33,34,38,39,40,44,45,46,47,48,49,50,51,55,56,59,62,64,76,79,81,89,92,97,107,122,123,124,125,127,148,164,177,184,194,208,219,228,232,236,238,245,65,66,67,68,69,70,71,72,73,275,276,277,280,281,282,285,288,289,292,295,296,299,300,301,304,311,312,315,316,317,320,324,325,328,331,332,335,336,337,340,345,346,349,350,351,354,355,356,359,360,361,364,374,375,378,379,380,383,384,385,388,389,390,393,394,395,397,410,415,416,417,418,419,421,434,435,436,437,438,440,453,460,461,463,468,469,470,472,480,481,482,485,490,491,493,503,504,515,516,517,519,528,532,533,535,539,546,547,548,550,555,558,559,561,566,567,568,570,575,576,577,579,584,587,588,590,602,621,627,629,631,669,672,674,676,692,698,699,702,703,704,706,716,720,723,724,726,736,740,743,744,746,755,758,759,761,766,769,770,772,785,786,787,790,791,792,795,796,797,800,801,802,805,808,809,812,813,814,817,818,819,822,823,824,827,828,829,832,833,834,836,841,842,843,845,850,851,852,855,856,857,860,861,862,868,871,872,875,881,882,885,890,891,894,900,901,903,908,909,910,912,917,922,923,925,935,945,948,949,952,953,956,957,958,961,973,974,977,984,985,988,992,993,997,999],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/utils.py":[10,11,12,13,14,15,16,20,21,22,23,24,27,28,29,30,31,34,37,39,41,44,60,73,83,89,105,111,123,149,160,177,189,238,287,306,307,313,319,327,334,338,341,348,355,369,378,382,386,392,414,429,444,450,454,458,462,466,472,474,480,484,485,486,494,545,573,574,575,577,583,587,592,598,601,602,604,608,615,617,619,623,631,634,639,640,641,647,85,86,69,70,314,315,316,317,321,322,323,324,325],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/_compat.py":[12,13,15,16,17,20,21,22,23,24,25,27,28,29,31,32,33,35,40,41,42,43,45,46,47,85,96,97,90,91,93,92],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/markupsafe/__init__.py":[11,12,13,15,16,17,18,19,20,21,23,25,27,28,31,66,68,70,77,80,85,90,95,97,104,107,110,112,115,117,120,122,125,127,152,162,172,184,185,202,173,175,180,181,182,204,207,210,215,221,224,227,233,235,240,251,254,258,260,261,264,285,293,294,296,300,303,306,308,311,314,320,321,325,326,327],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/markupsafe/_compat.py":[8,9,11,13,14,15,16,17,19,22],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/defaults.py":[10,11,12,16,17,18,19,20,21,22,23,24,25,26,27,31,32,34,35,36,37,38,39,45,46,47,48,49,50,51,56],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/filters.py":[10,11,12,13,14,16,17,18,20,21,22,25,26,29,37,48,56,62,87,94,115,116,44,45,143,148,153,154,189,196,206,245,247,52,53,281,282,310,325,326,340,341,355,377,378,427,432,441,450,33,34,459,489,498,500,537,577,578,614,616,636,641,662,673,688,693,701,737,768,807,808,809,811,855,856,876,883,890,895,912,937,966,988,1006,1027,1046,1047,1081,1103,1128,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/runtime.py":[10,11,13,14,16,17,19,21,27,28,29,30,34,37,39,40,43,54,60,80,81,83,86,90,97,101,103,125,133,151,154,155,157,175,187,196,208,219,223,234,268,279,285,280,281,282,283,286,287,290,294,296,299,308,104,105,106,317,318,319,324,325,327,333,343,351,352,354,355,356,357,359,366,372,379,380,381,382,383,384,386,392,398,401,410,411,413,421,423,437,450,453,460,461,462,463,465,468,471,482,483,487,501,502,577,584,591,592,605,606,609,615,637,648,650,653,656,659,662,665,669,671,673,677,758,759,771,772,774,785,786,804,805,808,813],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/exceptions.py":[10,11,14,15,17,33,36,44,45,46,50,52,60,64,70,72,80,81,82,84,95,119,124,127,130,133,134,137,140,143,146],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/tests.py":[10,11,12,13,14,15,16,18,19,22,25,30,35,40,57,62,67,72,77,82,90,95,107,120,129,134,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/lexer.py":[16,17,18,19,21,22,23,27,30,31,32,33,35,37,45,46,47,49,50,51,52,53,55,56,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,140,141,142,144,145,146,147,148,149,152,171,178,189,196,219,222,224,228,232,233,234,235,237,240,247,260,267,275,276,279,281,284,287,296,297,301,303,312,315,317,319,321,325,333,338,345,349,364,370,391,412,418,420,548,552,558,599],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/_identifier.py":[2],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/parser.py":[10,11,12,13,14,17,18,19,20,23,24,25,26,27,28,32,35,38,52,61,92,99,106,114,121,149,176,188,207,227,246,255,272,277,286,297,304,342,358,372,379,387,397,426,435,448,457,466,472,494,505,515,526,536,552,587,641,653,668,681,696,722,753,800,820,851,899],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/compiler.py":[10,11,12,13,14,15,16,17,18,19,20,21,23,28,29,30,31,32,33,34,35,40,43,45,48,49,50,55,56,60,63,75,87,108,121,123,130,131,133,165,172,178,189,192,193,196,197,199,203,207,211,215,219,221,225,233,237,241,244,247,313,317,322,327,345,349,353,360,365,371,382,397,402,409,462,475,492,500,505,582,593,600,605,614,623,627,634,637,640,643,649,655,661,665,695,811,843,890,944,965,1024,1154,1175,1186,1194,1205,1217,1221,1366,1374,1399,1418,1430,1437,1444,1453,1461,1471,1488,1501,1472,64,71,1486,1502,1503,1504,1505,1506,1507,1508,1509,1510,1489,1499,1511,1512,1513,1515,1530,1536,1540,1546,1561,1571,1603,1612,1629,1630,1644,1650,1655,1660,1663,1666,1669,1672,1675,1678,1681,1688,1702,1713],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/visitor.py":[10,11,14,24,26,34,41,47,56,58,80],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/optimizer.py":[18,19,20,23,30,32,35,48,49],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/idtracking.py":[1,2,5,6,7,8,11,19,25,27,39,43,50,56,62,69,77,95,99,104,130,140,151,153,156,162,164,168,172,176,192,198,203,204,206,209,218,221,239,242,246,254,259,265,268,271,275,279,282,285],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/loaders.py":[10,11,12,13,14,15,16,17,18,19,22,37,62,68,70,93,99,100,138,158,160,167,189,205,218,221,231,250,270,277,279,282,288,292,308,310,313,322,335,337,341,349,358,359,368,376,388,390,393,401,402,410,417,418,421,432,434,436,457,461,465,466],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/bccache.py":[16,17,18,19,20,21,22,23,24,25,26,27,31,32,33,48,56,57,60,67,69,75,79,98,106,110,117,144,146,153,160,166,176,180,190,195,211,213,219,260,263,271,278,291,335,338,344,354],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/asyncsupport.py":[11,12,13,14,15,17,18,19,22,31,43,58,73,82,99,115,123,133,141,156,163,169,175,181,190,193,199,206,210,211,213,216,219,233,170,142,143,44,51,55,144,145,146,147,148,74,79,149,150,124,130,151,152,153,171,157,158,159,83,90,96,160,100,107,112,172,164,165,166],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jinja2/asyncfilters.py":[1,3,4,7,18,27,55,61,56,58,57,28,29,30,31,38,47,50,52,69,77,78,33,36,82,34,35,48,87,92,97,102,107,115,116,127,128,133,134,135,136,139,140,141,142,143,144,145],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/app.py":[10,12,13,14,15,16,17,18,20,21,23,25,26,27,28,29,30,35,36,37,39,41,44,47,53,70,167,171,175,180,196,207,219,227,233,242,243,252,253,263,268,273,276,277,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,317,322,330,336,343,347,351,364,573,590,602,615,638,643,652,669,682,694,708,711,712,714,716,750,763,773,798,819,821,824,840,841,844,950,1006,1023,1042,1063,1080,57,67,1117,1124,1126,1224,1254,1270,1285,1320,1330,1355,1356,1373,1374,1383,1384,1408,1409,1420,1421,1440,1441,1452,1466,1479,1493,1532,1565,1571,1580,1597,1606,1629,1646,1680,1721,1750,1763,1779,1801,1818,1842,1858,1881,1891,1997,2030,2044,2064,2091,2117,2148,2169,2190,2206,2262,2305,2311,366,367,368,369,370,373,376,377,379,380,677,678,679,390,395,661,662,664,665,666,667,401,413,422,429,436,443,455,463,472,484,493,500,507,508,524,541,543,544,548,549,556,557,558,559,560,561,562,58,822,66,1179,1181,1182,1187,1188,1189,1192,1195,1199,1200,1201,1203,1204,1205,1206,1211,1213,1214,1216,1217,1218,1219,1222,571,583,588,1100,1102,1111,1112,1113,1115,1490,1491,1001,1002,1003,1004,2309,2287,2204,2012,2017,2018,2019,2020,2021,2288,2289,2290,2291,2188,2024,2292,1808,1849,1851,1852,1854,1856,1809,1810,1811,2075,2077,2078,2080,2083,2084,2086,1812,1813,1789,1790,1792,1795,1796,1799,1931,1934,1935,1938,1941,1942,1945,1955,1963,1985,1986,1989,1992,1995,1816,1831,1832,1833,2104,2105,2106,2107,2108,2109,2110,2111,2112,2113,2115,1834,1840,2299,2301,1889,2303,2138,2140,2141,2142,2144,2146,2163,2165,2167,1850],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/routing.py":[97,98,99,100,101,103,104,106,107,108,110,111,114,115,118,128,129,130,140,144,145,146,150,163,182,214,220,223,229,230,232,236,240,242,245,247,249,253,254,258,260,267,271,284,313,317,320,325,327,333,351,353,357,365,378,380,384,392,404,406,410,418,436,438,441,445,451,453,457,483,484,597,601,631,641,662,665,673,689,698,753,797,830,839,867,888,896,900,902,905,908,928,930,931,932,934,937,940,944,961,963,979,989,991,996,1005,1006,1007,1010,1015,1016,1018,1024,1033,1040,1055,1056,1057,1060,1071,1072,1073,1075,1079,1088,1089,1092,1095,1101,1102,1103,1104,1105,1106,1107,1111,1144,1148,1153,1176,1196,1208,1222,1261,1347,1363,1368,1372,1375,1389,1444,1583,1600,1613,1629,1649,1654,1670,1680,1702,1154,1155,1156,1157,1159,1160,1161,1162,1163,1164,1166,1167,1170,1171,1173,602,604,605,607,608,609,610,611,612,613,614,617,619,620,621,622,623,625,628,629,1214,663,1215,679,682,683,684,685,686,687,700,702,705,707,708,709,710,711,713,737,714,715,189,190,191,192,193,207,738,739,740,194,195,197,198,199,716,717,718,719,720,721,735,200,201,202,204,205,723,726,727,728,729,694,696,935,730,731,732,733,734,206,741,744,746,747,748,751,1216,1217,1218,196,208,209,211,742,749,1019,1020,1021,1022,1295,1297,1298,1300,1301,1303,1313,1315,1316,1320,1321,1322,1323,1324,1332,1334,1339,1335,1336,1337,1340,1341,1342,1343,1344,1345,1245,1246,1250,1252,1254,1255,1258,1259,1376,1377,1378,1379,1380,1381,1382,1383,1384,1385,1386,1521,1351,1354,1355,1358,885,886,1359,1360,893,894,1361,1522,1523,1526,1527,1528,1530,1531,1532,1535,1536,1537,1538,764,765,766,1546,1547,767,772,779,782,783,789,792,795,1548,1552,1553,1554,1635,1636,1640,1641,1555,1558,1574,1575,1352,1549,1550],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/cli.py":[10,12,14,15,16,17,18,19,20,21,22,23,25,26,28,29,30,31,33,34,35,36,39,40,43,100,119,143,203,231,259,270,271,272,273,274,275,276,280,285,287,297,308,315,321,334,341,343,354,400,403,416,422,424,436,445,466,469,486,499,521,540,560,567,616,643,647,649,651,655,683,722,723,724,725,726,727,652,653,728,729,730,731,732,733,734,736,737,739,740,742,743,744,774,775,409,413,808,809,810,811,812,814,818,819,820,821,823,859,871,872,873,470,472,473,475,476,477,479,480,481,482,484,877,897],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/__init__.py":[13,16,20,23,28,32,36,41,46,49,54,55,58,61,62,63,66,67,68,71,72,75,76,77,80,81,82,85,88,94,97],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/core.py":[1,2,3,4,5,6,7,9,10,12,14,15,16,17,19,20,23,26,27,29,30,34,39,48,61,80,84,99,100,118,133,211,219,343,348,354,355,392,420,425,437,443,456,463,471,480,490,498,502,506,512,518,557,576,596,598,600,602,604,615,618,621,644,652,659,762,767,792,797,818,823,830,835,844,852,867,874,882,886,901,915,927,934,950,959,981,982,983,987,1010,1015,1019,1056,1084,1098,1166,1192,1198,1205,1210,1212,1217,1227,1239,1251,1254,1258,1263,1265,1270,1274,1282,1289,1330,1331,1336,1361,1368,1378,1387,1390,1398,1419,1430,1437,1448,1459,1465,1486,1489,1492,1500,1537,1538,1545,1614,1658,1687,1735,1747,1766,1775,1786,1793,1799,1800,1802,1813,1819,1831,1844,1847,1850,1856,1546,1547,1338,1615,1616,1617,1618,1620,1621,1626,1627,1637,1638,1640,1641,1642,1643,1646,1651,1656,1340,1344,1345,1348,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359,1549,1551,1552,1555,1556,1557,1558,1561,1566,1567,1568,1569,1570,1571,1572,1573,1574,1575,1580,1581,1587,1588,1589,1590,1591,1592,1596,1598,1600,1602,1606,1628,1629,1630,1631,1632,1633,1634,1635,1562,1565,1577,798,609,610,611,613,801,805,808,810,811,812,813,814,815,816,1213,988,989,990,991,992,993,994,997,998,999,1002,1004,1215,1221,1222,1224,62,63,1225],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/types.py":[1,2,3,5,7,8,11,23,24,27,35,37,41,44,51,57,67,72,73,75,80,82,86,97,98,100,103,107,108,110,125,129,140,142,144,148,151,154,182,186,205,206,208,215,218,224,235,239,240,242,248,252,259,260,262,267,287,291,292,294,301,305,312,313,315,320,340,344,345,347,357,361,362,364,373,377,402,403,404,407,414,423,458,487,488,492,512,520,563,575,577,580,584,588,595,649,653,657,661,665,668,599,600,607,609,611,612,493,494,495,496,497,498,499,500,502,503,504,601,604,605,613,619,621,623,624,610,614,145,146],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/_compat.py":[1,2,3,4,5,6,9,10,12,14,15,18,21,26,37,45,53,56,64,78,84,89,97,99,104,107,118,130,145,156,262,263,264,265,266,267,268,269,271,274,282,294,309,324,332,348,378,407,414,421,428,435,442,449,458,472,520,521,522,528,530,536,540,552,555,558,561,565,566,567,570,574,585,647,650,651,654,658,665,685,686,666,667,682,687,688,689,690,694,695,696,700,701,702],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/exceptions.py":[1,2,5,11,12,15,17,25,28,31,37,43,50,51,53,58,73,89,92,97,109,119,122,126,156,161,164,171,182,190,192,197,203,205,209,210,212,220,224,225,228,233,234],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/utils.py":[1,2,4,6,11,12,18,21,25,35,45,70,75,78,97,100,105,123,128,135,138,141,146,148,151,154,157,160,163,167,264,280,298,330,351,368,419,426,428,431,439],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/globals.py":[1,4,7,29,34,39],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/termui.py":[1,2,3,4,5,7,9,10,11,12,17,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,38,41,46,57,142,185,232,265,354,373,452,464,482,518,546,549,575,580],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/formatting.py":[1,2,3,4,8,11,19,26,89,99,101,113,117,121,125,152,156,161,173,210,225,234,239],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/parser.py":[17,19,20,21,25,76,82,91,98,116,118,144,148,164,166,171,183,185,192,204,206,229,249,259,275,285,321,353,400,83,84,86,87,88],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/_unicodefun.py":[1,2,3,5,12,15,32,50,35,36],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/decorators.py":[1,2,4,6,7,8,9,12,21,31,69,92,121,130,139,156,178,208,228,286,311,53,66,112,113,114,118,166,175,54,65,168,170,171,172,173,131,134,135,136,174,115,70,73,74,75,76,79,80,81,82,86,87,88,89,116,117,16,18,77,78],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/_compat.py":[12,14,16,17,20,21,22,23,25,26,27,29,30,32,37,59,81,82,64,65,67,66],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/globals.py":[11,13,14,23,31,34,41,48,56,57,58,59,60,61,35,36,38,49,50,52],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/local.py":[10,11,12,13,14,19,20,21,22,23,24,25,28,51,52,54,58,61,65,68,74,82,89,115,117,120,123,126,128,129,131,139,147,160,171,187,189,203,216,223,231,246,253,254,289,290,292,300,312,319,326,332,338,344,349,352,355,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,415,416,417,418,419,420,118,55,56,293,294,295,298,165,166,69,70,71,72,167,168,141,142,143,75,76,77,78,79,80,144,145,345,347,305,306,327,328,151,152,154,155,48,66,156],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/helpers.py":[10,12,13,14,15,16,17,18,19,20,21,22,23,25,26,27,30,31,33,34,36,39,45,46,49,57,71,86,95,167,216,363,386,415,456,640,677,716,762,785,840,846,848,855,866,869,873,877,879,890,894,897,898,899,901,903,910,913,914,915,917,919,928,849,850,851,852,853,938,964,978,1006,1017,880,881,883,884,723,724,725,886,887,888,895,911,793,794,795,800,801,813,818,819,768,769,820,822,823,824,826,837,54,63,65,66,926,891,892,904,907,908,856,858,859,860,861,862,863,905,90,92,209,211,213],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/signals.py":[11,13,14,15,16,43,48,49,50,51,52,53,54,55,56,57],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/blinker/__init__.py":[1,12,13,14,15,16,17,18,22],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/blinker/base.py":[10,11,12,14,25,26,27,30,31,35,37,49,73,92,160,186,187,223,241,269,285,307,329,339,343,351,377,385,400,79,80,87,88,89,90,403,404,406,412,417,418,420,432,441,443,455,426,427,428,429,407,410,256,258,262,263,264],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/blinker/_utilities.py":[1,3,6,7,13,14,60,61,70,72,76,79,81,84,97,98,100,107,108,109,110,113,124,127,128,131,142,151,152,154,158,101,102,103,104,74,155,156],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/blinker/_saferef.py":[35,37,38,39,40,43,44,50,54,55,58,88,120,122,124,146,190,197,199,207,209,213,219],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/json/__init__.py":[8,9,10,11,12,13,14,16,17,21,26,29,30,31,34,40,48,54,56,84,89,92,111,124,167,185,194,208,217,252,257,326,306,307,309,313,315,316,320,321,177,94,95,96,97,98,99,102,105,178,179,180,182,322,199,113,114,115,116,117,118,200,201,202,203,134,136,139,140,204,205,310,311,73,74],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/__init__.py":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,22],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/_json.py":[1,2,3,4,7,8,10,14],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/encoding.py":[1,2,3,5,6,9,15,23,37,39,40,41,44,48],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/_compat.py":[1,2,3,4,6,8,13,14,16,19,46],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/exc.py":[1,2,5,10,12,14,18,21,28,29,31,42,45,47,58,61,64,70,72,84,91,93],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/jws.py":[1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,21,24,27,28,29,30,34,36,46,62,95,104,110,124,129,138,152,157,167,169,171,177,185,212,217],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/serializer.py":[1,3,4,5,6,7,8,11,16,65,69,75,78,88,105,128,135,143,161,172,178,191,195,211,228],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/signer.py":[1,2,4,5,6,7,8,9,12,15,17,21,28,31,33,37,38,43,45,50,55,74,81,89,98,119,140,147,151,160,171,46,48],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/timed.py":[1,2,4,5,6,7,8,9,10,11,12,13,14,17,22,24,30,36,44,101,111,114,116,118,144],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/itsdangerous/url_safe.py":[1,3,4,5,6,7,8,11,15,17,19,41,54,58,61,65],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/config.py":[10,12,13,14,16,17,18,21,22,24,28,36,40,82,84,88,111,141,175,200,223,264,25,26,85,86,29,31,32,34],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/ctx.py":[10,12,13,15,17,18,19,23,26,44,46,57,71,83,86,89,96,121,156,188,198,205,207,216,224,238,242,249,277,279,309,311,313,314,316,330,341,383,428,436,440,451,280,281,282,283,284,285,286,292,296,300,305,307,334,336,337,351,352,357,358,359,208,209,210,214,360,218,219,221,222,361,365,368,374,375,376,377,380,381,429,430,434,391,393,394,395,396,397,398,400,406,409,410,411,412,414,418,419,422,423,226,227,228,229,231,233,234,236,425,431,432,353,399],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/logging.py":[8,10,12,13,15,17,20,33,54,55,56,60],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/sessions.py":[10,12,13,14,15,17,18,20,21,24,25,27,32,39,44,49,52,59,66,72,74,81,85,89,94,98,100,105,106,109,137,143,150,152,164,173,231,240,247,253,260,269,285,293,302,305,308,311,313,316,320,321,323,334,348,335,324,325,336,337,162,75,79,171],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/json/tag.py":[43,45,46,47,49,50,52,53,56,57,59,63,65,69,73,78,83,89,94,96,97,99,106,110,115,116,118,121,126,129,130,131,133,136,139,143,144,146,149,152,155,156,157,159,162,165,169,172,174,175,177,180,183,187,188,189,191,194,197,201,202,203,205,208,211,215,228,230,235,236,239,246,274,282,294,298,240,241,243,244,260,67,261,263,264,267,269,270],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/templating.py":[10,12,15,16,19,34,38,40,47,50,52,55,60,80,88,98,113,122,138],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/wrappers.py":[10,12,13,15,16,19,24,26,28,41,49,52,92,111,122,133,137,143,145,151,161,167,182,198,200,202,205,164,165,65,68,35,37,71,50,73,74,87,88,90],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/blueprints.py":[11,12,13,15,18,23,25,62,83,91,93,94,98,101,108,112,116,121,134,147,158,165,189,199,210,224,236,248,262,276,290,304,313,321,328,337,345,356,365,373,381,390,399,408,415,422,439,122,123,124,125,126,127,128,129,130,131,132,333,153,156,140,145,335,193,197,194,195,203,204,205,206,207,196,177,178,163,27,30,34,40,42,43,44,48,50,51,52,55,59,60,180,181,182,183,67,73,74,76,77,79,80,186,187,208,75,154,155,334],"/Users/crycetruly/Desktop/SendITApi-v2/app/__init__.py":[1,2,3,4,5,6,9,10,11,12,13],"/Users/crycetruly/Desktop/SendITApi-v2/app/views/parcels.py":[1,2,3,4,5,6,7,8,9,10,11,12,13,15,18,19,26,27,28,82,83,84,142,143,144,209,210,211,228,229,245,246,247,281,282,283,312,313,314,352,356,362,366,367,369,370,371,372,373,376,377,378,379,380,381,382,383],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/psycopg2/__init__.py":[18,50,63,68,69,70,75,76,80,81,82,85,138,139,144,120,121,123,126,129,130,131,134],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/psycopg2/tz.py":[6,29,30,32,35,48,49,50,52,54,60,71,76,80,83,95,99,100,103,104,107,111,112,118,124,127,136,63,64,65,66,67,68,69,55,56,57,81],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/psycopg2/extensions.py":[12,35,37,44,45,49,50,52,53,60,70,71,72,73,74,75,79,80,81,82,83,84,87,91,92,93,94,98,99,100,101,102,105,111,112,113,117,120,131,135,140,141,144,148,183,184,199,201,202,203,207,211,107,212,219,220,221,223,150,154,155,156,159,166,168,173,174,188,191,192,195,177,179],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/psycopg2/_json.py":[6,30,32,33,37,38,47,48,51,52,55,66,67,78,82,97,100,107,111,117,155,168,181,203,164,165,141,144,145,183,184,187,189,194,195,196,200,147,149,150,152,177,178],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/psycopg2/_range.py":[3,27,29,30,31,34,43,44,46,57,64,69,74,79,86,93,100,107,129,132,139,142,149,165,171,177,183,190,195,226,231,232,234,237,241,244,274,280,281,295,330,386,398,400,402,433,441,446,447,450,451,452,455,456,457,460,461,462,470,471,472,496,502,503,282,283,301,302,306,307,308,309,313,317,318,319,321,322,326,285,287,289,290,291,504,434,435,436,438,506,507,508,510,511,512,514,515,303,304,516,518,519,520,522,523,524],"/Users/crycetruly/Desktop/SendITApi-v2/app/database/database.py":[1,2,3,4,7,8,10,25,74,85,103,116,124,139,150,163,178,188,194,200,222,228,234,241,250,256,263,270,276,284,292,299,307,314,321,328,335,340,346,353,359,375,16,18,19,20,21,27,31,32,33,35,37,39,40,41,43,44,64,65,66,70,71,72,251,252,253,254,42,144,145,146,147,148,81,82,83],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/security.py":[10,11,12,13,14,15,16,17,18,19,21,25,26,29,30,31,32,33,36,46,37,38,39,40,41,42,43,44,45,49,50,68,71,72,124,154,161,204,234,251,229,156,158,230,166,169,170,172,173,174,176,177,178,179,184,185,188,189,191,192,64,89,91,93,94,98,99,100,101,102,103,104,65,201,231,245,247,248,132,133,134,135,137,138],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask_mail.py":[10,12,14,16,17,18,19,20,21,23,24,25,26,27,28,29,30,32,34,36,38,39,40,41,42,48,51,52,56,61,92,102,124,128,134,135,137,140,150,154,169,204,215,216,219,228,231,239,256,271,294,298,305,380,383,389,392,395,418,424,429,442,454,456,484,494,504,513,516,530,534,536,543,558,573,577,579,582,537,538,539,566,544,545,546,547,548,549,550,551,552,553,554,555,517,518,519,520,521,522,523,524,525,526,527,569,570,571,273,275,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,491,506,507,508,138,141,144,155,156,160,162,164,165,167,146,148,492,427,175,296,177,181,400,401,402,130,132,405,406,416,184,185,187,188,103,104,68,69,105,107,108,111,112,121,189,125,190,384,385,307,308,310,312,314,302,303,327,328,93,94,100,330,331,333,335,337,340,343,347,348,375,376,378,191,192,194,196,198,151,152],"/Users/crycetruly/Desktop/SendITApi-v2/app/auth/decorator.py":[1,2,3,4,6,7,10,24,59,72,30,56,32,11,12,15,16,17,18,19,33,34,35,36,37,46,48,51,52,76,77,78,79,13,14,21,49,50],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jwt/__init__.py":[9,12,13,14,15,16,19,23,24],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jwt/api_jwt.py":[1,2,3,4,5,6,8,12,13,14,15,20,23,24,26,46,74,110,143,153,159,168,178,207,218,30,31,32,33,34,35,36,37,38,219,220,221,222,223,77,78,80,82,85,49,54,56,59,60,61,62,63,65,66,57,87,88,92,93],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jwt/api_jws.py":[1,2,3,4,5,7,11,14,15,19,22,23,25,40,46,59,71,82,135,161,172,212,229,233,238,26,27,28,31,32,35,36,38,43,239,240,241,242,243,173,176,177,178,84,86,89,93,95,99,100,101,102,103,107,108,111,112,113,114,115,126,128,174,180,181,183,184,182,186,187,191,192,196,199,200,204,205,209,138,139,141,142,144,146,149,151,155,156,157,214,216,219,220,221,223,224,226],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jwt/algorithms.py":[1,2,3,6,7,8,14,15,31,32,34,35,38,66,69,70,77,84,91,98,106,110,111,120,123,127,131,132,133,134,136,139,156,163,172,175,179,43,44,137,45,46,49,63,140,143,144,145,146,149,154,173,176],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jwt/compat.py":[4,6,7,8,11,14,15,16,21,24,25,45,46],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jwt/exceptions.py":[1,4,5,8,9,12,13,16,17,20,21,24,25,28,29,32,33,36,37,40,41,44,45,48,49,52,57,58,59],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jwt/utils.py":[1,2,3,5,7,8,11,12,15,24,33,45,49,61,71,84,90,94,103,72,73,25,26,46,34,37,39,42,40,75,76,77,81],"/Users/crycetruly/Desktop/SendITApi-v2/app/model/models.py":[1,4,5,7,16,21,24,26,44,47,30,31,32,33,34,35,36,51,52,53,37,38,39,40,41,42,45,8,9,10,11,12,13,14,17,18],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/validate_email.py":[20,21,22,23,25,26,27,28,31,32,35,36,38,39,54,55,56,57,59,60,61,63,66,68,69,70,71,72,73,74,76,79,81,82,84,87,89,90,93,95,96,99,112,179,120,124,126,127,128,129,177],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flasgger/__init__.py":[2,3,4,7,8,9,10,11],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/__init__.py":[10,12,15,18,22],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/exceptions.py":[1,2,3,4,6,7,10,11,13,16,20,21,22,23,24,25,26,27,28,56,59,84,85,90,94,104,114,119,127,128,131,132,135,136,139,140,145,157,158,164,165,170,173,174,180,184,186,188,200,208,223,226,234,242,245,256,263,257,260,266],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/_utils.py":[1,2,3,4,6,9,13,15,18,22,25,28,31,34,37,41,45,47,51,61,70,89,109,122,142,165,178,191,19,20,57,58,26,16],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/compat.py":[1,2,5,6,10,12,13,14,15,16,19,20,21,22,39,46,40,41,43],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/_format.py":[1,2,3,5,6,9,31,33,35,41,67,69,104,132,135,150,136,137,139,147,140,141,142,62,65,63,64,143,144,145,146,157,160,169,170,177,180,193,194,195,196,205,206,207,208,209,210,211,226,233,240,247,248,249,250,270,36,39,271],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/validators.py":[1,3,4,5,7,8,12,13,17,18,21,23,24,27,54,158,169,170,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,195,55,57,58,59,62,63,64,65,68,80,85,120,128,132,147,151,152,46,51,47,48,49,50,153,155,198,199,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,228,232,269,279,301,320,325,335,339,344,352,373,377,389,426,481,487],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/__init__.py":[41,43,44,45,46,49,74,86,87,50,51,54,58,59,61,62,63,66,67,69,70,71,94,95,101,102,105,106,108,109,110,112,113,114,115,116,117,118,125,126,128,131],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/__init__.py":[3,5,6,8,14,15,16,17,18,19,20,21,25,26,28,29,30,33,49,52,71,78,80,82,83,85,88],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/connectionpool.py":[1,2,3,4,5,7,8,11,26,27,28,29,35,36,38,39,40,41,42,43,44,47,49,51,55,59,61,62,64,72,76,79,84,92,95,153,155,156,157,159,160,161,162,199,212,250,280,286,290,302,319,404,407,425,446,447,448,449,736,752,754,755,757,758,759,760,761,762,763,764,782,799,807,831,850,878],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/exceptions.py":[1,2,8,9,10,13,14,15,18,19,20,24,29,30,31,35,40,41,42,45,46,47,50,51,52,55,56,57,61,66,74,76,85,86,88,94,95,96,99,104,105,108,109,110,115,116,117,120,121,122,125,126,127,130,131,132,135,136,137,140,141,143,150,151,152,153,156,157,158,161,162,163,166,167,168,171,172,173,176,177,178,181,182,183,186,190,191,194,195,196,199,203,204,207,214,215,218,223,224,225,228,229,232,237,238,239,244,245,246],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/packages/__init__.py":[1,3,5],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/packages/ssl_match_hostname/__init__.py":[1,3,6,9,19],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/packages/six.py":[1,23,25,26,27,28,29,31,32,36,37,38,40,41,42,43,44,45,47,75,80,86,88,91,103,105,114,117,124,126,130,136,139,141,159,164,171,173,177,181,184,189,195,209,218,224,226,174,175,229,231,232,236,142,89,143,144,146,147,148,151,152,237,238,239,240,149,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,106,107,108,109,110,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,303,308,309,310,311,178,179,312,314,316,127,128,317,320,322,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,350,351,352,354,356,357,360,362,366,367,368,370,371,372,374,376,377,380,382,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,420,421,422,424,426,427,430,432,436,437,438,439,441,442,443,445,447,448,451,453,457,459,460,461,463,465,466,469,471,472,473,182,474,475,476,477,479,482,483,486,491,502,503,504,506,507,508,509,520,521,525,528,529,535,536,539,541,544,561,562,77,565,566,567,568,569,570,573,574,577,580,583,586,588,590,610,611,612,613,614,615,618,619,622,624,625,626,627,628,629,630,631,632,633,634,635,639,640,662,663,666,670,674,678,679,92,115,82,83,93,94,97,100,681,706,712,713,715,721,722,776,786,788,797,800,812,828,849,850,851,852,856,857,862,863,866,868,185,186,216,190,191,196,198,199,200,201,202,205,206,207,118,119,203,160,161,187],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/connection.py":[1,2,3,4,5,6,7,8,9,10,12,13,14,22,24,30,36,38,47,49,51,54,55,62,65,66,67,70,92,94,98,101,103,116,135,145,172,180,184,223,224,226,228,229,230,244,263,267,268,269,270,271,272,277,299,372,386,388,389],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/__init__.py":[1,3,4,5,6,16,21,22,28,34],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/connection.py":[1,2,3,4,7,33,34,85,93,104,134,106,107,113,116,122,123,124,125,129,130,131],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/wait.py":[1,2,3,4,5,6,10,13,14,39,41,68,87,107,111,124,139,146],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/contrib/__init__.py":[1],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/contrib/_appengine_environ.py":[3,5,8,14,18,23,29,15,9,19,10,24,11,30],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/request.py":[1,2,4,5,7,8,12,77,95],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/response.py":[1,2,4,7,38,75],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/ssl_.py":[1,2,3,4,5,7,8,10,11,14,15,16,17,21,22,23,27,40,41,44,45,46,47,52,53,61,62,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,112,113,163,190,213,230,294,360],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/timeout.py":[1,4,5,7,11,15,18,88,91,93,99,103,140,156,171,182,195,213],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/retry.py":[1,2,3,4,5,6,7,9,17,20,24,25,28,147,149,150,152,154,157,159,160,161,162,163,184,199,200,213,227,243,253,261,267,283,289,295,304,320,330,404,411,165,166,167,168,170,174,175,176,177,178,179,180,181,182],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/url.py":[1,2,4,7,11,14,19,20,23,33,38,48,55,95,99,132,225],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/_collections.py":[1,2,3,6,7,17,18,19,22,25,28,40,42,44,51,58,73,80,84,87,97,102,134,136,147,151,155,158,161,169,172,176,178,181,186,203,209,225,251,264,265,266,269,271,274,282,287,294,300,303],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/request.py":[1,3,4,7,10,37,39,41,45,50,74,92],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/filepost.py":[1,2,3,4,6,8,9,10,12,15,25,45,63],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/fields.py":[1,2,3,5,8,22,50,62,63,71,105,116,138,158],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/response.py":[1,2,3,4,5,6,7,9,10,14,15,16,17,19,22,24,29,32,55,57,58,59,62,64,68,71,93,100,102,105,108,114,124,155,157,158,164,211,224,231,240,244,247,255,303,318,336,347,404,473,499,529,532,536,540,547,558,567,571,575,584,593,607,629,696],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/util/queue.py":[1,2,3,5,10,11,14,17,20],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/poolmanager.py":[1,2,3,4,6,7,8,9,10,11,12,13,16,19,21,27,55,58,112,113,117,118,122,148,150,152,163,166,171,198,207,230,243,267,282,302,362,387,390,412,420,435,449],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/__init__.py":[19,20,21,24],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/compat.py":[22,25,31,32,33,34],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/universaldetector.py":[36,39,40,41,43,44,45,46,47,48,51,66,68,69,70,71,72,73,74,75,76,77,78,79,81,94,111,220],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/charsetgroupprober.py":[28,29,32,33,39,49,57,65,85],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/enums.py":[5,8,11,12,13,14,17,21,22,23,24,25,26,27,28,29,32,35,36,37,38,41,44,45,46,47,50,53,54,55,56,57,59,65,71,72,73,74,75,76],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/charsetprober.py":[29,30,32,35,37,39,44,47,51,54,58,61,66,103],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/escprober.py":[28,29,30,31,35,40,42,58,69,73,77,83],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/codingstatemachine.py":[28,30,33,54,55,63,66,80,83,86],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/escsm.py":[28,31,66,67,68,69,70,71,74,76,77,78,79,80,81,84,119,120,121,122,123,124,125,126,129,131,132,133,134,135,136,139,174,175,176,177,178,179,180,181,182,185,187,188,189,190,191,192,195,230,231,232,233,234,237,239,240,241,242,243,244],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/latin1prober.py":[29,30,32,34,35,36,37,38,39,40,41,42,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,85,96,97,103,108,112,116,130],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/mbcsgroupprober.py":[30,31,32,33,34,35,36,37,38,41,42],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/utf8prober.py":[28,29,30,31,35,36,38,44,49,53,57,76],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/mbcssm.py":[28,33,68,69,70,73,75,76,77,78,79,84,104,105,106,107,108,109,110,113,115,116,117,118,119,124,159,160,161,162,163,166,168,169,170,171,172,177,212,213,216,218,219,220,221,222,227,262,263,264,265,266,267,270,272,273,274,275,276,281,316,317,318,319,320,321,329,331,332,333,334,335,340,377,378,379,382,384,385,386,387,388,393,428,429,430,431,432,433,434,437,439,440,441,442,443,448,483,484,485,486,487,488,489,492,494,495,496,497,498,503,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,566,568,569,570,571,572],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/sjisprober.py":[28,29,30,31,32,33,36,37,44,48,52,56,89],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/mbcharsetprober.py":[30,31,34,37,39,45,53,57,61,90],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/chardistribution.py":[28,30,32,34,36,40,41,42,43,44,46,61,70,84,100,105,113,114,120,132,133,139,151,152,158,170,171,177,192,193,199,217,218,224],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/euctwfreq.py":[44,47,50],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/euckrfreq.py":[41,43,47],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/gb2312freq.py":[42,44,47],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/big5freq.py":[43,46,49],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/jisfreq.py":[44,47,50],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/jpcntx.py":[31,116,117,118,119,120,121,123,131,143,170,173,180,183,184,188,192,212,213],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/eucjpprober.py":[28,29,30,31,32,33,36,37,44,48,52,56,89],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/gb2312prober.py":[28,29,30,31,33,34,40,44],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/euckrprober.py":[28,29,30,31,34,35,41,45],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/cp949prober.py":[28,29,30,31,34,35,43,47],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/big5prober.py":[28,29,30,31,34,35,41,45],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/euctwprober.py":[28,29,30,31,33,34,40,44],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/sbcsgroupprober.py":[29,30,31,34,35,37,38,39,40,43,44],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/sbcharsetprober.py":[29,30,33,34,35,36,37,39,53,63,70,77,124],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/langcyrillicmodel.py":[31,50,69,88,107,126,151,282,283,284,285,286,287,291,292,293,294,295,296,300,301,302,303,304,305,309,310,311,312,313,314,318,319,320,321,322,323,327,328,329,330,331,332],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/langgreekmodel.py":[35,54,79,210,211,212,213,214,215,219,220,221,222,223,224],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/langbulgarianmodel.py":[38,57,82,213,214,215,216,217,218,222,223,224,225,226,227],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/langthaimodel.py":[37,62,193,194,195,196,197,198],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/langhebrewmodel.py":[38,63,194,195,196,197,198,199],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/hebrewprober.py":[28,29,128,130,131,132,133,134,135,136,137,138,139,144,149,151,152,154,164,174,178,182,196,255,282,286],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/langturkishmodel.py":[37,56,187,188,189,190,191,192],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/chardet/version.py":[6,8,9],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/exceptions.py":[8,9,12,15,17,28,29,32,33,36,37,40,41,44,50,53,57,60,61,64,65,68,69,72,73,76,77,80,81,84,85,88,89,92,93,96,97,100,101,104,105,108,109,114,115,116,119,120,121,124,125,126],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/contrib/pyopenssl.py":[43,44,46],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/__version__.py":[5,6,7,8,9,10,11,12,13,14],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/utils.py":[9,11,12,13,14,15,16,17,18,19,20,21,23,24,26,27,28,32,33,34,37,39,41,44,98,107,168,219,227,259,284,312,344,379,404,419,430,450,475,496,514,524,561,562,565,589,611,626,637,648,672,694,755,767,793,802,814,852,853,854,857,889,906,923,924,927,948,965],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/certs.py":[14,15,17],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/certifi/__init__.py":[1,3],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/certifi/core.py":[9,10,11,14,18,21,27,36,22,24],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/_internal_utils.py":[9,11,14,30],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/compat.py":[9,11,13,20,23,26,28,29,30,31,37,56,57,58,59,60,61,62,63,65,66,67,68,69,70],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/cookies.py":[10,12,13,14,16,17,19,20,25,35,37,42,45,48,51,65,68,71,74,78,81,84,88,92,97,102,104,111,114,118,135,146,165,168,171,187,189,201,218,227,235,244,252,261,270,278,286,299,315,321,330,337,343,348,356,376,401,408,414,421,426,441,477,508,529],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/structures.py":[8,10,13,38,40,46,51,54,57,60,63,71,80,83,87,88,90,94,97,102,91,92],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/packages.py":[1,6,7,10,11,12],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/idna/__init__.py":[1,2],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/idna/package_data.py":[1],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/idna/core.py":[1,2,3,4,5,6,8,9,10,12,13,14,16,17,18,21,22,23,26,27,28,31,32,33,36,43,46,49,53,60,67,127,134,143,149,193,234,266,294,315,343,374],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/idna/idnadata.py":[3,6,44,63,74,80,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,764,1883,1886],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/idna/intranges.py":[6,8,10,31,34,38],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/models.py":[8,10,11,16,18,19,20,21,24,25,26,28,29,30,33,34,38,42,43,48,49,50,51,52,55,56,57,60,61,82,109,174,175,186,198,224,228,251,254,272,288,290,309,325,328,339,345,355,441,452,521,534,556,576,586,589,592,593,596,643,646,649,657,665,668,678,688,692,707,714,719,724,729,784,815,835,873,899,917,942],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/hooks.py":[13,14,17,23],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/auth.py":[8,10,11,12,13,14,15,17,19,20,21,22,24,25,28,72,73,75,79,80,82,86,92,95,100,101,103,108,109,111,117,127,229,234,278,298,304],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/status_codes.py":[18,20,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,42,43,44,45,46,47,48,49,50,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,89,90,91,92,93,94,95,96,97,98,99,102,104,120,105,106,107,108,109,111,118,117,112,113],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/api.py":[11,13,16,63,78,91,104,119,134,149],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/sessions.py":[9,10,11,12,13,15,16,17,19,20,21,22,23,26,27,29,34,37,40,46,49,80,95,97,118,144,256,276,317,340,357,360,361,362,365,420,423,426,469,537,548,559,570,583,595,607,617,690,719,733,738,749,753,758],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/requests/adapters.py":[9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33,36,37,38,41,43,44,49,50,51,52,55,56,58,62,79,84,109,110,111,113,114,115,131,134,146,166,203,255,292,319,329,358,372,394],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/urllib3/contrib/socks.py":[23,24,26,27,41,43,46,49,50,51,53,54,59,62,63,67,129,130,133,134,137,138,141,145,147,148,152],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/socks.py":[55,57,58,59,60,61,62,63,64,65,66,67,69,72,79,81,82,83,85,86,88,91,110,111,112,119,123,124,127,128,131,132,135,136,139,140,143,144,147,148,150,155,156,157,158,159,160,161,162,165,169,179,185,189,192,203,211,267,268,269,277,280,282,283,289,294,301,303,305,306,323,335,344,347,354,374,379,416,436,442,462,466,471,475,477,483,485,491,493,499,589,634,649,709,769,770,771,774,93,107,861],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/_validators.py":[1,3,4,5,8,21,49,65,85,102,119,133,138,143,152,160,168,173,178,200,205,222,243,269,277,287,294,309,317,324,331,337,361,375],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/jsonschema/_version.py":[3,4,5],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flasgger/base.py":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,51,54,55,60,113,116,117,121,128,131,132,138,141,145,146,147,148,151,153,154,157,158,159,160,165,181,206,228,235,246,257,448,458,464,526,536,622,675,698,701,702,166,167,168,169,170,171,172,173,174,175,176,177,178,179,185,186,188,462,190,192,470,476,477,478,479,480,481,482,483,484,486,487,489,492,493,494,495,56,57,58,496,497,471,474,502,503,504,512,513,514,515,516,517,518,519,520,524,193,530,195,203,204,532,534],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/__init__.py":[2,4,5,6,8,9,11,12,13,15,16,18,26,30,40,41,43,58,69,80,91,103,118,134,144,154,164,174,184,194,195,196,215,216,217,218,219,245,252,253,254,255,256,257,285,292,300,308,309,319,329,337,346,355,364,367,368,374,378,380,382,383,385,386,388,395,369,370],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/error.py":[2,4,6,14,37,45,46,48,51,58],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/tokens.py":[2,3,6,17,18,19,25,26,28,29,31,32,34,39,40,42,43,45,46,48,49,51,52,54,55,57,58,60,61,63,64,66,67,69,70,72,73,75,76,77,82,83,84,89,90,91,96,97,98],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/events.py":[4,5,8,15,16,21,23,31,32,36,37,42,43,45,47,54,56,61,62,64,66,75,76,78,79,81,82,84,85],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/nodes.py":[2,3,8,25,26,28,35,37,44,45,47,48],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/loader.py":[2,4,5,6,7,8,9,11,13,21,23,31,33,41,43,55,57],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/reader.py":[18,20,22,24,26,33,45,59,87,94,99,114,122,137,138,146,177],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/scanner.py":[27,29,30,32,33,35,38,46,48,113,125,135,145,156,264,279,295,312,325,349,359,371,390,402,405,408,424,427,430,447,450,453,470,484,517,545,601,612,623,634,637,640,651,654,657,668,683,690,698,706,711,721,731,752,787,806,827,844,857,867,876,885,899,935,976,1052,1092,1106,1121,1134,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1180,1181,1182,1185,1228,1252,1270,1311,1348,1372,1397,1416],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/parser.py":[62,64,65,66,67,69,70,72,77,78,81,89,94,107,114,127,139,159,190,208,217,264,267,270,273,376,381,402,422,427,446,471,476,502,512,526,537,542,569,583,587],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/composer.py":[2,4,5,6,8,9,11,13,16,24,29,50,63,88,99,117],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/constructor.py":[3,4,5,6,7,8,11,12,14,16,17,19,21,22,24,30,34,39,46,59,109,116,124,139,151,157,163,165,172,207,212,217,218,219,220,221,222,225,229,257,258,259,260,262,286,302,312,314,344,366,387,393,396,401,407,417,422,423,424,153,154,155,426,427,428,430,431,432,434,435,436,438,439,440,442,443,444,446,447,448,450,451,452,454,455,456,458,459,460,462,463,464,466,467,468,470,471,473,475,478,481,497,500,503,506,521,546,553,561,576,590,599,634,637,638,639,641,642,643,645,646,647,649,650,651,653,654,655,657,658,659,661,662,663,665,666,667,669,670,671,673,674,675,677,678,679,681,682,683,685,686,687,159,160,161,689,690,691,693,694,695,697,698,699,701,702,703,705,707,710,713,719,720],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/resolver.py":[2,4,5,7,9,10,12,14,15,16,18,19,21,25,37,38,91,114,120,143,167,168,170,171,172,174,175,27,28,29,31,32,34,35,177,178,179,183,184,186,187,188,192,193,195,196,197,198,200,201,202,204,205,207,208,209,213,214,216,217,218,219,223,224,225,226],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/dumper.py":[2,4,5,6,7,9,16,27,34,45,52],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/emitter.py":[9,11,12,14,15,17,18,31,34,35,39,106,111,120,133,146,160,170,175,178,214,226,233,260,266,274,280,292,310,316,333,354,359,368,373,376,388,392,395,409,414,422,426,430,437,459,469,494,515,539,545,557,580,616,626,788,792,797,801,814,827,838,845,854,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,926,980,991,1045,1080],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/serializer.py":[2,4,5,6,8,9,11,13,16,27,36,46,60,74,78],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/representer.py":[2,3,5,6,8,10,11,13,15,16,18,26,33,65,71,77,85,103,131,134,136,144,147,150,157,164,167,168,169,171,191,206,209,215,219,223,230,233,234,67,68,69,236,237,239,240,242,243,245,246,248,249,251,252,254,255,257,258,260,261,263,264,266,267,269,270,272,274,285,288,292,296,358,366,367,369,370,372,373,375,376,378,379,381,382,384,385,387,388,73,74,75],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/yaml/cyaml.py":[3,4,7],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/views.py":[10,12,13,16,17,20,50,53,56,67,69,76,110,113,115,133,147,149,116,118,119,121,122,129,123,130,86,90,101,102,103,104,105,106,107],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/mistune.py":[9,11,12,14,15,17,18,19,20,24,25,26,27,28,29,31,32,33,34,36,37,38,39,40,43,50,55,76,86,94,95,97,98,102,103,109,110,111,112,116,117,118,119,120,121,132,44,45,46,47,133,136,137,139,141,142,143,146,147,148,149,150,151,152,153,154,157,158,159,160,161,164,165,167,168,170,173,174,175,178,179,180,181,185,190,195,208,211,236,241,250,257,264,272,275,292,332,344,351,385,397,408,431,439,457,461,466,467,469,470,471,472,473,474,475,478,479,480,486,487,491,492,493,494,498,499,503,504,505,506,507,509,519,520,521,524,525,526,527,530,531,532,535,554,557,562,596,600,608,614,630,640,643,650,657,668,673,678,682,685,689,694,696,698,701,715,728,735,747,756,762,773,777,781,792,799,817,824,831,839,845,852,861,868,879,892,910,919,923,935,952,961,967,968,993,996,1003,1034,1040,1045,1056,1065,1071,1074,1077,1084,1089,1115,1121,1128,1138,1144,1154,1158,1167,1170,1174],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flasgger/constants.py":[2,3],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flasgger/utils.py":[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,26,43,67,170,256,371,400,415,420,425,431,443,452,459,496,508,566,580,616,639,709,719,730,742,747,749,759,762,765,768,771,774,777,780,783,786,789,792,795,798,801,806,810,811,818,825,828,829,836,192,200,220,223,251,225,226,201,193,194,195,570,571,577,196,197,202,204,208,209,228,229,231,232,234,238,249,240,248],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flasgger/marshmallow_apispec.py":[2,4,6,8,9,16,17,18,19,20,21,24,27,28,44,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,64,84],"/Users/crycetruly/Desktop/SendITApi-v2/app/util/__init__.py":[1],"/Users/crycetruly/Desktop/SendITApi-v2/app/util/helper.py":[1,2,3,4,6,7,10,11,26,36,39,47,68,86,105,123],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/__init__.py":[10,12,13,14,16,19],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/location.py":[3,5,6,9,13,20,22,24,42,52,61,70,79,89,99,105,108,110,122,125,128,132,140,143],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/point.py":[4,6,7,8,9,10,12,13,20,22,42,43,44,45,46,47,48,51,64,92,144,146,148,150,191,194,200,203,206,209,212,239,254,260,263,268,273,276,277,300,325,399,412],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/util.py":[3,5,7,9,12,13,14,15,20,22,25,33,40,58],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/compat.py":[3,5,6,7,8,9,11,13,14,18,19,24,25,37,38,44,45,53,54,55,56,60,68,127,136,128,129,130,131,139,155],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/units.py":[3,5,10,24,35,46,59,74,81,96,111,128,129,130,131,132,133,134,135],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/format.py":[3,5,6,8,9,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,33,37,38,39,42,43,44,47,48,49,52,53,54,57,58,59,62,63,64,68,84,86,87,88,89,90,91,95,104,105,106,107,108,109,110,111,112,113,114,115,116,117,120,121,124,123,126,129,128],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/__init__.py":[76,79,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,140,141,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,171],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/arcgis.py":[1,2,3,5,6,11,12,13,15,17,20,25,27,28,30,31,32,36,37,38,39,40,41,42,43,44,45,46,47,145,157,158,222,223,308],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/exc.py":[3,6,9,12,16,19,27,30,35,38,42,45,49,52,56,59,66,69,74,77,81,84,88],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/base.py":[1,2,3,4,5,7,16,17,28,31,35,38,152,165,166,167,168,169,170,175,176,179,180,181,182,183,184,185,186,187,188,189,190,194,197,201,202,203,204,205,206,242,243,265,266,286,298,299,300,301,401,409,415],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/azure.py":[2,3,5,8,15,17,18,23,24,25,26,27,28,29,67,74],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/tomtom.py":[1,2,3,4,6,9,16,18,19,24,25,26,27,28,29,30,72,73,74,75,76,131,132,165,169,174,179,189,195,205],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/baidu.py":[1,3,4,10,11,12,14,17,24,26,31,32,33,34,35,36,37,91,103,104,136,172,192,217,272],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/bing.py":[1,2,9,10,11,13,16,21,24,25,26,27,28,31,32,37,38,39,40,41,42,88,89,90,91,92,93,171,172,173,174,219,220],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/databc.py":[1,2,3,4,5,7,10,15,17,21,22,23,24,25,26,69,70,71,72,73,135],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/geocodeearth.py":[1,2,4,7,12,17,18,19,20,21,22,23,24,25],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/pelias.py":[1,3,4,5,6,8,11,23,25,26,31,32,33,34,35,36,37,38,39,104,105,163,164,205,214],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/geocodefarm.py":[1,2,7,8,9,11,14,19,21,22,26,27,28,29,30,31,32,81,109,146,167,182],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/geonames.py":[1,3,4,9,10,11,13,16,24,26,27,31,32,33,34,35,36,37,91,126,127,181],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/googlev3.py":[1,2,3,4,6,7,13,14,15,17,18,19,20,21,22,27,30,39,41,42,46,47,48,49,50,51,52,53,54,55,56,136,159,170,171,172,173,174,175,176,177,268,269,270,271,330,399,419],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytz/__init__.py":[9,11,12,13,15,16,17,18,19,20,21,25,26,27,29,32,33,34,35,36,37,41,45,47,78,111,120,123,183,188,189,192,197,198,200,201,202,204,209,212,215,218,221,227,235,238,242,245,275,278,285,288,319,320,324,343,346,351,352,366,371,373,375,381,384,387,390,393,396,402,411,477,480,486,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1083,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301,1302,1303,1304,1305,1306,1307,1308,1309,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358,1359,1360,1361,1362,1363,1364,1365,1366,1367,1368,1369,1370,1371,1372,1373,1374,1375,1376,1377,1378,1379,1380,1381,1382,1383,1384,1385,1386,1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1418,1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1527],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytz/exceptions.py":[3,6,7,11,23,24,27,28,31,39,42,48],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytz/lazy.py":[1,2,3,13,16,17,18,20,30,40,50,60,71,72,75,76,77,78,79,80,81,82,84,118,121,122,125,139,172,86,91,92,94,96,109,110,97,107,112,113,141,144,145,147,149,163,164,150,161,166,167],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytz/tzinfo.py":[1,3,4,5,6,10,11,13,15,18,27,28,31,42,45,58,20,21,22,23,24,25,61,66,68,69,70,72,76,81,82,88,96,104,112,118,147,150,156,162,166,170,172,176,177,179,193,203,258,396,427,466,504,518,529],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytz/tzfile.py":[4,6,7,9,10,13,17,15,20,25,126],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/here.py":[3,5,6,13,14,15,17,20,27,30,31,32,33,34,35,36,37,40,41,47,48,49,50,51,52,99,100,101,102,103,104,105,106,207,208,209,210,211,212,213,281,282],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/ignfrance.py":[1,2,4,6,7,8,9,11,14,19,35,37,42,43,44,45,46,47,48,49,50,51,138,139,140,141,142,143,245,246,247,248,249,348,401,436,437,522,540,541],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/mapbox.py":[1,2,3,4,5,7,10,17,19,24,25,26,27,28,29,30,70,90,91,92,93,94,153,154],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/opencage.py":[1,3,4,5,6,7,9,12,19,21,26,27,28,29,30,31,32,85,86,87,88,89,167,168,169,220,240],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/openmapquest.py":[1,2,3,5,8,16,18,19,23,24,25,26,27,28,29,30,31,32,33,103],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/osm.py":[1,3,4,5,6,7,9,11,14,35,38,39,40,41,42,43,46,47,51,52,53,54,55,56,57,58,59,60,149,167,168,169,170,171,172,173,313,314,315,316,372,384],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/photon.py":[1,2,3,4,6,9,18,20,21,25,26,27,28,29,30,31,77,78,79,80,81,82,152,153,154,155,204,205,217],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/pickpoint.py":[1,2,4,7,15,17,18,23,24,25,26,27,28,29,30,31,32,94],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/smartystreets.py":[1,2,3,4,5,7,10,15,17,23,24,25,26,27,28,29,97,119,126,138,149],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/what3words.py":[1,3,4,5,6,7,9,12,22,24,25,28,29,34,35,36,37,38,39,93,104,105,106,154,193,194,241],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/geocoders/yandex.py":[1,3,4,5,6,7,9,12,19,21,25,26,27,28,29,30,31,32,86,123,124,125,186],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geopy/distance.py":[88,89,91,92,94,96,97,98,99,104,115,116,117,118,119,120,124,149,153,155,169,177,180,183,186,192,194,197,200,202,208,211,214,220,221,224,227,230,233,236,239,243,247,251,255,259,263,267,271,275,280,297,299,303,322,351,354,376,378,379,380,382,391,409,424,449,452,481,483,484,485,487,499,516,616,704,707],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geographiclib/__init__.py":[1,3,6,7],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geographiclib/geodesic.py":[60,80,81,82,83,85,86,88,89,90,91,92,93,94,95,96,97,98,99,100,101,103,104,105,106,107,108,110,111,112,113,114,115,116,117,118,119,121,145,147,192,194,202,204,222,224,242,244,252,254,272,274,322,339,366,399,404,416,429,483,627,704,1018,1060,1069,1106,1143,1144,1164,1165,1178,1179,1201,1202,1224,1225,1252,1264,1266,1268,1270,1272,1274,1276,1279,1281,1283,1285,1287,1291,1293,285,287,289,290,291,292,293,295,296,297,299,309,310,311,313,315,316,317,318,325,326,327,328,329,330,332,333,334,335,336,337,319,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,358,359,360,361,362,363,364,320,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,391,392,393,394,395,396,397,1294],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geographiclib/geomath.py":[1,14,15,17,28,30,31,32,33,34,35,37,41,43,48,50,63,65,74,76,83,85,89,91,104,106,113,115,127,129,139,141,145,147,153,155,175,177,194,196,200,202,206,40,68,69,199,108,109,112,110,111],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geographiclib/constants.py":[1,14,17,19,21,22],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/geographiclib/geodesiccapability.py":[1,15,18,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask_cors/__init__.py":[10,11,12,13,15,18,19,23,24,26,27],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask_cors/decorator.py":[11,12,13,14,16,18],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask_cors/core.py":[9,10,11,12,13,14,15,16,18,21,22,23,24,25,26,29,30,32,33,34,35,36,37,38,42,46,47,48,49,50,51,52,53,54,55,56,57,58,61,93,108,156,171,219,249,258,266,270,283,298,312,328,334,345,349,289,290,300,303,305,307,308,291,292,293,295,353,355,356,360,346,338,339,263,361,365,372,329,330,319,320,331,373,321,322,323,375,378,62,78,79,340,343,325,102,103,104,105,272,274,250,253,256,275,229,236,240,172,109,110,114,134,135,142,145,173,175,178,179,181,183,188,205,209,210,216,242,244,245,247],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask_cors/extension.py":[10,11,12,14,16,124,126,131,170,127,128,129,134,138,144,145,150,151,153,171,186,154,158,159,164,173,177,178,179,180,181,182,185],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask_cors/version.py":[1],"/Users/crycetruly/Desktop/SendITApi-v2/app/views/users.py":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,18,21,22,91,92,158,159,181,182,183,229,230,242,243,244,268,269,275,291,28,31,32,33,35,36,39,40,41,44,45,49,53,56,59,61,64,67,70,73,74,76,78,79,82,85,86,98,99,100,101,87,88,37,38,34,102,103,105,106,110,111,112,116,117,118,119,121,126,127,128,129,130,132,133,134,135,137,138,139,141,142,143,144,145,146,147,148,151,152,153,154,155,65,66,46,47,50,51,52,68,69,29,30],"/Users/crycetruly/Desktop/SendITApi-v2/app/views/search.py":[1,3,4,5,7,8,9,12,13],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/unittest.py":[23,24,26,27,25,29,50,52,53,55,56,57,58,59,60,61,63,64,65,66,68,224,249,38,39,41,42,43,44,45,46,47,82,83,100,84,85,87,88,210,211,191,192,111,165,168,104,107,108],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/_code/source.py":[208,210,211,212,213,214,218,219,220,221,222,238,239,244,27,28,29,245,246,228,229,226,227],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/_code/code.py":[38,39,990,991,992,993,994,995,996,997,998,1002,40,41,44,45,42,43,46,60,61,63,70],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/more_itertools/recipes.py":[246],"/Users/crycetruly/Desktop/SendITApi-v2/tests/test_parcels.py":[1,2,5,6,11,7,8,9,14,15,16,17,18,19,20,21,22,25,26,27,28,30,31,32],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/atomicwrites/__init__.py":[214,128,135,137,140,141,142,143,149,153,154,155,156,170,171,172,176,177,178,179,157,158,184,185,38,159,189,190,93,49,50,42,43,44,46,160,162],"/Users/crycetruly/Desktop/SendITApi-v2/tests/test_users.py":[2,3,4,5,6,9,10,27,41,46,52,70,83,89,103,117,127,133,141,157,171,194,199,212,220,228,244,172,173,174,15,16,17,18,19,21,22,23,24,175,176,177,179,180,181,182,183,184,186,187,188,189,191,192,107,108,109,110,111,112,113,114,115,47,48,49,50,57,58,59,60,62,63,64,65,67,68,195,196,197,200,203,204,205,206,208,209,210,134,135,136,137,138,139,143,144,145,146,148,149,150,151,153,154,155,245,246,32,33,35,36,37,38,247,248,250,251,252,253,254,255,256,257,259,260,261,262,263,264,265,266,267,269,270,159,161,162,163,164,166,167,168,169,74,75,76,77,78,79,80,81,213,214,215,216,217,218,84,85,86,87,221,222,223,224,225,226,128,129,130,131,118,119,120,121,122,123,124,125,42,43,44,93,94,95,96,98,99,100,101,230,231,232,233,235,236,237,238,240,241,242],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/py/_io/capture.py":[1,2,3,4,6,7,11,18,20,21,29,31,32,34,52,69,80,91,119,120,124,133,137,140,141,154,156,171,178,183,185,198,234,242,246,259,271,282,287,288,305,313,328,332,345,351,352,354,355,356,358,360,362,365,366],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/_pytest/mark/evaluate.py":[27,28,29,30,51,52,74,76,42,78,111,34],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/flask/testing.py":[11,13,14,16,17,18,19,20,21,25,91,104,106,108,115,166,203,209,220,224,225,229,109,111,112,167,168,169,172,185,186,187,188,50,54,55,56,58,61,62,64,65,66,67,68,70,72,76,88,191,192,194,196,197,198,199,200,204,206,207,210,215,216,217],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/click/testing.py":[1,2,3,4,5,6,8,15,18,21,22,25,27,31,34,38,41,44,47,50,54,73,74,77,91,96,102,111,118,137,140,148,155,162,163,281,359],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/werkzeug/test.py":[10,11,12,13,14,15,16,17,19,20,21,22,23,24,28,31,32,33,35,36,37,39,43,115,124,132,135,137,140,148,156,160,162,165,169,173,175,185,195,214,281,284,287,290,297,356,373,377,391,394,395,397,407,413,416,417,419,422,427,430,437,441,442,448,449,451,457,460,461,463,481,482,464,466,476,479,483,486,487,489,492,496,498,499,501,508,512,514,515,517,524,528,529,530,532,537,547,553,571,633,644,649,652,681,684,695,707,712,721,762,827,832,837,842,847,852,857,862,867,874,896,685,686,687,688,691,829,830,298,299,301,302,303,304,305,378,383,384,387,388,389,306,309,310,313,525,526,314,315,316,319,320,322,323,324,325,326,327,328,329,330,493,494,331,452,453,332,334,353,573,490,574,420,576,423,398,399,400,402,404,424,577,579,585,590,597,599,600,601,603,606,502,503,504,608,609,610,604,611,612,613,535,614,540,541,543,545,615,616,617,618,619,620,621,622,623,624,625,627,629,630,631,558,560,561,467,469,470,471,472,474,564,569,787,788,789,790,791,792,794,795,796,803,714,715,179,180,182,716,913,914,915,917,923,918,920,921,924,925,929,941,943,945,948,717,718,189,190,163,138,191,166,149,150,151,153,719,806,807,808,809,811,821,822,823,825,548,549,559,839,840,321,408,411,335,337,339,340,341,342,343,344,455,405,425,580,581,582,583,584,628,468,562,563,317,318],"/Users/crycetruly/Desktop/SendITApi-v2/venv/lib/python3.7/site-packages/pytest_cov/compat.py":[19,20,21,27]}} -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | omit = 3 | app\database\database.py 4 | app\auth\decorator.py 5 | app/util/* 6 | app/model/__init__.py 7 | tests/* 8 | app\__init__.py 9 | app\database\__init__.py 10 | app\views\__init__.py 11 | venv\* 12 | .vscode 13 | .pytest_cache 14 | /home/travis/virtualenv/python3.6.3/* 15 | app/__init__.py 16 | app/auth/decorator.py 17 | app/database/__init__.py 18 | app/database/database.py 19 | app/views/__init__.py 20 | venv 21 | venv/* 22 | app/model/* -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: IgfWXt2821auqSIb0Alee6mZpsY88iCFL 2 | -------------------------------------------------------------------------------- /.env_sample: -------------------------------------------------------------------------------- 1 | export CONN_STR="dbname='dbname' user='user password='password' port=port host=host" 2 | export TRULYS_SECRET="" 3 | export SENDER_EMAIL=" 4 | export SENDER_PASSWORD="" 5 | export TRULYS_KEY="" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | .idea 3 | .vscode 4 | app/__pycache__/ 5 | __pycache__/ 6 | coverage 7 | .pytest_cache 8 | app/__init__.pyc 9 | app/views/__init__.pyc 10 | app/views/parcels.pyc 11 | .env 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | services: 5 | - postgresql 6 | before_script: 7 | - psql -c 'create database sendit;' -U postgres 8 | 9 | install: 10 | - pip install pipenv 11 | - pipenv install 12 | 13 | script: 14 | - pytest --cov --disable-warnings 15 | 16 | sudo: required 17 | 18 | after_success: 19 | - coveralls 20 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | astroid = "==2.3.2" 10 | flask = "*" 11 | psycopg2 = "*" 12 | pyjwt = "*" 13 | pytest = "*" 14 | pytest-cov = "*" 15 | requests = "*" 16 | twilio = "*" 17 | urllib3 = "*" 18 | validate-email = "*" 19 | gunicorn = "*" 20 | geopy = "*" 21 | flask-swagger-ui = "*" 22 | flask-script = "*" 23 | flask-mail = "*" 24 | flask-cors = "*" 25 | flasgger = "*" 26 | coveralls = "*" 27 | coverage = "*" 28 | 29 | [requires] 30 | python_version = "3.7" 31 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "e00aa3fc0ddd9b82efb1dcad4b896785f505e83eae9f1227ce5d4a6e4c25bc66" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "astroid": { 20 | "hashes": [ 21 | "sha256:09a3fba616519311f1af8a461f804b68f0370e100c9264a035aa7846d7852e33", 22 | "sha256:5a79c9b4bd6c4be777424593f957c996e20beb5f74e0bc332f47713c6f675efe" 23 | ], 24 | "index": "pypi", 25 | "version": "==2.3.2" 26 | }, 27 | "attrs": { 28 | "hashes": [ 29 | "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", 30 | "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" 31 | ], 32 | "version": "==19.3.0" 33 | }, 34 | "blinker": { 35 | "hashes": [ 36 | "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6" 37 | ], 38 | "version": "==1.4" 39 | }, 40 | "certifi": { 41 | "hashes": [ 42 | "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", 43 | "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" 44 | ], 45 | "version": "==2019.11.28" 46 | }, 47 | "chardet": { 48 | "hashes": [ 49 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 50 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 51 | ], 52 | "version": "==3.0.4" 53 | }, 54 | "click": { 55 | "hashes": [ 56 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 57 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 58 | ], 59 | "version": "==7.0" 60 | }, 61 | "coverage": { 62 | "hashes": [ 63 | "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6", 64 | "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650", 65 | "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5", 66 | "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d", 67 | "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351", 68 | "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755", 69 | "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef", 70 | "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca", 71 | "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca", 72 | "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9", 73 | "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc", 74 | "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5", 75 | "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f", 76 | "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe", 77 | "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888", 78 | "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5", 79 | "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce", 80 | "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5", 81 | "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e", 82 | "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e", 83 | "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9", 84 | "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437", 85 | "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1", 86 | "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c", 87 | "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24", 88 | "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47", 89 | "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2", 90 | "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28", 91 | "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c", 92 | "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7", 93 | "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0", 94 | "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025" 95 | ], 96 | "index": "pypi", 97 | "version": "==4.5.4" 98 | }, 99 | "coveralls": { 100 | "hashes": [ 101 | "sha256:25522a50cdf720d956601ca6ef480786e655ae2f0c94270c77e1a23d742de558", 102 | "sha256:8e3315e8620bb6b3c6f3179a75f498e7179c93b3ddc440352404f941b1f70524" 103 | ], 104 | "index": "pypi", 105 | "version": "==1.9.2" 106 | }, 107 | "docopt": { 108 | "hashes": [ 109 | "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" 110 | ], 111 | "version": "==0.6.2" 112 | }, 113 | "flasgger": { 114 | "hashes": [ 115 | "sha256:45ce85bf826f71e07d734e52a1af000b3fef82bd1a2337ea5bc4585a3383c56d", 116 | "sha256:fe62d086ca341f71072fd21c2d4f993b6958ce47ee8f0a4ae716e7c901afeb21" 117 | ], 118 | "index": "pypi", 119 | "version": "==0.9.3" 120 | }, 121 | "flask": { 122 | "hashes": [ 123 | "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", 124 | "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" 125 | ], 126 | "index": "pypi", 127 | "version": "==1.1.1" 128 | }, 129 | "flask-cors": { 130 | "hashes": [ 131 | "sha256:72170423eb4612f0847318afff8c247b38bd516b7737adfc10d1c2cdbb382d16", 132 | "sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a" 133 | ], 134 | "index": "pypi", 135 | "version": "==3.0.8" 136 | }, 137 | "flask-mail": { 138 | "hashes": [ 139 | "sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41" 140 | ], 141 | "index": "pypi", 142 | "version": "==0.9.1" 143 | }, 144 | "flask-script": { 145 | "hashes": [ 146 | "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65" 147 | ], 148 | "index": "pypi", 149 | "version": "==2.0.6" 150 | }, 151 | "flask-swagger-ui": { 152 | "hashes": [ 153 | "sha256:3282c770764c8053360f33b2fc120e1d169ecca2138537d0e6e1135b1f9d4ff2" 154 | ], 155 | "index": "pypi", 156 | "version": "==3.20.9" 157 | }, 158 | "geographiclib": { 159 | "hashes": [ 160 | "sha256:12bd46ee7ec25b291ea139b17aa991e7ef373e21abd053949b75c0e9ca55c632", 161 | "sha256:51cfa698e7183792bce27d8fb63ac8e83689cd8170a730bf35e1a5c5bf8849b9" 162 | ], 163 | "version": "==1.50" 164 | }, 165 | "geopy": { 166 | "hashes": [ 167 | "sha256:6239cbf4d8e8a10460c10cf2ae1949c9e9d011e9f25c4e49202734455cc5e884", 168 | "sha256:9419bc90ee6231590c4ae7acf1cf126cefbd0736942da7a6a1436946e80830e2" 169 | ], 170 | "index": "pypi", 171 | "version": "==1.20.0" 172 | }, 173 | "gunicorn": { 174 | "hashes": [ 175 | "sha256:0806b5e8a2eb8ba9ac1be65d7b743ec896fc25f5d6cb16c5e051540157b315bb", 176 | "sha256:ef69dea4814df95e64e3f40b47b7ffedc6911c5009233be9d01cfd0d14aa3f50" 177 | ], 178 | "index": "pypi", 179 | "version": "==20.0.0" 180 | }, 181 | "idna": { 182 | "hashes": [ 183 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 184 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 185 | ], 186 | "version": "==2.8" 187 | }, 188 | "importlib-metadata": { 189 | "hashes": [ 190 | "sha256:6e41b925839cad2e9808f037206e12291c1e61531c5cbc705056d3f11bcd13a6", 191 | "sha256:d0931f65e871b6132ca30ec9d074e7edc46a270466f632db684c0f07a241847b" 192 | ], 193 | "markers": "python_version < '3.8'", 194 | "version": "==1.1.2" 195 | }, 196 | "itsdangerous": { 197 | "hashes": [ 198 | "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", 199 | "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" 200 | ], 201 | "version": "==1.1.0" 202 | }, 203 | "jinja2": { 204 | "hashes": [ 205 | "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", 206 | "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" 207 | ], 208 | "version": "==2.10.3" 209 | }, 210 | "jsonschema": { 211 | "hashes": [ 212 | "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163", 213 | "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a" 214 | ], 215 | "version": "==3.2.0" 216 | }, 217 | "lazy-object-proxy": { 218 | "hashes": [ 219 | "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", 220 | "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", 221 | "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", 222 | "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", 223 | "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", 224 | "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", 225 | "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", 226 | "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", 227 | "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", 228 | "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", 229 | "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", 230 | "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", 231 | "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", 232 | "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", 233 | "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", 234 | "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", 235 | "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", 236 | "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", 237 | "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", 238 | "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", 239 | "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" 240 | ], 241 | "version": "==1.4.3" 242 | }, 243 | "markupsafe": { 244 | "hashes": [ 245 | "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", 246 | "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", 247 | "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", 248 | "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", 249 | "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", 250 | "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", 251 | "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", 252 | "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", 253 | "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", 254 | "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", 255 | "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", 256 | "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", 257 | "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", 258 | "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", 259 | "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", 260 | "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", 261 | "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", 262 | "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", 263 | "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", 264 | "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", 265 | "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", 266 | "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", 267 | "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", 268 | "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", 269 | "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", 270 | "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", 271 | "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", 272 | "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" 273 | ], 274 | "version": "==1.1.1" 275 | }, 276 | "mistune": { 277 | "hashes": [ 278 | "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", 279 | "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4" 280 | ], 281 | "version": "==0.8.4" 282 | }, 283 | "more-itertools": { 284 | "hashes": [ 285 | "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", 286 | "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" 287 | ], 288 | "version": "==8.0.0" 289 | }, 290 | "packaging": { 291 | "hashes": [ 292 | "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", 293 | "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108" 294 | ], 295 | "version": "==19.2" 296 | }, 297 | "pluggy": { 298 | "hashes": [ 299 | "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", 300 | "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" 301 | ], 302 | "version": "==0.13.1" 303 | }, 304 | "psycopg2": { 305 | "hashes": [ 306 | "sha256:4212ca404c4445dc5746c0d68db27d2cbfb87b523fe233dc84ecd24062e35677", 307 | "sha256:47fc642bf6f427805daf52d6e52619fe0637648fe27017062d898f3bf891419d", 308 | "sha256:72772181d9bad1fa349792a1e7384dde56742c14af2b9986013eb94a240f005b", 309 | "sha256:8396be6e5ff844282d4d49b81631772f80dabae5658d432202faf101f5283b7c", 310 | "sha256:893c11064b347b24ecdd277a094413e1954f8a4e8cdaf7ffbe7ca3db87c103f0", 311 | "sha256:92a07dfd4d7c325dd177548c4134052d4842222833576c8391aab6f74038fc3f", 312 | "sha256:965c4c93e33e6984d8031f74e51227bd755376a9df6993774fd5b6fb3288b1f4", 313 | "sha256:9ab75e0b2820880ae24b7136c4d230383e07db014456a476d096591172569c38", 314 | "sha256:b0845e3bdd4aa18dc2f9b6fb78fbd3d9d371ad167fd6d1b7ad01c0a6cdad4fc6", 315 | "sha256:dca2d7203f0dfce8ea4b3efd668f8ea65cd2b35112638e488a4c12594015f67b", 316 | "sha256:ed686e5926929887e2c7ae0a700e32c6129abb798b4ad2b846e933de21508151", 317 | "sha256:ef6df7e14698e79c59c7ee7cf94cd62e5b869db369ed4b1b8f7b729ea825712a", 318 | "sha256:f898e5cc0a662a9e12bde6f931263a1bbd350cfb18e1d5336a12927851825bb6" 319 | ], 320 | "index": "pypi", 321 | "version": "==2.8.4" 322 | }, 323 | "py": { 324 | "hashes": [ 325 | "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", 326 | "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" 327 | ], 328 | "version": "==1.8.0" 329 | }, 330 | "pyjwt": { 331 | "hashes": [ 332 | "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e", 333 | "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96" 334 | ], 335 | "index": "pypi", 336 | "version": "==1.7.1" 337 | }, 338 | "pyparsing": { 339 | "hashes": [ 340 | "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", 341 | "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" 342 | ], 343 | "version": "==2.4.5" 344 | }, 345 | "pyrsistent": { 346 | "hashes": [ 347 | "sha256:f3b280d030afb652f79d67c5586157c5c1355c9a58dfc7940566e28d28f3df1b" 348 | ], 349 | "version": "==0.15.6" 350 | }, 351 | "pytest": { 352 | "hashes": [ 353 | "sha256:63344a2e3bce2e4d522fd62b4fdebb647c019f1f9e4ca075debbd13219db4418", 354 | "sha256:f67403f33b2b1d25a6756184077394167fe5e2f9d8bdaab30707d19ccec35427" 355 | ], 356 | "index": "pypi", 357 | "version": "==5.3.1" 358 | }, 359 | "pytest-cov": { 360 | "hashes": [ 361 | "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b", 362 | "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626" 363 | ], 364 | "index": "pypi", 365 | "version": "==2.8.1" 366 | }, 367 | "pytz": { 368 | "hashes": [ 369 | "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", 370 | "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" 371 | ], 372 | "version": "==2019.3" 373 | }, 374 | "pyyaml": { 375 | "hashes": [ 376 | "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc", 377 | "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803", 378 | "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc", 379 | "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15", 380 | "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075", 381 | "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd", 382 | "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31", 383 | "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f", 384 | "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c", 385 | "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04", 386 | "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4" 387 | ], 388 | "version": "==5.2" 389 | }, 390 | "requests": { 391 | "hashes": [ 392 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 393 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 394 | ], 395 | "index": "pypi", 396 | "version": "==2.22.0" 397 | }, 398 | "six": { 399 | "hashes": [ 400 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 401 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 402 | ], 403 | "version": "==1.12.0" 404 | }, 405 | "twilio": { 406 | "hashes": [ 407 | "sha256:da282a9c02bd9dfb190b798528b478833d8d28cb51464e8c45da0f0794384cde" 408 | ], 409 | "index": "pypi", 410 | "version": "==6.34.0" 411 | }, 412 | "typed-ast": { 413 | "hashes": [ 414 | "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", 415 | "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", 416 | "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", 417 | "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", 418 | "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", 419 | "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", 420 | "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", 421 | "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", 422 | "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", 423 | "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", 424 | "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", 425 | "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", 426 | "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", 427 | "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", 428 | "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", 429 | "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", 430 | "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", 431 | "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", 432 | "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", 433 | "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" 434 | ], 435 | "markers": "implementation_name == 'cpython' and python_version < '3.8'", 436 | "version": "==1.4.0" 437 | }, 438 | "urllib3": { 439 | "hashes": [ 440 | "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", 441 | "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" 442 | ], 443 | "index": "pypi", 444 | "version": "==1.25.6" 445 | }, 446 | "validate-email": { 447 | "hashes": [ 448 | "sha256:784719dc5f780be319cdd185dc85dd93afebdb6ebb943811bc4c7c5f9c72aeaf" 449 | ], 450 | "index": "pypi", 451 | "version": "==1.3" 452 | }, 453 | "wcwidth": { 454 | "hashes": [ 455 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 456 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 457 | ], 458 | "version": "==0.1.7" 459 | }, 460 | "werkzeug": { 461 | "hashes": [ 462 | "sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7", 463 | "sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4" 464 | ], 465 | "version": "==0.16.0" 466 | }, 467 | "wrapt": { 468 | "hashes": [ 469 | "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" 470 | ], 471 | "version": "==1.11.2" 472 | }, 473 | "zipp": { 474 | "hashes": [ 475 | "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", 476 | "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335" 477 | ], 478 | "version": "==0.6.0" 479 | } 480 | }, 481 | "develop": {} 482 | } 483 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn run:app -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SendIT-Api 2 | [![Build Status](https://travis-ci.org/CryceTruly/SendITApi-v2.svg?branch=dev)](https://travis-ci.org/CryceTruly/SendITApi-v2) 3 | [![Coverage Status](https://coveralls.io/repos/github/CryceTruly/SendITApi-v2/badge.svg?branch=dev)](https://coveralls.io/github/CryceTruly/SendITApi-v2?branch=dev) 4 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/ac47983c1bc5459e9774c9af64f7974d)](https://www.codacy.com/app/CryceTruly/SendIT-Api?utm_source=github.com&utm_medium=referral&utm_content=CryceTruly/SendIT-Api&utm_campaign=Badge_Grade) 5 | ![Hackage-Deps](https://img.shields.io/hackage-deps/v/lens.svg) 6 | 7 | SendIT is a courier service app that helps users deliver parcels to different destinations. 8 | # Built with 9 | 10 | - Python 3.7.1 11 | - Flask 1.0.2 12 | - PostgreSQL 11.0 13 | 14 | 15 | # Features! 16 | 17 | - Users can create an account and log in. 18 | - Users can create a parcel delivery order. 19 | - Users can change the destination of a parcel delivery order. 20 | - Users can cancel a parcel delivery order. 21 | - Users can see the details of a delivery order. 22 | - Admin can change the status and present location of a parcel delivery order. 23 | 24 | 25 | 26 | 27 | And also: 28 | - The user gets real-time email notification when Admin changes the status of their parcel. 29 | - The user gets real-time email notification when Admin changes the present location their parcel. 30 | - The app stores Location details and computes travel distance between the pickup location and the destination. 31 | 32 | 33 | 34 | 35 | ## Installation 36 | Want to contribute Great! 37 | 38 | Create a virtual environment for the project. 39 | 40 | ``` 41 | virtualenv "name of the virtual environment" 42 | ``` 43 | Then Activate the venv using: 44 | ``` 45 | source "name of the virtual environment/bin/activate 46 | ``` 47 | 48 | Navigate to the application directory: 49 | 50 | ``` 51 | git clone https://github.com/CryceTruly/SendITApi-v2.git 52 | cd SendITApi-v2 53 | git checkout develop 54 | ``` 55 | 56 | Create a virtual environment to install the 57 | application in. You could install virtualenv and virtualenvwrapper. 58 | Within your virtual environment, install the application package dependencies with: 59 | 60 | ``` 61 | pip install -r requirements.txt 62 | ``` 63 | 64 | Run the application with: 65 | 66 | ``` 67 | python run.py 68 | ``` 69 | for tests run in terminal using: 70 | 71 | ``` 72 | py.test --cov 73 | ``` 74 | 75 | #### URL endpoints 76 | 77 | | URL Endpoint | HTTP Methods | Summary | 78 | | -------- | ------------- | --------- | 79 | | `api/v2/parcels` | `POST` | Creates a new Parcel delivery order| 80 | | `api/v2/parcels/` | `GET` | Retrieves a specific parcel 81 | | `api/v2/parcels//cancel` | `PUT` | Cancels a specific parcel 82 | | `api/v2/users` | `GET` | Retrieve all users | 83 | | `api/v2/auth/signup` | `POST` | Creates a new User | 84 | | `api/v2/auth/login` | `POST` | Log in a user | 85 | | `api/v2/parcels//destination`|`PUT`| Change the desination of a specific order 86 | | `api/v2/parcels//status` | `GET` | Retrieves parcel orders for a specific user | 87 | | `api/v2/parcels//presentLocation`|`PUT`| Change the present location of a specific parcel delivery order| 88 | | `api/v2/users//parcels` | `GET` | Retrieves parcel orders for a specific use| 89 | | `api/v2/auth/logout` | `POST` | Logs out a user | 90 | 91 | 92 | 93 | 94 | #### Example New User body 95 | ``` 96 | Example body 97 | { 98 | "fullname":"fullname", 99 | "username":"username", 100 | "phone_number":"0756778877", 101 | "email":"email@email.com", 102 | "password":"password" 103 | 104 | } 105 | ``` 106 | 107 | #### Example New Parcel Body 108 | ``` 109 | { 110 | "recipient_name": "Aron Mike", 111 | "parcel_description": "Here are my stuff", 112 | "weight":90, 113 | "quantity": 22, 114 | "pickup_address":"Mukono", 115 | "destination_address":"Entebbe", 116 | "recipient_phone_number":"0767878787", 117 | "recipient_email":"rme@gmail.com" 118 | } 119 | ``` 120 | 121 | #### Example Changedestination body 122 | ```' 123 | { 124 | "destination_address": "Mumbai" 125 | } 126 | ``` 127 | 128 | #### Example Changestatus body 129 | ```' 130 | { 131 | "status": "Delivered" 132 | } 133 | ``` 134 | 135 | ## Deployement 136 | [Heroku Deployement](https://trulysendit.herokuapp.com) 137 | 138 | ### Todos 139 | 140 | - Write MORE Tests 141 | - Format addresses 142 | - Build a mobile app 143 | 144 | License 145 | --- 146 | MIT 147 | ## Author 148 | [Cryce Truly](https://github.com/crycetruly) 149 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "addons": [ 3 | "heroku-postgresql" 4 | ], 5 | "buildpacks": [ 6 | { 7 | "url": "heroku/python" 8 | } 9 | ], 10 | "env": { 11 | }, 12 | "formation": { 13 | }, 14 | "name": "SendITApi-v2", 15 | "scripts": { 16 | }, 17 | "stack": "heroku-18" 18 | } 19 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | from app.views.parcels import ap 2 | from app.views.users import auth 3 | from app.views.search import search 4 | from flask import Flask 5 | from flask_cors import CORS 6 | from flasgger import Swagger 7 | 8 | 9 | app=Flask(__name__) 10 | Swagger(app) 11 | app.register_blueprint(ap) 12 | app.register_blueprint(auth) 13 | app.register_blueprint(search) 14 | -------------------------------------------------------------------------------- /app/auth/decorator.py: -------------------------------------------------------------------------------- 1 | import os 2 | from functools import wraps 3 | from flask import request, jsonify, make_response 4 | import jwt 5 | from app.database.database import Database 6 | from app.model.models import User 7 | 8 | 9 | def get_token(): 10 | token = None 11 | if 'Authorization' in request.headers: 12 | token = request.headers['Authorization'] 13 | token = token.split(" ")[1] 14 | if not token: 15 | return make_response(jsonify({ 16 | 'status': 'failed', 17 | 'message': 'Token is missing!' 18 | }), 401) 19 | 20 | return token 21 | 22 | 23 | def token_required(f): 24 | """ 25 | Decorator function to ensure that end points are accessed by 26 | only authorized users provided they have a valid token 27 | """ 28 | 29 | @wraps(f) 30 | def decorated(*args, **kwargs): 31 | token = get_token() 32 | try: 33 | database = Database() 34 | data = jwt.decode(get_token(), 35 | os.environ.get('TRULYS_SECRET', 'TRULYS_SECRET')) 36 | query = database.get_user_by_value( 37 | 'users', 'user_id', data['user_id'] 38 | ) 39 | if not query: 40 | return {"message": "User does not exist"}, 400 41 | current_user = User( 42 | query[0], query[1], query[2], query[3], query[4], query[5]) 43 | except jwt.ExpiredSignatureError as e: 44 | return response_message('error', 'token expired,please login again', 401) 45 | except jwt.InvalidSignatureError as serr: 46 | return response_message('error', 'Signature is invalid,please log in again', 401) 47 | except jwt.DecodeError: 48 | return response_message('error', 'please login', 401) 49 | 50 | return f(current_user, *args, **kwargs) 51 | 52 | return decorated 53 | 54 | 55 | def response(id, username, message, token, status_code): 56 | """ 57 | method to make http response for authorization token 58 | """ 59 | return jsonify({ 60 | "user_id": id, 61 | "username": username, 62 | "message": message, 63 | "auth_token": token 64 | }), status_code 65 | 66 | 67 | def response_message(status, message, status_code): 68 | """ 69 | method to handle response messages 70 | """ 71 | return jsonify({ 72 | "status": status, 73 | "message": message 74 | }), status_code 75 | -------------------------------------------------------------------------------- /app/database/database.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import json 3 | from werkzeug.security import generate_password_hash 4 | import os 5 | 6 | 7 | class Database(object): 8 | """class for the database""" 9 | 10 | def __init__(self): 11 | """initialize connection """ 12 | """ 13 | creates a db 14 | """ 15 | try: 16 | # use our connection values to establish a connection 17 | self.connection = psycopg2.connect( 18 | os.environ.get("DATABASE_URL", "")) 19 | self.connection.autocommit = True 20 | self.cursor = self.connection.cursor() 21 | self.create_tables() 22 | except Exception as i: 23 | print(i) 24 | 25 | def create_tables(self): 26 | """ create tables """ 27 | img = 'https://bit.ly/2GNPIyC' 28 | create_table = """CREATE TABLE IF NOT EXISTS users 29 | (user_id SERIAL PRIMARY KEY, full_name VARCHAR(255),username VARCHAR(30) UNIQUE,imageUrl VARCHAR(500) DEFAULT '{}', 30 | email VARCHAR(255),password VARCHAR(150), phone_number VARCHAR(100),is_verified BOOLEAN DEFAULT False, 31 | is_admin BOOLEAN DEFAULT FALSE ,joined TIMESTAMPTZ DEFAULT Now())""".format(img) 32 | self.cursor.execute(create_table) 33 | self.connection.commit() 34 | password = generate_password_hash('adminuser') 35 | try: 36 | sql = """INSERT INTO users(user_id,full_name,username,password,phone_number,email,is_admin,is_verified) 37 | VALUES (100,'Admin User','senditadmin','{}','0700000000','admin@sendit.com',True,True)""".format( 38 | password) 39 | self.cursor.execute(sql) 40 | self.connection.commit() 41 | except Exception as identifier: 42 | pass 43 | 44 | create_table = """ CREATE TABLE IF NOT EXISTS parcels( 45 | parcel_id SERIAL PRIMARY KEY, 46 | user_id INTEGER NOT NULL REFERENCES users(user_id), 47 | destination_address VARCHAR(500) NOT NULL, 48 | pickup_address VARCHAR (255), 49 | parcel_description VARCHAR (500), 50 | sender_email VARCHAR (255), 51 | status VARCHAR (25) DEFAULT 'order_placed', 52 | recipient_name VARCHAR (255), 53 | weight INTEGER , 54 | qty INTEGER, 55 | current_location VARCHAR (255), 56 | recipient_email VARCHAR (255), 57 | recipient_phone VARCHAR (255), 58 | pickplatlng json, 59 | destlatlng json , 60 | distance DOUBLE PRECISION, 61 | price DOUBLE PRECISION, 62 | created TIMESTAMPTZ DEFAULT Now() ,last_modified TIMESTAMPTZ DEFAULT Now())""" 63 | self.cursor.execute(create_table) 64 | self.connection.commit() 65 | 66 | create_table = """CREATE TABLE IF NOT EXISTS tokens 67 | (id SERIAL PRIMARY KEY,token TEXT, is_valid BOOLEAN DEFAULT TRUE, 68 | last_used TIMESTAMPTZ DEFAULT Now())""" 69 | self.cursor.execute(create_table) 70 | self.connection.commit() 71 | 72 | def insert_into_user(self, fullname, username, email, phone_number, password): 73 | """ 74 | Query to add a new user 75 | :admin,user 76 | """ 77 | user = """INSERT INTO users(full_name, username,email, phone_number,password) 78 | VALUES ('{}','{}','{}','{}','{}'); 79 | """.format(fullname, username, email, phone_number, password) 80 | self.cursor.execute(user) 81 | self.connection.commit() 82 | 83 | def insert_into_parcels(self, destination_address, pickup_address, 84 | parcel_description, user_id, sender_email, 85 | recipient_phone, recipient_email, recipient_name, weight, qty, latlng, destlatlng, distance, 86 | price): 87 | """ 88 | Query to add parcel order to the database : by user 89 | """ 90 | order_query = """INSERT INTO parcels(destination_address, pickup_address, parcel_description, 91 | user_id, sender_email, recipient_phone, 92 | recipient_email, recipient_name, weight,qty,current_location,pickplatlng,destlatlng,distance,price) 93 | VALUES('{}','{}','{}','{}','{}','{}','{}','{}',{},{} ,'{}','{}','{}',{},{}); """.format( 94 | destination_address, pickup_address, parcel_description, user_id, sender_email, recipient_phone, 95 | recipient_email, recipient_name, weight, qty, pickup_address, json.dumps( 96 | latlng), json.dumps(destlatlng), 97 | distance, price) 98 | self.cursor.execute(order_query) 99 | self.connection.commit() 100 | 101 | def get_all_parcels(self): 102 | """ 103 | Query gets all parcel-orders that are available 104 | :admin 105 | """ 106 | 107 | self.cursor.execute("SELECT * FROM parcels ORDER BY parcel_id DESC") 108 | all_parcels = self.cursor.fetchall() 109 | parcel_list = [] 110 | for parcel in all_parcels: 111 | parcel_list.append(parcel) 112 | return parcel_list 113 | 114 | def get_users(self): 115 | self.cursor.execute("SELECT * FROM users ORDER BY joined DESC") 116 | users = self.cursor.fetchall() 117 | user_list = [] 118 | for user in users: 119 | user_list.append(user) 120 | return user_list 121 | 122 | def get_parcel_by_value(self, table_name, table_column, value): 123 | """ 124 | Function gets items from the 125 | same table with similar ids :admin 126 | """ 127 | try: 128 | 129 | query = "SELECT * FROM {} WHERE {} = '{}';".format( 130 | table_name, table_column, value) 131 | self.cursor.execute(query) 132 | results = self.cursor.fetchone() 133 | return results 134 | except Exception as e: 135 | return None 136 | 137 | def get_user_by_value(self, table_name, column, value): 138 | """ 139 | Function gets items from the 140 | same table with similar email :email 141 | """ 142 | query = "SELECT * FROM {} WHERE {} = '{}';".format( 143 | table_name, column, value) 144 | self.cursor.execute(query) 145 | results = self.cursor.fetchone() 146 | return results 147 | 148 | def get_user_parcels(self, user_id): 149 | """ 150 | Select from parcels where parcel.user_id = user.user_id 151 | :Admin 152 | """ 153 | query = "SELECT * FROM parcels WHERE user_id = '{}' ORDER BY parcel_id DESC;".format( 154 | user_id) 155 | self.cursor.execute(query) 156 | results = self.cursor.fetchall() 157 | if results: 158 | return results 159 | return "No parcels yet" 160 | 161 | def update_parcel_status(self, stat, id): 162 | """ 163 | update table parcels set status ='' where parcel_id = id 164 | :Admin 165 | """ 166 | 167 | status = ['pickup_started', 'in_transit', 'cancelled', 'delivered'] 168 | if stat in status: 169 | query = """UPDATE parcels SET status = '{}' 170 | WHERE parcel_id ='{}' """.format(stat, id) 171 | self.cursor.execute(query) 172 | self.connection.commit() 173 | return "status updated successfully" 174 | return "Invalid status name" 175 | 176 | def is_order_delivered(self, id): 177 | sql = "SELECT status FROM parcels WHERE parcel_id ='{}'".format(id) 178 | self.cursor.execute(sql) 179 | self.connection.commit() 180 | order = self.cursor.fetchone() 181 | status = order[0] 182 | if status == 'delivered': 183 | return True 184 | return False 185 | 186 | def update_role(self, id): 187 | query = """UPDATE users SET is_admin = {} 188 | WHERE user_id ='{}' """.format(True, id) 189 | self.cursor.execute(query) 190 | self.connection.commit() 191 | 192 | def revoke_admin_previledges(self, id): 193 | query = """UPDATE users SET is_admin = {} 194 | WHERE user_id ='{}' """.format(False, id) 195 | self.cursor.execute(query) 196 | self.connection.commit() 197 | 198 | def change_destination(self, new_value, parcel_id, destlatlng, new_distance, new_price): 199 | query = "UPDATE parcels SET destination_address = '{}' WHERE parcel_id ={};".format( 200 | new_value, parcel_id) 201 | self.cursor.execute(query) 202 | self.connection.commit() 203 | query2 = "UPDATE parcels SET destlatlng = '{}' WHERE parcel_id ={};".format( 204 | json.dumps(destlatlng), parcel_id) 205 | self.cursor.execute(query2) 206 | self.connection.commit() 207 | 208 | query3 = "UPDATE parcels SET price = '{}' WHERE parcel_id ={};".format( 209 | new_price, parcel_id) 210 | self.cursor.execute(query3) 211 | self.connection.commit() 212 | 213 | query4 = "UPDATE parcels SET distance = '{}' WHERE parcel_id ={};".format( 214 | new_distance, parcel_id) 215 | self.cursor.execute(query4) 216 | self.connection.commit() 217 | 218 | return new_value 219 | 220 | def delete_table_column(self, table_name, table_colum, id): 221 | delete_query = "DELETE from {} WHERE {} = '{}';".format( 222 | table_name, table_colum, id) 223 | self.cursor.execute(delete_query) 224 | self.connection.commit() 225 | 226 | def change_present_location(self, location, parcel_id): 227 | query = "UPDATE parcels SET current_location = '{}' WHERE parcel_id ={};".format( 228 | location, parcel_id) 229 | self.cursor.execute(query) 230 | self.connection.commit() 231 | 232 | def cancel_parcel(self, id): 233 | sql = "UPDATE parcels SET status='{}' WHERE parcel_id = '{}'".format( 234 | 'cancelled', id) 235 | self.cursor.execute(sql) 236 | self.connection.commit() 237 | return self.get_parcel_by_value('parcels', 'parcel_id', id) 238 | 239 | def is_parcel_owner(self, parcel_id, user_id): 240 | sql = "SELECT user_id FROM parcels WHERE user_id ='{}'".format(user_id) 241 | self.cursor.execute(sql) 242 | self.connection.commit() 243 | order = self.cursor.fetchone() 244 | if order: 245 | return True 246 | return False 247 | 248 | def drop_tables(self): 249 | drop_query = "DROP TABLE IF EXISTS {0} CASCADE" 250 | tables = ["users", "parcels"] 251 | for table in tables: 252 | self.cursor.execute(drop_query.format(table)) 253 | 254 | def is_admin(self, user_id): 255 | sql = "SELECT is_admin from users WHERE user_id ={}".format(user_id) 256 | self.cursor.execute(sql) 257 | self.connection.commit() 258 | results = self.cursor.fetchone() 259 | return results[0] 260 | 261 | def get_user_email(self, user_id): 262 | sql = "SELECT email from users WHERE user_id ={}".format(user_id) 263 | self.cursor.execute(sql) 264 | self.connection.commit() 265 | results = self.cursor.fetchone() 266 | return results[0] 267 | 268 | def change_status(self, param, id): 269 | query = "UPDATE parcels SET status = '{}' WHERE parcel_id ={};".format( 270 | param, id) 271 | self.cursor.execute(query) 272 | self.connection.commit() 273 | 274 | def new_parcel_has_fishy_behaviour(self, user_id, reciever_email, desc): 275 | sql = "SELECT * FROM parcels WHERE user_id = {} AND recipient_email= '{}' and parcel_description = '{}'".format( 276 | user_id, reciever_email, desc) 277 | self.cursor.execute(sql) 278 | self.connection.commit() 279 | results = self.cursor.fetchall() 280 | return results 281 | 282 | def get_current_location(self, id): 283 | query = "SELECT current_location FROM parcels WHERE parcel_id={}".format( 284 | id) 285 | self.cursor.execute(query) 286 | self.connection.commit() 287 | results = self.cursor.fetchone() 288 | return results[0] 289 | 290 | def get_parce_owner_id(self, id): 291 | query = "SELECT user_id FROM parcels WHERE parcel_id={}".format(id) 292 | self.cursor.execute(query) 293 | self.connection.commit() 294 | results = self.cursor.fetchone() 295 | return results[0] 296 | 297 | def get_destination_address(self, id): 298 | query = "SELECT destination_address FROM parcels WHERE parcel_id={}".format( 299 | id) 300 | self.cursor.execute(query) 301 | self.connection.commit() 302 | results = self.cursor.fetchone() 303 | return results[0] 304 | 305 | def get_pick_up_latlng(self, id): 306 | query = "SELECT pickplatlng FROM parcels WHERE parcel_id={}".format(id) 307 | self.cursor.execute(query) 308 | self.connection.commit() 309 | results = self.cursor.fetchone() 310 | return results[0] 311 | 312 | def get_last_insert_id(self): 313 | query = "SELECT parcel_id FROM parcels ORDER BY parcel_id DESC" 314 | self.cursor.execute(query) 315 | self.connection.commit() 316 | results = self.cursor.fetchone() 317 | return results[0] 318 | 319 | def get_destination_latlng(self, id): 320 | query = "SELECT destlatlng FROM parcels WHERE parcel_id={}".format(id) 321 | self.cursor.execute(query) 322 | self.connection.commit() 323 | results = self.cursor.fetchone() 324 | return results[0] 325 | 326 | def get_parcel_weight(self, id): 327 | query = "SELECT weight FROM parcels WHERE parcel_id={}".format(id) 328 | self.cursor.execute(query) 329 | self.connection.commit() 330 | results = self.cursor.fetchone() 331 | return results[0] 332 | 333 | def save_token(self, token): 334 | query = "INSERT INTO tokens(token) VALUES = '{}'".format(str(token)) 335 | self.cursor.execute(query) 336 | self.connection.commit() 337 | 338 | def invalidate_a_token(self, token): 339 | query = "UPDATE tokens SET is_valid ={} WHERE token = '{}'".format( 340 | False, token) 341 | self.cursor.execute(query) 342 | self.connection.commit() 343 | 344 | def is_token_invalid(self, token): 345 | query = "SELECT is_valid FROM tokens WHERE token={}".format(token) 346 | self.cursor.execute(query) 347 | self.connection.commit() 348 | results = self.cursor.fetchone() 349 | return results[0] 350 | 351 | def delete_parcel(self, id): 352 | sql = "DELETE FROM parcels WHERE parcel_id = '{}'".format(id) 353 | self.cursor.execute(sql) 354 | self.connection.commit() 355 | return id 356 | 357 | def search_app(self, query): 358 | # q = """SELECT users.user_id,users.full_name,users.email FROM users WHERE email LIKE '%{}%' OR username LIKE '%{}%' 359 | # OR full_name LIKE '%{}%' OR phone_number LIKE '%{}%' 360 | # UNION 361 | # SELECT parcels.parcel_id,parcels.sender_email,parcels.status FROM parcels WHERE destination_address LIKE '%{}%' OR sender_email LIKE '%{}%' OR pickup_address LIKE '%{}%' OR recipient_email LIKE '%{}%' OR parcel_description LIKE '%{}%' """ \ 362 | # .format(query, query, query, query, query, query, query, query, query) 363 | q = """SELECT users.user_id,users.full_name,users.email FROM users WHERE email LIKE '%{}%' OR username LIKE '%{}%' 364 | OR full_name LIKE '%{}%' OR phone_number LIKE '%{}%'""".format(query, query, query, query) 365 | 366 | self.cursor.execute(q) 367 | self.connection.commit() 368 | results = self.cursor.fetchall() 369 | if results: 370 | return results 371 | return "No matches found" 372 | 373 | def verify_user(self, user): 374 | query = "UPDATE users SET is_verified = {} WHERE email = '{}';".format( 375 | True, str(user['email'])) 376 | self.cursor.execute(query) 377 | self.connection.commit() 378 | 379 | def change_user_password(self, email, password): 380 | password_hash = generate_password_hash(password) 381 | query = "UPDATE users SET password = '{}' WHERE email = '{}';".format( 382 | password_hash, email) 383 | self.cursor.execute(query) 384 | self.connection.commit() 385 | -------------------------------------------------------------------------------- /app/doc/cancel_parcell.yml: -------------------------------------------------------------------------------- 1 | cancel 2 | --- 3 | tags: 4 | - Cancels a Parcel order 5 | parameters: 6 | - name: body 7 | description: Updates the application by a cancelling out a placed order 8 | in: body 9 | required: true 10 | schema: 11 | type: object 12 | required: 13 | - "status" 14 | properties: 15 | status: 16 | type: "string" 17 | example: "delivered" 18 | responses: 19 | 200: 20 | description: a user successfully cancels a parcel order 21 | 22 | 401: 23 | description: a user has no enough previledges to cancel an order 24 | 25 | 26 | security: 27 | - JWT: 28 | description: Pass in jwt token. i.e Bearer 29 | type: apiKey 30 | scheme: bearer 31 | name: token 32 | in: header 33 | bearerFormat: JWT 34 | -------------------------------------------------------------------------------- /app/doc/change_present_locationn.yml: -------------------------------------------------------------------------------- 1 | location 2 | --- 3 | tags: 4 | - Change a parcels current location 5 | parameters: 6 | - name: body 7 | description: Updates the application with a new location of a parcel when it changes/in transit 8 | in: body 9 | required: true 10 | schema: 11 | type: object 12 | required: 13 | - "current_location" 14 | properties: 15 | status: 16 | type: "string" 17 | example: "Kampala" 18 | responses: 19 | 200: 20 | description: a user successfully updates a parcel location 21 | 22 | 401: 23 | description: a user has no enough previledges to change a location 24 | 25 | 26 | security: 27 | - JWT: 28 | description: Pass in jwt token. i.e Bearer 29 | type: apiKey 30 | scheme: bearer 31 | name: token 32 | in: header 33 | bearerFormat: JWT 34 | -------------------------------------------------------------------------------- /app/doc/changedestination.yml: -------------------------------------------------------------------------------- 1 | Destination 2 | --- 3 | tags: 4 | - Change a destination of a parcel delivery order 5 | parameters: 6 | - name: body 7 | description: A user updates parcel destination 8 | in: body 9 | required: true 10 | schema: 11 | type: object 12 | required: 13 | - "new_destination" 14 | properties: 15 | new_destination: 16 | type: "string" 17 | example: "Kampa" 18 | 19 | responses: 20 | 200: 21 | description: successfully updates a destination 22 | 23 | 401: 24 | description: A user may not have enough previledges 25 | 26 | security: 27 | - JWT: 28 | description: Pass in jwt token. i.e Bearer 29 | type: apiKey 30 | scheme: bearer 31 | name: token 32 | in: header 33 | bearerFormat: JWT -------------------------------------------------------------------------------- /app/doc/get_a_parcel.yml: -------------------------------------------------------------------------------- 1 | Single Parcel 2 | --- 3 | tags: 4 | - Get Parcel Details 5 | responses: 6 | 200: 7 | description: Users can view details of a parcel by its Id 8 | 9 | 404: 10 | description: Parcel delivery request details not found 11 | 12 | 401: 13 | description: The route is protected to only parcel owners and admin users 14 | 15 | 16 | security: 17 | - JWT: 18 | description: Pass in jwt token. i.e Bearer 19 | type: apiKey 20 | scheme: bearer 21 | name: token 22 | in: header 23 | bearerFormat: JWT 24 | -------------------------------------------------------------------------------- /app/doc/get_all_parcels.yml: -------------------------------------------------------------------------------- 1 | All Parcels 2 | --- 3 | tags: 4 | - All Parcels 5 | responses: 6 | 200: 7 | description: Retrieves all parcels posted 8 | 9 | 404: 10 | description: The applications has no parcels to show 11 | security: 12 | - JWT: 13 | description: Pass in jwt token. i.e Bearer 14 | type: apiKey 15 | scheme: bearer 16 | name: token 17 | in: header 18 | bearerFormat: JWT 19 | 20 | -------------------------------------------------------------------------------- /app/doc/login.yml: -------------------------------------------------------------------------------- 1 | User log in 2 | --- 3 | tags: 4 | - User login 5 | parameters: 6 | - name: body 7 | description: The body should contain the user login credentials 8 | in: body 9 | required: true 10 | schema: 11 | type: object 12 | required: 13 | - "email" 14 | - "password" 15 | properties: 16 | email: 17 | type: "email" 18 | example: "crycetruly@gmail.com" 19 | password: 20 | type: "string" 21 | format: password 22 | example: "********" 23 | responses: 24 | 200: 25 | description: When a user succesfully logs in 26 | 27 | 400: 28 | description: Fails to login due to bad request data 29 | 30 | 401: 31 | description: A user supplies incorrect credentials 32 | -------------------------------------------------------------------------------- /app/doc/new_parcel.yml: -------------------------------------------------------------------------------- 1 | New Parcels 2 | --- 3 | tags: 4 | - Create a new Parcel 5 | parameters: 6 | - name: body 7 | description: User submits an order for a parcel delivery 8 | in: body 9 | required: true 10 | schema: 11 | type: object 12 | required: 13 | - recipient_name 14 | - parcel_description 15 | - weight 16 | - quantity 17 | - pickup_address 18 | - destination_address 19 | - recipient_phone_number 20 | - recipient_email 21 | properties: 22 | recipient_name: 23 | type: string 24 | example: Aron Mike 25 | parcel_description: 26 | type: string 27 | example: Here are my stuff,contains laptops,please deliver 28 | weight: 29 | type: integer 30 | example: 10 31 | quantity: 32 | type: integer 33 | example: 19 34 | pickup_address: 35 | type: string 36 | example: Mukono 37 | destination_address: 38 | type: string 39 | example: Kampala 40 | recipient_phone_number: 41 | type: string 42 | example: 0756747474 43 | recipient_email: 44 | type: string 45 | example: fredgreg@gmail.com 46 | 47 | 48 | responses: 49 | 201: 50 | description: a user successfully creates a parcel order 51 | 52 | 401: 53 | description: A user has no previledges to post parcels 54 | -------------------------------------------------------------------------------- /app/doc/signup.yml: -------------------------------------------------------------------------------- 1 | User Account SignUp 2 | --- 3 | tags: 4 | - User SignUp 5 | parameters: 6 | - name: body 7 | description: The body should contain the user registration details 8 | in: body 9 | required: true 10 | schema: 11 | type: object 12 | required: 13 | - "fullname" 14 | - "username" 15 | - "email" 16 | - "phone_number" 17 | - "password" 18 | 19 | 20 | properties: 21 | fullname: 22 | type: "string" 23 | example: "Greg Fred" 24 | 25 | username: 26 | type: "string" 27 | example: "fred" 28 | email: 29 | type: "string" 30 | format: "email" 31 | example: "fred@gmail.com" 32 | phone_number: 33 | type: "string" 34 | example: "0756778888" 35 | password: 36 | type: "string" 37 | format: password 38 | example: "Xvq6thCutest" 39 | 40 | 41 | responses: 42 | 201: 43 | description: User account gets successfully created. 44 | schema: 45 | type: object 46 | properties: 47 | fullname: 48 | type: "string" 49 | example: "Greg Fred" 50 | 51 | username: 52 | type: "string" 53 | example: "fred" 54 | email: 55 | type: "string" 56 | format: "email" 57 | example: "fred@gmail.com" 58 | phone_number: 59 | type: "string" 60 | example: "0756778888" 61 | password: 62 | type: "string" 63 | format: password 64 | example: "Xvq6thCutest" 65 | 400: 66 | description: Account creation fails when a user doesnot send valid type of data 67 | schema: 68 | type: object 69 | properties: 70 | fullname: 71 | type: "string" 72 | example: "Greg" 73 | 74 | username: 75 | type: "string" 76 | example: "" 77 | email: 78 | type: "string" 79 | format: "email" 80 | example: "" 81 | phone_number: 82 | type: "string" 83 | example: "" 84 | password: 85 | type: "string" 86 | format: password 87 | example: "" 88 | 89 | 409: 90 | description: Fails to create an account because someone with the same email exists 91 | -------------------------------------------------------------------------------- /app/doc/status.yml: -------------------------------------------------------------------------------- 1 | Status 2 | --- 3 | tags: 4 | - Change a a status of a parcel delivery order when it changes 5 | parameters: 6 | - name: body 7 | description: An admin user updates parcel status 8 | in: body 9 | required: true 10 | schema: 11 | type: object 12 | required: 13 | - "status" 14 | properties: 15 | new_destination: 16 | type: "string" 17 | example: "delivered" 18 | 19 | responses: 20 | 200: 21 | description: successfully updates a status 22 | 23 | 401: 24 | description: A user may not have enough previledges 25 | 26 | security: 27 | - JWT: 28 | description: Pass in jwt token. i.e Bearer 29 | type: apiKey 30 | scheme: bearer 31 | name: token 32 | in: header 33 | bearerFormat: JWT -------------------------------------------------------------------------------- /app/doc/user_parcels.yml: -------------------------------------------------------------------------------- 1 | Get a user`s Parcels 2 | --- 3 | tags: 4 | - Get all parcels that belong to a certain user 5 | responses: 6 | 200: 7 | description: Retrieves a list of parcels 8 | 9 | 401: 10 | description: Not authorized a user may not have permission to access this route 11 | 12 | security: 13 | - JWT: 14 | description: Pass in jwt token. i.e Bearer 15 | type: apiKey 16 | scheme: bearer 17 | name: token 18 | in: header 19 | bearerFormat: JWT 20 | -------------------------------------------------------------------------------- /app/model/models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | 4 | class User: 5 | """ user class """ 6 | 7 | def __init__(self, user_id, username, email, phone_number, password, is_admin): 8 | self.user_id = user_id 9 | self.username = username 10 | self.phone_number = phone_number, 11 | self.email = email 12 | self.password = password 13 | self.is_admin = is_admin 14 | self.joined = datetime.datetime.now() 15 | 16 | def __str__(self): 17 | return "user: {} username:{} phonenumber:{} with email {}:isadmin:{}".format(self.user_id, self.username, 18 | self.phone_number, self.email, self.is_admin) 19 | 20 | 21 | class Parcel: 22 | """ 23 | class define order entities and return order dictionary 24 | """ 25 | 26 | def __init__(self, parcel_id, destination_address, pickup_address, 27 | comment_description, 28 | user_id, sender_email, recipient_phone, 29 | recipient_email, recipient_name, weight, quantity): 30 | self.parcel_id = parcel_id 31 | self.destination_address = destination_address 32 | self.pickup_address = pickup_address 33 | self.comment_description = comment_description 34 | self.user_id = user_id 35 | self.sender_email = sender_email 36 | self.status = self.parcel_status()[0] 37 | self.recipient_name = recipient_name 38 | self.quantity = quantity 39 | self.weight = weight 40 | self.current_location = pickup_address 41 | self.recipient_email = recipient_email 42 | self.recipient_phone = recipient_phone 43 | 44 | def __str__(self): 45 | return "id:{} senderemail:{} recieveremail:{} ".format(self.parcel_id, self.sender_email, self.recipient_email) 46 | 47 | def parcel_status(self): 48 | """ 49 | Generate status list for parcel orders 50 | """ 51 | status = ['pickup_started', 'rejected', 52 | 'in_transit', 'cancelled', 'delivered'] 53 | return status 54 | -------------------------------------------------------------------------------- /app/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CryceTruly/SendIT-API-v2/de93b61fae52368a4d8194d30d2ba1c8ef68a7f9/app/util/__init__.py -------------------------------------------------------------------------------- /app/util/helper.py: -------------------------------------------------------------------------------- 1 | import geopy.distance 2 | import jwt 3 | import requests 4 | import os 5 | 6 | from app.auth.decorator import response_message, get_token 7 | from app.database.database import Database 8 | 9 | 10 | class Helper: 11 | def __init__(self): 12 | self.base_price = 1.5 13 | self.trulyKey = os.environ.get('TRULYS_KEY', '') 14 | self.categories = {"0-5": 1, 15 | "6-30": 3, 16 | "31-60": 6, 17 | "70-100": 9, 18 | "101-300": 12, 19 | "301-499": 15, 20 | "500-1000": 18, 21 | "1001-5000": 25 22 | } 23 | db = Database() 24 | self.parcels = db.get_all_parcels() 25 | 26 | def get_charge(self, weight, distance, quantity): 27 | """ 28 | calculates the charge 29 | :param weight: 30 | :param distance: 31 | :param distance 32 | :return: 33 | """ 34 | return self.base_price * (self.get_my_charge(weight) + distance) 35 | 36 | def get_values(self, start, finish): 37 | return self.categories[str(start) + "-" + str(finish)] 38 | 39 | def get_my_charge(self, weight): 40 | if weight > 5000: 41 | return self.get_values(1001, 5000) 42 | for category in self.categories.keys(): 43 | start, finish = category.split('-')[0], category.split('-')[1] 44 | if weight in range(int(start), int(finish)): 45 | return self.get_values(start, finish) 46 | 47 | def get_distance(self, point1, point2): 48 | """ 49 | calculates distance between two latlong values 50 | :param point1: 51 | :param point2: 52 | :return: 53 | """ 54 | ls = [] 55 | try: 56 | for i in point1.values(): 57 | ls.append(i) 58 | cords_1 = (tuple(ls)) 59 | ls2 = [] 60 | for x in point2.values(): 61 | ls2.append(x) 62 | cords_2 = (tuple(ls2)) 63 | 64 | return geopy.distance.vincenty(cords_1, cords_2).km 65 | except Exception as identifier: 66 | return 55 67 | 68 | def get_formatted_address(self, address): 69 | ''' 70 | 71 | :param add: 72 | :return: 73 | ''' 74 | 75 | try: 76 | r = requests.get( 77 | "https://maps.googleapis.com/maps/api/geocode/json?address=" + 78 | address + "&key="+os.environ.get('TRULYS_KEY', '')) 79 | data = r.json() 80 | results = data['results'] 81 | address = results[0] 82 | return address['formatted_address'] 83 | except Exception as identifier: 84 | return None 85 | 86 | def get_pickup_latlong(self, add): 87 | """ 88 | calculates the latlong given an address 89 | :param add: 90 | :return: 91 | """ 92 | try: 93 | r = requests.get( 94 | "https://maps.googleapis.com/maps/api/geocode/json?address=" + 95 | add + "&key="+os.environ.get('TRULYS_KEY', '')) 96 | data = r.json() 97 | results = data['results'] 98 | address = results[0] 99 | # Geometry 100 | geometry = address['geometry'] 101 | return geometry['location'] 102 | except Exception as identifier: 103 | return {'lat': 37.06250000000001, 'lng': -95.677068} 104 | 105 | def get_dest_latlong(self, add): 106 | """ 107 | calculates the latlong given an address 108 | :param add: 109 | :return: 110 | """ 111 | try: 112 | r = requests.get( 113 | "https://maps.googleapis.com/maps/api/geocode/json?address=" + add + "&key="+os.environ.get('TRULYS_KEY', '')) 114 | data = r.json() 115 | results = data['results'] 116 | address = results[0] 117 | # Geometry 118 | geometry = address['geometry'] 119 | return geometry['location'] 120 | except Exception as identifier: 121 | return {'lat': 0.3475964, 'lng': 32.5825197} 122 | 123 | def get_current_user_id(self): 124 | 125 | try: 126 | token = get_token() 127 | data = jwt.decode(token, os.environ.get('TRULYS_KEY', '')) 128 | return data['user_id'] 129 | 130 | except jwt.ExpiredSignatureError: 131 | return 'Signature expired. Please log in again.', 401 132 | except jwt.InvalidTokenError: 133 | return 'Invalid token. Please log in again.', 401 134 | -------------------------------------------------------------------------------- /app/views/parcels.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, Blueprint 2 | import psycopg2 3 | from app.database.database import Database 4 | from flask import current_app as app 5 | from flask_mail import Message, Mail 6 | import os 7 | from app.auth.decorator import response_message, token_required 8 | from validate_email import validate_email 9 | from flasgger import swag_from 10 | from app.util.helper import Helper 11 | from flask_cors import CORS 12 | ap = Blueprint('parcels', __name__) 13 | CORS(ap) 14 | 15 | db = Database() 16 | 17 | 18 | @ap.route("/") 19 | @token_required 20 | def welcome(current_user): 21 | return response_message("ok", "Welcome to the sendit api v2", 200) 22 | 23 | 24 | # GET parcels 25 | 26 | @ap.route('/api/v2/parcels', methods=['GET']) 27 | @token_required 28 | @swag_from('../doc/get_all_parcels.yml') 29 | def get_parcels(current_user): 30 | if not db.is_admin(current_user.user_id): 31 | return response_message('unauthorized operation', 'Only admin users can view all orders', 401) 32 | all = db.get_all_parcels() 33 | if all: 34 | parcel_list = [] 35 | for parcel in all: 36 | results = db.get_user_by_value('users', 'user_id', parcel[1]) 37 | user_dict = { 38 | "user_id": results[0], 39 | "fullname": results[1], 40 | "username": results[2], 41 | "telephone_number": results[5], 42 | "is_admin": results[7], 43 | "joined": results[8], 44 | "email": results[3] 45 | 46 | } 47 | 48 | parcel_dict = { 49 | "parcel_id": parcel[0], 50 | "sender": user_dict, 51 | "recipient": { 52 | "email": parcel[11], 53 | "recipient fullname": parcel[12], 54 | "phone_number": parcel[7] 55 | }, 56 | "addresses": { 57 | "pickup": parcel[3], 58 | "destination": parcel[2], 59 | "current": parcel[10], 60 | }, 61 | "latLngCodinates": { 62 | "pickup": parcel[13], 63 | "destination": parcel[14], 64 | }, 65 | "stats": { 66 | "weight": parcel[9], 67 | "status": parcel[6], 68 | "price": parcel[16], 69 | "tripDistance": parcel[15], 70 | "quantity": parcel[8] 71 | }, 72 | "created": parcel[18], 73 | "last_modified": parcel[17], 74 | "parcel_description": parcel[4] 75 | } 76 | parcel_list.append(parcel_dict) 77 | return jsonify({"parcels": parcel_list}), 200 78 | return jsonify({'message': 'No parcel delivery orders posted yet'}), 404 79 | 80 | 81 | # GET parcels/id 82 | @ap.route('/api/v2/parcels/') 83 | @token_required 84 | @swag_from('../doc/get_a_parcel.yml') 85 | def get_a_parcel(current_user, id): 86 | """ 87 | return order request details for a specific order 88 | :param id: 89 | :return: 90 | """ 91 | if not db.is_admin(current_user.user_id): 92 | if current_user.user_id != db.get_parce_owner_id(id): 93 | return response_message('unauthorized operation', 'You do not have enough permissions to access that', 401) 94 | if db.get_parcel_by_value('parcels', 'parcel_id', id) is None: 95 | return jsonify({"message": "parcel delivery request order not found"}), 404 96 | results = db.get_parcel_by_value('parcels', 'parcel_id', id) 97 | user_results = db.get_user_by_value('users', 'user_id', results[1]) 98 | user_dict = { 99 | "user_id": user_results[0], 100 | "fullname": user_results[1], 101 | "username": user_results[2], 102 | "telephone_number": user_results[6], 103 | "is_admin": user_results[8], 104 | "joined": user_results[9], 105 | "email": user_results[4], 106 | "imageUrl": user_results[3] 107 | 108 | } 109 | 110 | parcel_dict = { 111 | "parcel_id": results[0], 112 | "sender": user_dict, 113 | "recipient": { 114 | "email": results[11], 115 | "fullname": results[12], 116 | "phone_number": results[7] 117 | }, 118 | "addresses": { 119 | "pickup": results[3], 120 | "destination": results[2], 121 | "current": results[10], 122 | }, 123 | "latLngCodinates": { 124 | "pickup": results[13], 125 | "destination": results[14], 126 | }, 127 | "stats": { 128 | "weight": results[9], 129 | "status": results[6], 130 | "price": results[16], 131 | "tripDistance": results[15], 132 | "quantity": results[8] 133 | }, 134 | "created": results[18], 135 | "last_modified": results[17], 136 | "parcel_description": results[4] 137 | } 138 | return jsonify(parcel_dict), 200 139 | 140 | 141 | # POST parcels 142 | @ap.route('/api/v2/parcels', methods=['POST']) 143 | @token_required 144 | @swag_from('../doc/new_parcel.yml') 145 | def add_parcel(current_user): 146 | if not request.content_type == 'application/json': 147 | return jsonify({"failed": 'Content-type must be application/json'}), 415 148 | request_data = request.get_json() 149 | helper = Helper() 150 | try: 151 | if not validate_email(request_data['recipient_email']): 152 | return jsonify({"message": "Recipient email is invalid"}), 400 153 | if len(str(request_data['recipient_phone_number'])) < 10: 154 | return jsonify({"message": "Recipient Phone number should be atleast 10 characters"}), 400 155 | 156 | if len(str(request_data['parcel_description'])) < 5: 157 | return jsonify({"message": "Your Parcel description should be atleast 5 characters"}), 400 158 | if not isinstance(request_data['parcel_description'], str): 159 | return jsonify({"message": "Description should be string values"}), 400 160 | if not isinstance(request_data['pickup_address'], str): 161 | return jsonify({"message": "pickup_address should be string values"}), 400 162 | 163 | if (helper.get_formatted_address(request_data['pickup_address'])) is None: 164 | return jsonify({"message": "pickup_address not found"}), 400 165 | 166 | if not isinstance(request_data['destination_address'], str): 167 | return jsonify({"message": "destination_address should be string values"}), 400 168 | if (helper.get_formatted_address(request_data['destination_address'])) is None: 169 | return jsonify({"message": "destination_address not found"}), 400 170 | if not isinstance(request_data['quantity'], int): 171 | return jsonify({"message": "quantity should be integer values"}), 400 172 | 173 | if not isinstance(request_data['weight'], int): 174 | return jsonify({"message": "weight should be integer values"}), 400 175 | 176 | if not isinstance(request_data['recipient_name'], str): 177 | return jsonify({"message": "recipient_name should be string values"}), 400 178 | 179 | except KeyError as keyerr: 180 | return response_message('Failed', f"{keyerr}" + 'is missing', 400) 181 | dest_lat_lng = helper.get_dest_latlong(request_data['destination_address']) 182 | pickup_latlng = helper.get_pickup_latlong(request_data['pickup_address']) 183 | distance = helper.get_distance(pickup_latlng, dest_lat_lng) 184 | price = helper.get_charge( 185 | request_data['weight'], distance, request_data['quantity']) 186 | 187 | try: 188 | 189 | db.insert_into_parcels(helper.get_formatted_address(request_data['destination_address']), 190 | helper.get_formatted_address( 191 | request_data['pickup_address']), 192 | request_data['parcel_description'], 193 | current_user.user_id, 194 | db.get_user_email(current_user.user_id), 195 | request_data['recipient_name'], 196 | request_data['recipient_email'], 197 | request_data['recipient_phone_number'], 198 | request_data['weight'], 199 | request_data['quantity'], 200 | pickup_latlng, 201 | dest_lat_lng, 202 | distance, 203 | price) 204 | except psycopg2.IntegrityError: 205 | return response_message('message', 'something went wrong', 403) 206 | return jsonify({"status": "success", "message": "Parcel request has been created successfully", "parcel": db.get_last_insert_id()}), 201 207 | 208 | 209 | # PUT /parcels//cancel 210 | @ap.route('/api/v2/parcels//cancel', methods=['PUT']) 211 | @swag_from('../doc/cancel_parcell.yml') 212 | @token_required 213 | def cancel_parcel_request(current_user, id): 214 | ''' 215 | cancels a specific request given its identifier 216 | ''' 217 | if db.get_parcel_by_value('parcels', 'parcel_id', id) is None: 218 | return jsonify({"message": "parcel delivery request not found"}), 404 219 | 220 | if db.is_order_delivered(id): 221 | return jsonify({"message": "Not allowed parcel request has already been delivered"}), 403 222 | if not db.is_parcel_owner(id, current_user.user_id): 223 | return jsonify({"message": "Not authorized"}), 401 224 | return jsonify( 225 | {"message": "parcel request was cancelled successfully", "parcel_id": db.cancel_parcel(id)[0], 226 | "new_parcel_status": db.cancel_parcel(id)[6]}), 200 227 | 228 | 229 | @ap.route('/api/v2/parcels//delete', methods=['DELETE']) 230 | @token_required 231 | def delete_parcel_request(current_user, id): 232 | ''' 233 | deletes a specific request given its identifier 234 | ''' 235 | if db.get_parcel_by_value('parcels', 'parcel_id', id) is None: 236 | return jsonify({"message": "parcel delivery request not found"}), 404 237 | 238 | if db.is_order_delivered(id): 239 | return jsonify({"message": "Not allowed parcel request has already been delivered"}), 403 240 | if not db.is_parcel_owner(id, current_user.user_id): 241 | return jsonify({"message": "You cannot do that,not enough previledges"}), 401 242 | return jsonify( 243 | {"message": "parcel request was deleted successfully", "parcel_id": db.delete_parcel(id)}), 200 244 | 245 | 246 | @ap.route('/api/v2/parcels//presentLocation', methods=['PUT']) 247 | @token_required 248 | @swag_from('../doc/change_present_locationn.yml') 249 | def change_present_location(current_user, id): 250 | heper = Helper() 251 | if not db.is_admin(current_user.user_id): 252 | return response_message('Unauthorized', 'Not enough access previleges', 401) 253 | request_data = request.get_json() 254 | try: 255 | if not isinstance(request_data['current_location'], str): 256 | return response_message('error', 'current location should be string value', 400) 257 | if not db.get_parcel_by_value('parcels', 'parcel_id', id): 258 | return response_message('error', 'order not found', 404) 259 | if is_should_update(request_data): 260 | cl = heper.get_formatted_address(request_data['current_location']) 261 | if cl is None: 262 | return response_message('error', 'current location address does not exist', 400) 263 | 264 | db.change_present_location(cl, id) 265 | if heper.get_dest_latlong(heper.get_formatted_address(request_data['current_location'])) == db.get_destination_latlng(id): 266 | db.update_parcel_status('delivered', id) 267 | else: 268 | db.update_parcel_status('in_transit', id) 269 | our_user = db.get_user_by_value( 270 | 'users', 'user_id', db.get_parce_owner_id(id)) 271 | sendemail(our_user[3], 'Order Update', 272 | 'Hello there ' + our_user[1] + '\nYour parcels location is now ' + db.get_current_location(id)) 273 | return jsonify({'message': 'current location updated successfully', 274 | 'Present Location': db.get_current_location(id)}), 200 275 | else: 276 | return jsonify({'message': 'bad request object, current location missing'}), 400 277 | 278 | except KeyError as identifier: 279 | return jsonify({'message': str(identifier) + 'is missing'}) 280 | 281 | 282 | @ap.route('/api/v2/parcels//status', methods=['PUT']) 283 | @token_required 284 | @swag_from('../doc/status.yml') 285 | def change_order_status(current_user, id): 286 | if not db.is_admin(current_user.user_id): 287 | return response_message('Unauthorized', 'Not enough access privileges', 401) 288 | request_data = request.get_json() 289 | try: 290 | 291 | if not isinstance(request_data['status'], str): 292 | return response_message('error', 'status should be string value', 400) 293 | status = ['pickup_started', 'rejected', 294 | 'in_transit', 'cancelled', 'delivered'] 295 | 296 | if not db.get_parcel_by_value('parcels', 'parcel_id', id): 297 | return jsonify({'message': 'order not found'}), 404 298 | if not request_data['status'] in status: 299 | return jsonify( 300 | { 301 | 'message': "invalid status,parcels can be cancelled,delivered,in_transit,rejected,pickup_started"}), 400 302 | 303 | db.change_status(request_data['status'], id) 304 | our_user = db.get_user_by_value( 305 | 'users', 'user_id', db.get_parce_owner_id(id)) 306 | sendemail(our_user[3], 'Order Status Update', 307 | 'Hello there ' + our_user[1] + '\nYour parcels status ' + request_data['status']) 308 | return jsonify({'message': 'order status updated successfully', 'new_status': request_data['status']}), 200 309 | except KeyError as e: 310 | return response_message('error', 'status is missing', 400) 311 | 312 | 313 | @ap.route('/api/v2/parcels//destination', methods=['PUT']) 314 | @token_required 315 | @swag_from('../doc/changedestination.yml') 316 | def change_destination(current_user, id): 317 | rdata = request.get_json() 318 | helper = Helper() 319 | if not "destination_address" in rdata: 320 | return jsonify({'message': 'Please add a new destination address'}), 400 321 | newdest = rdata['destination_address'] 322 | if db.get_parcel_by_value('parcels', 'parcel_id', id) is None: 323 | return jsonify({"message": "parcel delivery request not found"}), 404 324 | if (helper.get_formatted_address(rdata['destination_address'])) is None: 325 | return jsonify({"message": "destination_address not found"}), 400 326 | # CHECK SAME DESTINATION ADDRESSES 327 | if str(db.get_destination_address(id)).lower() == str(newdest).lower(): 328 | return response_message('Forbidden', 'cannot change to the same destination', 403) 329 | if not db.is_order_delivered(id): 330 | if db.is_parcel_owner(id, current_user.user_id): 331 | our_user = db.get_user_by_value( 332 | 'users', 'user_id', db.get_parce_owner_id(id)) 333 | new_lat_lng = helper.get_dest_latlong(newdest) 334 | new_distance = helper.get_distance( 335 | new_lat_lng, db.get_pick_up_latlng(id)) 336 | parcel_weight = db.get_parcel_weight(id) 337 | new_price = helper.get_charge( 338 | parcel_weight, new_distance, quantity=None) 339 | res = db.change_destination(helper.get_formatted_address( 340 | newdest), id, new_lat_lng, new_distance, new_price) 341 | sendemail(our_user[3], 'Destination Update', 342 | 'Hello there \n New Destination Update for ' + current_user.username + '\nNew Destination is ' + res) 343 | 344 | return jsonify({'message': 'destination updated successfully', 345 | 'new_destination': res}), 200 346 | else: 347 | return response_message('Forbidden', 'Not authorised to perform operation', 403) 348 | 349 | else: 350 | return jsonify({'message': 'order already delivered cant update'}), 403 351 | 352 | 353 | def not_validresponse(): 354 | return jsonify({"error": 'Bad Request object,expected data is missing'}), 400 355 | 356 | 357 | def is_should_update(data): 358 | if 'current_location' in data: 359 | return True 360 | return False 361 | 362 | 363 | def sendemail(email, subject, body): 364 | ''' 365 | send an email to a user 366 | ''' 367 | app.config.update( 368 | DEBUG=True, 369 | # EMAIL SETTINGS 370 | MAIL_SERVER='smtp.gmail.com', 371 | MAIL_PORT=465, 372 | MAIL_USE_SSL=True, 373 | MAIL_USERNAME=os.environ.get("SENDER_EMAIL", ""), 374 | MAIL_PASSWORD=os.environ.get("SENDER_PASSWORD", "") 375 | 376 | ) 377 | mail = Mail(app) 378 | try: 379 | message = Message(subject, 380 | sender="crycetruly@gmail.com", 381 | recipients=[email]) 382 | message.body = body 383 | mail.send(message) 384 | return 'mail sent' 385 | except Exception as identifier: 386 | print(identifier) 387 | pass 388 | -------------------------------------------------------------------------------- /app/views/search.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, Blueprint, jsonify, request 2 | 3 | from app.auth.decorator import response_message, token_required 4 | from app.database.database import Database 5 | from flask_cors import CORS 6 | 7 | search = Blueprint('search', __name__) 8 | CORS(search) 9 | db = Database() 10 | 11 | 12 | @search.route("/api/v2/search/", methods=['POST']) 13 | @token_required 14 | def search_app(current_user): 15 | data = request.get_json() 16 | 17 | if not 'query' in data: 18 | return response_message("failed", "please search", 400) 19 | query = data['query'] 20 | results = db.search_app(query) 21 | response_data = [] 22 | if isinstance(results, str): 23 | return response_message("not_found", results, 404) 24 | print(results) 25 | for result in results: 26 | res_obj = { 27 | "user_id": result[0], 28 | "user_email": result[2], 29 | "user_name": result[1], 30 | 31 | } 32 | response_data.append(res_obj) 33 | 34 | return jsonify({"message": "found", "data": response_data}), 200 35 | -------------------------------------------------------------------------------- /app/views/users.py: -------------------------------------------------------------------------------- 1 | import jwt 2 | import re 3 | import datetime 4 | from app.auth.decorator import response_message, token_required 5 | from werkzeug.security import generate_password_hash, check_password_hash 6 | from flask import Blueprint, request, jsonify, redirect 7 | from flasgger import swag_from 8 | from validate_email import validate_email 9 | from app.database.database import Database 10 | from app.model.models import User 11 | from app.views.parcels import sendemail 12 | from app.auth.decorator import get_token 13 | from flask_cors import CORS 14 | import os 15 | auth = Blueprint('auth', __name__) 16 | 17 | db = Database() 18 | CORS(auth) 19 | 20 | 21 | @auth.route('/api/v2/auth/signup', methods=['POST']) 22 | @swag_from('../doc/signup.yml') 23 | def create_user(): 24 | """ 25 | User creates an account 26 | User sign up details are added to the data base 27 | """ 28 | if request.content_type != 'application/json': 29 | return response_message( 30 | 'Bad request', 'Content-type must be json type', 400) 31 | request_data = request.get_json() 32 | try: 33 | if not request_data: 34 | return jsonify({"message": "Empty request"}), 400 35 | username = str(request_data['username']) 36 | if not username.isalnum(): 37 | return response_message('Missing', 38 | 'Usernames must contain only letters and numbers', 400) 39 | email = request_data['email'] 40 | fullname = request_data['fullname'] 41 | if not fullname: 42 | return response_message('Missing', 'FullName is required', 400) 43 | 44 | phone_number = str(request_data['phone_number']) 45 | if len(phone_number) < 10: 46 | return response_message('Invalid', 47 | 'phone number should be atleast 10 characters long', 400) 48 | 49 | if not re.match("[0-9]", phone_number): 50 | return response_message('Invalid', 51 | 'phone number should not contain letters', 52 | 400) 53 | if not isinstance(fullname, str) or not isinstance(username, str): 54 | return response_message('Invalid', 55 | 'fullname and username should be of type string', 400) 56 | if len(str(fullname)) < 3 or len(username) < 3: 57 | return response_message('Invalid', 58 | 'FullName and username should be atleast 3 characters long', 400) 59 | if not username: 60 | return response_message('Missing', 'Username required', 400) 61 | if not validate_email(email): 62 | return response_message( 63 | 'Error', 'Missing or wrong email format', 400) 64 | if not len(request_data['password']) > 5: 65 | return response_message( 66 | 'Failed', 'Ensure password is atleast 6 characters', 400) 67 | if db.get_user_by_value('users', 'email', email): 68 | return response_message( 69 | 'Failed', 'User with email ' + email + ' already exists', 409) 70 | if db.get_user_by_value('users', 'username', username): 71 | return response_message( 72 | 'Failed', username + ' is taken', 409) 73 | password = generate_password_hash(request_data['password']) 74 | db.insert_into_user(fullname, username, email, phone_number, password) 75 | redirect_url = f"{request.url_root}api/v2/auth/email_verify?token={jwt.encode({'email':email},os.environ.get('TRULYS_SECRET','TRULYS_SECRET')).decode('utf-8')}" 76 | email_message = { 77 | "subject": 'Welcome to SendIT', 78 | "body": 'Hello there ' + fullname + 79 | '\nClick this link to verify your email\n' + 80 | '\n '+redirect_url 81 | } 82 | send_mail(request, email_message, email) 83 | 84 | return response_message('Success', 85 | 'Please visit your email to verify your account', 201) 86 | except KeyError as e: 87 | return jsonify({'Error': str(e) + ' is missing'}), 400 88 | 89 | 90 | @auth.route('/api/v2/auth/login', methods=['POST']) 91 | @swag_from('../doc/login.yml') 92 | def login_user(): 93 | """ 94 | User login if he supplies correct credentials 95 | token is generated and given to a user 96 | """ 97 | try: 98 | if request.content_type != 'application/json': 99 | return response_message( 100 | 'Bad request', 'Content-type must be in json', 400) 101 | request_data = request.get_json() 102 | if not request_data: 103 | return jsonify({"Failed": "Empty request"}), 400 104 | email = request_data['email'] 105 | if not validate_email(email): 106 | return response_message( 107 | 'Failed', 'email is invalid', 400) 108 | 109 | password = request_data['password'] 110 | db_user = db.get_user_by_value('users', 'email', email) 111 | if not db_user[7]: 112 | return response_message( 113 | 'Failed', 'email is not verified,please visit your mailbox', 114 | 401) 115 | new_user = User( 116 | db_user[0], db_user[1], db_user[2], db_user[3], 117 | db_user[5], db_user[7]) 118 | passed = check_password_hash(new_user.password, password) 119 | 120 | if not passed: 121 | return response_message('Failed', 'email or password is invalid', 122 | 400) 123 | payload = { 124 | 125 | 'exp': datetime.datetime.utcnow() + 126 | datetime.timedelta(days=0, hours=23), 127 | 'user_id': new_user.user_id, 128 | 'email': new_user.email, 129 | 'is_admin': new_user.is_admin 130 | } 131 | token = jwt.encode( 132 | payload, 133 | os.environ.get('TRULYS_SECRET', 'TRULYS_SECRET'), 134 | algorithm='HS256' 135 | ) 136 | if token: 137 | results = db.get_user_by_value( 138 | 'users', 'user_id', new_user.user_id) 139 | user_dict = { 140 | "user_id": results[0], 141 | "fullname": results[1], 142 | "username": results[2], 143 | "telephone_number": results[6], 144 | "is_admin": results[8], 145 | "joined": results[9], 146 | "email": results[4], 147 | "imageUrl": results[3] 148 | 149 | } 150 | response = { 151 | "data": { 152 | "auth_token": token.decode('UTF-8'), 153 | "user": user_dict, 154 | "status": "success", 155 | "message": "successfully loged in" 156 | } 157 | } 158 | return jsonify(response), 200 159 | except Exception as er: 160 | return response_message( 161 | 'Failed', 'email or password is invalid', 400) 162 | 163 | 164 | @auth.route('/api/v2/users', methods=['GET']) 165 | @token_required 166 | def get_users(current_user): 167 | if not db.is_admin(current_user.user_id): 168 | return response_message('unauthorized operation', 169 | 'Only admin users can view all users', 401) 170 | users = Database().get_users() 171 | user_list = [] 172 | for user in users: 173 | user_dict = { 174 | "user_id": user[0], 175 | "fullname": user[1], 176 | "username": user[2], 177 | "telephone_number": user[6], 178 | "is_admin": user[8], 179 | "joined": user[9], 180 | "email": user[4], 181 | "imageUrl": user[3] 182 | } 183 | user_list.append(user_dict) 184 | return jsonify({"users": user_list}), 200 185 | 186 | 187 | @auth.route('/api/v2/users//parcels', methods=['GET']) 188 | @token_required 189 | @swag_from('../doc/user_parcels.yml') 190 | def get_user_parcels(current_user, id): 191 | if not db.is_admin(current_user.user_id): 192 | if current_user.user_id != id: 193 | return response_message('unauthorized operation', 194 | 'You do not have permissions to access that', 401) 195 | if not db.get_user_by_value('users', 'user_id', id) is None: 196 | try: 197 | parcel_list = [] 198 | for results in db.get_user_parcels(id): 199 | parcel_dict = { 200 | "parcel_id": results[0], 201 | "recipient": { 202 | "email": results[11], 203 | "fullname": results[12], 204 | "phone_number": results[7] 205 | }, 206 | "addresses": { 207 | "pickup": results[3], 208 | "destination": results[2], 209 | "current": results[10], 210 | }, 211 | "latLngCodinates": { 212 | "pickup": results[13], 213 | "destination": results[14], 214 | }, 215 | "stats": { 216 | "weight": results[9], 217 | "status": results[6], 218 | "price": results[16], 219 | "tripDistance": results[15], 220 | "quantity": results[8] 221 | }, 222 | "created": results[18], 223 | "last_modified": results[17], 224 | "parcel_description": results[4] 225 | } 226 | parcel_list.append(parcel_dict) 227 | return jsonify({"parcels": parcel_list}), 200 228 | except IndexError as e: 229 | return jsonify({"message": 'User does not exist'}), 404 230 | 231 | else: 232 | return jsonify({"message": 'User does not exist'}), 404 233 | 234 | 235 | @auth.route('/api/v2/auth//promote_user', methods=['PUT']) 236 | @token_required 237 | def change_user_type(current_user, user_id): 238 | 239 | if not db.is_admin(current_user.user_id): 240 | return response_message('unauthorised', 'cant access that', 401) 241 | if db.get_user_by_value('users', 'user_id', user_id) is None: 242 | return response_message('Error', 'User does not exist', 404) 243 | db.update_role(user_id) 244 | return response_message('success', 'User is now admin', 200) 245 | 246 | 247 | # GET user/id 248 | @auth.route('/api/v2/users/') 249 | @token_required 250 | @swag_from('../doc/get_user.yml') 251 | def get_a_user(current_user, id): 252 | """ 253 | return order request details for a specific order 254 | :param id: 255 | :return: 256 | """ 257 | if db.get_user_by_value('users', 'user_id', id) is None: 258 | return jsonify({"message": "user does not exist"}), 404 259 | results = db.get_user_by_value('users', 'user_id', id) 260 | user_dict = { 261 | "user_id": results[0], 262 | "fullname": results[1], 263 | "username": results[2], 264 | "telephone_number": results[6], 265 | "is_admin": results[8], 266 | "joined": results[9], 267 | "email": results[4], 268 | "imageUrl": results[3] 269 | 270 | } 271 | return jsonify(user_dict), 200 272 | 273 | 274 | @auth.route('/api/v2/auth/logout', methods=['POST']) 275 | @token_required 276 | def logout(current_user): 277 | db.invalidate_a_token(get_token()) 278 | return response_message('success', 'you have successfully logged out', 200) 279 | 280 | 281 | @auth.route('/api/v2/auth/email_verify', methods=['GET']) 282 | def verify_user(): 283 | "verifies a users email" 284 | try: 285 | user = jwt.decode(request.args.get('token'), os.environ.get( 286 | 'TRULYS_SECRET', 'TRULYS_SECRET')) 287 | db.verify_user(user) 288 | return redirect(os.environ.get('FRONT_END_URL', '')+'login?message=success', code=302) 289 | 290 | except jwt.InvalidSignatureError as identifier: 291 | return jsonify({"message": "verification is invalid or expired"}), 400 292 | 293 | except jwt.exceptions.DecodeError as e: 294 | return jsonify({"message": "Verification link is invalid"}), 400 295 | 296 | 297 | @auth.route('/api/v2/auth/reset_password/', methods=['POST']) 298 | def send_password_reset_link(): 299 | "sends a reset email to a user" 300 | email = request.get_json().get("email", None) 301 | if not email: 302 | return jsonify({"message": "Please provide your email"}), 400 303 | 304 | user = db.get_user_by_value("users", "email", email) 305 | if not user: 306 | return jsonify({"message": "There is no account associated with that email"}), 404 307 | 308 | redirect_url = f"{request.url_root}api/v2/auth/password_change?token={jwt.encode({'email':email},os.environ.get('TRULYS_SECRET','TRULYS_SECRET')).decode('utf-8')}" 309 | email_message = { 310 | "subject": "Password reset", 311 | "body": f"Please click the link below to reset your password \n {redirect_url}" 312 | } 313 | send_mail(request, email_message, user[4]) 314 | return response_message("success", "Please check your email for reset instructions", 200) 315 | 316 | 317 | @auth.route("/api/v2/auth/password_change", methods=["GET"]) 318 | def change_password(): 319 | token = request.args.get("token", "tamperedwith") 320 | return redirect(os.environ.get("FRONT_END_URL")+"change_password?auth_token="+token) 321 | 322 | 323 | @auth.route("/api/v2/auth/password_change", methods=["POST"]) 324 | def comfirmchange_password(): 325 | try: 326 | token = request.get_json().get("token", None) 327 | user = jwt.decode(token, os.environ.get( 328 | 'TRULYS_SECRET', 'TRULYS_SECRET')) 329 | password = request.get_json().get("password", None) 330 | if password is None: 331 | return response_message("Error", "New password is required", 400) 332 | if len(password) < 6: 333 | return response_message("Error", "Password is too short", 400) 334 | db.change_user_password(user['email'], password) 335 | return response_message("success", "password changed successfully", 200) 336 | except jwt.DecodeError as identifier: 337 | return response_message("Error", "link is invalid", 400) 338 | except jwt.ExpiredSignatureError as e: 339 | return response_message( 340 | "Error", "Reset Link is expired,Please request a new one", 400) 341 | 342 | 343 | def send_mail(request, email_message, email): 344 | sendemail( 345 | email, 346 | email_message["subject"], 347 | email_message["body"] 348 | ) 349 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from app import app 3 | from flask_script import Manager 4 | import pytest 5 | 6 | manager = Manager(app) 7 | 8 | 9 | @manager.command 10 | def test(): 11 | """Runs the unit tests without coverage.""" 12 | tests = unittest.TestLoader().discover('tests') 13 | unittest.TextTestRunner(verbosity=2).run(tests) 14 | 15 | 16 | if __name__ == '__main__': 17 | manager.run() 18 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | if __name__ == "__main__": 4 | app.run(port=3000,debug=True) 5 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.7.0 -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CryceTruly/SendIT-API-v2/de93b61fae52368a4d8194d30d2ba1c8ef68a7f9/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_base.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | from flask import Flask 4 | from app.model.models import Parcel, User 5 | from app import app 6 | from app.auth.decorator import get_token, token_required, response_message 7 | from app.database.database import Database 8 | 9 | 10 | class TestsStart(unittest.TestCase): 11 | def setUp(self): 12 | self.app = app.test_client() 13 | db = Database() 14 | db.create_tables() 15 | def test_if_cant_get_userswithouttoken(self): 16 | response = self.app.get('api/v2/users') 17 | data = json.loads(response.data.decode()) 18 | self.assertEqual(response.status_code, 401) 19 | self.assertEqual('please login', data['message']) 20 | 21 | def test_parcel(self): 22 | parcel = Parcel(2, "destination_address", "pickup_address", "parcel_description", 23 | 2, "sender_email@gmail.com", "0789888878" 24 | "recipient@gmail.com", "name", 22, 10, 10) 25 | self.assertIn("id:2 senderemail:sender_email@gmail.com recieveremail:name", str(parcel)) 26 | 27 | def test_user(self): 28 | user = User(11, 'crycetruly', 'crycetruly@gmail.com', '075633434432', 'password', False) 29 | self.assertNotEqual(str(user), 'user: 11 username:crycetruly with email crycetruly@gmail.com:isadmin:False') 30 | 31 | def test_db(self): 32 | db = Database() 33 | self.assertTrue(db) 34 | 35 | def test_db_connection(self): 36 | db = Database() 37 | self.assertTrue(db.connection) 38 | 39 | def tearDown(self): 40 | db = Database() 41 | db.drop_tables() 42 | 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /tests/test_parcels.py: -------------------------------------------------------------------------------- 1 | import json 2 | from tests.test_base import TestsStart 3 | 4 | 5 | class TestsParcel(TestsStart): 6 | def test_if_can_get_parcels(self): 7 | response = self.app.get('api/v2/parcels') 8 | data = json.loads(response.data.decode()) 9 | self.assertEqual('please login', data['message']) 10 | 11 | def test_parcel_request_not_json(self): 12 | """ Test order content to be posted not in json format """ 13 | expectedreq = { 14 | 'pickup_address': 'Kampala Kikoni Makerere 13', 15 | 'destination_address': 'Mabarara Kikoni Home 13', 16 | 'comment_description': 'My parcels contain a laptop,please deliver', 17 | 'status': 'In Transit', 18 | 'current_location': 'Mabarara Kikoni Home 13', 19 | 'created': "Sat, 10 Nov 2018 13:46:41 GMT", 20 | 'recipient_address': 'Julie Muli', 21 | 'recipient_phone': '0767876666', 22 | 'recipient_email': 'recipient@email.com' 23 | 24 | } 25 | result = self.app.post( 26 | '/api/v2/parcels', 27 | content_type='text/html', 28 | data=json.dumps(expectedreq) 29 | ) 30 | data=json.loads(result.data.decode()) 31 | self.assertEqual(result.status_code, 401) 32 | self.assertEqual("please login", data['message']) 33 | -------------------------------------------------------------------------------- /tests/test_users.py: -------------------------------------------------------------------------------- 1 | 2 | from app.model.models import User 3 | from flask import Flask 4 | import json 5 | from app.model.models import Parcel, User 6 | from tests.test_base import TestsStart 7 | 8 | 9 | class TestAuth(TestsStart): 10 | def signup_user(self, fullname, username, email, phone_number, password): 11 | """ 12 | Method to define user registration details 13 | """ 14 | exp_obj = { 15 | "fullname": fullname, 16 | "username": username, 17 | "email": email, 18 | "phone_number": phone_number, 19 | "password": password 20 | } 21 | return self.app.post( 22 | '/api/v2/auth/signup', 23 | content_type="application/json", 24 | data=json.dumps(exp_obj) 25 | ) 26 | 27 | def login_user(self, email, password): 28 | """ 29 | Method to define user login details 30 | """ 31 | obj = { 32 | "email": email, 33 | "password": password 34 | } 35 | return self.app.post( 36 | '/api/v2/auth/login', 37 | content_type="application/json", 38 | data=json.dumps(obj) 39 | ) 40 | 41 | def test_user_class(self): 42 | user = User(1, "Cryce Truly", "crycetruly", 43 | "crycetruly@gmail.com", "0756778877", 'password') 44 | self.assertTrue(user) 45 | 46 | def test_details_json_format(self): 47 | with self.app: 48 | result = self.signup_user( 49 | "Cryce Truly", "crycetruly", "crycetruly@gmail.com", "0756778877", 'password') 50 | self.assertTrue(result.content_type == "application/json") 51 | 52 | def test_email_not_valid(self): 53 | """ 54 | Test for invalid email address 55 | """ 56 | register = { 57 | "username": "crycetruly", 58 | "email": "email", 59 | "phone_number": "0756787865", 60 | "password": "password", 61 | } 62 | rs = self.app.post( 63 | '/api/v2/auth/signup', 64 | content_type="application/json", 65 | data=json.dumps(register) 66 | ) 67 | data = json.loads(rs.data.decode()) 68 | self.assertEqual(rs.status_code, 400) 69 | 70 | def test_short_password(self): 71 | """ 72 | Test for short password 73 | """ 74 | with self.app: 75 | result = self.signup_user( 76 | "Cryce Truly", "crycetruly", "crycetruly@gmail.com", "0756778877", 'pas') 77 | self.assertEqual(result.status_code, 400) 78 | data = json.loads(result.data.decode()) 79 | self.assertTrue(data['status'] == 'Failed') 80 | self.assertTrue(data['message'] == 81 | 'Ensure password is atleast 6 characters') 82 | 83 | def test_spaces_in_username(self): 84 | with self.app: 85 | result = self.signup_user( 86 | "Cryce Truly", "cryce truly", "crycetruly@gmail.com", "0756778877", 'password') 87 | self.assertEqual(result.status_code, 400) 88 | 89 | def test_user_data_not_json(self): 90 | """ 91 | Test Content_type not application/json for sign up request 92 | """ 93 | rv = self.app.post( 94 | '/api/v2/auth/signup', 95 | content_type="text", 96 | data="") 97 | 98 | data = json.loads(rv.data.decode()) 99 | self.assertEqual(rv.status_code, 400) 100 | self.assertEqual( 101 | "Content-type must be json type", data['message']) 102 | 103 | def test_content_type_4_login_not_json(self): 104 | """ 105 | Test Content_type not application/json for login request 106 | """ 107 | rv = self.app.post( 108 | '/api/v2/auth/login', 109 | content_type="text") 110 | data = json.loads(rv.data.decode()) 111 | self.assertEqual(rv.status_code, 400) 112 | self.assertEqual( 113 | "Content-type must be in json", data['message']) 114 | self.assertEqual( 115 | "Bad request", data['status']) 116 | 117 | def test_user_already_exist(self): 118 | with self.app: 119 | self.signup_user( 120 | "Cryce Truly", "crycetruly", "crycetruly@gmail.com", "0756778877", 'password') 121 | result = self.signup_user( 122 | "Cryce Truly", "crycetruly", "crycetruly@gmail.com", "0756778877", 'password') 123 | self.assertEqual(result.status_code, 409) 124 | data = json.loads(result.data.decode()) 125 | self.assertTrue(data['status'] == 'Failed') 126 | 127 | def test_successful_signup(self): 128 | with self.app: 129 | result = self.signup_user( 130 | "Cryce Trurely", "cryceddddtrruly", "crfedydydy@gmail.com", "0756778887", "pashsword") 131 | self.assertEqual(result.status_code, 201) 132 | 133 | def test_invalid_token(self): 134 | res = self.app.get( 135 | '/api/v2/parcels', 136 | headers=dict(Authorization='Bearer ywjjkjkjkwe')) 137 | data = json.loads(res.data.decode()) 138 | self.assertEqual("please login", data['message']) 139 | self.assertEqual(res.status_code, 401) 140 | 141 | def test_missing_username_keyword(self): 142 | obj = { 143 | "email": "email@gmail.com", 144 | "phone_number": "0756434545", 145 | "password": "password", 146 | "is_admin": True 147 | } 148 | rs = self.app.post( 149 | '/api/v2/auth/signup', 150 | content_type="application/json", 151 | data=json.dumps(obj) 152 | ) 153 | self.assertEqual(rs.status_code, 400) 154 | data = json.loads(rs.data.decode()) 155 | self.assertEqual(data['Error'], "'username' is missing") 156 | 157 | def test_password_keyword_missing(self): 158 | login = { 159 | "email": "fred@gmail.com" 160 | } 161 | rv = self.app.post( 162 | '/api/v2/auth/login', 163 | content_type="application/json", 164 | data=json.dumps(login) 165 | ) 166 | data = json.loads(rv.data.decode()) 167 | self.assertEqual(data['status'], "Failed") 168 | self.assertEqual(data['message'], "email or password is invalid") 169 | self.assertEqual(rv.status_code, 400) 170 | 171 | def test_all_users_details(self): 172 | with self.app: 173 | result = self.signup_user( 174 | "Cryce TrulyTest", "TrulyTest", "TrulyTest@gmail.com", "0756778877", 'pasTrulyTestsword') 175 | self.assertEqual(result.status_code, 201) 176 | res = json.loads(result.data.decode()) 177 | self.assertTrue(res['status'] == 'Success') 178 | user_dict = { 179 | "user_id": 1, 180 | "fullname": "Cryce TrulyTest", 181 | "username": "crycetruly", 182 | "email": "TrulyTest@gmail.com", 183 | "phone_number": "0756778877", 184 | "is_admin": False 185 | } 186 | rs = self.app.get( 187 | '/api/v2/users', 188 | content_type="application/json", 189 | data=json.dumps(user_dict) 190 | ) 191 | data = json.loads(rs.data.decode()) 192 | self.assertEqual(rs.status_code, 401) 193 | 194 | def test_empty_user_request(self): 195 | response = self.signup_user("", "", "", "", "") 196 | res = json.loads(response.data.decode()) 197 | self.assertEqual(response.status_code, 400) 198 | 199 | def test_empty_user_request_text(self): 200 | exp_obj = { 201 | 202 | } 203 | response = self.app.post( 204 | '/api/v2/auth/signup', 205 | content_type="application/json", 206 | data=json.dumps(exp_obj) 207 | ) 208 | res = json.loads(response.data.decode()) 209 | self.assertEqual(response.status_code, 400) 210 | self.assertEqual(res['message'], 'Empty request') 211 | 212 | def test_short_phone_number(self): 213 | response = self.signup_user( 214 | "geosh@gmail.com", "geoshtest", "geosh@gmail.com", "076", "pppppppppppppppppp") 215 | res = json.loads(response.data.decode()) 216 | self.assertEqual(response.status_code, 400) 217 | self.assertEqual( 218 | res['message'], 'phone number should be atleast 10 characters long') 219 | 220 | def test_string_phone(self): 221 | response = self.signup_user("geosgmail.com", "geoshtelst", "geoslh@gmail.com", "pppppppppppppppp", 222 | "pppppppppppppppppp") 223 | res = json.loads(response.data.decode()) 224 | self.assertEqual(response.status_code, 400) 225 | self.assertEqual( 226 | res['message'], 'phone number should not contain letters') 227 | 228 | def test_username_missing(self): 229 | exp_obj = { 230 | "fullname": "Elain", 231 | "email": "crycetruly@email.com", 232 | "phone_number": "testuserbre", 233 | "password": "password" 234 | } 235 | response = self.app.post( 236 | '/api/v2/auth/signup', 237 | content_type="application/json", 238 | data=json.dumps(exp_obj) 239 | ) 240 | self.assertEqual(response.status_code, 400) 241 | res = json.loads(response.data.decode()) 242 | self.assertEqual(res['Error'], "'username' is missing") 243 | 244 | def test_parcels_get_returned(self): 245 | with self.app: 246 | res = self.login_user('admin@sendit.com', 'adminuser') 247 | data = json.loads(res.data.decode()) 248 | token = data['data']['auth_token'] 249 | expectedreq = { 250 | "recipient_name": "Aron Mike", 251 | "parcel_description": "Here are my stuff", 252 | "weight": 90, 253 | "quantity": 22, 254 | "pickup_address": "Mukono", 255 | "destination_address": "Entebbe", 256 | "recipient_phone_number": "0767878787", 257 | "recipient_email": "rme@gmail.com" 258 | } 259 | self.app.post( 260 | '/api/v2/parcels', 261 | content_type='application/json', 262 | headers=dict(Authorization='Bearer' " " + token), 263 | data=json.dumps(expectedreq)) 264 | rs = self.app.get( 265 | '/api/v2/parcels', 266 | content_type="application/json", 267 | headers=dict(Authorization='Bearer' " " + token) 268 | ) 269 | data = json.loads(rs.data.decode()) 270 | self.assertIn('message', data) 271 | --------------------------------------------------------------------------------