From bf6510161809953900b4c1851d775db9c7b6c85f Mon Sep 17 00:00:00 2001 From: Daeun Date: Tue, 12 Jun 2018 14:20:53 +0100 Subject: [PATCH 1/9] Demo --- hello/templates/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hello/templates/index.html b/hello/templates/index.html index 8af29028b..d456f243d 100644 --- a/hello/templates/index.html +++ b/hello/templates/index.html @@ -8,7 +8,7 @@ -

Getting Started with Python on Heroku

+

2poundsmeal

This is a sample Python application deployed to Heroku. It's a reasonably simple app - but a good foundation for understanding how to get the most out of the Heroku platform.

Getting Started with Python Source on GitHub From 475e8df29d48736abf6ebff7f3b3a60b4711650a Mon Sep 17 00:00:00 2001 From: Daeun Date: Tue, 12 Jun 2018 14:42:20 +0100 Subject: [PATCH 2/9] panel --- .DS_Store | Bin 0 -> 6148 bytes hello/.DS_Store | Bin 0 -> 6148 bytes hello/static/.DS_Store | Bin 0 -> 6148 bytes hello/static/2poundsmeal.jpg | Bin 0 -> 51680 bytes hello/static/lang-logo.png | Bin 2217 -> 0 bytes hello/templates/base.html | 2 +- hello/templates/index.html | 6 +++--- 7 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 .DS_Store create mode 100644 hello/.DS_Store create mode 100644 hello/static/.DS_Store create mode 100644 hello/static/2poundsmeal.jpg delete mode 100644 hello/static/lang-logo.png diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e5c79600d7c2c79113a5470b995e7b02f51d192e GIT binary patch literal 6148 zcmeH~F%H5o3`K1y5)w;C#w;9w8$=bHfD0ff5<{g9h&@N=+wnqSMiu(5oF6+0v~Q?u z5z+41??qY>S;I|bXT!u4@k;J;lL6P*Kd!@N90yCwBKBGU$8Wr^$1;TkNPq-LfCNZj zMg+WwqyBGZbWgk$5+H$j5U}?{f}7^hQq^A_7`z35Hb}c+?!5#wSpu3vOI2iG8r_4& zQS~;&@_rneVqXp|RqJulXg)OlS*?qKX+18Q5WqCMa3BE^m=IXjxV8F!gn#S*Cr22P z015mV0c{SaLx+co+WPT$mOn?<<^>M*?HJxZ0uWd!-oo9mo@@fmp`|J^Fn$O)1`Z_f GQvxsJ0TYP; literal 0 HcmV?d00001 diff --git a/hello/.DS_Store b/hello/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2a43b86b965c6735a338394c4d17e6a7a5a71078 GIT binary patch literal 6148 zcmeHKISv9b4733uB$_QL^96noA$S2F;A)VND1d$|-o?`x9|dTkg90>`oJkx{qD-+~ zi-^uHyQxScA|tq=Ty5x@?VES3mk|ZRamHS@yVLftIUWz4?AHO~4&@|gS;HUrwn3u; zRDcRl0V+TRKCVEP*wOgoC-XomKm~qX0sB4_xM59f1O3y1!CL^}0AV-Gy_WzM3jk|k z8;A%@g9;3)W{aUgN4#WSO>6^$E}G4U=FOTNiu&z1zj(T64dh4#sKB!V{a7|u{}=E# z{r@wGD=I(*{z?HI%$M^So|Lt<^Ej)u1-^n?&JAvcxl=HBIR<(;#=^?+#FHYg*c|&c Vu?=)O;!X$hXTWr!QGstOa0m456^#G@ literal 0 HcmV?d00001 diff --git a/hello/static/.DS_Store b/hello/static/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 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 Nn4z7h4t<;Z{{ZTCk8J<| literal 0 HcmV?d00001 diff --git a/hello/static/lang-logo.png b/hello/static/lang-logo.png deleted file mode 100644 index f04aff1e38eeaeadba93390eb5cd356388b8a9f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2217 zcmaJ@c~}!?8V{ES5J5Ras&x#4Sjb5@5(!5xzz~)VpeP8&WPm)7Og4-ru!tat1eHTn zltSYHA_!`w93BV~D@C_zU8Rby1Vxl08WH5yPAJ%Y_HjEi-+bTu&ii}c-+TNq%8+1x zGZQ-#6bfY)7{KHqyHK~#^N_dn*`Y9GTMDxx;7}+Qmh)vG%1;Pw1%W^@KMv%9d|~oG zIzTTJ%D_n!76C_agQx;XjN|KUa0;;$L8DM!J_;#ckO0EKRxnN^p<&)$ZovQ|Aq}&^ zjZ5H48DP99AVmg-rUZuxQW69dA;!lW@KR6_2VxNB0}64XB#Ek^VP^bNk+V*X#{e@X zZ~_hU2dM~d2*7}3AmD~`#|j8UGT=eM5#8K9C>|~V36b#xPdtf$B~Yktu2cdEn7uGa zG?_4#%44!;V<8d^6A!~uDjqMF%W-m793+dw6Dbr5oq!3gUTS60}VwK>87DKLmkb!&{l7&G~ z;#~Ja;vpDHiie~CgE7-Bz=_Kjh$K4Y8eM^B`GPXhc2LNcL1JL0LR8TgSg;u+7K!EI z%JQQSXEGoW=nOW6@2aP|oS1Q;s&z#P{JJq*ux-`bs~p_~-7+ zYD!+5s{VEjH2F#WiNE5*bmM5(KqPXLZ^hPSoB02X&hmBAUulu+i_XGW=pRQ!#gfm* z*)PnO7uCx(dwTXw589+YDDm~GGV?M_`})m%A0wZI@=NXRjXkbfQIn1L0w{Cms)F9? z?C#8%-OkGg-=n9zuYBj5>zv#7@T(0y{kG1Uj)olj0yfz=dQ_hhdra?KGa>CYUyAPdqmBD`~BUt_i4qwX765iYW=nKiAU_9J9Q0(BfV3V zV^?FYb{F8ZquO*UtG$cOu@`FRbI6?+HmSQS4B1ht)@C>T@rTUjG*PItqQM=w_mW2W zv8OGfsm>9w6c<+bHClECx5w)bT{uzMj9ZVzvrqnDm^~C5;Lqb25#AheDhs$hl3Muq z=0v_o!KgmeY9Zc*zlL|HxhMYxB|Ns+c4&KQ{M&2yvnFk8^a3CUU+?tMHt1joO&(o4 zx~NKSy6n12d)d7!ZC-R#+tq3^s=v3N-C2CTR_OFymPeba%ltyG=}rS5t9$B~203TE zqAn`WXtrsC$MF7>huqf$hL`mK>#OghuCuPbj{T~CMcu6f=SH#upZ+Uar0U~qouzC2 zf9+x$6!28NFZv{Pw<5LVroXjzpVVX=yr-;tN-sD)&QUNm)>$08^wQv_|G15DBVBeF znQsW$cJ_hAo@I!NKBhm_u-NuQ=2`ixlURs{b*E^Ectmrrg--%)^>abZ(@5_(; z+*?O$8wWUv=Q+j;mZ~3aW)wPP$ye2&he;{J_4#EJznMP@>76JR_I6} zcAeZD`|8mvQTNaXEQfLQ2(iCln3EXXG#I5Y&1h=LD>QDLb}2XQu~oMYA483|uU8wF zD;>2q{WZrn-xyo~ zjZbW-mOahOG~`9BUYE0aNR+F*l>MnFDAnDy!KEyyWe;pXFZJ|wv?|*8qHArJiuo?G zXF$8RPqO-mRd{C5eQOS|{}sjZMf~`>?F9|49b2BXZ|a<`y5ZfN;1!k$svfLtLywy& zIoS&W&K};E%B4;|W?6#tn-*F2D`RR-b;Ol7|F+Sx!oaQ;^COo9cC(uMOrLd!TcBBhA$<`#BgBvnSY=qCk>`Ij*84tM aZ$a%}dZnx*#!~kSf(m2>Gtbhu?D`$|L6nF9 diff --git a/hello/templates/base.html b/hello/templates/base.html index 97ec07386..3e12d3e9a 100644 --- a/hello/templates/base.html +++ b/hello/templates/base.html @@ -12,7 +12,7 @@ padding-bottom: 80px } .jumbotron .btn-primary { - background: #845ac7; + background: white; border-color: #845ac7 } .jumbotron .btn-primary:hover { diff --git a/hello/templates/index.html b/hello/templates/index.html index d456f243d..50fc5da1f 100644 --- a/hello/templates/index.html +++ b/hello/templates/index.html @@ -5,11 +5,11 @@
- +

2poundsmeal

-

This is a sample Python application deployed to Heroku. It's a reasonably simple app - but a good foundation for understanding how to get the most out of the Heroku platform.

+

This is a sample application to recognise food images.

Getting Started with Python Source on GitHub
From 65fae0b35416ddca0baac04d46f55184be867e86 Mon Sep 17 00:00:00 2001 From: Daeun Date: Tue, 12 Jun 2018 14:48:31 +0100 Subject: [PATCH 3/9] .jpg size --- hello/static/2poundsmeal.jpg | Bin 51680 -> 7674 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/hello/static/2poundsmeal.jpg b/hello/static/2poundsmeal.jpg index 567562d4492faaf693c39670483c4165f5a96dd3..71b6e418defb9d3e76cfaf3824317605ef3f4254 100644 GIT binary patch delta 4315 zcmeHKdsI_L9-f;AFWn`)ghr4gf>e$sAv}ag2q^D@JVX(~6iGr7B(TbbR>em> zfT-=-T?KRztSf?ut-Ggy6j5141VpLr5}{CBDKAlj$lbXi;AuUF|LkAP~xgsf<8|@#cH-5nW&J+RT7yLrQt3GLRp52$AF=ANy)`Bl}ss?$>+WQii3Hv zjtK^Be$I_i2&Bq1iBbtYM~jk03XwEX#AC1nKX8rNkSSB7rpJm?6T>)LRa^CD7I+wL zh*YUkWF+DVDS3>dNFmB#L9{GATzoiBoDc1HXz1?{W1$d>@fTv}-OCpFfA<_O6k(D7 zh1hxbvPJ&iJ;w`0Smb{pc77v!Q;LbCu>Q(~g%-eu!HvkdGKS{LWCPC)A>kyvBm|(q z7TFGn%N>JkVWCI>1Y1H1u>4h#QsghSo3(J%Sx^SOZc~5MWbQf7A^kmMz4|}ZHLsSS z*MON3*aC8h2m=tz5TY4^eFyAeMM*|&PJd%#8t|i>tbJ|A!*ePJ0=+c&qGsl|tj&}s^%np|sa@*eg5 z)y|$?+~APV*lls!Q^b;wGu2ty`||f6I9Pc6^Dq8-qU0atXDcetUA$a(ckjRAubMQRRNRXYVYs z;-klA!JR&%hgWl{R*&#n(;UW*IgpS0^f1Tga2B6mdI;?SONqM7&EU@d;?Y(2cJH*j z;Wg=Y#ogmCkYPSf=rdpiU^fy^In1vjNp*9?D^hW83WR^^5C z%G#5X+N*E7?n3LjupVFpIY!6?4D4!MRTg{u4~}uDXVs7WWfM^n_hM~x&ZJkN`^3SMG0@UKh$%u)HSMdX-zH z$BTxlguC5lRE1TYLxtVjYfLa;=r{et`X}#o#1DFkZ91N!lf!52;)d*AZr0vm6-=Fm zXU~;_zz;nF-ry00S9l3x2Iww+QG)cmqSETd(RPYvjK=njOpxammRxMO+dIb~pN@e^ zcHh4b8@2RUC3HRueNlWG1GisOspa&bpsOD{AQ4C6F|h8Lvq#*YLhi9voJc~^>lo11 zmpwYgMO&;Irt7C4WH^nkK|PPSU&^KLr_?t1qFS-6ezKbE{gAz~)qau`u>;l8Z;sa8 zxah|@bJyUcy634z^5s!tl2cc)gL{hPayI=WXJ4dwCI)`0$vQM1f1)r8Mh${lzBy`? zi8gQihqf_a;3Lmg_`GA?X6ilBYNKq?$=bYeHq7t~NCXIh4F7lFSF|w#%;+u@NCz!_ zv?#B%y1h{|_7)rXR#PxA;MelMQ*A>wVbf{+eC?I9eSGR<$2c>u8v_S)tfBbEc>PJ>DhcfL2>*<=q5l3mNZ@x6=*g9}ON^T+fnVRFA_wwTI zYS1dj?nk@gjMvp>^?m(1(uhX0pOK_I!a#F!yM3HdPVyHY2~XyTSbhBI?kY~$DI{vb zSK?y(pup~+;kT8?HTE;xnhl2zjCr7gOTGP@b9emtur~&7UO3jN#lU!Um~nwv6v4A{ z;GQ4r=`!5EH_u|l*r7iXRGw=79p|gY91F-dVhFjYV(J d2nJgB2bfw4IYB;8OI=fgi<>a;GrUO;{|_BFg(v_3 literal 51680 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 Nn4z7h4t<;Z{{ZTCk8J<| From baa5fbd947be2ec08fcbc9e6b6ff1b26e0b4aecf Mon Sep 17 00:00:00 2001 From: Daeun Date: Fri, 22 Jun 2018 19:42:53 +0100 Subject: [PATCH 4/9] visual-recognition-v3 --- .DS_Store | Bin 6148 -> 6148 bytes hello/.DS_Store | Bin 6148 -> 6148 bytes hello/templates/base.html | 2 +- hello/templates/index.html | 4 +- hello/test/test_visual_recognition.py | 64 + hello/unit/test_visual_recognition_v3.py | 250 ++++ hello/visual_recognition_v3.py | 1495 ++++++++++++++++++++++ 7 files changed, 1812 insertions(+), 3 deletions(-) create mode 100644 hello/test/test_visual_recognition.py create mode 100644 hello/unit/test_visual_recognition_v3.py create mode 100644 hello/visual_recognition_v3.py diff --git a/.DS_Store b/.DS_Store index e5c79600d7c2c79113a5470b995e7b02f51d192e..9df7c75b70fff485793f8fef48b83d1e6cffe111 100644 GIT binary patch delta 293 zcmZoMXfc=|#>B`mu~3YagMono$PnRV0y02=b7H`5M$U;kQuQnhB@C$y#SA4m>4w3{ z`MCuU1z-{`mz(e6l2Tfd%)oFx=#v_{TnbLPf($HXmNMiqWWwwVL$eSniDIE8)Izu% wl7(n;Fbg*p@~}-d5MkNO&B4t941|q^-!0zhH1p@{Hi50O300Ei1fg#Z8m delta 78 zcmZoMXfc=|#>AjHu~3+iaWW616c+;%0|YQm4A{L{fayCckYHup%+A5j0aUn|k?A}0 ZWPTAvPDY@z1d!6nCOpcUV?B5c?3>v+{_+C= DSl

