Repository: SebRut/pygrocy
Pull request #26 (2019-09-08 12:02:31)

Add product picture
## Description

You can add a picture to a product :)

**Related issue (if applicable):** fixes #<issue number goes here>

## Checklist
  - [x] The code change is tested and works locally.
  - [x] tests pass. **Your PR won't be merged unless tests pass**
  - [x] There is no commented out code in this PR



Patch:
diff --git a/pygrocy/grocy.py b/pygrocy/grocy.py
index a45046c..fc2f38b 100644
--- a/pygrocy/grocy.py
+++ b/pygrocy/grocy.py
@@ -218,4 +218,8 @@ class Grocy(object):
             return
         return [Group(resp) for resp in raw_groups]
         
+    def add_product_pic(self, product_id: int, pic_path: str):
+        if self._api_client.upload_product_picture(product_id, pic_path).status_code != 204:
+            return
+        return self._api_client.update_product_pic(product_id)
         
\ No newline at end of file
diff --git a/pygrocy/grocy_api_client.py b/pygrocy/grocy_api_client.py
index a49b40b..3ab4401 100644
--- a/pygrocy/grocy_api_client.py
+++ b/pygrocy/grocy_api_client.py
@@ -2,7 +2,9 @@ from datetime import datetime
 from enum import Enum
 from typing import List
 from urllib.parse import urljoin
-
+import os
+import base64
+import json
 import requests
 
 from pygrocy.utils import parse_date, parse_float, parse_int
@@ -400,4 +402,25 @@ class GrocyApiClient(object):
         parsed_json = resp.json()
         return [LocationData(response) for response in parsed_json]
         
-        
\ No newline at end of file
+    def upload_product_picture(self, product_id: int, pic_path: str):
+        if not os.path.exists(pic_path):
+            return
+        up_header = self._headers.copy()
+        up_header['accept'] = '*/*'
+        up_header['Content-Type'] = 'application/octet-stream'
+        b64fn = base64.b64encode('{}.jpg'.format(product_id).encode('ascii'))
+        req_url = urljoin(urljoin(self._base_url, "files/productpictures/" ), str(b64fn, "utf-8"))
+        with open(pic_path,'rb') as pic:
+            resp =requests.put(req_url, verify=self._verify_ssl, headers=up_header , data=pic)
+            return resp
+            
+    def update_product_pic(self, product_id: int):
+        pic_name = '{}.jpg'.format(product_id)
+        data = { "picture_file_name":  pic_name }
+        up_header = self._headers.copy()
+        up_header['accept'] = '*/*'
+        up_header['Content-Type'] = 'application/json'
+        req_url = urljoin(urljoin(self._base_url, "objects/products/"), str(product_id))
+        resp = requests.put(req_url, verify=self._verify_ssl, headers=up_header , data=json.dumps(data))
+        return resp
+            


Test patch:
diff --git a/test/test_grocy.py b/test/test_grocy.py
index 3e7cd06..f902016 100644
--- a/test/test_grocy.py
+++ b/test/test_grocy.py
@@ -1,5 +1,6 @@
 from unittest import TestCase
-
+from unittest.mock import patch, mock_open
+from io import FileIO
 import responses
 from pygrocy import Grocy
 from pygrocy.grocy import Product
@@ -222,5 +223,43 @@ class TestGrocy(TestCase):
         ]
         responses.add(responses.GET, "https://example.com/api/objects/product_groups", json=resp, status=200)
         
+    @responses.activate
+    def test_upload_product_picture_valid(self):
+        with patch("os.path.exists" ) as m_exist:
+            with patch("builtins.open", mock_open()) as m_open:
+                m_exist.return_value = True
+                api_client = GrocyApiClient("https://example.com/api/", "api_key")
+                responses.add(responses.PUT, "https://example.com/api/files/productpictures/MS5qcGc=", status=204)
+                assert api_client.upload_product_picture(1,"/somepath/pic.jpg").status_code == 204
+            
+    @responses.activate
+    def test_upload_product_picture_invalid_missing_data(self):
+        with patch("os.path.exists" ) as m_exist:
+            m_exist.return_value = False
+            api_client = GrocyApiClient("https://example.com/api/", "api_key")
+            responses.add(responses.PUT, "https://example.com/api/files/productpictures/MS5qcGc=", status=204)
+            assert api_client.upload_product_picture(1,"/somepath/pic.jpg") is None
+        
+    @responses.activate
+    def test_upload_product_picture_error(self):
+        with patch("os.path.exists" ) as m_exist:
+            with patch("builtins.open", mock_open()) as m_open:
+                m_exist.return_value = True
+                api_client = GrocyApiClient("https://example.com/api/", "api_key")
+                responses.add(responses.PUT, "https://example.com/api/files/productpictures/MS5qcGc=", status=400)
+                assert api_client.upload_product_picture(1,"/somepath/pic.jpg").status_code != 204
+                
+    @responses.activate
+    def test_update_product_pic_valid(self):
+        api_client = GrocyApiClient("https://example.com/api/", "api_key")
+        responses.add(responses.PUT, "https://example.com/api/objects/products/1", status=204)
+        assert api_client.update_product_pic(1).status_code == 204
+        
+    @responses.activate
+    def test_update_product_pic_error(self):
+        api_client = GrocyApiClient("https://example.com/api/", "api_key")
+        responses.add(responses.PUT, "https://example.com/api/objects/products/1", status=400)
+        assert api_client.update_product_pic(1).status_code != 204
+        
         
         
\ No newline at end of file