diff --git a/.env.sample b/.env.sample index 27be902..518df6a 100644 --- a/.env.sample +++ b/.env.sample @@ -40,6 +40,7 @@ EMAIL_USE_TLS= EMAIL_USERNAME= # Cal1Card API URL +MOCK_C1C=false C1C_API_DOMAIN= C1C_API_USERNAME= C1C_API_PASSWORD= diff --git a/config.py b/config.py index fda386c..8f165c1 100644 --- a/config.py +++ b/config.py @@ -46,6 +46,7 @@ def getenv(key, default: Optional[str] = None, *, optional=False): EMAIL_PASSWORD = getenv('EMAIL_PASSWORD', optional=True) # C1C API setup + MOCK_C1C = getenv('MOCK_C1C', 'false').lower() == 'true' C1C_PROXY_URL = getenv('C1C_PROXY_URL', optional=True) C1C_API_DOMAIN = getenv('C1C_API_DOMAIN', optional=True) C1C_API_USERNAME = getenv('C1C_API_USERNAME', optional=True) diff --git a/server/cache.py b/server/cache.py index 1cbc9db..2af22bc 100644 --- a/server/cache.py +++ b/server/cache.py @@ -12,4 +12,4 @@ def cache_key_photo(canvas_id: str): return f'student_photo_{canvas_id}' -cache_life_photo = app.config.get('C1C_PHOTO_CACHE_LIFE') +cache_life_photo = int(app.config.get('C1C_PHOTO_CACHE_LIFE')) diff --git a/server/services/c1c/__init__.py b/server/services/c1c/__init__.py new file mode 100644 index 0000000..54c9c6e --- /dev/null +++ b/server/services/c1c/__init__.py @@ -0,0 +1,43 @@ +from server import app +from server.services.c1c import fake_data + + +def is_mock_c1c() -> bool: + return app.config['MOCK_C1C'] and \ + app.config['FLASK_ENV'].lower() != 'production' + + +class C1C: + def __init__(self, proxy_url, api_domain, username, password): + self.proxy_dict = { + 'http': proxy_url, + 'https': proxy_url + } if proxy_url else None + self.api_domain = api_domain + self.username = username + self.password = password + + def _make_request(self, path, method='GET'): + import requests + url = f'{self.api_domain}{path}' + if self.proxy_dict: + return requests.request(method, url, proxies=self.proxy_dict, + auth=(self.username, self.password)) + else: + return requests.request(method, url, auth=(self.username, self.password)) + + def get_student_photo(self, student_canvas_id): + if is_mock_c1c(): + return fake_data.get_fake_photo(student_canvas_id) + try: + r = self._make_request(f'/c1c-api/v1/photo/{student_canvas_id}') + if r.status_code == 200: + return r.content + else: + return None + except: + return None + + +c1c_client = C1C(app.config['C1C_PROXY_URL'], app.config['C1C_API_DOMAIN'], + app.config['C1C_API_USERNAME'], app.config['C1C_API_PASSWORD']) diff --git a/server/services/c1c/fake_data/__init__.py b/server/services/c1c/fake_data/__init__.py new file mode 100644 index 0000000..46db5b9 --- /dev/null +++ b/server/services/c1c/fake_data/__init__.py @@ -0,0 +1,18 @@ +import json + +FAKE_PHOTO_DICT = None + +if not FAKE_PHOTO_DICT: + with open('server/services/c1c/fake_data/photos.json', 'rb') as f: + FAKE_PHOTO_DICT = json.load(f) + + +def get_fake_photo(student_canvas_id): + print(FAKE_PHOTO_DICT) + try: + with open(f"server/services/c1c/fake_data/photos/{FAKE_PHOTO_DICT[student_canvas_id]}", + 'rb') as f: + return f.read() + except Exception as e: + print(e) + return None diff --git a/server/services/c1c/fake_data/photos.json b/server/services/c1c/fake_data/photos.json new file mode 100644 index 0000000..a25b343 --- /dev/null +++ b/server/services/c1c/fake_data/photos.json @@ -0,0 +1,6 @@ +{ + "123456": "sample1.jpg", + "234567": "sample1.jpg", + "345678": "sample1.jpg", + "456789": "sample1.jpg" +} \ No newline at end of file diff --git a/server/services/c1c/fake_data/photos/sample1.jpg b/server/services/c1c/fake_data/photos/sample1.jpg new file mode 100644 index 0000000..0855568 Binary files /dev/null and b/server/services/c1c/fake_data/photos/sample1.jpg differ diff --git a/server/views.py b/server/views.py index f9f67f9..69fdf81 100644 --- a/server/views.py +++ b/server/views.py @@ -908,30 +908,12 @@ def student_photo(exam_student): photo = cache_store.get(cache_key_photo(student_canvas_id)) if photo is not None: return send_file(io.BytesIO(photo), mimetype='image/jpeg') - - proxy_url = app.config.get('C1C_PROXY_URL') - username = app.config.get('C1C_API_USERNAME') - password = app.config.get('C1C_API_PASSWORD') - url = app.config.get('C1C_API_DOMAIN') + '/c1c-api/v1/photo/' + student_canvas_id - import requests - try: - r = None - if proxy_url: - proxy_dict = { - "http": proxy_url, - "https": proxy_url - } - r = requests.get(url, auth=(username, password), proxies=proxy_dict) - else: - r = requests.get(url, auth=(username, password)) - if r.status_code == 200: - photo = r.content - cache_store.set(cache_key_photo(student_canvas_id), photo, timeout=cache_life_photo) - return send_file(io.BytesIO(photo), mimetype='image/jpeg') - else: - return jsonify({"error": "Photo not available."}), r.status_code - except requests.exceptions.RequestException as e: - return jsonify({"error": "Failed to fetch the photo due to an internal error:" + e.message}), 500 + from server.services.c1c import c1c_client + photo = c1c_client.get_student_photo(student_canvas_id) + if photo is not None: + cache_store.set(cache_key_photo(student_canvas_id), photo, timeout=cache_life_photo) + return send_file(io.BytesIO(photo), mimetype='image/jpeg') + return send_file('static/img/photo-placeholder.png', mimetype='image/png') # endregion