From 30cac87894a31b27d29eb85c4f6e243f8b53d00d Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 12:06:20 +0530 Subject: [PATCH 1/9] Sending GCP code for Cloud Function V1 --- Architecture1.png | Bin 0 -> 36731 bytes Dockerfile | 17 +++++++ README.md | 67 ++++++---------------------- cloudbuild.yaml | 25 +++++++++++ main.py | 40 +++++++++++++++++ requirements.txt | 3 ++ test_cases.py | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 54 deletions(-) create mode 100644 Architecture1.png create mode 100644 Dockerfile create mode 100644 cloudbuild.yaml create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 test_cases.py diff --git a/Architecture1.png b/Architecture1.png new file mode 100644 index 0000000000000000000000000000000000000000..89b33da7aa1d302430eebff6498990959b97d92f GIT binary patch literal 36731 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

>ftQUtzCiW{g-4s5Q58<^C4k(z!0OF}Phq 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 literal 0 HcmV?d00001 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f4f4954 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +from alpine:latest + +RUN apk add --no-cache python3-dev \ + && pip3 install --upgrade pip + +WORKDIR /app +COPY . /app + +RUN pip3 --no-cache-dir install -r requirements.txt + +RUN pylint main.py +RUN pytest test_cases.py + +EXPOSE 5000 + +ENTRYPOINT ["python3"] +CMD ["main.py"] diff --git a/README.md b/README.md index 4a77c09..81cbfcc 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,17 @@ -# Clock Exercise +Function clock_angle() accepts 2 parameters as json -We are interested in running code of course, but even more in your development process and understanding of Software Development Lifecycle Management. +Input: +Sample JSON: +{ + "hrs":3, + "mins":0 +} +Accessing Function: +1. curl -X POST "https://us-central1-ind-coe.cloudfunctions.net/clock_repository" -H "Content-Type:application/json" -d '{"hrs":10,"mins":20}' -**Fork this repo, then get to work.** Remember that this is a DevOps team, so make sure your repo reflects that. Spend however much time you feel is reasonable. It doesn’t matter if the project is ‘done’, nothing ever is. **When you’re ready push your changes back to Github and put in a pull request back to the base repo.** +2. https://us-central1-ind-coe.cloudfunctions.net/clock_repository?hrs=10&mins=10 -This exercise is not meant to take an excessive amount of time. It is an opportunity for you to demonstrate your skills without the stress of an interview. If you start to run out of time, it’s ok to leave an imaginary team member a TODO list that details all the things you didn’t quite have time to do in order for your solution to go to prod. -If you need clarification, or would like to request additional information, pease reach out to the interviewer by email. - -## Scenario - -You have just joined a DevOps team. This team lives by DevOps principles and you want to let them know you mean business! This particular team is developing a product that is deployed in a Google Cloud Project. - -This sprint, the team has been asked to work on a new feature that depends on being able to calculate the angle between the hands on a clock face. They’ve asked you to write some code to help out with that. This is an IOT project, and they have sensors emitting times at a pretty low frequency (about 10 a minute), and for some reason they need to be processed and stored as angles. - -You may need to make some assupmtions, that's OK, just document what they are and move on. - -The team loves innovation, so you can use whatever languages and technologies you like to complete this. Approach this problem as if your code will go to production. Whilst we don’t expect the code to be perfect, we are not looking for a hacked together script. - -Your solution should offer the rest of the team a way to submit a time and receive an angle in return or store it somewhere. They are little fuzzy on the best way to get this low frequency data to your service, so if you can offer them any hints on that, they’d be really happy. - -## How to proceed - -**Fork this repo, then get to work.** Remember that this is a DevOps team, so make sure your repo reflects that. Spend however much time you feel is reasonable. It doesn’t matter if the project is ‘done’, nothing ever is. **When you’re ready push your changes back to Github and put in a pull request back to the base repo.** - -Be sure to add in instructions for how to deploy your solution, and document things in a way that the rest of the team can pick this up and run with it. Remember you have all the tools in the GCP arsenal at your disposal. - -We are looking for you to demonstrate your abilities in software practices and DevOps, including reusability, portability, reliability, ease of maintenance etc. - -Think about how this will actually be deployed and maintained in the future as you build on it and expand it. You don’t have to implement deployment practices if you don’t have the time or resources, its ok to just document those. - ---- - -## Product Backlog Item (Sprint Story) - -Here is the story that is in the backlog. - -As with all stories, the team may have been optimistic with how much can be done in the time permitted. It's ok to meet some of the acceptance criteria by documenting what you would do in the next sprint! Prioritize your time and make sure you have some technical content to deliver. - -### Description:- - -As a team
-We need a serivce that we can send a time value to and have it return or store an angle value
-So that we can use it in downstream processing - -### Detail:- - -We need to calculate the angle between the hands on a clock face. For example input 03:00 would yield 90 degrees. - -### Acceptance Criteria:- - -1) Code to perform the calculation -1) How will you deploy this solution (in code or as a todo list if time is limited). i.e. how and where will this run? -1) How will you manage any infrastructure needed? -1) Delivered as a feature branch in the repo fork -1) Bonus points for a working deployed solution in GCP that you can demo at the "sprint review" (ie interview) -1) Any DevOps/Cicd components that would support this feature in a production setting +Output: + return string +e.g. 'Angle -> 90.0' diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 0000000..c06d3ef --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,25 @@ +steps: + - + name: gcr.io/cloud-builders/docker + args: + - build + - '-t' + - 'gcr.io/ind-coe/ar-clock:$COMMIT_SHA' + - . + - + name: gcr.io/cloud-builders/docker + args: + - push + - 'gcr.io/ind-coe/ar-clock:$COMMIT_SHA' + - + name: gcr.io/cloud-builders/gcloud + args: + - functions + - deploy + - clock_angle + - '--runtime' + - python37 + - '--trigger-http' + - '--allow-unauthenticated' + - '--source=https://source.developers.google.com/projects/ind-coe/repos/ar_cloud_clock/moveable-aliases/master/paths/' + dir: 'functions/autodeploy' diff --git a/main.py b/main.py new file mode 100644 index 0000000..b02a3d0 --- /dev/null +++ b/main.py @@ -0,0 +1,40 @@ +""" + _author_ = Arpit Rawal + Funciton gets deploy on google cloud functions +""" +def clock_angle(request): + """Responds to any HTTP request. + Args: + request (flask.Request): HTTP request object. + Returns: + The response text or any set of values that can be turned into a + Response object using + """ + request_json = request.get_json() + request_args = request.args + if request.args and 'hrs' in request.args: + hrs = int(request.args.get('hrs')) + mins = int(request.args.get('mins')) + elif request_json and 'hrs' in request_json: + hrs = int(request_json['hrs']) + mins = int(request_json['mins']) + elif request_args and 'hrs' in request_args: + hrs = int(request_args['hrs']) + mins = int(request_args['mins']) + else: + ans_str = "Cannot compute due to some error" + + if (0 <= int(hrs) <= 12 and 0 <= int(mins) <= 59): + degree_per_min = 6 + degree_per_hour = 30 + degree_intern = 0.5 + + if hrs == 12: + hrs = 0 + + angle_between = abs(hrs*degree_per_hour - mins*degree_per_min + mins*degree_intern) + ans_str = "Angle -> " + str(angle_between) + + else: + ans_str = "Invalid Input, cannot compute, Try Again" + return ans_str diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0f50bb9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask==1.1.2 +pylint==2.5.0 +pytest==5.4.1 diff --git a/test_cases.py b/test_cases.py new file mode 100644 index 0000000..ee150ce --- /dev/null +++ b/test_cases.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Apr 27 17:08:37 2020 + +@author: arawal +Function to unitesting of http function +""" + + + +import os +import sys +import unittest + +from unittest.mock import Mock +from flask import Flask + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +import main + + +app = Flask(__name__) + +class MyTestClass(unittest.TestCase): + """ + Class to implment TestCases for Clock Angle function in main + """ + # initialization logic for the test suite declared in the test module + # code that is executed before all tests in one test run + @classmethod + def setUpClass(cls): + pass + + # clean up logic for the test suite declared in the test module + # code that is executed after all tests in one test run + @classmethod + def tearDownClass(cls): + pass + + # initialization logic + # code that is executed before each test + def setUp(self): + # creates a test client + self.app = app.test_client() + # propagate the exceptions to the test client + self.app.testing = True + + # clean up logic + # code that is executed after each test + def tearDown(self): + pass + + # Tesing angle between the codes + def test_h3m0(self): + """ + Testing at 3 hrs 0 mins - Angle = 90 + """ + hrs = 3 + mins = 0 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 90.0) + + def test_2m45(self): + """ + Testing at 2 hrs 45 mins - Angle = 187.5 + """ + hrs = 2 + mins = 45 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 187.5) + + def test_h8m15(self): + """ + Testing at 8 hrs 15 mins - Angle = 157.5 + """ + hrs = 8 + mins = 15 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 157.5) + + def test_h6m0(self): + """ + Testing at 6 hrs 0 mins - Angle = 180 + """ + hrs = 6 + mins = 0 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 180) + + +# runs the unit tests in the module +if __name__ == '__main__': + unittest.main() From a1196275c722db765b7556693baac794b9701b74 Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 12:22:20 +0530 Subject: [PATCH 2/9] testing git1 --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index b02a3d0..41563c4 100644 --- a/main.py +++ b/main.py @@ -33,7 +33,7 @@ def clock_angle(request): hrs = 0 angle_between = abs(hrs*degree_per_hour - mins*degree_per_min + mins*degree_intern) - ans_str = "Angle -> " + str(angle_between) + ans_str = "Angle between clock hands {0} hrs and {1} mins is {2}-> ".format(hr,mins,str(angle_between)) else: ans_str = "Invalid Input, cannot compute, Try Again" From 268aabfac4868eda074ac12e4212b6b6ccb06d1f Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 12:28:52 +0530 Subject: [PATCH 3/9] Code add to master branch --- Architecture1.png | Bin 0 -> 36731 bytes Dockerfile | 17 +++++++ README.md | 67 ++++++---------------------- cloudbuild.yaml | 25 +++++++++++ main.py | 40 +++++++++++++++++ requirements.txt | 3 ++ test_cases.py | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 54 deletions(-) create mode 100644 Architecture1.png create mode 100644 Dockerfile create mode 100644 cloudbuild.yaml create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 test_cases.py diff --git a/Architecture1.png b/Architecture1.png new file mode 100644 index 0000000000000000000000000000000000000000..89b33da7aa1d302430eebff6498990959b97d92f GIT binary patch literal 36731 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

>ftQUtzCiW{g-4s5Q58<^C4k(z!0OF}Phq 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 literal 0 HcmV?d00001 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f4f4954 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +from alpine:latest + +RUN apk add --no-cache python3-dev \ + && pip3 install --upgrade pip + +WORKDIR /app +COPY . /app + +RUN pip3 --no-cache-dir install -r requirements.txt + +RUN pylint main.py +RUN pytest test_cases.py + +EXPOSE 5000 + +ENTRYPOINT ["python3"] +CMD ["main.py"] diff --git a/README.md b/README.md index 4a77c09..81cbfcc 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,17 @@ -# Clock Exercise +Function clock_angle() accepts 2 parameters as json -We are interested in running code of course, but even more in your development process and understanding of Software Development Lifecycle Management. +Input: +Sample JSON: +{ + "hrs":3, + "mins":0 +} +Accessing Function: +1. curl -X POST "https://us-central1-ind-coe.cloudfunctions.net/clock_repository" -H "Content-Type:application/json" -d '{"hrs":10,"mins":20}' -**Fork this repo, then get to work.** Remember that this is a DevOps team, so make sure your repo reflects that. Spend however much time you feel is reasonable. It doesn’t matter if the project is ‘done’, nothing ever is. **When you’re ready push your changes back to Github and put in a pull request back to the base repo.** +2. https://us-central1-ind-coe.cloudfunctions.net/clock_repository?hrs=10&mins=10 -This exercise is not meant to take an excessive amount of time. It is an opportunity for you to demonstrate your skills without the stress of an interview. If you start to run out of time, it’s ok to leave an imaginary team member a TODO list that details all the things you didn’t quite have time to do in order for your solution to go to prod. -If you need clarification, or would like to request additional information, pease reach out to the interviewer by email. - -## Scenario - -You have just joined a DevOps team. This team lives by DevOps principles and you want to let them know you mean business! This particular team is developing a product that is deployed in a Google Cloud Project. - -This sprint, the team has been asked to work on a new feature that depends on being able to calculate the angle between the hands on a clock face. They’ve asked you to write some code to help out with that. This is an IOT project, and they have sensors emitting times at a pretty low frequency (about 10 a minute), and for some reason they need to be processed and stored as angles. - -You may need to make some assupmtions, that's OK, just document what they are and move on. - -The team loves innovation, so you can use whatever languages and technologies you like to complete this. Approach this problem as if your code will go to production. Whilst we don’t expect the code to be perfect, we are not looking for a hacked together script. - -Your solution should offer the rest of the team a way to submit a time and receive an angle in return or store it somewhere. They are little fuzzy on the best way to get this low frequency data to your service, so if you can offer them any hints on that, they’d be really happy. - -## How to proceed - -**Fork this repo, then get to work.** Remember that this is a DevOps team, so make sure your repo reflects that. Spend however much time you feel is reasonable. It doesn’t matter if the project is ‘done’, nothing ever is. **When you’re ready push your changes back to Github and put in a pull request back to the base repo.** - -Be sure to add in instructions for how to deploy your solution, and document things in a way that the rest of the team can pick this up and run with it. Remember you have all the tools in the GCP arsenal at your disposal. - -We are looking for you to demonstrate your abilities in software practices and DevOps, including reusability, portability, reliability, ease of maintenance etc. - -Think about how this will actually be deployed and maintained in the future as you build on it and expand it. You don’t have to implement deployment practices if you don’t have the time or resources, its ok to just document those. - ---- - -## Product Backlog Item (Sprint Story) - -Here is the story that is in the backlog. - -As with all stories, the team may have been optimistic with how much can be done in the time permitted. It's ok to meet some of the acceptance criteria by documenting what you would do in the next sprint! Prioritize your time and make sure you have some technical content to deliver. - -### Description:- - -As a team
-We need a serivce that we can send a time value to and have it return or store an angle value
-So that we can use it in downstream processing - -### Detail:- - -We need to calculate the angle between the hands on a clock face. For example input 03:00 would yield 90 degrees. - -### Acceptance Criteria:- - -1) Code to perform the calculation -1) How will you deploy this solution (in code or as a todo list if time is limited). i.e. how and where will this run? -1) How will you manage any infrastructure needed? -1) Delivered as a feature branch in the repo fork -1) Bonus points for a working deployed solution in GCP that you can demo at the "sprint review" (ie interview) -1) Any DevOps/Cicd components that would support this feature in a production setting +Output: + return string +e.g. 'Angle -> 90.0' diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 0000000..c06d3ef --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,25 @@ +steps: + - + name: gcr.io/cloud-builders/docker + args: + - build + - '-t' + - 'gcr.io/ind-coe/ar-clock:$COMMIT_SHA' + - . + - + name: gcr.io/cloud-builders/docker + args: + - push + - 'gcr.io/ind-coe/ar-clock:$COMMIT_SHA' + - + name: gcr.io/cloud-builders/gcloud + args: + - functions + - deploy + - clock_angle + - '--runtime' + - python37 + - '--trigger-http' + - '--allow-unauthenticated' + - '--source=https://source.developers.google.com/projects/ind-coe/repos/ar_cloud_clock/moveable-aliases/master/paths/' + dir: 'functions/autodeploy' diff --git a/main.py b/main.py new file mode 100644 index 0000000..b02a3d0 --- /dev/null +++ b/main.py @@ -0,0 +1,40 @@ +""" + _author_ = Arpit Rawal + Funciton gets deploy on google cloud functions +""" +def clock_angle(request): + """Responds to any HTTP request. + Args: + request (flask.Request): HTTP request object. + Returns: + The response text or any set of values that can be turned into a + Response object using + """ + request_json = request.get_json() + request_args = request.args + if request.args and 'hrs' in request.args: + hrs = int(request.args.get('hrs')) + mins = int(request.args.get('mins')) + elif request_json and 'hrs' in request_json: + hrs = int(request_json['hrs']) + mins = int(request_json['mins']) + elif request_args and 'hrs' in request_args: + hrs = int(request_args['hrs']) + mins = int(request_args['mins']) + else: + ans_str = "Cannot compute due to some error" + + if (0 <= int(hrs) <= 12 and 0 <= int(mins) <= 59): + degree_per_min = 6 + degree_per_hour = 30 + degree_intern = 0.5 + + if hrs == 12: + hrs = 0 + + angle_between = abs(hrs*degree_per_hour - mins*degree_per_min + mins*degree_intern) + ans_str = "Angle -> " + str(angle_between) + + else: + ans_str = "Invalid Input, cannot compute, Try Again" + return ans_str diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0f50bb9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask==1.1.2 +pylint==2.5.0 +pytest==5.4.1 diff --git a/test_cases.py b/test_cases.py new file mode 100644 index 0000000..ee150ce --- /dev/null +++ b/test_cases.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Apr 27 17:08:37 2020 + +@author: arawal +Function to unitesting of http function +""" + + + +import os +import sys +import unittest + +from unittest.mock import Mock +from flask import Flask + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +import main + + +app = Flask(__name__) + +class MyTestClass(unittest.TestCase): + """ + Class to implment TestCases for Clock Angle function in main + """ + # initialization logic for the test suite declared in the test module + # code that is executed before all tests in one test run + @classmethod + def setUpClass(cls): + pass + + # clean up logic for the test suite declared in the test module + # code that is executed after all tests in one test run + @classmethod + def tearDownClass(cls): + pass + + # initialization logic + # code that is executed before each test + def setUp(self): + # creates a test client + self.app = app.test_client() + # propagate the exceptions to the test client + self.app.testing = True + + # clean up logic + # code that is executed after each test + def tearDown(self): + pass + + # Tesing angle between the codes + def test_h3m0(self): + """ + Testing at 3 hrs 0 mins - Angle = 90 + """ + hrs = 3 + mins = 0 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 90.0) + + def test_2m45(self): + """ + Testing at 2 hrs 45 mins - Angle = 187.5 + """ + hrs = 2 + mins = 45 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 187.5) + + def test_h8m15(self): + """ + Testing at 8 hrs 15 mins - Angle = 157.5 + """ + hrs = 8 + mins = 15 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 157.5) + + def test_h6m0(self): + """ + Testing at 6 hrs 0 mins - Angle = 180 + """ + hrs = 6 + mins = 0 + data = {'hrs': hrs, 'mins': mins} + req = Mock(get_json=Mock(return_value=data), args=data) + + # Call tested function + angle1 = float(main.clock_angle(req).split('Angle ->')[1]) + self.assertEqual(angle1, 180) + + +# runs the unit tests in the module +if __name__ == '__main__': + unittest.main() From 4f2877747e612cf69d52f70c78506d2c77517985 Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 12:39:47 +0530 Subject: [PATCH 4/9] Resolving error --- main.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/main.py b/main.py index 838f52b..ffb03a1 100644 --- a/main.py +++ b/main.py @@ -33,11 +33,8 @@ def clock_angle(request): hrs = 0 angle_between = abs(hrs*degree_per_hour - mins*degree_per_min + mins*degree_intern) -<<<<<<< HEAD ans_str = "Angle between clock hands {0} hrs and {1} mins is {2}-> ".format(hrs,mins,str(angle_between)) -======= ans_str = "Angle -> " + str(angle_between) ->>>>>>> develop else: ans_str = "Invalid Input, cannot compute, Try Again" From 58806dd06b59cc1b6ee772940527a6c077fcea21 Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 12:43:44 +0530 Subject: [PATCH 5/9] Resolving error --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index ffb03a1..6a39f49 100644 --- a/main.py +++ b/main.py @@ -33,8 +33,8 @@ def clock_angle(request): hrs = 0 angle_between = abs(hrs*degree_per_hour - mins*degree_per_min + mins*degree_intern) - ans_str = "Angle between clock hands {0} hrs and {1} mins is {2}-> ".format(hrs,mins,str(angle_between)) - ans_str = "Angle -> " + str(angle_between) + ans_str = "Angle between clock hands {0} hrs and {1} mins is {2}-> ". \ + format(hrs, mins, str(angle_between)) else: ans_str = "Invalid Input, cannot compute, Try Again" From 76ee73a3d0376c395c19ad5ef524aebba1f1fef4 Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 12:47:02 +0530 Subject: [PATCH 6/9] Resolving error --- main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.py b/main.py index 6a39f49..b9040cd 100644 --- a/main.py +++ b/main.py @@ -33,8 +33,7 @@ def clock_angle(request): hrs = 0 angle_between = abs(hrs*degree_per_hour - mins*degree_per_min + mins*degree_intern) - ans_str = "Angle between clock hands {0} hrs and {1} mins is {2}-> ". \ - format(hrs, mins, str(angle_between)) + ans_str = "Angle ->".format(hrs, mins, str(angle_between)) else: ans_str = "Invalid Input, cannot compute, Try Again" From 75dc931aa4f87354ce383c9b80830fe80c699721 Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 12:49:01 +0530 Subject: [PATCH 7/9] Resolving error --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index b9040cd..157e628 100644 --- a/main.py +++ b/main.py @@ -33,7 +33,7 @@ def clock_angle(request): hrs = 0 angle_between = abs(hrs*degree_per_hour - mins*degree_per_min + mins*degree_intern) - ans_str = "Angle ->".format(hrs, mins, str(angle_between)) + ans_str = "Angle ->{0}".format(str(angle_between)) else: ans_str = "Invalid Input, cannot compute, Try Again" From ea5288e81ab09ef8c7ccea882dfae0dd34d99617 Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 13:16:13 +0530 Subject: [PATCH 8/9] Updated Readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 81cbfcc..23cceaa 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,7 @@ Accessing Function: Output: return string e.g. 'Angle -> 90.0' + +Deploying Method: + The code automatically push changes to Google cloud function. + Google cloud "Source repositroy" has Trigger build on "Google Build" which deploys code to Cloud function "clock_angle". From a907acf339b20334c7db2d097270607841dedadf Mon Sep 17 00:00:00 2001 From: Rawal Date: Thu, 30 Apr 2020 18:26:34 +0530 Subject: [PATCH 9/9] Updated docstring for function --- main.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 157e628..23134cd 100644 --- a/main.py +++ b/main.py @@ -1,14 +1,15 @@ """ _author_ = Arpit Rawal - Funciton gets deploy on google cloud functions """ def clock_angle(request): - """Responds to any HTTP request. + """ + 1. Function calculates angle clock hands + of hours and mins + 2. Responds to any HTTP request. Args: request (flask.Request): HTTP request object. Returns: - The response text or any set of values that can be turned into a - Response object using + The response text will be string in form Angle ->90 """ request_json = request.get_json() request_args = request.args