Skip to content
Snippets Groups Projects
Commit 2b74f3af authored by h4-rahman's avatar h4-rahman
Browse files

Added model performance metrics to ML engineer dashbboard, may add more.

Fixed error of creating log when payment is successful.
parent aad141c1
No related branches found
No related tags found
1 merge request!35Added model performance metrics to ML engineer dashbboard, may add more.
...@@ -2333,3 +2333,228 @@ Watching for file changes with StatReloader ...@@ -2333,3 +2333,228 @@ Watching for file changes with StatReloader
Watching for file changes with StatReloader Watching for file changes with StatReloader
/usr/src/app/myproject/settings.py changed, reloading. /usr/src/app/myproject/settings.py changed, reloading.
Watching for file changes with StatReloader Watching for file changes with StatReloader
Watching for file changes with StatReloader
Internal Server Error: /model_performance/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
return view_func(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
return self.dispatch(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/views.py", line 326, in get
if request.user.user_type != 0:
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/utils/functional.py", line 253, in inner
return func(_wrapped, *args)
^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'User' object has no attribute 'user_type'
Internal Server Error: /model_performance/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
return view_func(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
return self.dispatch(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/usr/local/lib/python3.11/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/views.py", line 326, in get
if request.user.user_type != 0:
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/utils/functional.py", line 253, in inner
return func(_wrapped, *args)
^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'User' object has no attribute 'user_type'
/usr/src/app/myapp/models.py changed, reloading.
Watching for file changes with StatReloader
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
Not Found: /model_performance/
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
Not Found: /model_performance/
Watching for file changes with StatReloader
Not Found: /model_performance/
Not Found: /model_performan
Not Found: /model_performance/
Watching for file changes with StatReloader
Not Found: /model_performance/
Not Found: /model_performance/
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
Not Found: /model_performance/
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
Not Found: /model_performance/
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
Not Found: /model_performance/
Request[POST]: https://api.sandbox.paypal.com/v1/oauth2/token
Response[200]: OK, Duration: 0.494332s.
PayPal-Request-Id: e390a7ec-e4d5-469d-ac32-a7ed08f347ad
Request[POST]: https://api.sandbox.paypal.com/v1/payments/payment
Response[201]: Created, Duration: 1.121100s.
Request[POST]: https://api.sandbox.paypal.com/v1/oauth2/token
Response[200]: OK, Duration: 0.638618s.
Request[GET]: https://api.sandbox.paypal.com/v1/payments/payment/PAYID-MYXIHOA1Y946802D6847292A
Response[200]: OK, Duration: 0.332956s.
PayPal-Request-Id: a37bbb23-b0bc-41e7-b52c-9ffe709dab87
Request[POST]: https://api.sandbox.paypal.com/v1/payments/payment/PAYID-MYXIHOA1Y946802D6847292A/execute
Response[200]: OK, Duration: 1.20697s.
Internal Server Error: /payment_success/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/payments.py", line 117, in payment_success
log_data = get_log_data(Action.PAYMENT_SUCCESSFUL, 'success', user=request.user.username)
^^^^^^
NameError: name 'Action' is not defined
/usr/src/app/myapp/models.py changed, reloading.
Watching for file changes with StatReloader
/usr/src/app/myapp/models.py changed, reloading.
Watching for file changes with StatReloader
Not Found: /model_performance/
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
Internal Server Error: /model_performance/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 220, in _get_response
response = response.render()
^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/template/response.py", line 114, in render
self.content = self.rendered_content
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/template/response.py", line 90, in rendered_content
template = self.resolve_template(self.template_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/template/response.py", line 72, in resolve_template
return select_template(template, using=self.using)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/template/loader.py", line 47, in select_template
raise TemplateDoesNotExist(", ".join(template_name_list), chain=chain)
django.template.exceptions.TemplateDoesNotExist: model_performance.html
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
/usr/src/app/myapp/views.py changed, reloading.
Watching for file changes with StatReloader
Request[POST]: https://api.sandbox.paypal.com/v1/oauth2/token
Response[200]: OK, Duration: 0.554958s.
PayPal-Request-Id: 3c73cad9-5766-44f2-8ee7-57c3698da0f6
Request[POST]: https://api.sandbox.paypal.com/v1/payments/payment
Response[201]: Created, Duration: 1.42957s.
Request[POST]: https://api.sandbox.paypal.com/v1/oauth2/token
Response[200]: OK, Duration: 0.283287s.
Request[GET]: https://api.sandbox.paypal.com/v1/payments/payment/PAYID-MYXI6GA67R166720J095691C
Response[200]: OK, Duration: 0.784875s.
PayPal-Request-Id: 6946ac5c-726c-41fe-adad-d0c12b35cccf
Request[POST]: https://api.sandbox.paypal.com/v1/payments/payment/PAYID-MYXI6GA67R166720J095691C/execute
Response[200]: OK, Duration: 1.276130s.
Internal Server Error: /payment_success/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/payments.py", line 117, in payment_success
log_data = get_log_data(Action.PAYMENT_SUCCESSFUL, 'success', user=request.user.username)
^^^^^^
NameError: name 'Action' is not defined
/usr/src/app/myapp/payments.py changed, reloading.
Watching for file changes with StatReloader
Internal Server Error: /payment_success/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/payments.py", line 117, in payment_success
log_data = get_log_data(Action.PAYMENT_SUCCESSFUL, 'success', user=request.user.username)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: get_log_data() got an unexpected keyword argument 'user'
/usr/src/app/myapp/payments.py changed, reloading.
Watching for file changes with StatReloader
Internal Server Error: /payment_success/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/payments.py", line 118, in payment_success
create_log(log_data)
TypeError: create_log() missing 1 required positional argument: 'log_data'
/usr/src/app/myapp/payments.py changed, reloading.
Watching for file changes with StatReloader
Internal Server Error: /payment_success/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/payments.py", line 118, in payment_success
create_log(log_data)
TypeError: create_log() missing 1 required positional argument: 'log_data'
Internal Server Error: /payment_success/
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/app/myapp/payments.py", line 118, in payment_success
create_log(log_data)
TypeError: create_log() missing 1 required positional argument: 'log_data'
/usr/src/app/myapp/payments.py changed, reloading.
Watching for file changes with StatReloader
/usr/src/app/myapp/payments.py changed, reloading.
Watching for file changes with StatReloader
...@@ -43,6 +43,7 @@ services: ...@@ -43,6 +43,7 @@ services:
image: tensorflow/serving image: tensorflow/serving
volumes: volumes:
- ./models:/models - ./models:/models
- ./monitoring_config.txt:/etc/tensorflow_serving/monitoring_config.txt
environment: environment:
- MODEL_NAME=instrument_model - MODEL_NAME=instrument_model
- MODEL_BASE_PATH=/models/instrument_model - MODEL_BASE_PATH=/models/instrument_model
...@@ -50,6 +51,16 @@ services: ...@@ -50,6 +51,16 @@ services:
- --model_base_path=/models/instrument_model - --model_base_path=/models/instrument_model
- --rest_api_port=8501 - --rest_api_port=8501
- --model_name=instrument_model - --model_name=instrument_model
- --monitoring_config_file=/etc/tensorflow_serving/monitoring_config.txt
ports:
- "8501:8501"
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
volumes: volumes:
static_volume: static_volume:
......
prometheus_config {
enable: true,
path: "/monitoring/prometheus/metrics"
}
\ No newline at end of file
...@@ -5,7 +5,7 @@ from django.shortcuts import redirect, render ...@@ -5,7 +5,7 @@ from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from .models import UserTokenCount from .models import Action, UserTokenCount
from .views import get_log_data, create_log from .views import get_log_data, create_log
# Create a payment that can be made via the PayPal API # Create a payment that can be made via the PayPal API
# Create a payment that can be made via the PayPal API # Create a payment that can be made via the PayPal API
...@@ -114,6 +114,6 @@ def payment_cancelled(request): ...@@ -114,6 +114,6 @@ def payment_cancelled(request):
return render(request, 'payment_cancelled.html') return render(request, 'payment_cancelled.html')
def payment_success(request): def payment_success(request):
log_data = get_log_data(Action.PAYMENT_SUCCESSFUL, 'success', user=request.user.username) log_data = get_log_data(Action.PAYMENT_SUCCESSFUL, 'success')
create_log(log_data) create_log(request.user, log_data)
return render(request,'payment_success.html') return render(request,'payment_success.html')
{% extends "_base.html" %}
{% block content %}
<div class="container mx-auto px-4">
<h1 class="text-2xl font-bold mb-4">Model Performance</h1>
{% if metrics %}
<div class="bg-white shadow-md rounded p-6">
<p><strong>Request Count:</strong> {{ metrics.request_count }}</p>
<p><strong>Average Request Latency:</strong> {{ metrics.avg_request_latency|floatformat:2 }} ms</p>
<p><strong>Average Runtime Latency:</strong> {{ metrics.avg_runtime_latency|floatformat:2 }} ms</p>
<p><strong>Model Load Latency:</strong> {{ metrics.model_load_latency }} μs</p>
</div>
{% else %}
<p>Failed to retrieve model performance metrics.</p>
{% endif %}
</div>
{% endblock content %}
\ No newline at end of file
...@@ -127,8 +127,13 @@ ...@@ -127,8 +127,13 @@
</div> </div>
{% comment %} AI content {% endcomment %} {% endif%} {% comment %} AI content {% endcomment %} {% endif%}
</div> </div>
<div class="col-span-2"> <div class="col-span-2">
{% if user_profile.user_type == 2 or user.is_superuser %}
<div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
<h3 class="mb-4 text-xl font-semibold dark:text-white">Model Performance</h3>
<a href="{% url 'model_performance' %}" class="text-blue-500 hover:underline">View Model Performance</a>
</div>
{% endif %}
<div <div
class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800" class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800"
> >
......
from django.urls import path from django.urls import path
from .views import InstrumentDetectionView, index, log_fileupload, users, maintenance, handler404, handler500, terms_conditions, privacy_policy, handling_music_file, pricing, generate_pdf, admin_table, change_user_type from .views import InstrumentDetectionView, ModelPerformanceView, index, log_fileupload, users, maintenance, handler404, handler500, terms_conditions, privacy_policy, handling_music_file, pricing, generate_pdf, admin_table, change_user_type
from .payments import create_payment, execute_payment, payment_cancelled, payment_success from .payments import create_payment, execute_payment, payment_cancelled, payment_success
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
...@@ -23,6 +23,7 @@ urlpatterns = [ ...@@ -23,6 +23,7 @@ urlpatterns = [
path('pricing/', pricing, name='pricing'), path('pricing/', pricing, name='pricing'),
path('generate_pdf/', generate_pdf, name='generate_pdf'), path('generate_pdf/', generate_pdf, name='generate_pdf'),
path('instrument_detection/', InstrumentDetectionView.as_view(), name='instrument_detection'), path('instrument_detection/', InstrumentDetectionView.as_view(), name='instrument_detection'),
path('model_performance/', ModelPerformanceView.as_view(), name='model_performance'),
path('password_change/', auth_views.PasswordChangeView.as_view(template_name='password_change_form.html'), name='password_change'), path('password_change/', auth_views.PasswordChangeView.as_view(template_name='password_change_form.html'), name='password_change'),
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(template_name='password_change_done.html'), name='password_change_done'), path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(template_name='password_change_done.html'), name='password_change_done'),
......
...@@ -31,6 +31,10 @@ from .models import Profile ...@@ -31,6 +31,10 @@ from .models import Profile
from .forms import UserRegisterForm, LoginAuthenticationForm from .forms import UserRegisterForm, LoginAuthenticationForm
from django.contrib.auth.views import LoginView from django.contrib.auth.views import LoginView
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic import TemplateView
import re
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -340,6 +344,46 @@ class InstrumentDetectionView(APIView): ...@@ -340,6 +344,46 @@ class InstrumentDetectionView(APIView):
formatted_scores = "<br>".join([f"{instruments[i]} - {score:.2f}" for i, score in enumerate(prediction)]) formatted_scores = "<br>".join([f"{instruments[i]} - {score:.2f}" for i, score in enumerate(prediction)])
formatted_predictions.append(f"{formatted_window}{formatted_scores}") formatted_predictions.append(f"{formatted_window}{formatted_scores}")
return formatted_predictions return formatted_predictions
class ModelPerformanceView(UserPassesTestMixin, TemplateView):
template_name = 'model_performance.html'
def test_func(self):
return self.request.user.is_authenticated and (self.request.user.is_superuser or self.request.user.profile.user_type == 2)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
metrics_url = 'http://tensorflow_serving:8501/monitoring/prometheus/metrics'
response = requests.get(metrics_url)
if response.status_code == 200:
metrics_data = response.text
# Extract metrics using regular expressions
request_count = re.search(r':tensorflow:serving:request_count{model_name="instrument_model",status="OK"} (\d+)', metrics_data)
request_latency_sum = re.search(r':tensorflow:serving:request_latency_sum{model_name="instrument_model",API="predict",entrypoint="REST"} ([\d\.e\+]+)', metrics_data)
request_latency_count = re.search(r':tensorflow:serving:request_latency_count{model_name="instrument_model",API="predict",entrypoint="REST"} (\d+)', metrics_data)
runtime_latency_sum = re.search(r':tensorflow:serving:runtime_latency_sum{model_name="instrument_model",API="Predict",runtime="TF1"} ([\d\.e\+]+)', metrics_data)
runtime_latency_count = re.search(r':tensorflow:serving:runtime_latency_count{model_name="instrument_model",API="Predict",runtime="TF1"} (\d+)', metrics_data)
model_load_latency = re.search(r':tensorflow:cc:saved_model:load_latency{model_path="/models/instrument_model/2"} (\d+)', metrics_data)
# Calculate average latencies
avg_request_latency = float(request_latency_sum.group(1)) / float(request_latency_count.group(1)) if request_latency_sum and request_latency_count else None
avg_runtime_latency = float(runtime_latency_sum.group(1)) / float(runtime_latency_count.group(1)) if runtime_latency_sum and runtime_latency_count else None
context['metrics'] = {
'request_count': request_count.group(1) if request_count else None,
'avg_request_latency': avg_request_latency,
'avg_runtime_latency': avg_runtime_latency,
'model_load_latency': model_load_latency.group(1) if model_load_latency else None
}
else:
context['metrics'] = None
return context
def change_user_type(request, user_id): def change_user_type(request, user_id):
if request.method == 'POST': if request.method == 'POST':
user_type = request.POST.get('user_type') user_type = request.POST.get('user_type')
......
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'tensorflow_serving'
scrape_interval: 5s
metrics_path: /monitoring/prometheus/metrics
static_configs:
- targets: ['tensorflow_serving:8501']
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment