diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4f2207 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +*.pyc +.idea/* +venv/* +__pycache__/* + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +Desktop.in \ No newline at end of file diff --git a/README.md b/README.md index 4a77c09..ed60b49 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,20 @@ We need to calculate the angle between the hands on a clock face. For example in ### 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 +1) Code to perform the calculation
+ Code can found in the repo along with required tests and environment setup. +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?
+ To deploy this solution I have used GCP Cloud Functions. As our data is coming from sensors at low frequency and as the compute needed is pretty low, as far as I think there is no need of dedicated instance. + The deployed solution can be tested by hitting this url,
+ https://us-central1-get-ride-1568029178700.cloudfunctions.net/cal_angle?time=23:00
+ with appropiate time param
+ Currently I have used the core computation code in a script and deployed the same, but for the purpose of maintenance, my repo can made into a GCP cloud repo which can be used as a backend to the service. + +1) How will you manage any infrastructure needed?
+As the solution is deployed as GCP Cloud Function, scalibility is taken care by GCP. Also the cost is pretty low, as first 2 million requests are free of cost. +1) Delivered as a feature branch in the repo fork
+ Already have raised a PR against the upstream branch +1) Bonus points for a working deployed solution in GCP that you can demo at the "sprint review" (ie interview)
+ Solution is ready and can be tested by browser +1) Any DevOps/Cicd components that would support this feature in a production setting
+At the current scale, no other DeveOps/Cicd components are required diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..6ae7e5a --- /dev/null +++ b/app.yaml @@ -0,0 +1 @@ +runtime: python37 \ No newline at end of file diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..cad6fdd --- /dev/null +++ b/constants.py @@ -0,0 +1,3 @@ +DEGREES_PER_HOUR = 30 +DEGREES_PER_MIN = 6 + diff --git a/main.py b/main.py new file mode 100644 index 0000000..ddee287 --- /dev/null +++ b/main.py @@ -0,0 +1,46 @@ +from flask import Flask, jsonify +import datetime +from constants import DEGREES_PER_HOUR, DEGREES_PER_MIN + +app = Flask(__name__) + +@app.route('/cal_angle/', methods=['GET']) +def return_angle(time): + """ + A function which takes time '03:00' as input and returns the clockwise angle between hands of clock as response + To calculate angle, the method used is as follows + Angle per hour = 30(360/12) + Angle per min = 6(360/60) + Angle covered by hour hand = 30 * hour + Angle covered by minute hand = 6 * minute + """ + if not check_valid_time(time): + return jsonify({'response': 'The format for time is not valid, format should be of type "03:00"'}) + hour, minute = [int(i) for i in time.split(':')] + # Converting hour to range to 1-12 + hour = (hour-12) if hour > 12 else hour + # Per hour rotation is 30 degrees + hour_hand_angle = hour * DEGREES_PER_HOUR if hour != 12 else 0 + # Per minute rotation is 6 degrees + minute_hand_angle = minute * DEGREES_PER_MIN if minute != 60 else 0 + # Calculating final angle + angle = abs(hour_hand_angle - minute_hand_angle) + # Return as response + return jsonify({'response': angle}) + + +def check_valid_time(s): + """ + To check the validity of the passed argument to return_angle function + """ + try: + datetime.datetime.strptime(s, "%H:%M") + return True + except Exception: + return False + + + +if __name__ == "__main__": + app.run(host='127.0.0.1', port=8000, debug=True) + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7deaf3b --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Flask==1.1.2 \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/main_test.py b/tests/main_test.py new file mode 100644 index 0000000..3ddac0e --- /dev/null +++ b/tests/main_test.py @@ -0,0 +1,35 @@ +import unittest + +from main import app, return_angle, check_valid_time + +class TestClocks(unittest.TestCase): + + def setUp(self): + self.app_context = app.app_context() + self.app_context.push() + + def test_return_angle(self): + """Test cases for return_angle function""" + + response = return_angle("03:00").get_json() + expected_response = {'response': 90} + + exception_response = return_angle("24:00").get_json() + expected_exception_response = {'response': 'The format for time is not valid, ' + 'format should be of type "03:00"'} + + self.assertEqual(response['response'], expected_response['response']) + self.assertEqual(exception_response['response'], expected_exception_response['response']) + + def test_check_valid_time(self): + """Test case for check_valid_time function""" + valid_response = check_valid_time("04:00") + invalid_response = check_valid_time("24:00") + + self.assertTrue(valid_response) + self.assertFalse(invalid_response) + + +if __name__ == "__main__": + unittest.main() +