2poundsmeal

This is a sample application to recognise food images.

- Getting Started with Python - Source on GitHub + Getting Started with Python + Source on GitHub
diff --git a/hello/test/test_visual_recognition.py b/hello/test/test_visual_recognition.py new file mode 100644 index 000000000..529624bfb --- /dev/null +++ b/hello/test/test_visual_recognition.py @@ -0,0 +1,64 @@ +# coding: utf-8 +import pytest +import watson_developer_cloud +import os +from os.path import join, dirname +from unittest import TestCase +import json + +@pytest.mark.skipif( + os.getenv('VCAP_SERVICES') is None, reason='requires VCAP_SERVICES') +class IntegrationTestVisualRecognitionV3(TestCase): + visual_recognition = None + classifier_id = None + + @classmethod + def setup_class(cls): + cls.visual_recognition = watson_developer_cloud.VisualRecognitionV3( + '2016-05-20', api_key="5578277a486a2c548a852ad0e6a935f91f7fb9cf") + cls.visual_recognition.set_default_headers({ + 'X-Watson-Learning-Opt-Out': + '1', + 'X-Watson-Test': + '1' + }) + cls.classifier_id = 'doxnotxdeletexintegrationxtest_397877192' + + def test_classify(self): + dog_path = join(dirname(__file__), '../../resources/dog.jpg') + with open(dog_path, 'rb') as image_file: + dog_results = self.visual_recognition.classify( + images_file=image_file, + threshold='0.1', + classifier_ids=['default']) + assert dog_results is not None + + def test_detect_faces(self): + output = self.visual_recognition.detect_faces( + parameters=json.dumps({ + 'url': + 'https://www.ibm.com/ibm/ginni/images/ginni_bio_780x981_v4_03162016.jpg' + })) + assert output is not None + + @pytest.mark.skip(reason="Time consuming") + def test_custom_classifier(self): + with open(os.path.join(os.path.dirname(__file__), '../../resources/cars.zip'), 'rb') as cars, \ + open(os.path.join(os.path.dirname(__file__), '../../resources/trucks.zip'), 'rb') as trucks: + classifier = self.visual_recognition.create_classifier( + 'CarsVsTrucks', + cars_positive_examples=cars, + negative_examples=trucks, + ) + + assert classifier is not None + + classifier_id = classifier['classifier_id'] + output = self.visual_recognition.get_classifier(classifier_id) + assert output is not None + + output = self.visual_recognition.delete_classifier(classifier_id) + + def test_core_ml_model(self): + core_ml_model = self.visual_recognition.get_core_ml_model(self.classifier_id) + assert core_ml_model.ok diff --git a/hello/unit/test_visual_recognition_v3.py b/hello/unit/test_visual_recognition_v3.py new file mode 100644 index 000000000..77714cf36 --- /dev/null +++ b/hello/unit/test_visual_recognition_v3.py @@ -0,0 +1,250 @@ +# coding: utf-8 +import responses +import watson_developer_cloud +import json +import os + +from unittest import TestCase + +base_url = "https://gateway-a.watsonplatform.net/visual-recognition/api/" + +class TestVisualRecognitionV3(TestCase): + @responses.activate + def test_get_classifier(self): + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + + gc_url = "{0}{1}".format(base_url, 'v3/classifiers/bogusnumber') + + response = { + "classifier_id": "bogusnumber", + "name": "Dog Breeds", + "owner": "58b61352-678c-44d1-9f40-40edf4ea8d19", + "status": "failed", + "created": "2017-08-25T06:39:01.968Z", + "classes": [{"class": "goldenretriever"}] + } + + responses.add(responses.GET, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + vr_service.get_classifier(classifier_id='bogusnumber') + + assert len(responses.calls) == 1 + + @responses.activate + def test_delete_classifier(self): + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + + gc_url = "{0}{1}".format(base_url, 'v3/classifiers/bogusnumber') + + responses.add(responses.DELETE, + gc_url, + body=json.dumps({'response': 200}), + status=200, + content_type='application/json') + vr_service.delete_classifier(classifier_id='bogusnumber') + + assert len(responses.calls) == 1 + + @responses.activate + def test_list_classifiers(self): + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + + gc_url = "{0}{1}".format(base_url, 'v3/classifiers') + + response = {"classifiers": [ + { + "classifier_id": "InsuranceClaims_1362331461", + "name": "Insurance Claims", + "status": "ready" + }, + { + "classifier_id": "DogBreeds_1539707331", + "name": "Dog Breeds", + "status": "ready" + } + ]} + + responses.add(responses.GET, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + vr_service.list_classifiers() + + assert len(responses.calls) == 1 + + @responses.activate + def test_create_classifier(self): + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + + gc_url = "{0}{1}".format(base_url, 'v3/classifiers') + + response = { + "classifier_id": "DogBreeds_2014254824", + "name": "Dog Breeds", + "owner": "58b61352-678c-44d1-9f40-40edf4ea8d19", + "status": "failed", + "created": "2017-08-25T06:39:01.968Z", + "classes": [{"class": "goldenretriever"}] + } + + responses.add(responses.POST, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + + with open(os.path.join(os.path.dirname(__file__), '../../resources/cars.zip'), 'rb') as cars, \ + open(os.path.join(os.path.dirname(__file__), '../../resources/trucks.zip'), 'rb') as trucks: + vr_service.create_classifier('Cars vs Trucks', cars_positive_examples=cars, negative_examples=trucks) + + assert len(responses.calls) == 1 + + @responses.activate + def test_update_classifier(self): + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + + gc_url = "{0}{1}".format(base_url, 'v3/classifiers/bogusid') + + response = { + "classifier_id": "bogusid", + "name": "Insurance Claims", + "owner": "58b61352-678c-44d1-9f40-40edf4ea8d19", + "status": "ready", + "created": "2017-07-17T22:17:14.860Z", + "classes": [ + {"class": "motorcycleaccident"}, + {"class": "flattire"}, + {"class": "brokenwinshield"} + ] + } + + responses.add(responses.POST, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + + vr_service.update_classifier(classifier_id="bogusid") + assert len(responses.calls) == 1 + + @responses.activate + def test_classify(self): + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + + gc_url = "{0}{1}".format(base_url, 'v3/classify') + + response = {"images": [ + {"image": "test.jpg", + "classifiers": [ + {"classes": [ + {"score": 0.95, "class": "tiger", "type_hierarchy": "/animal/mammal/carnivore/feline/big cat/tiger"}, + {"score": 0.997, "class": "big cat"}, + {"score": 0.998, "class": "feline"}, + {"score": 0.998, "class": "carnivore"}, + {"score": 0.998, "class": "mammal"}, + {"score": 0.999, "class": "animal"} + ], + "classifier_id": "default", + "name": "default"} + ] + } + ], + "custom_classes": 0, + "images_processed": 1 + } + + responses.add(responses.GET, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + responses.add(responses.POST, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + + vr_service.classify(parameters='{"url": "http://google.com"}') + + vr_service.classify(parameters=json.dumps({'url': 'http://google.com', 'classifier_ids': ['one', 'two', 'three']})) + vr_service.classify(parameters=json.dumps({'url': 'http://google.com', 'owners': ['me', 'IBM']})) + + with open(os.path.join(os.path.dirname(__file__), '../../resources/test.jpg'), 'rb') as image_file: + vr_service.classify(images_file=image_file) + assert len(responses.calls) == 4 + + @responses.activate + def test_detect_faces(self): + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + + gc_url = "{0}{1}".format(base_url, 'v3/detect_faces') + + response = { + "images": [ + { + "faces": [ + { + "age": { + "max": 44, + "min": 35, + "score": 0.446989 + }, + "face_location": { + "height": 159, + "left": 256, + "top": 64, + "width": 92 + }, + "gender": { + "gender": "MALE", + "score": 0.99593 + }, + "identity": { + "name": "Barack Obama", + "score": 0.970688, + "type_hierarchy": "/people/politicians/democrats/barack obama" + } + } + ], + "resolved_url": "https://watson-developer-cloud.github.io/doc-tutorial-downloads/visual-recognition/prez.jpg", + "source_url": "https://watson-developer-cloud.github.io/doc-tutorial-downloads/visual-recognition/prez.jpg" + } + ], + "images_processed": 1 + } + + responses.add(responses.GET, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + + responses.add(responses.POST, + gc_url, + body=json.dumps(response), + status=200, + content_type='application/json') + + vr_service.detect_faces(parameters='{"url": "http://google.com"}') + with open(os.path.join(os.path.dirname(__file__), '../../resources/test.jpg'), 'rb') as image_file: + vr_service.detect_faces(images_file=image_file) + assert len(responses.calls) == 2 + +@responses.activate +def test_delete_user_data(): + url = "{0}{1}".format(base_url, 'v3/user_data') + responses.add( + responses.DELETE, + url, + body='{"description": "success" }', + status=200, + content_type='application_json') + + vr_service = watson_developer_cloud.VisualRecognitionV3('2016-10-20', api_key='bogusapikey') + response = vr_service.delete_user_data('id') + assert response is None + assert len(responses.calls) == 1 diff --git a/hello/visual_recognition_v3.py b/hello/visual_recognition_v3.py new file mode 100644 index 000000000..85271a983 --- /dev/null +++ b/hello/visual_recognition_v3.py @@ -0,0 +1,1495 @@ +# coding: utf-8 + +# Copyright 2018 IBM All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +The IBM Watson Visual Recognition service uses deep learning algorithms to identify +scenes, objects, and faces in images you upload to the service. You can create and train +a custom classifier to identify subjects that suit your needs. +""" + +from __future__ import absolute_import + +import json +from .watson_service import datetime_to_string, string_to_datetime +from .watson_service import WatsonService + +############################################################################## +# Service +############################################################################## + + +class VisualRecognitionV3(WatsonService): + """The Visual Recognition V3 service.""" + + default_url = 'https://gateway.watsonplatform.net/visual-recognition/api' + + def __init__( + self, + version, + url="https://gateway-a.watsonplatform.net/visual-recognition/api", + api_key="5578277a486a2c548a852ad0e6a935f91f7fb9cf", + iam_api_key=None, + iam_access_token=None, + iam_url=None, + ): + """ + Construct a new client for the Visual Recognition service. + + :param str version: The API version date to use with the service, in + "YYYY-MM-DD" format. Whenever the API is changed in a backwards + incompatible way, a new minor version of the API is released. + The service uses the API version for the date you specify, or + the most recent version before that date. Note that you should + not programmatically specify the current date at runtime, in + case the API has been updated since your application's release. + Instead, specify a version date that is compatible with your + application, and don't change it until your application is + ready for a later version. + + :param str url: The base url to use when contacting the service (e.g. + "https://gateway.watsonplatform.net/visual-recognition/api"). + The base url may differ between Bluemix regions. + + :param str api_key: The API Key used to authenticate. + + :param str iam_api_key: An API key that can be used to request IAM tokens. If + this API key is provided, the SDK will manage the token and handle the + refreshing. + + :param str iam_access_token: An IAM access token is fully managed by the application. + Responsibility falls on the application to refresh the token, either before + it expires or reactively upon receiving a 401 from the service as any requests + made with an expired token will fail. + + :param str iam_url: An optional URL for the IAM service API. Defaults to + 'https://iam.ng.bluemix.net/identity/token'. + """ + + WatsonService.__init__( + self, + vcap_services_name='watson_vision_combined', + url="https://gateway-a.watsonplatform.net/visual-recognition/api", + api_key="5578277a486a2c548a852ad0e6a935f91f7fb9cf", + iam_api_key=iam_api_key, + iam_access_token=iam_access_token, + iam_url=iam_url, + use_vcap_services=True) + self.version = version + + ######################### + # General + ######################### + + def classify(self, + images_file=None, + parameters=None, + accept_language=None, + images_file_content_type=None, + images_filename=None, + url=None, + threshold=None, + owners=None, + classifier_ids=None, + **kwargs): + """ + Classify images. + + Classify images with built-in or custom classifiers. + + :param file images_file: An image file (.jpg, .png) or .zip file with images. Maximum image size is 10 MB. Include no more than 20 images and limit the .zip file to 100 MB. Encode the image and .zip file names in UTF-8 if they contain non-ASCII characters. The service assumes UTF-8 encoding if it encounters non-ASCII characters. You can also include images with the `url` property in the **parameters** object. + :param str parameters: (Deprecated) A JSON object that specifies additional request options. The parameter can be sent as a string or a file, and can include these inputs: - **url**: A string with the image URL to analyze. Must be in .jpg, or .png format. The minimum recommended pixel density is 32X32 pixels per inch, and the maximum image size is 10 MB. You can also include images in the **images_file** parameter. - **threshold**: A floating point value that specifies the minimum score a class must have to be displayed in the response. The default threshold for returning scores from a classifier is `0.5`. Set the threshold to `0.0` to ignore the classification score and return all values. - **owners**: An array of the categories of classifiers to apply. Use `IBM` to classify against the `default` general classifier, and use `me` to classify against your custom classifiers. To analyze the image against both classifier categories, set the value to both `IBM` and `me`. The built-in `default` classifier is used if both **classifier_ids** and **owners** parameters are empty. The **classifier_ids** parameter overrides **owners**, so make sure that **classifier_ids** is empty. - **classifier_ids**: Specifies which classifiers to apply and overrides the **owners** parameter. You can specify both custom and built-in classifiers. The built-in `default` classifier is used if both **classifier_ids** and **owners** parameters are empty. The following built-in classifier IDs require no training: - `default`: Returns classes from thousands of general tags. - `food`: (Beta) Enhances specificity and accuracy for images of food items. - `explicit`: (Beta) Evaluates whether the image might be pornographic. Example: `{\"classifier_ids\":[\"CarsvsTrucks_1479118188\",\"explicit\"],\"threshold\":0.6}`. + :param str accept_language: Specifies the language of the output class names. Can be `en` (English), `ar` (Arabic), `de` (German), `es` (Spanish), `it` (Italian), `ja` (Japanese), or `ko` (Korean). Classes for which no translation is available are omitted. The response might not be in the specified language under these conditions: - English is returned when the requested language is not supported. - Classes are not returned when there is no translation for them. - Custom classifiers returned with this method return tags in the language of the custom classifier. + :param str images_file_content_type: The content type of images_file. + :param str images_filename: The filename for images_file. + :param str url: A string with the image URL to analyze. Must be in .jpg, or .png format. The minimum recommended pixel density is 32X32 pixels per inch, and the maximum image size is 10 MB. You can also include images in the **images_file** parameter. + :param float threshold: A floating point value that specifies the minimum score a class must have to be displayed in the response. The default threshold for returning scores from a classifier is `0.5`. Set the threshold to `0.0` to ignore the classification score and return all values. + :param list[str] owners: An array of the categories of classifiers to apply. Use `IBM` to classify against the `default` general classifier, and use `me` to classify against your custom classifiers. To analyze the image against both classifier categories, set the value to both `IBM` and `me`. The built-in `default` classifier is used if both **classifier_ids** and **owners** parameters are empty. The **classifier_ids** parameter overrides **owners**, so make sure that **classifier_ids** is empty. + :param list[str] classifier_ids: The **classifier_ids** parameter overrides **owners**, so make sure that **classifier_ids** is empty. - **classifier_ids**: Specifies which classifiers to apply and overrides the **owners** parameter. You can specify both custom and built-in classifiers. The built-in `default` classifier is used if both **classifier_ids** and **owners** parameters are empty. The following built-in classifier IDs require no training: - `default`: Returns classes from thousands of general tags. - `food`: (Beta) Enhances specificity and accuracy for images of food items. - `explicit`: (Beta) Evaluates whether the image might be pornographic. Example: `\"classifier_ids=\"CarsvsTrucks_1479118188\",\"explicit\"`. + :param dict headers: A `dict` containing the request headers + :return: A `dict` containing the `ClassifiedImages` response. + :rtype: dict + """ + headers = {'Accept-Language': accept_language} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version} + images_file_tuple = None + if images_file: + if not images_filename and hasattr(images_file, 'name'): + images_filename = images_file.name + mime_type = images_file_content_type or 'application/octet-stream' + images_file_tuple = (images_filename, images_file, mime_type) + + parameters_tuple = None + if parameters is not None: + parameters_tuple = (None, parameters, 'text/plain') + + url_tuple = None + if url: + url_tuple = (None, url, 'text/plain') + threshold_tuple = None + if threshold: + threshold_tuple = (None, threshold, 'application/json') + owners_tuple = None + if owners: + if isinstance(owners, (list,)): + owners = ','.join(owners) + owners_tuple = (None, owners, 'application/json') + classifier_ids_tuple = None + if classifier_ids: + if isinstance(classifier_ids, (list,)): + classifier_ids = ','.join(classifier_ids) + classifier_ids_tuple = (None, classifier_ids, 'application/json') + url = '/v3/classify' + response = self.request( + method='POST', + url=url, + headers=headers, + params=params, + files={ + 'parameters': parameters_tuple, + 'images_file': images_file_tuple, + 'url': url_tuple, + 'threshold': threshold_tuple, + 'owners': owners_tuple, + 'classifier_ids': classifier_ids_tuple + }, + accept_json=True) + return response + + ######################### + # Face + ######################### + + def detect_faces(self, + images_file=None, + parameters=None, + images_file_content_type=None, + images_filename=None, + url=None, + **kwargs): + """ + Detect faces in images. + + Analyze and get data about faces in images. Responses can include estimated age + and gender, and the service can identify celebrities. This feature uses a built-in + classifier, so you do not train it on custom classifiers. The Detect faces method + does not support general biometric facial recognition. + + :param file images_file: An image file (.jpg, .png) or .zip file with images. Include no more than 15 images. You can also include images with the `url` property in the **parameters** object. All faces are detected, but if there are more than 10 faces in an image, age and gender confidence scores might return scores of 0. + :param str parameters: (Deprecated) A JSON object that specifies a single image (.jpg, .png) to analyze by URL. The parameter can be sent as a string or a file. Example: `{\"url\":\"http://www.example.com/images/myimage.jpg\"}`. + :param str images_file_content_type: The content type of images_file. + :param str images_filename: The filename for images_file. + :param str url: The URL of an image to analyze. Must be in .gif, .jpg, .png, or .tif format. The minimum recommended pixel density is 32X32 pixels per inch, and the maximum image size is 10 MB. Redirects are followed, so you can use a shortened URL. You can also include images with the **images_file** parameter. + :param dict headers: A `dict` containing the request headers + :return: A `dict` containing the `DetectedFaces` response. + :rtype: dict + """ + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version} + images_file_tuple = None + if images_file: + if not images_filename and hasattr(images_file, 'name'): + images_filename = images_file.name + mime_type = images_file_content_type or 'application/octet-stream' + images_file_tuple = (images_filename, images_file, mime_type) + parameters_tuple = None + if parameters is not None: + parameters_tuple = (None, parameters, 'text/plain') + url_tuple = None + if url: + url_tuple = (None, url, 'text/plain') + url = '/v3/detect_faces' + response = self.request( + method='POST', + url=url, + headers=headers, + params=params, + files={'images_file': images_file_tuple, + 'parameters': parameters_tuple, + 'url': url_tuple}, + accept_json=True) + return response + + ######################### + # Custom + ######################### + + def create_classifier(self, + name, + **kwargs): + """ + Create a classifier. + :param str name: The name of the new classifier. Encode special characters in UTF-8. + :param file _positive_examples: A compressed (.zip) file of images that depict the visual subject for a class within the new classifier. Must contain a minimum of 10 images. The swagger limits you to training only one class. To train more classes, use the API functionality. + :param file negative_examples: A compressed (.zip) file of images that do not depict the visual subject of any of the classes of the new classifier. Must contain a minimum of 10 images. + :param dict headers: A `dict` containing the request headers + :return: A `dict` containing the `Classifier` response. + :rtype: dict + """ + if name is None: + raise ValueError('name must be provided') + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version} + data = {'name': name} + url = '/v3/classifiers' + response = self.request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + files=kwargs, + accept_json=True) + return response + + def delete_classifier(self, classifier_id, **kwargs): + """ + Delete a classifier. + + :param str classifier_id: The ID of the classifier. + :param dict headers: A `dict` containing the request headers + :rtype: None + """ + if classifier_id is None: + raise ValueError('classifier_id must be provided') + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version} + url = '/v3/classifiers/{0}'.format( + *self._encode_path_vars(classifier_id)) + self.request( + method='DELETE', + url=url, + headers=headers, + params=params, + accept_json=True) + return None + + def get_classifier(self, classifier_id, **kwargs): + """ + Retrieve classifier details. + + Retrieve information about a custom classifier. + + :param str classifier_id: The ID of the classifier. + :param dict headers: A `dict` containing the request headers + :return: A `dict` containing the `Classifier` response. + :rtype: dict + """ + if classifier_id is None: + raise ValueError('classifier_id must be provided') + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version} + url = '/v3/classifiers/{0}'.format( + *self._encode_path_vars(classifier_id)) + response = self.request( + method='GET', + url=url, + headers=headers, + params=params, + accept_json=True) + return response + + def list_classifiers(self, verbose=None, **kwargs): + """ + Retrieve a list of classifiers. + + :param bool verbose: Specify `true` to return details about the classifiers. Omit this parameter to return a brief list of classifiers. + :param dict headers: A `dict` containing the request headers + :return: A `dict` containing the `Classifiers` response. + :rtype: dict + """ + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version, 'verbose': verbose} + url = '/v3/classifiers' + response = self.request( + method='GET', + url=url, + headers=headers, + params=params, + accept_json=True) + return response + + def update_classifier(self, + classifier_id, + **kwargs): + """ + Update a classifier. + :param str classifier_id: The ID of the classifier. + :param file _positive_examples: A compressed (.zip) file of images that depict the visual subject for a class within the classifier. Must contain a minimum of 10 images. + :param file negative_examples: A compressed (.zip) file of images that do not depict the visual subject of any of the classes of the new classifier. Must contain a minimum of 10 images. + :param dict headers: A `dict` containing the request headers + :return: A `dict` containing the `Classifier` response. + :rtype: dict + """ + if classifier_id is None: + raise ValueError('classifier_id must be provided') + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version} + url = '/v3/classifiers/{0}'.format( + *self._encode_path_vars(classifier_id)) + response = self.request( + method='POST', + url=url, + headers=headers, + params=params, + files=kwargs, + accept_json=True) + return response + + ######################### + # Core ML + ######################### + + def get_core_ml_model(self, classifier_id, **kwargs): + """ + Retrieve a Core ML model of a classifier. + + Download a Core ML model file (.mlmodel) of a custom classifier that returns + \"core_ml_enabled\": true in the classifier details. + + :param str classifier_id: The ID of the classifier. + :param dict headers: A `dict` containing the request headers + :return: A `Response ` object representing the response. + :rtype: requests.models.Response + """ + if classifier_id is None: + raise ValueError('classifier_id must be provided') + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version} + url = '/v3/classifiers/{0}/core_ml_model'.format( + *self._encode_path_vars(classifier_id)) + response = self.request( + method='GET', + url=url, + headers=headers, + params=params, + accept_json=False) + return response + + ######################### + # User data + ######################### + + def delete_user_data(self, customer_id, **kwargs): + """ + Delete labeled data. + + Deletes all data associated with a specified customer ID. The method has no effect + if no data is associated with the customer ID. You associate a customer ID with + data by passing the `X-Watson-Metadata` header with a request that passes data. + For more information about personal data and customer IDs, see [Information + security](https://console.bluemix.net/docs/services/visual-recognition/information-security.html). + + :param str customer_id: The customer ID for which all data is to be deleted. + :param dict headers: A `dict` containing the request headers + :rtype: None + """ + if customer_id is None: + raise ValueError('customer_id must be provided') + headers = {} + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + params = {'version': self.version, 'customer_id': customer_id} + url = '/v3/user_data' + self.request( + method='DELETE', + url=url, + headers=headers, + params=params, + accept_json=True) + return None + + +############################################################################## +# Models +############################################################################## + + +class Class(object): + """ + A category within a classifier. + + :attr str class_name: The name of the class. + """ + + def __init__(self, class_name): + """ + Initialize a Class object. + + :param str class_name: The name of the class. + """ + self.class_name = class_name + + @classmethod + def _from_dict(cls, _dict): + """Initialize a Class object from a json dictionary.""" + args = {} + if 'class' in _dict or 'class_name' in _dict: + args['class_name'] = _dict.get('class') or _dict.get('class_name') + else: + raise ValueError( + 'Required property \'class\' not present in Class JSON') + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'class_name') and self.class_name is not None: + _dict['class'] = self.class_name + return _dict + + def __str__(self): + """Return a `str` version of this Class object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class ClassResult(object): + """ + Result of a class within a classifier. + + :attr str class_name: Name of the class. + :attr float score: (optional) Confidence score for the property in the range of 0 to 1. A higher score indicates greater likelihood that the class is depicted in the image. The default threshold for returning scores from a classifier is 0.5. + :attr str type_hierarchy: (optional) Knowledge graph of the property. For example, `/fruit/pome/apple/eating apple/Granny Smith`. Included only if identified. + """ + + def __init__(self, class_name, score=None, type_hierarchy=None): + """ + Initialize a ClassResult object. + + :param str class_name: Name of the class. + :param float score: (optional) Confidence score for the property in the range of 0 to 1. A higher score indicates greater likelihood that the class is depicted in the image. The default threshold for returning scores from a classifier is 0.5. + :param str type_hierarchy: (optional) Knowledge graph of the property. For example, `/fruit/pome/apple/eating apple/Granny Smith`. Included only if identified. + """ + self.class_name = class_name + self.score = score + self.type_hierarchy = type_hierarchy + + @classmethod + def _from_dict(cls, _dict): + """Initialize a ClassResult object from a json dictionary.""" + args = {} + if 'class' in _dict or 'class_name' in _dict: + args['class_name'] = _dict.get('class') or _dict.get('class_name') + else: + raise ValueError( + 'Required property \'class\' not present in ClassResult JSON') + if 'score' in _dict: + args['score'] = _dict.get('score') + if 'type_hierarchy' in _dict: + args['type_hierarchy'] = _dict.get('type_hierarchy') + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'class_name') and self.class_name is not None: + _dict['class'] = self.class_name + if hasattr(self, 'score') and self.score is not None: + _dict['score'] = self.score + if hasattr(self, 'type_hierarchy') and self.type_hierarchy is not None: + _dict['type_hierarchy'] = self.type_hierarchy + return _dict + + def __str__(self): + """Return a `str` version of this ClassResult object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class ClassifiedImage(object): + """ + Results for one image. + + :attr str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. + :attr str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. + :attr str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. + :attr ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. + :attr list[ClassifierResult] classifiers: The classifiers. + """ + + def __init__(self, + classifiers, + source_url=None, + resolved_url=None, + image=None, + error=None): + """ + Initialize a ClassifiedImage object. + + :param list[ClassifierResult] classifiers: The classifiers. + :param str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. + :param str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. + :param str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. + :param ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. + """ + self.source_url = source_url + self.resolved_url = resolved_url + self.image = image + self.error = error + self.classifiers = classifiers + + @classmethod + def _from_dict(cls, _dict): + """Initialize a ClassifiedImage object from a json dictionary.""" + args = {} + if 'source_url' in _dict: + args['source_url'] = _dict.get('source_url') + if 'resolved_url' in _dict: + args['resolved_url'] = _dict.get('resolved_url') + if 'image' in _dict: + args['image'] = _dict.get('image') + if 'error' in _dict: + args['error'] = ErrorInfo._from_dict(_dict.get('error')) + if 'classifiers' in _dict: + args['classifiers'] = [ + ClassifierResult._from_dict(x) + for x in (_dict.get('classifiers')) + ] + else: + raise ValueError( + 'Required property \'classifiers\' not present in ClassifiedImage JSON' + ) + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'source_url') and self.source_url is not None: + _dict['source_url'] = self.source_url + if hasattr(self, 'resolved_url') and self.resolved_url is not None: + _dict['resolved_url'] = self.resolved_url + if hasattr(self, 'image') and self.image is not None: + _dict['image'] = self.image + if hasattr(self, 'error') and self.error is not None: + _dict['error'] = self.error._to_dict() + if hasattr(self, 'classifiers') and self.classifiers is not None: + _dict['classifiers'] = [x._to_dict() for x in self.classifiers] + return _dict + + def __str__(self): + """Return a `str` version of this ClassifiedImage object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class ClassifiedImages(object): + """ + Results for all images. + + :attr int custom_classes: (optional) Number of custom classes identified in the images. + :attr int images_processed: (optional) Number of images processed for the API call. + :attr list[ClassifiedImage] images: Classified images. + :attr list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. + """ + + def __init__(self, + images, + custom_classes=None, + images_processed=None, + warnings=None): + """ + Initialize a ClassifiedImages object. + + :param list[ClassifiedImage] images: Classified images. + :param int custom_classes: (optional) Number of custom classes identified in the images. + :param int images_processed: (optional) Number of images processed for the API call. + :param list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. + """ + self.custom_classes = custom_classes + self.images_processed = images_processed + self.images = images + self.warnings = warnings + + @classmethod + def _from_dict(cls, _dict): + """Initialize a ClassifiedImages object from a json dictionary.""" + args = {} + if 'custom_classes' in _dict: + args['custom_classes'] = _dict.get('custom_classes') + if 'images_processed' in _dict: + args['images_processed'] = _dict.get('images_processed') + if 'images' in _dict: + args['images'] = [ + ClassifiedImage._from_dict(x) for x in (_dict.get('images')) + ] + else: + raise ValueError( + 'Required property \'images\' not present in ClassifiedImages JSON' + ) + if 'warnings' in _dict: + args['warnings'] = [ + WarningInfo._from_dict(x) for x in (_dict.get('warnings')) + ] + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'custom_classes') and self.custom_classes is not None: + _dict['custom_classes'] = self.custom_classes + if hasattr(self, + 'images_processed') and self.images_processed is not None: + _dict['images_processed'] = self.images_processed + if hasattr(self, 'images') and self.images is not None: + _dict['images'] = [x._to_dict() for x in self.images] + if hasattr(self, 'warnings') and self.warnings is not None: + _dict['warnings'] = [x._to_dict() for x in self.warnings] + return _dict + + def __str__(self): + """Return a `str` version of this ClassifiedImages object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class Classifier(object): + """ + Information about a classifier. + + :attr str classifier_id: ID of a classifier identified in the image. + :attr str name: Name of the classifier. + :attr str owner: (optional) Unique ID of the account who owns the classifier. Returned when verbose=`true`. Might not be returned by some requests. + :attr str status: (optional) Training status of classifier. + :attr bool core_ml_enabled: Whether the classifier can be downloaded as a Core ML model after the training status is `ready`. + :attr str explanation: (optional) If classifier training has failed, this field may explain why. + :attr datetime created: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was created. + :attr list[Class] classes: (optional) Classes that define a classifier. + :attr datetime retrained: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was updated. Returned when verbose=`true`. Might not be returned by some requests. Identical to `updated` and retained for backward compatibility. + :attr datetime updated: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was most recently updated. The field matches either `retrained` or `created`. Returned when verbose=`true`. Might not be returned by some requests. + """ + + def __init__(self, + classifier_id, + name, + core_ml_enabled, + owner=None, + status=None, + explanation=None, + created=None, + classes=None, + retrained=None, + updated=None): + """ + Initialize a Classifier object. + + :param str classifier_id: ID of a classifier identified in the image. + :param str name: Name of the classifier. + :param bool core_ml_enabled: Whether the classifier can be downloaded as a Core ML model after the training status is `ready`. + :param str owner: (optional) Unique ID of the account who owns the classifier. Returned when verbose=`true`. Might not be returned by some requests. + :param str status: (optional) Training status of classifier. + :param str explanation: (optional) If classifier training has failed, this field may explain why. + :param datetime created: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was created. + :param list[Class] classes: (optional) Classes that define a classifier. + :param datetime retrained: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was updated. Returned when verbose=`true`. Might not be returned by some requests. Identical to `updated` and retained for backward compatibility. + :param datetime updated: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was most recently updated. The field matches either `retrained` or `created`. Returned when verbose=`true`. Might not be returned by some requests. + """ + self.classifier_id = classifier_id + self.name = name + self.owner = owner + self.status = status + self.core_ml_enabled = core_ml_enabled + self.explanation = explanation + self.created = created + self.classes = classes + self.retrained = retrained + self.updated = updated + + @classmethod + def _from_dict(cls, _dict): + """Initialize a Classifier object from a json dictionary.""" + args = {} + if 'classifier_id' in _dict: + args['classifier_id'] = _dict.get('classifier_id') + else: + raise ValueError( + 'Required property \'classifier_id\' not present in Classifier JSON' + ) + if 'name' in _dict: + args['name'] = _dict.get('name') + else: + raise ValueError( + 'Required property \'name\' not present in Classifier JSON') + if 'owner' in _dict: + args['owner'] = _dict.get('owner') + if 'status' in _dict: + args['status'] = _dict.get('status') + if 'core_ml_enabled' in _dict: + args['core_ml_enabled'] = _dict.get('core_ml_enabled') + else: + raise ValueError( + 'Required property \'core_ml_enabled\' not present in Classifier JSON' + ) + if 'explanation' in _dict: + args['explanation'] = _dict.get('explanation') + if 'created' in _dict: + args['created'] = string_to_datetime(_dict.get('created')) + if 'classes' in _dict: + args['classes'] = [ + Class._from_dict(x) for x in (_dict.get('classes')) + ] + if 'retrained' in _dict: + args['retrained'] = string_to_datetime(_dict.get('retrained')) + if 'updated' in _dict: + args['updated'] = string_to_datetime(_dict.get('updated')) + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'classifier_id') and self.classifier_id is not None: + _dict['classifier_id'] = self.classifier_id + if hasattr(self, 'name') and self.name is not None: + _dict['name'] = self.name + if hasattr(self, 'owner') and self.owner is not None: + _dict['owner'] = self.owner + if hasattr(self, 'status') and self.status is not None: + _dict['status'] = self.status + if hasattr(self, + 'core_ml_enabled') and self.core_ml_enabled is not None: + _dict['core_ml_enabled'] = self.core_ml_enabled + if hasattr(self, 'explanation') and self.explanation is not None: + _dict['explanation'] = self.explanation + if hasattr(self, 'created') and self.created is not None: + _dict['created'] = datetime_to_string(self.created) + if hasattr(self, 'classes') and self.classes is not None: + _dict['classes'] = [x._to_dict() for x in self.classes] + if hasattr(self, 'retrained') and self.retrained is not None: + _dict['retrained'] = datetime_to_string(self.retrained) + if hasattr(self, 'updated') and self.updated is not None: + _dict['updated'] = datetime_to_string(self.updated) + return _dict + + def __str__(self): + """Return a `str` version of this Classifier object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class ClassifierResult(object): + """ + Classifier and score combination. + + :attr str name: Name of the classifier. + :attr str classifier_id: ID of a classifier identified in the image. + :attr list[ClassResult] classes: Classes within the classifier. + """ + + def __init__(self, name, classifier_id, classes): + """ + Initialize a ClassifierResult object. + + :param str name: Name of the classifier. + :param str classifier_id: ID of a classifier identified in the image. + :param list[ClassResult] classes: Classes within the classifier. + """ + self.name = name + self.classifier_id = classifier_id + self.classes = classes + + @classmethod + def _from_dict(cls, _dict): + """Initialize a ClassifierResult object from a json dictionary.""" + args = {} + if 'name' in _dict: + args['name'] = _dict.get('name') + else: + raise ValueError( + 'Required property \'name\' not present in ClassifierResult JSON' + ) + if 'classifier_id' in _dict: + args['classifier_id'] = _dict.get('classifier_id') + else: + raise ValueError( + 'Required property \'classifier_id\' not present in ClassifierResult JSON' + ) + if 'classes' in _dict: + args['classes'] = [ + ClassResult._from_dict(x) for x in (_dict.get('classes')) + ] + else: + raise ValueError( + 'Required property \'classes\' not present in ClassifierResult JSON' + ) + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'name') and self.name is not None: + _dict['name'] = self.name + if hasattr(self, 'classifier_id') and self.classifier_id is not None: + _dict['classifier_id'] = self.classifier_id + if hasattr(self, 'classes') and self.classes is not None: + _dict['classes'] = [x._to_dict() for x in self.classes] + return _dict + + def __str__(self): + """Return a `str` version of this ClassifierResult object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class Classifiers(object): + """ + A container for the list of classifiers. + + :attr list[Classifier] classifiers: List of classifiers. + """ + + def __init__(self, classifiers): + """ + Initialize a Classifiers object. + + :param list[Classifier] classifiers: List of classifiers. + """ + self.classifiers = classifiers + + @classmethod + def _from_dict(cls, _dict): + """Initialize a Classifiers object from a json dictionary.""" + args = {} + if 'classifiers' in _dict: + args['classifiers'] = [ + Classifier._from_dict(x) for x in (_dict.get('classifiers')) + ] + else: + raise ValueError( + 'Required property \'classifiers\' not present in Classifiers JSON' + ) + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'classifiers') and self.classifiers is not None: + _dict['classifiers'] = [x._to_dict() for x in self.classifiers] + return _dict + + def __str__(self): + """Return a `str` version of this Classifiers object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class DetectedFaces(object): + """ + Results for all faces. + + :attr int images_processed: (optional) Number of images processed for the API call. + :attr list[ImageWithFaces] images: The images. + :attr list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. + """ + + def __init__(self, images, images_processed=None, warnings=None): + """ + Initialize a DetectedFaces object. + + :param list[ImageWithFaces] images: The images. + :param int images_processed: (optional) Number of images processed for the API call. + :param list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. + """ + self.images_processed = images_processed + self.images = images + self.warnings = warnings + + @classmethod + def _from_dict(cls, _dict): + """Initialize a DetectedFaces object from a json dictionary.""" + args = {} + if 'images_processed' in _dict: + args['images_processed'] = _dict.get('images_processed') + if 'images' in _dict: + args['images'] = [ + ImageWithFaces._from_dict(x) for x in (_dict.get('images')) + ] + else: + raise ValueError( + 'Required property \'images\' not present in DetectedFaces JSON' + ) + if 'warnings' in _dict: + args['warnings'] = [ + WarningInfo._from_dict(x) for x in (_dict.get('warnings')) + ] + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, + 'images_processed') and self.images_processed is not None: + _dict['images_processed'] = self.images_processed + if hasattr(self, 'images') and self.images is not None: + _dict['images'] = [x._to_dict() for x in self.images] + if hasattr(self, 'warnings') and self.warnings is not None: + _dict['warnings'] = [x._to_dict() for x in self.warnings] + return _dict + + def __str__(self): + """Return a `str` version of this DetectedFaces object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class ErrorInfo(object): + """ + Information about what might have caused a failure, such as an image that is too + large. Not returned when there is no error. + + :attr int code: HTTP status code. + :attr str description: Human-readable error description. For example, `File size limit exceeded`. + :attr str error_id: Codified error string. For example, `limit_exceeded`. + """ + + def __init__(self, code, description, error_id): + """ + Initialize a ErrorInfo object. + + :param int code: HTTP status code. + :param str description: Human-readable error description. For example, `File size limit exceeded`. + :param str error_id: Codified error string. For example, `limit_exceeded`. + """ + self.code = code + self.description = description + self.error_id = error_id + + @classmethod + def _from_dict(cls, _dict): + """Initialize a ErrorInfo object from a json dictionary.""" + args = {} + if 'code' in _dict: + args['code'] = _dict.get('code') + else: + raise ValueError( + 'Required property \'code\' not present in ErrorInfo JSON') + if 'description' in _dict: + args['description'] = _dict.get('description') + else: + raise ValueError( + 'Required property \'description\' not present in ErrorInfo JSON' + ) + if 'error_id' in _dict: + args['error_id'] = _dict.get('error_id') + else: + raise ValueError( + 'Required property \'error_id\' not present in ErrorInfo JSON') + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'code') and self.code is not None: + _dict['code'] = self.code + if hasattr(self, 'description') and self.description is not None: + _dict['description'] = self.description + if hasattr(self, 'error_id') and self.error_id is not None: + _dict['error_id'] = self.error_id + return _dict + + def __str__(self): + """Return a `str` version of this ErrorInfo object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class Face(object): + """ + Information about the face. + + :attr FaceAge age: (optional) Age information about a face. + :attr FaceGender gender: (optional) Information about the gender of the face. + :attr FaceLocation face_location: (optional) The location of the bounding box around the face. + """ + + def __init__(self, age=None, gender=None, face_location=None): + """ + Initialize a Face object. + + :param FaceAge age: (optional) Age information about a face. + :param FaceGender gender: (optional) Information about the gender of the face. + :param FaceLocation face_location: (optional) The location of the bounding box around the face. + """ + self.age = age + self.gender = gender + self.face_location = face_location + + @classmethod + def _from_dict(cls, _dict): + """Initialize a Face object from a json dictionary.""" + args = {} + if 'age' in _dict: + args['age'] = FaceAge._from_dict(_dict.get('age')) + if 'gender' in _dict: + args['gender'] = FaceGender._from_dict(_dict.get('gender')) + if 'face_location' in _dict: + args['face_location'] = FaceLocation._from_dict( + _dict.get('face_location')) + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'age') and self.age is not None: + _dict['age'] = self.age._to_dict() + if hasattr(self, 'gender') and self.gender is not None: + _dict['gender'] = self.gender._to_dict() + if hasattr(self, 'face_location') and self.face_location is not None: + _dict['face_location'] = self.face_location._to_dict() + return _dict + + def __str__(self): + """Return a `str` version of this Face object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class FaceAge(object): + """ + Age information about a face. + + :attr int min: (optional) Estimated minimum age. + :attr int max: (optional) Estimated maximum age. + :attr float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. + """ + + def __init__(self, min=None, max=None, score=None): + """ + Initialize a FaceAge object. + + :param int min: (optional) Estimated minimum age. + :param int max: (optional) Estimated maximum age. + :param float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. + """ + self.min = min + self.max = max + self.score = score + + @classmethod + def _from_dict(cls, _dict): + """Initialize a FaceAge object from a json dictionary.""" + args = {} + if 'min' in _dict: + args['min'] = _dict.get('min') + if 'max' in _dict: + args['max'] = _dict.get('max') + if 'score' in _dict: + args['score'] = _dict.get('score') + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'min') and self.min is not None: + _dict['min'] = self.min + if hasattr(self, 'max') and self.max is not None: + _dict['max'] = self.max + if hasattr(self, 'score') and self.score is not None: + _dict['score'] = self.score + return _dict + + def __str__(self): + """Return a `str` version of this FaceAge object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class FaceGender(object): + """ + Information about the gender of the face. + + :attr str gender: Gender identified by the face. For example, `MALE` or `FEMALE`. + :attr float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. + """ + + def __init__(self, gender, score=None): + """ + Initialize a FaceGender object. + + :param str gender: Gender identified by the face. For example, `MALE` or `FEMALE`. + :param float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. + """ + self.gender = gender + self.score = score + + @classmethod + def _from_dict(cls, _dict): + """Initialize a FaceGender object from a json dictionary.""" + args = {} + if 'gender' in _dict: + args['gender'] = _dict.get('gender') + else: + raise ValueError( + 'Required property \'gender\' not present in FaceGender JSON') + if 'score' in _dict: + args['score'] = _dict.get('score') + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'gender') and self.gender is not None: + _dict['gender'] = self.gender + if hasattr(self, 'score') and self.score is not None: + _dict['score'] = self.score + return _dict + + def __str__(self): + """Return a `str` version of this FaceGender object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class FaceLocation(object): + """ + The location of the bounding box around the face. + + :attr float width: Width in pixels of face region. + :attr float height: Height in pixels of face region. + :attr float left: X-position of top-left pixel of face region. + :attr float top: Y-position of top-left pixel of face region. + """ + + def __init__(self, width, height, left, top): + """ + Initialize a FaceLocation object. + + :param float width: Width in pixels of face region. + :param float height: Height in pixels of face region. + :param float left: X-position of top-left pixel of face region. + :param float top: Y-position of top-left pixel of face region. + """ + self.width = width + self.height = height + self.left = left + self.top = top + + @classmethod + def _from_dict(cls, _dict): + """Initialize a FaceLocation object from a json dictionary.""" + args = {} + if 'width' in _dict: + args['width'] = _dict.get('width') + else: + raise ValueError( + 'Required property \'width\' not present in FaceLocation JSON') + if 'height' in _dict: + args['height'] = _dict.get('height') + else: + raise ValueError( + 'Required property \'height\' not present in FaceLocation JSON' + ) + if 'left' in _dict: + args['left'] = _dict.get('left') + else: + raise ValueError( + 'Required property \'left\' not present in FaceLocation JSON') + if 'top' in _dict: + args['top'] = _dict.get('top') + else: + raise ValueError( + 'Required property \'top\' not present in FaceLocation JSON') + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'width') and self.width is not None: + _dict['width'] = self.width + if hasattr(self, 'height') and self.height is not None: + _dict['height'] = self.height + if hasattr(self, 'left') and self.left is not None: + _dict['left'] = self.left + if hasattr(self, 'top') and self.top is not None: + _dict['top'] = self.top + return _dict + + def __str__(self): + """Return a `str` version of this FaceLocation object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class ImageWithFaces(object): + """ + Information about faces in the image. + + :attr list[Face] faces: Faces detected in the images. + :attr str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. + :attr str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. + :attr str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. + :attr ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. + """ + + def __init__(self, + faces, + image=None, + source_url=None, + resolved_url=None, + error=None): + """ + Initialize a ImageWithFaces object. + + :param list[Face] faces: Faces detected in the images. + :param str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. + :param str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. + :param str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. + :param ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. + """ + self.faces = faces + self.image = image + self.source_url = source_url + self.resolved_url = resolved_url + self.error = error + + @classmethod + def _from_dict(cls, _dict): + """Initialize a ImageWithFaces object from a json dictionary.""" + args = {} + if 'faces' in _dict: + args['faces'] = [Face._from_dict(x) for x in _dict.get('faces')] + else: + raise ValueError( + 'Required property \'faces\' not present in ImageWithFaces JSON' + ) + if 'image' in _dict: + args['image'] = _dict.get('image') + if 'source_url' in _dict: + args['source_url'] = _dict.get('source_url') + if 'resolved_url' in _dict: + args['resolved_url'] = _dict.get('resolved_url') + if 'error' in _dict: + args['error'] = ErrorInfo._from_dict(_dict.get('error')) + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'faces') and self.faces is not None: + _dict['faces'] = [x._to_dict() for x in self.faces] + if hasattr(self, 'image') and self.image is not None: + _dict['image'] = self.image + if hasattr(self, 'source_url') and self.source_url is not None: + _dict['source_url'] = self.source_url + if hasattr(self, 'resolved_url') and self.resolved_url is not None: + _dict['resolved_url'] = self.resolved_url + if hasattr(self, 'error') and self.error is not None: + _dict['error'] = self.error._to_dict() + return _dict + + def __str__(self): + """Return a `str` version of this ImageWithFaces object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class WarningInfo(object): + """ + Information about something that went wrong. + + :attr str warning_id: Codified warning string, such as `limit_reached`. + :attr str description: Information about the error. + """ + + def __init__(self, warning_id, description): + """ + Initialize a WarningInfo object. + + :param str warning_id: Codified warning string, such as `limit_reached`. + :param str description: Information about the error. + """ + self.warning_id = warning_id + self.description = description + + @classmethod + def _from_dict(cls, _dict): + """Initialize a WarningInfo object from a json dictionary.""" + args = {} + if 'warning_id' in _dict: + args['warning_id'] = _dict.get('warning_id') + else: + raise ValueError( + 'Required property \'warning_id\' not present in WarningInfo JSON' + ) + if 'description' in _dict: + args['description'] = _dict.get('description') + else: + raise ValueError( + 'Required property \'description\' not present in WarningInfo JSON' + ) + return cls(**args) + + def _to_dict(self): + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'warning_id') and self.warning_id is not None: + _dict['warning_id'] = self.warning_id + if hasattr(self, 'description') and self.description is not None: + _dict['description'] = self.description + return _dict + + def __str__(self): + """Return a `str` version of this WarningInfo object.""" + return json.dumps(self._to_dict(), indent=2) + + def __eq__(self, other): + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other From bb57abf92075b67e08c5e67c0edd472b9b45f10c Mon Sep 17 00:00:00 2001 From: kayla220 Date: Mon, 25 Jun 2018 21:06:32 +0100 Subject: [PATCH 5/9] Update README.md --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 23ff5a077..cba578b7b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,22 @@ -# Python: Getting Started +# 2poundsmeal +Food Intake Analysis Application -A barebones Django app, which can easily be deployed to Heroku. +This prototype page was developed to visualise the concept of 2poundsmeal’s application. The application, combined with VIsual recognition technology of IBM Watson and Clouding technology, will allow users to identify the nutrient content of their food intake from the food photos they provide. +We only provide data on branded foods around £3 that students can see around Sheffield University. +- Tesco sandwiches +- Sainsbury’s sandwiches +- Domino Pizza Personal Pizza - KFC Burgers +- Subway 6” Sub +- McDonalds Burgers + +## How to use +- Upload food picture +- Insert your basic information +- Check the recognition result +- Identify excess / deficient nutrients + +## This application supports the [Getting Started with Python on Heroku](https://devcenter.heroku.com/articles/getting-started-with-python) article - check it out. ## Running Locally @@ -42,3 +57,11 @@ or For more information about using Python on Heroku, see these Dev Center articles: - [Python on Heroku](https://devcenter.heroku.com/categories/python) + +## Built With +- IBM Watson - Visual recognition +- IBM Cloud – Food nutrition data + +## Authors +- Jinhyuk Kim - Project Director +- Da Eun Kim - Front/Backend Developer From e2f5d29ddb7cee68a8b606abb7220261256f509b Mon Sep 17 00:00:00 2001 From: Daeun Date: Mon, 25 Jun 2018 21:26:47 +0100 Subject: [PATCH 6/9] API-key --- .DS_Store | Bin 6148 -> 6148 bytes hello/admin.py | 14 ++++++++++++++ hello/templates/index.html | 4 ++++ hello/visual_recognition_v3.py | 2 +- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.DS_Store b/.DS_Store index 9df7c75b70fff485793f8fef48b83d1e6cffe111..bbac2dae7f1a9e76696fe93a23d7829889e80fe3 100644 GIT binary patch delta 143 zcmZoMXfc=|#>CJ*u~2NHo}wrd0|Nsi1A_nqLncEWLpnn-LkUCT#6tDS1|lprd_aLz zpg;+-BtsF9pURMuQkXTPK^JyC;S z_WT8P*gjC#Jg+uYyW5W{I@)$^S=N155xw2N-z1Nbtv+enZzs;|>6KniN|ivg7=a+=-*2&(44| zkTWpT Helpful Links
  • Getting Started with Python on Heroku
  • Configuring Django Apps for Heroku
  • + +
      +
    • open files
    • +
    +

    Next Steps

    • If you are following the Getting Started guide, then please head back to the tutorial and follow the next steps!
    • If you deployed this app by deploying the Heroku Button, then in a command line shell, run:
      • -
      • git clone https://github.com/heroku/python-getting-started.git - this will create a local copy of the source code for the app
      • +
      • git clone https://github.com/kayla220/python-getting-started.git - this will create a local copy of the source code for the app
      • cd python-getting-started - change directory into the local source code repository
      • heroku git:remote -a <your-app-name> - associate the Heroku app with the repository
      • You'll now be set up to run the app locally, or deploy changes to Heroku

    Helpful Links

    -
    • open files
    • From feba0e91cbe4af75572098e0ca44abcb5b48a2d3 Mon Sep 17 00:00:00 2001 From: Daeun Date: Tue, 26 Jun 2018 01:52:46 +0100 Subject: [PATCH 8/9] delete module --- .DS_Store | Bin 6148 -> 6148 bytes hello/.DS_Store | Bin 6148 -> 6148 bytes hello/test/test_visual_recognition.py | 64 -- hello/visual_recognition_v3.py | 1495 ------------------------- 4 files changed, 1559 deletions(-) delete mode 100644 hello/test/test_visual_recognition.py delete mode 100644 hello/visual_recognition_v3.py diff --git a/.DS_Store b/.DS_Store index bbac2dae7f1a9e76696fe93a23d7829889e80fe3..faa955f8783951d2fb8d7da6d87bcece649f5ff3 100644 GIT binary patch delta 61 zcmZoMXfc@J&&WJ6z#2&O*ef$KFfed46f=}CBm!|JLo!1ekj!C7WhhQ5PR>cn&(GO- MQIdT#JI7ys0Lt$WQ~&?~ delta 30 mcmZoMXfc@J&&V_}z_positive_examples: A compressed (.zip) file of images that depict the visual subject for a class within the new classifier. Must contain a minimum of 10 images. The swagger limits you to training only one class. To train more classes, use the API functionality. - :param file negative_examples: A compressed (.zip) file of images that do not depict the visual subject of any of the classes of the new classifier. Must contain a minimum of 10 images. - :param dict headers: A `dict` containing the request headers - :return: A `dict` containing the `Classifier` response. - :rtype: dict - """ - if name is None: - raise ValueError('name must be provided') - headers = {} - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - params = {'version': self.version} - data = {'name': name} - url = '/v3/classifiers' - response = self.request( - method='POST', - url=url, - headers=headers, - params=params, - data=data, - files=kwargs, - accept_json=True) - return response - - def delete_classifier(self, classifier_id, **kwargs): - """ - Delete a classifier. - - :param str classifier_id: The ID of the classifier. - :param dict headers: A `dict` containing the request headers - :rtype: None - """ - if classifier_id is None: - raise ValueError('classifier_id must be provided') - headers = {} - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - params = {'version': self.version} - url = '/v3/classifiers/{0}'.format( - *self._encode_path_vars(classifier_id)) - self.request( - method='DELETE', - url=url, - headers=headers, - params=params, - accept_json=True) - return None - - def get_classifier(self, classifier_id, **kwargs): - """ - Retrieve classifier details. - - Retrieve information about a custom classifier. - - :param str classifier_id: The ID of the classifier. - :param dict headers: A `dict` containing the request headers - :return: A `dict` containing the `Classifier` response. - :rtype: dict - """ - if classifier_id is None: - raise ValueError('classifier_id must be provided') - headers = {} - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - params = {'version': self.version} - url = '/v3/classifiers/{0}'.format( - *self._encode_path_vars(classifier_id)) - response = self.request( - method='GET', - url=url, - headers=headers, - params=params, - accept_json=True) - return response - - def list_classifiers(self, verbose=None, **kwargs): - """ - Retrieve a list of classifiers. - - :param bool verbose: Specify `true` to return details about the classifiers. Omit this parameter to return a brief list of classifiers. - :param dict headers: A `dict` containing the request headers - :return: A `dict` containing the `Classifiers` response. - :rtype: dict - """ - headers = {} - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - params = {'version': self.version, 'verbose': verbose} - url = '/v3/classifiers' - response = self.request( - method='GET', - url=url, - headers=headers, - params=params, - accept_json=True) - return response - - def update_classifier(self, - classifier_id, - **kwargs): - """ - Update a classifier. - :param str classifier_id: The ID of the classifier. - :param file _positive_examples: A compressed (.zip) file of images that depict the visual subject for a class within the classifier. Must contain a minimum of 10 images. - :param file negative_examples: A compressed (.zip) file of images that do not depict the visual subject of any of the classes of the new classifier. Must contain a minimum of 10 images. - :param dict headers: A `dict` containing the request headers - :return: A `dict` containing the `Classifier` response. - :rtype: dict - """ - if classifier_id is None: - raise ValueError('classifier_id must be provided') - headers = {} - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - params = {'version': self.version} - url = '/v3/classifiers/{0}'.format( - *self._encode_path_vars(classifier_id)) - response = self.request( - method='POST', - url=url, - headers=headers, - params=params, - files=kwargs, - accept_json=True) - return response - - ######################### - # Core ML - ######################### - - def get_core_ml_model(self, classifier_id, **kwargs): - """ - Retrieve a Core ML model of a classifier. - - Download a Core ML model file (.mlmodel) of a custom classifier that returns - \"core_ml_enabled\": true in the classifier details. - - :param str classifier_id: The ID of the classifier. - :param dict headers: A `dict` containing the request headers - :return: A `Response ` object representing the response. - :rtype: requests.models.Response - """ - if classifier_id is None: - raise ValueError('classifier_id must be provided') - headers = {} - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - params = {'version': self.version} - url = '/v3/classifiers/{0}/core_ml_model'.format( - *self._encode_path_vars(classifier_id)) - response = self.request( - method='GET', - url=url, - headers=headers, - params=params, - accept_json=False) - return response - - ######################### - # User data - ######################### - - def delete_user_data(self, customer_id, **kwargs): - """ - Delete labeled data. - - Deletes all data associated with a specified customer ID. The method has no effect - if no data is associated with the customer ID. You associate a customer ID with - data by passing the `X-Watson-Metadata` header with a request that passes data. - For more information about personal data and customer IDs, see [Information - security](https://console.bluemix.net/docs/services/visual-recognition/information-security.html). - - :param str customer_id: The customer ID for which all data is to be deleted. - :param dict headers: A `dict` containing the request headers - :rtype: None - """ - if customer_id is None: - raise ValueError('customer_id must be provided') - headers = {} - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - params = {'version': self.version, 'customer_id': customer_id} - url = '/v3/user_data' - self.request( - method='DELETE', - url=url, - headers=headers, - params=params, - accept_json=True) - return None - - -############################################################################## -# Models -############################################################################## - - -class Class(object): - """ - A category within a classifier. - - :attr str class_name: The name of the class. - """ - - def __init__(self, class_name): - """ - Initialize a Class object. - - :param str class_name: The name of the class. - """ - self.class_name = class_name - - @classmethod - def _from_dict(cls, _dict): - """Initialize a Class object from a json dictionary.""" - args = {} - if 'class' in _dict or 'class_name' in _dict: - args['class_name'] = _dict.get('class') or _dict.get('class_name') - else: - raise ValueError( - 'Required property \'class\' not present in Class JSON') - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'class_name') and self.class_name is not None: - _dict['class'] = self.class_name - return _dict - - def __str__(self): - """Return a `str` version of this Class object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class ClassResult(object): - """ - Result of a class within a classifier. - - :attr str class_name: Name of the class. - :attr float score: (optional) Confidence score for the property in the range of 0 to 1. A higher score indicates greater likelihood that the class is depicted in the image. The default threshold for returning scores from a classifier is 0.5. - :attr str type_hierarchy: (optional) Knowledge graph of the property. For example, `/fruit/pome/apple/eating apple/Granny Smith`. Included only if identified. - """ - - def __init__(self, class_name, score=None, type_hierarchy=None): - """ - Initialize a ClassResult object. - - :param str class_name: Name of the class. - :param float score: (optional) Confidence score for the property in the range of 0 to 1. A higher score indicates greater likelihood that the class is depicted in the image. The default threshold for returning scores from a classifier is 0.5. - :param str type_hierarchy: (optional) Knowledge graph of the property. For example, `/fruit/pome/apple/eating apple/Granny Smith`. Included only if identified. - """ - self.class_name = class_name - self.score = score - self.type_hierarchy = type_hierarchy - - @classmethod - def _from_dict(cls, _dict): - """Initialize a ClassResult object from a json dictionary.""" - args = {} - if 'class' in _dict or 'class_name' in _dict: - args['class_name'] = _dict.get('class') or _dict.get('class_name') - else: - raise ValueError( - 'Required property \'class\' not present in ClassResult JSON') - if 'score' in _dict: - args['score'] = _dict.get('score') - if 'type_hierarchy' in _dict: - args['type_hierarchy'] = _dict.get('type_hierarchy') - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'class_name') and self.class_name is not None: - _dict['class'] = self.class_name - if hasattr(self, 'score') and self.score is not None: - _dict['score'] = self.score - if hasattr(self, 'type_hierarchy') and self.type_hierarchy is not None: - _dict['type_hierarchy'] = self.type_hierarchy - return _dict - - def __str__(self): - """Return a `str` version of this ClassResult object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class ClassifiedImage(object): - """ - Results for one image. - - :attr str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. - :attr str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. - :attr str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. - :attr ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. - :attr list[ClassifierResult] classifiers: The classifiers. - """ - - def __init__(self, - classifiers, - source_url=None, - resolved_url=None, - image=None, - error=None): - """ - Initialize a ClassifiedImage object. - - :param list[ClassifierResult] classifiers: The classifiers. - :param str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. - :param str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. - :param str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. - :param ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. - """ - self.source_url = source_url - self.resolved_url = resolved_url - self.image = image - self.error = error - self.classifiers = classifiers - - @classmethod - def _from_dict(cls, _dict): - """Initialize a ClassifiedImage object from a json dictionary.""" - args = {} - if 'source_url' in _dict: - args['source_url'] = _dict.get('source_url') - if 'resolved_url' in _dict: - args['resolved_url'] = _dict.get('resolved_url') - if 'image' in _dict: - args['image'] = _dict.get('image') - if 'error' in _dict: - args['error'] = ErrorInfo._from_dict(_dict.get('error')) - if 'classifiers' in _dict: - args['classifiers'] = [ - ClassifierResult._from_dict(x) - for x in (_dict.get('classifiers')) - ] - else: - raise ValueError( - 'Required property \'classifiers\' not present in ClassifiedImage JSON' - ) - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'source_url') and self.source_url is not None: - _dict['source_url'] = self.source_url - if hasattr(self, 'resolved_url') and self.resolved_url is not None: - _dict['resolved_url'] = self.resolved_url - if hasattr(self, 'image') and self.image is not None: - _dict['image'] = self.image - if hasattr(self, 'error') and self.error is not None: - _dict['error'] = self.error._to_dict() - if hasattr(self, 'classifiers') and self.classifiers is not None: - _dict['classifiers'] = [x._to_dict() for x in self.classifiers] - return _dict - - def __str__(self): - """Return a `str` version of this ClassifiedImage object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class ClassifiedImages(object): - """ - Results for all images. - - :attr int custom_classes: (optional) Number of custom classes identified in the images. - :attr int images_processed: (optional) Number of images processed for the API call. - :attr list[ClassifiedImage] images: Classified images. - :attr list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. - """ - - def __init__(self, - images, - custom_classes=None, - images_processed=None, - warnings=None): - """ - Initialize a ClassifiedImages object. - - :param list[ClassifiedImage] images: Classified images. - :param int custom_classes: (optional) Number of custom classes identified in the images. - :param int images_processed: (optional) Number of images processed for the API call. - :param list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. - """ - self.custom_classes = custom_classes - self.images_processed = images_processed - self.images = images - self.warnings = warnings - - @classmethod - def _from_dict(cls, _dict): - """Initialize a ClassifiedImages object from a json dictionary.""" - args = {} - if 'custom_classes' in _dict: - args['custom_classes'] = _dict.get('custom_classes') - if 'images_processed' in _dict: - args['images_processed'] = _dict.get('images_processed') - if 'images' in _dict: - args['images'] = [ - ClassifiedImage._from_dict(x) for x in (_dict.get('images')) - ] - else: - raise ValueError( - 'Required property \'images\' not present in ClassifiedImages JSON' - ) - if 'warnings' in _dict: - args['warnings'] = [ - WarningInfo._from_dict(x) for x in (_dict.get('warnings')) - ] - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'custom_classes') and self.custom_classes is not None: - _dict['custom_classes'] = self.custom_classes - if hasattr(self, - 'images_processed') and self.images_processed is not None: - _dict['images_processed'] = self.images_processed - if hasattr(self, 'images') and self.images is not None: - _dict['images'] = [x._to_dict() for x in self.images] - if hasattr(self, 'warnings') and self.warnings is not None: - _dict['warnings'] = [x._to_dict() for x in self.warnings] - return _dict - - def __str__(self): - """Return a `str` version of this ClassifiedImages object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class Classifier(object): - """ - Information about a classifier. - - :attr str classifier_id: ID of a classifier identified in the image. - :attr str name: Name of the classifier. - :attr str owner: (optional) Unique ID of the account who owns the classifier. Returned when verbose=`true`. Might not be returned by some requests. - :attr str status: (optional) Training status of classifier. - :attr bool core_ml_enabled: Whether the classifier can be downloaded as a Core ML model after the training status is `ready`. - :attr str explanation: (optional) If classifier training has failed, this field may explain why. - :attr datetime created: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was created. - :attr list[Class] classes: (optional) Classes that define a classifier. - :attr datetime retrained: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was updated. Returned when verbose=`true`. Might not be returned by some requests. Identical to `updated` and retained for backward compatibility. - :attr datetime updated: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was most recently updated. The field matches either `retrained` or `created`. Returned when verbose=`true`. Might not be returned by some requests. - """ - - def __init__(self, - classifier_id, - name, - core_ml_enabled, - owner=None, - status=None, - explanation=None, - created=None, - classes=None, - retrained=None, - updated=None): - """ - Initialize a Classifier object. - - :param str classifier_id: ID of a classifier identified in the image. - :param str name: Name of the classifier. - :param bool core_ml_enabled: Whether the classifier can be downloaded as a Core ML model after the training status is `ready`. - :param str owner: (optional) Unique ID of the account who owns the classifier. Returned when verbose=`true`. Might not be returned by some requests. - :param str status: (optional) Training status of classifier. - :param str explanation: (optional) If classifier training has failed, this field may explain why. - :param datetime created: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was created. - :param list[Class] classes: (optional) Classes that define a classifier. - :param datetime retrained: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was updated. Returned when verbose=`true`. Might not be returned by some requests. Identical to `updated` and retained for backward compatibility. - :param datetime updated: (optional) Date and time in Coordinated Universal Time (UTC) that the classifier was most recently updated. The field matches either `retrained` or `created`. Returned when verbose=`true`. Might not be returned by some requests. - """ - self.classifier_id = classifier_id - self.name = name - self.owner = owner - self.status = status - self.core_ml_enabled = core_ml_enabled - self.explanation = explanation - self.created = created - self.classes = classes - self.retrained = retrained - self.updated = updated - - @classmethod - def _from_dict(cls, _dict): - """Initialize a Classifier object from a json dictionary.""" - args = {} - if 'classifier_id' in _dict: - args['classifier_id'] = _dict.get('classifier_id') - else: - raise ValueError( - 'Required property \'classifier_id\' not present in Classifier JSON' - ) - if 'name' in _dict: - args['name'] = _dict.get('name') - else: - raise ValueError( - 'Required property \'name\' not present in Classifier JSON') - if 'owner' in _dict: - args['owner'] = _dict.get('owner') - if 'status' in _dict: - args['status'] = _dict.get('status') - if 'core_ml_enabled' in _dict: - args['core_ml_enabled'] = _dict.get('core_ml_enabled') - else: - raise ValueError( - 'Required property \'core_ml_enabled\' not present in Classifier JSON' - ) - if 'explanation' in _dict: - args['explanation'] = _dict.get('explanation') - if 'created' in _dict: - args['created'] = string_to_datetime(_dict.get('created')) - if 'classes' in _dict: - args['classes'] = [ - Class._from_dict(x) for x in (_dict.get('classes')) - ] - if 'retrained' in _dict: - args['retrained'] = string_to_datetime(_dict.get('retrained')) - if 'updated' in _dict: - args['updated'] = string_to_datetime(_dict.get('updated')) - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'classifier_id') and self.classifier_id is not None: - _dict['classifier_id'] = self.classifier_id - if hasattr(self, 'name') and self.name is not None: - _dict['name'] = self.name - if hasattr(self, 'owner') and self.owner is not None: - _dict['owner'] = self.owner - if hasattr(self, 'status') and self.status is not None: - _dict['status'] = self.status - if hasattr(self, - 'core_ml_enabled') and self.core_ml_enabled is not None: - _dict['core_ml_enabled'] = self.core_ml_enabled - if hasattr(self, 'explanation') and self.explanation is not None: - _dict['explanation'] = self.explanation - if hasattr(self, 'created') and self.created is not None: - _dict['created'] = datetime_to_string(self.created) - if hasattr(self, 'classes') and self.classes is not None: - _dict['classes'] = [x._to_dict() for x in self.classes] - if hasattr(self, 'retrained') and self.retrained is not None: - _dict['retrained'] = datetime_to_string(self.retrained) - if hasattr(self, 'updated') and self.updated is not None: - _dict['updated'] = datetime_to_string(self.updated) - return _dict - - def __str__(self): - """Return a `str` version of this Classifier object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class ClassifierResult(object): - """ - Classifier and score combination. - - :attr str name: Name of the classifier. - :attr str classifier_id: ID of a classifier identified in the image. - :attr list[ClassResult] classes: Classes within the classifier. - """ - - def __init__(self, name, classifier_id, classes): - """ - Initialize a ClassifierResult object. - - :param str name: Name of the classifier. - :param str classifier_id: ID of a classifier identified in the image. - :param list[ClassResult] classes: Classes within the classifier. - """ - self.name = name - self.classifier_id = classifier_id - self.classes = classes - - @classmethod - def _from_dict(cls, _dict): - """Initialize a ClassifierResult object from a json dictionary.""" - args = {} - if 'name' in _dict: - args['name'] = _dict.get('name') - else: - raise ValueError( - 'Required property \'name\' not present in ClassifierResult JSON' - ) - if 'classifier_id' in _dict: - args['classifier_id'] = _dict.get('classifier_id') - else: - raise ValueError( - 'Required property \'classifier_id\' not present in ClassifierResult JSON' - ) - if 'classes' in _dict: - args['classes'] = [ - ClassResult._from_dict(x) for x in (_dict.get('classes')) - ] - else: - raise ValueError( - 'Required property \'classes\' not present in ClassifierResult JSON' - ) - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'name') and self.name is not None: - _dict['name'] = self.name - if hasattr(self, 'classifier_id') and self.classifier_id is not None: - _dict['classifier_id'] = self.classifier_id - if hasattr(self, 'classes') and self.classes is not None: - _dict['classes'] = [x._to_dict() for x in self.classes] - return _dict - - def __str__(self): - """Return a `str` version of this ClassifierResult object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class Classifiers(object): - """ - A container for the list of classifiers. - - :attr list[Classifier] classifiers: List of classifiers. - """ - - def __init__(self, classifiers): - """ - Initialize a Classifiers object. - - :param list[Classifier] classifiers: List of classifiers. - """ - self.classifiers = classifiers - - @classmethod - def _from_dict(cls, _dict): - """Initialize a Classifiers object from a json dictionary.""" - args = {} - if 'classifiers' in _dict: - args['classifiers'] = [ - Classifier._from_dict(x) for x in (_dict.get('classifiers')) - ] - else: - raise ValueError( - 'Required property \'classifiers\' not present in Classifiers JSON' - ) - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'classifiers') and self.classifiers is not None: - _dict['classifiers'] = [x._to_dict() for x in self.classifiers] - return _dict - - def __str__(self): - """Return a `str` version of this Classifiers object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class DetectedFaces(object): - """ - Results for all faces. - - :attr int images_processed: (optional) Number of images processed for the API call. - :attr list[ImageWithFaces] images: The images. - :attr list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. - """ - - def __init__(self, images, images_processed=None, warnings=None): - """ - Initialize a DetectedFaces object. - - :param list[ImageWithFaces] images: The images. - :param int images_processed: (optional) Number of images processed for the API call. - :param list[WarningInfo] warnings: (optional) Information about what might cause less than optimal output. For example, a request sent with a corrupt .zip file and a list of image URLs will still complete, but does not return the expected output. Not returned when there is no warning. - """ - self.images_processed = images_processed - self.images = images - self.warnings = warnings - - @classmethod - def _from_dict(cls, _dict): - """Initialize a DetectedFaces object from a json dictionary.""" - args = {} - if 'images_processed' in _dict: - args['images_processed'] = _dict.get('images_processed') - if 'images' in _dict: - args['images'] = [ - ImageWithFaces._from_dict(x) for x in (_dict.get('images')) - ] - else: - raise ValueError( - 'Required property \'images\' not present in DetectedFaces JSON' - ) - if 'warnings' in _dict: - args['warnings'] = [ - WarningInfo._from_dict(x) for x in (_dict.get('warnings')) - ] - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, - 'images_processed') and self.images_processed is not None: - _dict['images_processed'] = self.images_processed - if hasattr(self, 'images') and self.images is not None: - _dict['images'] = [x._to_dict() for x in self.images] - if hasattr(self, 'warnings') and self.warnings is not None: - _dict['warnings'] = [x._to_dict() for x in self.warnings] - return _dict - - def __str__(self): - """Return a `str` version of this DetectedFaces object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class ErrorInfo(object): - """ - Information about what might have caused a failure, such as an image that is too - large. Not returned when there is no error. - - :attr int code: HTTP status code. - :attr str description: Human-readable error description. For example, `File size limit exceeded`. - :attr str error_id: Codified error string. For example, `limit_exceeded`. - """ - - def __init__(self, code, description, error_id): - """ - Initialize a ErrorInfo object. - - :param int code: HTTP status code. - :param str description: Human-readable error description. For example, `File size limit exceeded`. - :param str error_id: Codified error string. For example, `limit_exceeded`. - """ - self.code = code - self.description = description - self.error_id = error_id - - @classmethod - def _from_dict(cls, _dict): - """Initialize a ErrorInfo object from a json dictionary.""" - args = {} - if 'code' in _dict: - args['code'] = _dict.get('code') - else: - raise ValueError( - 'Required property \'code\' not present in ErrorInfo JSON') - if 'description' in _dict: - args['description'] = _dict.get('description') - else: - raise ValueError( - 'Required property \'description\' not present in ErrorInfo JSON' - ) - if 'error_id' in _dict: - args['error_id'] = _dict.get('error_id') - else: - raise ValueError( - 'Required property \'error_id\' not present in ErrorInfo JSON') - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'code') and self.code is not None: - _dict['code'] = self.code - if hasattr(self, 'description') and self.description is not None: - _dict['description'] = self.description - if hasattr(self, 'error_id') and self.error_id is not None: - _dict['error_id'] = self.error_id - return _dict - - def __str__(self): - """Return a `str` version of this ErrorInfo object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class Face(object): - """ - Information about the face. - - :attr FaceAge age: (optional) Age information about a face. - :attr FaceGender gender: (optional) Information about the gender of the face. - :attr FaceLocation face_location: (optional) The location of the bounding box around the face. - """ - - def __init__(self, age=None, gender=None, face_location=None): - """ - Initialize a Face object. - - :param FaceAge age: (optional) Age information about a face. - :param FaceGender gender: (optional) Information about the gender of the face. - :param FaceLocation face_location: (optional) The location of the bounding box around the face. - """ - self.age = age - self.gender = gender - self.face_location = face_location - - @classmethod - def _from_dict(cls, _dict): - """Initialize a Face object from a json dictionary.""" - args = {} - if 'age' in _dict: - args['age'] = FaceAge._from_dict(_dict.get('age')) - if 'gender' in _dict: - args['gender'] = FaceGender._from_dict(_dict.get('gender')) - if 'face_location' in _dict: - args['face_location'] = FaceLocation._from_dict( - _dict.get('face_location')) - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'age') and self.age is not None: - _dict['age'] = self.age._to_dict() - if hasattr(self, 'gender') and self.gender is not None: - _dict['gender'] = self.gender._to_dict() - if hasattr(self, 'face_location') and self.face_location is not None: - _dict['face_location'] = self.face_location._to_dict() - return _dict - - def __str__(self): - """Return a `str` version of this Face object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class FaceAge(object): - """ - Age information about a face. - - :attr int min: (optional) Estimated minimum age. - :attr int max: (optional) Estimated maximum age. - :attr float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. - """ - - def __init__(self, min=None, max=None, score=None): - """ - Initialize a FaceAge object. - - :param int min: (optional) Estimated minimum age. - :param int max: (optional) Estimated maximum age. - :param float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. - """ - self.min = min - self.max = max - self.score = score - - @classmethod - def _from_dict(cls, _dict): - """Initialize a FaceAge object from a json dictionary.""" - args = {} - if 'min' in _dict: - args['min'] = _dict.get('min') - if 'max' in _dict: - args['max'] = _dict.get('max') - if 'score' in _dict: - args['score'] = _dict.get('score') - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'min') and self.min is not None: - _dict['min'] = self.min - if hasattr(self, 'max') and self.max is not None: - _dict['max'] = self.max - if hasattr(self, 'score') and self.score is not None: - _dict['score'] = self.score - return _dict - - def __str__(self): - """Return a `str` version of this FaceAge object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class FaceGender(object): - """ - Information about the gender of the face. - - :attr str gender: Gender identified by the face. For example, `MALE` or `FEMALE`. - :attr float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. - """ - - def __init__(self, gender, score=None): - """ - Initialize a FaceGender object. - - :param str gender: Gender identified by the face. For example, `MALE` or `FEMALE`. - :param float score: (optional) Confidence score in the range of 0 to 1. A higher score indicates greater confidence in the estimated value for the property. - """ - self.gender = gender - self.score = score - - @classmethod - def _from_dict(cls, _dict): - """Initialize a FaceGender object from a json dictionary.""" - args = {} - if 'gender' in _dict: - args['gender'] = _dict.get('gender') - else: - raise ValueError( - 'Required property \'gender\' not present in FaceGender JSON') - if 'score' in _dict: - args['score'] = _dict.get('score') - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'gender') and self.gender is not None: - _dict['gender'] = self.gender - if hasattr(self, 'score') and self.score is not None: - _dict['score'] = self.score - return _dict - - def __str__(self): - """Return a `str` version of this FaceGender object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class FaceLocation(object): - """ - The location of the bounding box around the face. - - :attr float width: Width in pixels of face region. - :attr float height: Height in pixels of face region. - :attr float left: X-position of top-left pixel of face region. - :attr float top: Y-position of top-left pixel of face region. - """ - - def __init__(self, width, height, left, top): - """ - Initialize a FaceLocation object. - - :param float width: Width in pixels of face region. - :param float height: Height in pixels of face region. - :param float left: X-position of top-left pixel of face region. - :param float top: Y-position of top-left pixel of face region. - """ - self.width = width - self.height = height - self.left = left - self.top = top - - @classmethod - def _from_dict(cls, _dict): - """Initialize a FaceLocation object from a json dictionary.""" - args = {} - if 'width' in _dict: - args['width'] = _dict.get('width') - else: - raise ValueError( - 'Required property \'width\' not present in FaceLocation JSON') - if 'height' in _dict: - args['height'] = _dict.get('height') - else: - raise ValueError( - 'Required property \'height\' not present in FaceLocation JSON' - ) - if 'left' in _dict: - args['left'] = _dict.get('left') - else: - raise ValueError( - 'Required property \'left\' not present in FaceLocation JSON') - if 'top' in _dict: - args['top'] = _dict.get('top') - else: - raise ValueError( - 'Required property \'top\' not present in FaceLocation JSON') - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'width') and self.width is not None: - _dict['width'] = self.width - if hasattr(self, 'height') and self.height is not None: - _dict['height'] = self.height - if hasattr(self, 'left') and self.left is not None: - _dict['left'] = self.left - if hasattr(self, 'top') and self.top is not None: - _dict['top'] = self.top - return _dict - - def __str__(self): - """Return a `str` version of this FaceLocation object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class ImageWithFaces(object): - """ - Information about faces in the image. - - :attr list[Face] faces: Faces detected in the images. - :attr str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. - :attr str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. - :attr str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. - :attr ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. - """ - - def __init__(self, - faces, - image=None, - source_url=None, - resolved_url=None, - error=None): - """ - Initialize a ImageWithFaces object. - - :param list[Face] faces: Faces detected in the images. - :param str image: (optional) Relative path of the image file if uploaded directly. Not returned when the image is passed by URL. - :param str source_url: (optional) Source of the image before any redirects. Not returned when the image is uploaded. - :param str resolved_url: (optional) Fully resolved URL of the image after redirects are followed. Not returned when the image is uploaded. - :param ErrorInfo error: (optional) Information about what might have caused a failure, such as an image that is too large. Not returned when there is no error. - """ - self.faces = faces - self.image = image - self.source_url = source_url - self.resolved_url = resolved_url - self.error = error - - @classmethod - def _from_dict(cls, _dict): - """Initialize a ImageWithFaces object from a json dictionary.""" - args = {} - if 'faces' in _dict: - args['faces'] = [Face._from_dict(x) for x in _dict.get('faces')] - else: - raise ValueError( - 'Required property \'faces\' not present in ImageWithFaces JSON' - ) - if 'image' in _dict: - args['image'] = _dict.get('image') - if 'source_url' in _dict: - args['source_url'] = _dict.get('source_url') - if 'resolved_url' in _dict: - args['resolved_url'] = _dict.get('resolved_url') - if 'error' in _dict: - args['error'] = ErrorInfo._from_dict(_dict.get('error')) - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'faces') and self.faces is not None: - _dict['faces'] = [x._to_dict() for x in self.faces] - if hasattr(self, 'image') and self.image is not None: - _dict['image'] = self.image - if hasattr(self, 'source_url') and self.source_url is not None: - _dict['source_url'] = self.source_url - if hasattr(self, 'resolved_url') and self.resolved_url is not None: - _dict['resolved_url'] = self.resolved_url - if hasattr(self, 'error') and self.error is not None: - _dict['error'] = self.error._to_dict() - return _dict - - def __str__(self): - """Return a `str` version of this ImageWithFaces object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - -class WarningInfo(object): - """ - Information about something that went wrong. - - :attr str warning_id: Codified warning string, such as `limit_reached`. - :attr str description: Information about the error. - """ - - def __init__(self, warning_id, description): - """ - Initialize a WarningInfo object. - - :param str warning_id: Codified warning string, such as `limit_reached`. - :param str description: Information about the error. - """ - self.warning_id = warning_id - self.description = description - - @classmethod - def _from_dict(cls, _dict): - """Initialize a WarningInfo object from a json dictionary.""" - args = {} - if 'warning_id' in _dict: - args['warning_id'] = _dict.get('warning_id') - else: - raise ValueError( - 'Required property \'warning_id\' not present in WarningInfo JSON' - ) - if 'description' in _dict: - args['description'] = _dict.get('description') - else: - raise ValueError( - 'Required property \'description\' not present in WarningInfo JSON' - ) - return cls(**args) - - def _to_dict(self): - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'warning_id') and self.warning_id is not None: - _dict['warning_id'] = self.warning_id - if hasattr(self, 'description') and self.description is not None: - _dict['description'] = self.description - return _dict - - def __str__(self): - """Return a `str` version of this WarningInfo object.""" - return json.dumps(self._to_dict(), indent=2) - - def __eq__(self, other): - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other): - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other From 797dbd177edd80bed97573e2dc245f66961781d8 Mon Sep 17 00:00:00 2001 From: Eric Chen Date: Fri, 27 Mar 2020 17:06:56 -0700 Subject: [PATCH 9/9] Change 'staticfiles' to 'static' with new Django version --- hello/templates/db.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hello/templates/db.html b/hello/templates/db.html index e0e03d4c6..2d3a390ee 100644 --- a/hello/templates/db.html +++ b/hello/templates/db.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load staticfiles %} +{% load static %} {% block content %}
      @@ -18,4 +18,4 @@

      Page View Report

      -{% endblock %} \ No newline at end of file +{% endblock %}