diff --git a/apps/challenges/aws_utils.py b/apps/challenges/aws_utils.py index d9fae7905c..0a8b2b03bb 100644 --- a/apps/challenges/aws_utils.py +++ b/apps/challenges/aws_utils.py @@ -16,6 +16,16 @@ construct_and_send_worker_start_mail, construct_and_send_eks_cluster_creation_mail, ) +from .task_definitions import ( + container_definition_code_upload_worker, + container_definition_submission_worker, + delete_service_args, + task_definition, + task_definition_code_upload_worker, + task_definition_static_code_upload_worker, + service_definition, + update_service_args, +) from base.utils import get_boto3_client, send_email from evalai.celery import app @@ -83,489 +93,6 @@ } -task_definition = """ -{{ - "family":"{queue_name}", - "executionRoleArn":"{EXECUTION_ROLE_ARN}", - "networkMode":"awsvpc", - "containerDefinitions":[ - {{ - "name": "{container_name}", - "image": "{WORKER_IMAGE}", - "essential": True, - "environment": [ - {{ - "name": "AWS_DEFAULT_REGION", - "value": "{AWS_REGION}" - }}, - {{ - "name": "AWS_ACCOUNT_ID", - "value": "{AWS_ACCOUNT_ID}" - }}, - {{ - "name": "AWS_ACCESS_KEY_ID", - "value": "{AWS_ACCESS_KEY_ID}" - }}, - {{ - "name": "AWS_SECRET_ACCESS_KEY", - "value": "{AWS_SECRET_ACCESS_KEY}" - }}, - {{ - "name": "AWS_STORAGE_BUCKET_NAME", - "value": "{AWS_STORAGE_BUCKET_NAME}" - }}, - {{ - "name": "CHALLENGE_PK", - "value": "{challenge_pk}" - }}, - {{ - "name": "CHALLENGE_QUEUE", - "value": "{queue_name}" - }}, - {{ - "name": "DJANGO_SERVER", - "value": "{DJANGO_SERVER}" - }}, - {{ - "name": "DJANGO_SETTINGS_MODULE", - "value": "settings.{ENV}" - }}, - {{ - "name": "DEBUG", - "value": "{DEBUG}" - }}, - {{ - "name": "EMAIL_HOST", - "value": "{EMAIL_HOST}" - }}, - {{ - "name": "EMAIL_HOST_PASSWORD", - "value": "{EMAIL_HOST_PASSWORD}" - }}, - {{ - "name": "EMAIL_HOST_USER", - "value": "{EMAIL_HOST_USER}" - }}, - {{ - "name": "EMAIL_PORT", - "value": "{EMAIL_PORT}" - }}, - {{ - "name": "EMAIL_USE_TLS", - "value": "{EMAIL_USE_TLS}" - }}, - {{ - "name": "MEMCACHED_LOCATION", - "value": "{MEMCACHED_LOCATION}" - }}, - {{ - "name": "PYTHONUNBUFFERED", - "value": "1" - }}, - {{ - "name": "RDS_DB_NAME", - "value": "{RDS_DB_NAME}" - }}, - {{ - "name": "RDS_HOSTNAME", - "value": "{RDS_HOSTNAME}" - }}, - {{ - "name": "RDS_PASSWORD", - "value": "{RDS_PASSWORD}" - }}, - {{ - "name": "RDS_USERNAME", - "value": "{RDS_USERNAME}" - }}, - {{ - "name": "RDS_PORT", - "value": "{RDS_PORT}" - }}, - {{ - "name": "SECRET_KEY", - "value": "{SECRET_KEY}" - }}, - {{ - "name": "SENTRY_URL", - "value": "{SENTRY_URL}" - }}, - {{ - "name": "AWS_SES_REGION_NAME", - "value": "{AWS_SES_REGION_NAME}" - }}, - {{ - "name": "AWS_SES_REGION_ENDPOINT", - "value": "{AWS_SES_REGION_ENDPOINT}" - }} - ], - "workingDirectory": "/code", - "readonlyRootFilesystem": False, - "logConfiguration": {{ - "logDriver": "awslogs", - "options": {{ - "awslogs-group": "{log_group_name}", - "awslogs-region": "us-east-1", - "awslogs-stream-prefix": "{queue_name}", - "awslogs-create-group": "true", - }}, - }}, - }} - ], - "requiresCompatibilities":[ - "FARGATE" - ], - "cpu": "{CPU}", - "memory": "{MEMORY}", -}} -""" - -task_definition_code_upload_worker = """ -{{ - "family":"{queue_name}", - "executionRoleArn":"{EXECUTION_ROLE_ARN}", - "networkMode":"awsvpc", - "containerDefinitions":[ - {{ - "name": "{code_upload_container_name}", - "image": "{CODE_UPLOAD_WORKER_IMAGE}", - "essential": True, - "environment": [ - {{ - "name": "AWS_DEFAULT_REGION", - "value": "{AWS_REGION}" - }}, - {{ - "name": "AWS_ACCESS_KEY_ID", - "value": "{AWS_ACCESS_KEY_ID}" - }}, - {{ - "name": "AWS_SECRET_ACCESS_KEY", - "value": "{AWS_SECRET_ACCESS_KEY}" - }}, - {{ - "name": "CLUSTER_NAME", - "value": "{cluster_name}" - }}, - {{ - "name": "CLUSTER_ENDPOINT", - "value": "{cluster_endpoint}" - }}, - {{ - "name": "CERTIFICATE", - "value": "{certificate}" - }}, - {{ - "name": "CIDR", - "value": "{CIDR}" - }}, - {{ - "name": "QUEUE_NAME", - "value": "{queue_name}" - }}, - {{ - "name": "EVALAI_API_SERVER", - "value": "{EVALAI_API_SERVER}" - }}, - - {{ - "name": "AUTH_TOKEN", - "value": "{auth_token}" - }}, - - {{ - "name": "EVALAI_DNS", - "value": "{EVALAI_DNS}" - }}, - - {{ - "name": "EFS_ID", - "value": "{EFS_ID}" - }} - - ], - "workingDirectory": "/code", - "readonlyRootFilesystem": False, - "logConfiguration": {{ - "logDriver": "awslogs", - "options": {{ - "awslogs-group": "{log_group_name}", - "awslogs-region": "us-east-1", - "awslogs-stream-prefix": "{queue_name}", - "awslogs-create-group": "true", - }}, - }}, - }} - ], - "requiresCompatibilities":[ - "FARGATE" - ], - "cpu": "{CPU}", - "memory": "{MEMORY}", -}} -""" - -task_definition_static_code_upload_worker = """ -{{ - "family":"{queue_name}", - "executionRoleArn":"{EXECUTION_ROLE_ARN}", - "networkMode":"awsvpc", - "containerDefinitions":[ - {{ - "name": "{code_upload_container_name}", - "image": "{CODE_UPLOAD_WORKER_IMAGE}", - "essential": True, - "environment": [ - {{ - "name": "AWS_DEFAULT_REGION", - "value": "{AWS_REGION}" - }}, - {{ - "name": "AWS_ACCESS_KEY_ID", - "value": "{AWS_ACCESS_KEY_ID}" - }}, - {{ - "name": "AWS_SECRET_ACCESS_KEY", - "value": "{AWS_SECRET_ACCESS_KEY}" - }}, - {{ - "name": "CLUSTER_NAME", - "value": "{cluster_name}" - }}, - {{ - "name": "CLUSTER_ENDPOINT", - "value": "{cluster_endpoint}" - }}, - {{ - "name": "CERTIFICATE", - "value": "{certificate}" - }}, - {{ - "name": "CIDR", - "value": "{CIDR}" - }}, - {{ - "name": "QUEUE_NAME", - "value": "{queue_name}" - }}, - {{ - "name": "EVALAI_API_SERVER", - "value": "{EVALAI_API_SERVER}" - }}, - - {{ - "name": "AUTH_TOKEN", - "value": "{auth_token}" - }}, - - {{ - "name": "EVALAI_DNS", - "value": "{EVALAI_DNS}" - }}, - - {{ - "name": "EFS_ID", - "value": "{EFS_ID}" - }} - - ], - "workingDirectory": "/code", - "readonlyRootFilesystem": False, - "logConfiguration": {{ - "logDriver": "awslogs", - "options": {{ - "awslogs-group": "{log_group_name}", - "awslogs-region": "us-east-1", - "awslogs-stream-prefix": "{queue_name}", - "awslogs-create-group": "true", - }}, - }}, - }}, - {{ - "name": "{container_name}", - "image": "{WORKER_IMAGE}", - "essential": True, - "environment": [ - {{ - "name": "AWS_DEFAULT_REGION", - "value": "{AWS_REGION}" - }}, - {{ - "name": "AWS_ACCOUNT_ID", - "value": "{AWS_ACCOUNT_ID}" - }}, - {{ - "name": "AWS_ACCESS_KEY_ID", - "value": "{AWS_ACCESS_KEY_ID}" - }}, - {{ - "name": "AWS_SECRET_ACCESS_KEY", - "value": "{AWS_SECRET_ACCESS_KEY}" - }}, - {{ - "name": "AWS_STORAGE_BUCKET_NAME", - "value": "{AWS_STORAGE_BUCKET_NAME}" - }}, - {{ - "name": "CHALLENGE_PK", - "value": "{challenge_pk}" - }}, - {{ - "name": "CHALLENGE_QUEUE", - "value": "{queue_name}" - }}, - {{ - "name": "DJANGO_SERVER", - "value": "{DJANGO_SERVER}" - }}, - {{ - "name": "DJANGO_SETTINGS_MODULE", - "value": "settings.{ENV}" - }}, - {{ - "name": "DEBUG", - "value": "{DEBUG}" - }}, - {{ - "name": "EMAIL_HOST", - "value": "{EMAIL_HOST}" - }}, - {{ - "name": "EMAIL_HOST_PASSWORD", - "value": "{EMAIL_HOST_PASSWORD}" - }}, - {{ - "name": "EMAIL_HOST_USER", - "value": "{EMAIL_HOST_USER}" - }}, - {{ - "name": "EMAIL_PORT", - "value": "{EMAIL_PORT}" - }}, - {{ - "name": "EMAIL_USE_TLS", - "value": "{EMAIL_USE_TLS}" - }}, - {{ - "name": "MEMCACHED_LOCATION", - "value": "{MEMCACHED_LOCATION}" - }}, - {{ - "name": "PYTHONUNBUFFERED", - "value": "1" - }}, - {{ - "name": "RDS_DB_NAME", - "value": "{RDS_DB_NAME}" - }}, - {{ - "name": "RDS_HOSTNAME", - "value": "{RDS_HOSTNAME}" - }}, - {{ - "name": "RDS_PASSWORD", - "value": "{RDS_PASSWORD}" - }}, - {{ - "name": "RDS_USERNAME", - "value": "{RDS_USERNAME}" - }}, - {{ - "name": "RDS_PORT", - "value": "{RDS_PORT}" - }}, - {{ - "name": "SECRET_KEY", - "value": "{SECRET_KEY}" - }}, - {{ - "name": "SENTRY_URL", - "value": "{SENTRY_URL}" - }}, - {{ - "name": "AWS_SES_REGION_NAME", - "value": "{AWS_SES_REGION_NAME}" - }}, - {{ - "name": "AWS_SES_REGION_ENDPOINT", - "value": "{AWS_SES_REGION_ENDPOINT}" - }} - ], - "workingDirectory": "/code", - "readonlyRootFilesystem": False, - "logConfiguration": {{ - "logDriver": "awslogs", - "options": {{ - "awslogs-group": "{log_group_name}", - "awslogs-region": "us-east-1", - "awslogs-stream-prefix": "{queue_name}", - "awslogs-create-group": "true", - }}, - }}, - }} - - ], - "requiresCompatibilities":[ - "FARGATE" - ], - "cpu": "{CPU}", - "memory": "{MEMORY}", -}} -""" - -service_definition = """ -{{ - "cluster":"{CLUSTER}", - "serviceName":"{service_name}", - "taskDefinition":"{task_def_arn}", - "desiredCount":1, - "clientToken":"{client_token}", - "launchType":"FARGATE", - "platformVersion":"LATEST", - "networkConfiguration":{{ - "awsvpcConfiguration": {{ - "subnets": [ - "{SUBNET_1}", - "{SUBNET_2}", - ], - 'securityGroups': [ - "{SUBNET_SECURITY_GROUP}", - ], - "assignPublicIp": "ENABLED" - }} - }}, - "schedulingStrategy":"REPLICA", - "deploymentController":{{ - "type": "ECS" - }}, - "deploymentConfiguration":{{ - "deploymentCircuitBreaker":{{ - "enable": True, - "rollback": False - }} - }} -}} -""" - -update_service_args = """ -{{ - "cluster":"{CLUSTER}", - "service":"{service_name}", - "desiredCount":num_of_tasks, - "taskDefinition":"{task_def_arn}", - "forceNewDeployment":{force_new_deployment} -}} -""" - -delete_service_args = """ -{{ - "cluster": "{CLUSTER}", - "service": "{service_name}", - "force": False -}} -""" - - def get_code_upload_setup_meta_for_challenge(challenge_pk): """ Return the EKS cluster network and arn meta for a challenge @@ -668,25 +195,41 @@ def register_task_def_by_challenge_pk(client, queue_name, challenge): # challenge host auth token to be used by code-upload-worker token = JwtToken.objects.get(user=challenge.creator.created_by) if challenge.is_static_dataset_code_upload: + code_upload_container = ( + container_definition_code_upload_worker.format( + queue_name=queue_name, + code_upload_container_name=code_upload_container_name, + auth_token=token.refresh_token, + cluster_name=cluster_name, + cluster_endpoint=cluster_endpoint, + certificate=cluster_certificate, + log_group_name=log_group_name, + EVALAI_DNS=EVALAI_DNS, + EFS_ID=efs_id, + **COMMON_SETTINGS_DICT, + **challenge_aws_keys, + ) + ) + submission_container = ( + container_definition_submission_worker.format( + queue_name=queue_name, + container_name=container_name, + ENV=ENV, + challenge_pk=challenge.pk, + log_group_name=log_group_name, + AWS_SES_REGION_NAME=AWS_SES_REGION_NAME, + AWS_SES_REGION_ENDPOINT=AWS_SES_REGION_ENDPOINT, + **COMMON_SETTINGS_DICT, + **aws_keys, + ) + ) definition = task_definition_static_code_upload_worker.format( queue_name=queue_name, - container_name=container_name, - code_upload_container_name=code_upload_container_name, - ENV=ENV, - challenge_pk=challenge.pk, - auth_token=token.refresh_token, - cluster_name=cluster_name, - cluster_endpoint=cluster_endpoint, - certificate=cluster_certificate, + code_upload_container=code_upload_container, + submission_container=submission_container, CPU=worker_cpu_cores, MEMORY=worker_memory, - log_group_name=log_group_name, - EVALAI_DNS=EVALAI_DNS, - EFS_ID=efs_id, - AWS_SES_REGION_NAME=AWS_SES_REGION_NAME, - AWS_SES_REGION_ENDPOINT=AWS_SES_REGION_ENDPOINT, **COMMON_SETTINGS_DICT, - **challenge_aws_keys, ) else: definition = task_definition_code_upload_worker.format( diff --git a/apps/challenges/serializers.py b/apps/challenges/serializers.py index 4fa6a24bee..e93250a399 100644 --- a/apps/challenges/serializers.py +++ b/apps/challenges/serializers.py @@ -64,6 +64,7 @@ class Meta: "cli_version", "remote_evaluation", "workers", + "created_at", ) diff --git a/apps/challenges/task_definitions.py b/apps/challenges/task_definitions.py new file mode 100644 index 0000000000..a737513594 --- /dev/null +++ b/apps/challenges/task_definitions.py @@ -0,0 +1,488 @@ +task_definition = """ +{{ + "family":"{queue_name}", + "executionRoleArn":"{EXECUTION_ROLE_ARN}", + "networkMode":"awsvpc", + "containerDefinitions":[ + {{ + "name": "{container_name}", + "image": "{WORKER_IMAGE}", + "essential": True, + "environment": [ + {{ + "name": "AWS_DEFAULT_REGION", + "value": "{AWS_REGION}" + }}, + {{ + "name": "AWS_ACCOUNT_ID", + "value": "{AWS_ACCOUNT_ID}" + }}, + {{ + "name": "AWS_ACCESS_KEY_ID", + "value": "{AWS_ACCESS_KEY_ID}" + }}, + {{ + "name": "AWS_SECRET_ACCESS_KEY", + "value": "{AWS_SECRET_ACCESS_KEY}" + }}, + {{ + "name": "AWS_STORAGE_BUCKET_NAME", + "value": "{AWS_STORAGE_BUCKET_NAME}" + }}, + {{ + "name": "CHALLENGE_PK", + "value": "{challenge_pk}" + }}, + {{ + "name": "CHALLENGE_QUEUE", + "value": "{queue_name}" + }}, + {{ + "name": "DJANGO_SERVER", + "value": "{DJANGO_SERVER}" + }}, + {{ + "name": "DJANGO_SETTINGS_MODULE", + "value": "settings.{ENV}" + }}, + {{ + "name": "DEBUG", + "value": "{DEBUG}" + }}, + {{ + "name": "EMAIL_HOST", + "value": "{EMAIL_HOST}" + }}, + {{ + "name": "EMAIL_HOST_PASSWORD", + "value": "{EMAIL_HOST_PASSWORD}" + }}, + {{ + "name": "EMAIL_HOST_USER", + "value": "{EMAIL_HOST_USER}" + }}, + {{ + "name": "EMAIL_PORT", + "value": "{EMAIL_PORT}" + }}, + {{ + "name": "EMAIL_USE_TLS", + "value": "{EMAIL_USE_TLS}" + }}, + {{ + "name": "MEMCACHED_LOCATION", + "value": "{MEMCACHED_LOCATION}" + }}, + {{ + "name": "PYTHONUNBUFFERED", + "value": "1" + }}, + {{ + "name": "RDS_DB_NAME", + "value": "{RDS_DB_NAME}" + }}, + {{ + "name": "RDS_HOSTNAME", + "value": "{RDS_HOSTNAME}" + }}, + {{ + "name": "RDS_PASSWORD", + "value": "{RDS_PASSWORD}" + }}, + {{ + "name": "RDS_USERNAME", + "value": "{RDS_USERNAME}" + }}, + {{ + "name": "RDS_PORT", + "value": "{RDS_PORT}" + }}, + {{ + "name": "SECRET_KEY", + "value": "{SECRET_KEY}" + }}, + {{ + "name": "SENTRY_URL", + "value": "{SENTRY_URL}" + }}, + {{ + "name": "AWS_SES_REGION_NAME", + "value": "{AWS_SES_REGION_NAME}" + }}, + {{ + "name": "AWS_SES_REGION_ENDPOINT", + "value": "{AWS_SES_REGION_ENDPOINT}" + }} + ], + "workingDirectory": "/code", + "readonlyRootFilesystem": False, + "logConfiguration": {{ + "logDriver": "awslogs", + "options": {{ + "awslogs-group": "{log_group_name}", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "{queue_name}", + "awslogs-create-group": "true", + }}, + }}, + }} + ], + "requiresCompatibilities":[ + "FARGATE" + ], + "cpu": "{CPU}", + "memory": "{MEMORY}", +}} +""" + +task_definition_code_upload_worker = """ +{{ + "family":"{queue_name}", + "executionRoleArn":"{EXECUTION_ROLE_ARN}", + "networkMode":"awsvpc", + "containerDefinitions":[ + {{ + "name": "{code_upload_container_name}", + "image": "{CODE_UPLOAD_WORKER_IMAGE}", + "essential": True, + "environment": [ + {{ + "name": "AWS_DEFAULT_REGION", + "value": "{AWS_REGION}" + }}, + {{ + "name": "AWS_ACCESS_KEY_ID", + "value": "{AWS_ACCESS_KEY_ID}" + }}, + {{ + "name": "AWS_SECRET_ACCESS_KEY", + "value": "{AWS_SECRET_ACCESS_KEY}" + }}, + {{ + "name": "CLUSTER_NAME", + "value": "{cluster_name}" + }}, + {{ + "name": "CLUSTER_ENDPOINT", + "value": "{cluster_endpoint}" + }}, + {{ + "name": "CERTIFICATE", + "value": "{certificate}" + }}, + {{ + "name": "CIDR", + "value": "{CIDR}" + }}, + {{ + "name": "QUEUE_NAME", + "value": "{queue_name}" + }}, + {{ + "name": "EVALAI_API_SERVER", + "value": "{EVALAI_API_SERVER}" + }}, + + {{ + "name": "AUTH_TOKEN", + "value": "{auth_token}" + }}, + + {{ + "name": "EVALAI_DNS", + "value": "{EVALAI_DNS}" + }}, + + {{ + "name": "EFS_ID", + "value": "{EFS_ID}" + }} + + ], + "workingDirectory": "/code", + "readonlyRootFilesystem": False, + "logConfiguration": {{ + "logDriver": "awslogs", + "options": {{ + "awslogs-group": "{log_group_name}", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "{queue_name}", + "awslogs-create-group": "true", + }}, + }}, + }} + ], + "requiresCompatibilities":[ + "FARGATE" + ], + "cpu": "{CPU}", + "memory": "{MEMORY}", +}} +""" + +task_definition_static_code_upload_worker = """ +{{ + "family":"{queue_name}", + "executionRoleArn":"{EXECUTION_ROLE_ARN}", + "networkMode":"awsvpc", + "containerDefinitions":[ + {code_upload_container}, + {submission_container} + ], + "requiresCompatibilities":[ + "FARGATE" + ], + "cpu": "{CPU}", + "memory": "{MEMORY}", +}} +""" + +container_definition_submission_worker = """ +{{ + "name": "{container_name}", + "image": "{WORKER_IMAGE}", + "essential": True, + "environment": [ + {{ + "name": "AWS_DEFAULT_REGION", + "value": "{AWS_REGION}" + }}, + {{ + "name": "AWS_ACCOUNT_ID", + "value": "{AWS_ACCOUNT_ID}" + }}, + {{ + "name": "AWS_ACCESS_KEY_ID", + "value": "{AWS_ACCESS_KEY_ID}" + }}, + {{ + "name": "AWS_SECRET_ACCESS_KEY", + "value": "{AWS_SECRET_ACCESS_KEY}" + }}, + {{ + "name": "AWS_STORAGE_BUCKET_NAME", + "value": "{AWS_STORAGE_BUCKET_NAME}" + }}, + {{ + "name": "CHALLENGE_PK", + "value": "{challenge_pk}" + }}, + {{ + "name": "CHALLENGE_QUEUE", + "value": "{queue_name}" + }}, + {{ + "name": "DJANGO_SERVER", + "value": "{DJANGO_SERVER}" + }}, + {{ + "name": "DJANGO_SETTINGS_MODULE", + "value": "settings.{ENV}" + }}, + {{ + "name": "DEBUG", + "value": "{DEBUG}" + }}, + {{ + "name": "EMAIL_HOST", + "value": "{EMAIL_HOST}" + }}, + {{ + "name": "EMAIL_HOST_PASSWORD", + "value": "{EMAIL_HOST_PASSWORD}" + }}, + {{ + "name": "EMAIL_HOST_USER", + "value": "{EMAIL_HOST_USER}" + }}, + {{ + "name": "EMAIL_PORT", + "value": "{EMAIL_PORT}" + }}, + {{ + "name": "EMAIL_USE_TLS", + "value": "{EMAIL_USE_TLS}" + }}, + {{ + "name": "MEMCACHED_LOCATION", + "value": "{MEMCACHED_LOCATION}" + }}, + {{ + "name": "PYTHONUNBUFFERED", + "value": "1" + }}, + {{ + "name": "RDS_DB_NAME", + "value": "{RDS_DB_NAME}" + }}, + {{ + "name": "RDS_HOSTNAME", + "value": "{RDS_HOSTNAME}" + }}, + {{ + "name": "RDS_PASSWORD", + "value": "{RDS_PASSWORD}" + }}, + {{ + "name": "RDS_USERNAME", + "value": "{RDS_USERNAME}" + }}, + {{ + "name": "RDS_PORT", + "value": "{RDS_PORT}" + }}, + {{ + "name": "SECRET_KEY", + "value": "{SECRET_KEY}" + }}, + {{ + "name": "SENTRY_URL", + "value": "{SENTRY_URL}" + }}, + {{ + "name": "AWS_SES_REGION_NAME", + "value": "{AWS_SES_REGION_NAME}" + }}, + {{ + "name": "AWS_SES_REGION_ENDPOINT", + "value": "{AWS_SES_REGION_ENDPOINT}" + }} + ], + "workingDirectory": "/code", + "readonlyRootFilesystem": False, + "logConfiguration": {{ + "logDriver": "awslogs", + "options": {{ + "awslogs-group": "{log_group_name}", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "{queue_name}", + "awslogs-create-group": "true", + }}, + }}, +}} +""" + +container_definition_code_upload_worker = """ +{{ + "name": "{code_upload_container_name}", + "image": "{CODE_UPLOAD_WORKER_IMAGE}", + "essential": True, + "environment": [ + {{ + "name": "AWS_DEFAULT_REGION", + "value": "{AWS_REGION}" + }}, + {{ + "name": "AWS_ACCESS_KEY_ID", + "value": "{AWS_ACCESS_KEY_ID}" + }}, + {{ + "name": "AWS_SECRET_ACCESS_KEY", + "value": "{AWS_SECRET_ACCESS_KEY}" + }}, + {{ + "name": "CLUSTER_NAME", + "value": "{cluster_name}" + }}, + {{ + "name": "CLUSTER_ENDPOINT", + "value": "{cluster_endpoint}" + }}, + {{ + "name": "CERTIFICATE", + "value": "{certificate}" + }}, + {{ + "name": "CIDR", + "value": "{CIDR}" + }}, + {{ + "name": "QUEUE_NAME", + "value": "{queue_name}" + }}, + {{ + "name": "EVALAI_API_SERVER", + "value": "{EVALAI_API_SERVER}" + }}, + + {{ + "name": "AUTH_TOKEN", + "value": "{auth_token}" + }}, + + {{ + "name": "EVALAI_DNS", + "value": "{EVALAI_DNS}" + }}, + + {{ + "name": "EFS_ID", + "value": "{EFS_ID}" + }} + + ], + "workingDirectory": "/code", + "readonlyRootFilesystem": False, + "logConfiguration": {{ + "logDriver": "awslogs", + "options": {{ + "awslogs-group": "{log_group_name}", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "{queue_name}", + "awslogs-create-group": "true", + }}, + }}, +}} +""" + +service_definition = """ +{{ + "cluster":"{CLUSTER}", + "serviceName":"{service_name}", + "taskDefinition":"{task_def_arn}", + "desiredCount":1, + "clientToken":"{client_token}", + "launchType":"FARGATE", + "platformVersion":"LATEST", + "networkConfiguration":{{ + "awsvpcConfiguration": {{ + "subnets": [ + "{SUBNET_1}", + "{SUBNET_2}", + ], + 'securityGroups': [ + "{SUBNET_SECURITY_GROUP}", + ], + "assignPublicIp": "ENABLED" + }} + }}, + "schedulingStrategy":"REPLICA", + "deploymentController":{{ + "type": "ECS" + }}, + "deploymentConfiguration":{{ + "deploymentCircuitBreaker":{{ + "enable": True, + "rollback": False + }} + }} +}} +""" + +update_service_args = """ +{{ + "cluster":"{CLUSTER}", + "service":"{service_name}", + "desiredCount":num_of_tasks, + "taskDefinition":"{task_def_arn}", + "forceNewDeployment":{force_new_deployment} +}} +""" + +delete_service_args = """ +{{ + "cluster": "{CLUSTER}", + "service": "{service_name}", + "force": False +}} +""" diff --git a/apps/challenges/views.py b/apps/challenges/views.py index f6f2a7b014..1397f6142b 100644 --- a/apps/challenges/views.py +++ b/apps/challenges/views.py @@ -123,6 +123,7 @@ ) from .aws_utils import ( + delete_workers, start_workers, stop_workers, restart_workers, @@ -2822,12 +2823,19 @@ def manage_worker(request, challenge_pk, action): return Response(response_data, status=status.HTTP_400_BAD_REQUEST) # make sure that the action is valid. - if action not in ("start", "stop", "restart"): + if action not in ("start", "stop", "restart", "delete"): response_data = { "error": "The action {} is invalid for worker".format(action) } return Response(response_data, status=status.HTTP_406_NOT_ACCEPTABLE) + # Only allow EvalAI admins to delete workers + if action == "delete" and not request.user.is_staff: + response_data = { + "error": "Sorry, you are not authorized for access worker operations." + } + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + challenge = get_challenge_model(challenge_pk) response_data = {} @@ -2838,6 +2846,8 @@ def manage_worker(request, challenge_pk, action): response = stop_workers([challenge]) elif action == "restart": response = restart_workers([challenge]) + elif action == "delete": + response = delete_workers([challenge]) if response: count, failures = response["count"], response["failures"] diff --git a/apps/jobs/sender.py b/apps/jobs/sender.py index 0927a9685e..ef67410786 100644 --- a/apps/jobs/sender.py +++ b/apps/jobs/sender.py @@ -11,6 +11,7 @@ from base.utils import send_slack_notification from challenges.models import Challenge from .utils import get_submission_model +from monitoring.statsd.metrics import NUM_SUBMISSIONS_IN_QUEUE, increment_statsd_counter logger = logging.getLogger(__name__) @@ -83,6 +84,11 @@ def publish_submission_message(message): queue_name = challenge.queue slack_url = challenge.slack_webhook_url queue = get_or_create_sqs_queue(queue_name) + # increase counter for submission pushed into queue + submission_metric_tags = [ + "queue_name:%s" % queue_name, + ] + increment_statsd_counter(NUM_SUBMISSIONS_IN_QUEUE, submission_metric_tags, 1) response = queue.send_message(MessageBody=json.dumps(message)) # send slack notification if slack_url: diff --git a/apps/jobs/views.py b/apps/jobs/views.py index 29b4850da0..cef16b95d9 100644 --- a/apps/jobs/views.py +++ b/apps/jobs/views.py @@ -50,6 +50,7 @@ get_challenge_model, get_challenge_phase_model, get_challenge_phase_split_model, + get_participant_model, ) from hosts.models import ChallengeHost from hosts.utils import is_user_a_host_of_challenge @@ -2824,7 +2825,35 @@ def update_submission_meta(request, challenge_pk, submission_pk): serializer.errors, status=status.HTTP_400_BAD_REQUEST ) else: - response_data = { - "error": "Sorry, you are not authorized to make this request" - } - return Response(response_data, status=status.HTTP_403_FORBIDDEN) + participant_team_pk = get_participant_team_id_of_user_for_a_challenge( + request.user, challenge_pk + ) + + participant_team = get_participant_model(participant_team_pk) + + try: + submission = Submission.objects.get( + id=submission_pk, + participant_team=participant_team, + ) + except Submission.DoesNotExist: + response_data = { + "error": "Submission {} does not exist".format(submission_pk) + } + return Response(response_data, status=status.HTTP_404_NOT_FOUND) + + serializer = SubmissionSerializer( + submission, + data=request.data, + context={"request": request}, + partial=True, + ) + + if serializer.is_valid(): + serializer.save() + response_data = serializer.data + return Response(response_data, status=status.HTTP_200_OK) + else: + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) diff --git a/docker-compose-monitoring.yml b/docker-compose-monitoring.yml new file mode 100644 index 0000000000..33bef404e0 --- /dev/null +++ b/docker-compose-monitoring.yml @@ -0,0 +1,32 @@ +version: "3" +services: + prometheus: + image: prom/prometheus:latest + user: "1000" + volumes: + - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + - ./monitoring/prometheus/prometheus_db:/var/lib/prometheus + - ./monitoring/prometheus/prometheus_db:/prometheus + - ./monitoring/prometheus/prometheus_db:/etc/prometheus + command: + - "--config.file=/etc/prometheus/prometheus.yml" + ports: + - "9090:9090" + + grafana: + image: grafana/grafana:latest + user: "1000" + env_file: + - docker/dev/docker.env + volumes: + - ./monitoring/grafana/grafana_db:/var/lib/grafana + depends_on: + - prometheus + ports: + - "3000:3000" + + node_exporter: + hostname: node_exporter + image: prom/node-exporter + ports: + - "9100:9100" diff --git a/docker-compose-staging.yml b/docker-compose-staging.yml index 4789017ebd..6f5342a969 100644 --- a/docker-compose-staging.yml +++ b/docker-compose-staging.yml @@ -130,7 +130,8 @@ services: - '--log.level=info' - '--web.telemetry-path=/statsd/metrics' ports: - - '9125:9125' + - '9125:9125/udp' + - '9125:9125/tcp' - '9102:9102' node_exporter: @@ -149,6 +150,7 @@ services: depends_on: - prometheus - grafana + - statsd-exporter ports: - '80:80' - '443:443' diff --git a/docker-compose.yml b/docker-compose.yml index b3d7cab852..e0d1f7e833 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,31 +66,6 @@ services: - ./frontend_v2:/code - /code/node_modules - prometheus: - image: prom/prometheus:latest - user: "1000" - volumes: - - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - - ./monitoring/prometheus/prometheus_db:/var/lib/prometheus - - ./monitoring/prometheus/prometheus_db:/prometheus - - ./monitoring/prometheus/prometheus_db:/etc/prometheus - command: - - '--config.file=/etc/prometheus/prometheus.yml' - ports: - - '9090:9090' - - grafana: - image: grafana/grafana:latest - user: "1000" - env_file: - - docker/dev/docker.env - volumes: - - ./monitoring/grafana/grafana_db:/var/lib/grafana - depends_on: - - prometheus - ports: - - '3000:3000' - statsd-exporter: hostname: statsd image: prom/statsd-exporter:latest @@ -100,9 +75,3 @@ services: ports: - '9125:9125' - '9102:9102' - - node_exporter: - hostname: node_exporter - image: prom/node-exporter - ports: - - '9100:9100' diff --git a/docker/prod/docker_staging.env b/docker/prod/docker_staging.env index e561b8a2be..c1df14a55a 100644 --- a/docker/prod/docker_staging.env +++ b/docker/prod/docker_staging.env @@ -61,5 +61,5 @@ GF_SECURITY_ADMIN_PASSWORD=x GF_SERVER_ROOT_URL=http://localhost:3000/grafana GF_SERVER_SERVE_FROM_SUB_PATH=true -STATSD_ENDPOINT=statsd +STATSD_ENDPOINT=x STATSD_PORT=9125 diff --git a/docker/prod/nginx-ingress/nginx_staging.conf b/docker/prod/nginx-ingress/nginx_staging.conf index b8e31a57ec..d65f61b1a0 100644 --- a/docker/prod/nginx-ingress/nginx_staging.conf +++ b/docker/prod/nginx-ingress/nginx_staging.conf @@ -6,6 +6,10 @@ upstream grafana { server grafana:3000 fail_timeout=0; } +upstream statsd_exporter { + server statsd:9102 fail_timeout=0; +} + server { server_name monitoring-staging.eval.ai; listen 80; @@ -34,14 +38,20 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass https://prometheus; + proxy_pass http://prometheus; } location /grafana { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass https://grafana; + proxy_pass http://grafana; } + location /statsd { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://statsd_exporter; + } } diff --git a/docker/prod/nodejs/nginx_staging.conf b/docker/prod/nodejs/nginx_staging.conf index 8e7cdd02ce..28c01898e3 100644 --- a/docker/prod/nodejs/nginx_staging.conf +++ b/docker/prod/nodejs/nginx_staging.conf @@ -2,10 +2,6 @@ upstream django_app { server django:8000 fail_timeout=0; } -upstream statsd_exporter { - server statsd:9102 fail_timeout=0; -} - upstream node_exporter { server node_exporter:9100 fail_timeout=0; } @@ -61,13 +57,6 @@ server { proxy_pass http://django_app; } - location /statsd { - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://statsd_exporter; - } - location /node_exporter { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; diff --git a/docker/prod/nodejs_v2/nginx_staging.conf b/docker/prod/nodejs_v2/nginx_staging.conf index 74214c17a1..86dd7e669b 100644 --- a/docker/prod/nodejs_v2/nginx_staging.conf +++ b/docker/prod/nodejs_v2/nginx_staging.conf @@ -2,10 +2,6 @@ upstream django_app { server django:8000 fail_timeout=0; } -upstream statsd_exporter { - server statsd:9102 fail_timeout=0; -} - upstream node_exporter { server node_exporter:9100 fail_timeout=0; } @@ -52,13 +48,6 @@ server { proxy_pass http://django_app; } - location /statsd { - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://statsd_exporter; - } - location /node_exporter { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; diff --git a/frontend/src/css/modules/leaderboard.scss b/frontend/src/css/modules/leaderboard.scss index a2e7884dbd..48a6e0c765 100644 --- a/frontend/src/css/modules/leaderboard.scss +++ b/frontend/src/css/modules/leaderboard.scss @@ -1,60 +1,61 @@ /* Styles for leaderboard page */ .horizontal-scroll { - overflow-x: auto; + overflow-x: auto; } #baseline-badge { - font-weight: 300; - min-width: 0rem; - font-size: 16px; - float: right; - margin-left: 5px; + font-weight: 300; + min-width: 0rem; + font-size: 16px; + float: right; + margin-left: 5px; } #baseline-badge-desc { - font-weight: 300; - min-width: 0rem; - font-size: 16px; - margin-left: 0px; - float: none; + font-weight: 300; + min-width: 0rem; + font-size: 16px; + margin-left: 0px; + float: none; } #verified-badge { - font-weight: 300; - min-width: 0rem; - font-size: 16px; - float: right; - margin-left: 5px; + font-weight: 300; + min-width: 0rem; + font-size: 16px; + float: right; + margin-left: 5px; } #verified-badge-desc { - font-weight: 300; - min-width: 0rem; - font-size: 16px; - margin-left: 0px; - float: none; + font-weight: 300; + min-width: 0rem; + font-size: 16px; + margin-left: 0px; + float: none; } .highlightLeaderboard { - border-color: #fff3e0; - box-shadow: 0 0 0 0.2em #ffcc80 !important; - border: 1px solid #d1d5da; - border-radius: 3px; + border-color: #fff3e0; + box-shadow: 0 0 0 0.2em #ffcc80 !important; + border: 1px solid #d1d5da; + border-radius: 3px; + background-color: #f2f2f2; } .sort-leaderboard-switch { - vertical-align: middle; - display: inline-block; + vertical-align: middle; + display: inline-block; } .baseline-tag { - margin-right: 4%; + margin-right: 4%; } .complete-leaderboard { - vertical-align:top; + vertical-align:top; } span.badge.partial-evaluation { @@ -66,3 +67,24 @@ span.badge.partial-evaluation { float: left; box-sizing: border-box; } + +.leaderboard-label .description { + visibility: hidden; + width: auto; + max-width: 250px; + height: auto; + background-color: #f2f2f2; + color: #000000; + text-align: center; + border-radius: 6px; + padding: 10px 10px 10px 10px; + font-size: 1.05rem; + + /* Position the tooltip */ + position: absolute; + z-index: 1; +} + +.leaderboard-label:hover .description { + visibility: visible; +} diff --git a/frontend/src/js/controllers/challengeCtrl.js b/frontend/src/js/controllers/challengeCtrl.js index 264c52cf52..8fbce15eb1 100644 --- a/frontend/src/js/controllers/challengeCtrl.js +++ b/frontend/src/js/controllers/challengeCtrl.js @@ -87,6 +87,7 @@ vm.refreshJWT = utilities.getData('refreshJWT'); vm.subErrors = {}; + vm.currentHighlightedLeaderboardEntry = null; vm.isChallengeLeaderboardPrivate = false; vm.previousPublicSubmissionId = null; @@ -204,16 +205,18 @@ $interval.cancel(vm.logs_poller); }; - // scroll to the specific entry of the leaderboard - vm.scrollToSpecificEntryLeaderboard = function (elementId) { - var newHash = elementId.toString(); - if ($location.hash() !== newHash) { - $location.hash(elementId); - } else { - $anchorScroll(); + // highlight the specific entry of the leaderboard + vm.highlightSpecificLeaderboardEntry = function (key) { + key = '#' + key; + // Remove highlight from previous clicked entry + if (vm.currentHighlightedLeaderboardEntry != null) { + let prevEntry = angular.element(vm.currentHighlightedLeaderboardEntry)[0]; + prevEntry.setAttribute("class", ""); } + let entry = angular.element(key)[0]; + entry.setAttribute("class", "highlightLeaderboard"); + vm.currentHighlightedLeaderboardEntry = key; $scope.isHighlight = false; - $anchorScroll.yOffset = 90; }; // get names of the team that has participated in the current challenge @@ -873,6 +876,32 @@ scope.sortColumn = column; }; + vm.isMetricOrderedAscending = function(metric) { + let schema = vm.leaderboard[0].leaderboard__schema; + let metadata = schema.metadata; + if (metadata != null && metadata != undefined) { + // By default all metrics are considered higher is better + if (metadata[metric] == undefined) { + return false; + } + return metadata[metric].sort_ascending; + } + return false; + }; + + vm.getLabelDescription = function(metric) { + let schema = vm.leaderboard[0].leaderboard__schema; + let metadata = schema.metadata; + if (metadata != null && metadata != undefined) { + // By default all metrics are considered higher is better + if (metadata[metric] == undefined || metadata[metric].description == undefined) { + return ""; + } + return metadata[metric].description; + } + return ""; + }; + // my submissions vm.isResult = false; @@ -2001,7 +2030,44 @@ } }; - vm.hideVisibilityDialog = function() { + vm.cancelSubmission = function(submissionId) { + parameters.url = "jobs/challenges/" + vm.challengeId + "/submissions/" + submissionId + "/update_submission_meta/"; + parameters.method = 'PATCH'; + parameters.data = { + "status": "cancelled", + }; + parameters.callback = { + onSuccess: function(response) { + var status = response.status; + if (status === 200) { + $mdDialog.hide(); + $rootScope.notify("success", "Submission cancelled successfully!"); + } + }, + onError: function(response) { + $mdDialog.hide(); + var error = response.data; + $rootScope.notify("error", error); + } + }; + + utilities.sendRequest(parameters); + }; + + vm.showCancelSubmissionDialog = function(submissionId, status) { + if (status != "submitted") { + $rootScope.notify("error", "Only unproccessed submissions can be cancelled"); + return; + } + vm.submissionId = submissionId; + $mdDialog.show({ + scope: $scope, + preserveScope: true, + templateUrl: 'dist/views/web/challenge/cancel-submission.html' + }); + }; + + vm.hideDialog = function() { $mdDialog.hide(); }; diff --git a/frontend/src/views/web/challenge/cancel-submission.html b/frontend/src/views/web/challenge/cancel-submission.html new file mode 100644 index 0000000000..5b6370963a --- /dev/null +++ b/frontend/src/views/web/challenge/cancel-submission.html @@ -0,0 +1,16 @@ +
+
+
+
+
+
Are you sure you want to cancel submission?
+ + +
+
+
+
+
diff --git a/frontend/src/views/web/challenge/leaderboard.html b/frontend/src/views/web/challenge/leaderboard.html index 18f4db825c..6e7f5b6923 100644 --- a/frontend/src/views/web/challenge/leaderboard.html +++ b/frontend/src/views/web/challenge/leaderboard.html @@ -93,7 +93,8 @@
Leaderboard
- {{key}} + {{key}} (↓) {{challenge.getLabelDescription(key)}} + {{key}} (↑) {{challenge.getLabelDescription(key)}} @@ -131,8 +132,7 @@
Leaderboard
- {{challenge.initial_ranking[key.id]}} diff --git a/frontend/src/views/web/challenge/my-submission.html b/frontend/src/views/web/challenge/my-submission.html index 54e56bf517..e89e34fb30 100644 --- a/frontend/src/views/web/challenge/my-submission.html +++ b/frontend/src/views/web/challenge/my-submission.html @@ -70,6 +70,7 @@
My Participated Team: {{challenge.participated_team_name}}
Baseline Edit + Cancel @@ -133,6 +134,8 @@
My Participated Team: {{challenge.participated_team_name}}
+ diff --git a/frontend/src/views/web/challenge/update-submission-visibility.html b/frontend/src/views/web/challenge/update-submission-visibility.html index 76efbdcd68..e2ee9b34af 100644 --- a/frontend/src/views/web/challenge/update-submission-visibility.html +++ b/frontend/src/views/web/challenge/update-submission-visibility.html @@ -6,7 +6,7 @@
Are you sure you want to override your current public submission?
diff --git a/frontend_v2/src/app/components/about/about.component.ts b/frontend_v2/src/app/components/about/about.component.ts index 3235a9908b..ea75489bd3 100644 --- a/frontend_v2/src/app/components/about/about.component.ts +++ b/frontend_v2/src/app/components/about/about.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Inject } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { GlobalService } from '../../services/global.service'; /** diff --git a/frontend_v2/src/app/components/auth/auth.component.ts b/frontend_v2/src/app/components/auth/auth.component.ts index 8a5b57d8ee..dd5d0a8160 100644 --- a/frontend_v2/src/app/components/auth/auth.component.ts +++ b/frontend_v2/src/app/components/auth/auth.component.ts @@ -1,5 +1,5 @@ -import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; +import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; import { GlobalService } from '../../services/global.service'; import { AuthService } from '../../services/auth.service'; @@ -22,13 +22,11 @@ export class AuthComponent implements OnInit { /** * Constructor. * @param router Router Injection. - * @param route ActivatedRoute Injection. * @param globalService GlobalService Injection. * @param authService AuthServiceInjection */ constructor( private router: Router, - private route: ActivatedRoute, private globalService: GlobalService, public authService: AuthService ) {} diff --git a/frontend_v2/src/app/components/auth/login/login.component.html b/frontend_v2/src/app/components/auth/login/login.component.html index c6cd7c8055..37dd4390a7 100644 --- a/frontend_v2/src/app/components/auth/login/login.component.html +++ b/frontend_v2/src/app/components/auth/login/login.component.html @@ -27,16 +27,16 @@ id="name" class="dark-autofill" name="name" - (focusin)="isnameFocused = true" - (focusout)="isnameFocused = authService.getUser['name'] !== ''" + (focusin)="isNameFocused = true" + (focusout)="isNameFocused = authService.getUser['name'] !== ''" [(ngModel)]="authService.getUser['name']" - (change)="isnameFocused = authService.getUser['name'] !== ''" + (change)="isNameFocused = authService.getUser['name'] !== ''" #name="ngModel" minLength="3" required /> - +

Username is too short.

Username is required.

@@ -50,9 +50,9 @@ (paste)="authService.getUser['password'] = ''" class="dark-autofill" name="password" - (focusin)="ispasswordFocused = true" - (focusout)="ispasswordFocused = authService.getUser['password'] !== ''" - (change)="ispasswordFocused = authService.getUser['password'] !== ''" + (focusin)="isPasswordFocused = true" + (focusout)="isPasswordFocused = authService.getUser['password'] !== ''" + (change)="isPasswordFocused = authService.getUser['password'] !== ''" [(ngModel)]="authService.getUser['password']" #password="ngModel" autocomplete="new-password" @@ -63,7 +63,7 @@ - +
Leaderboard
{ diff --git a/frontend_v2/src/app/components/challenge/challengeoverview/challengeoverview.component.ts b/frontend_v2/src/app/components/challenge/challengeoverview/challengeoverview.component.ts index 94431e287e..ad0debfd69 100644 --- a/frontend_v2/src/app/components/challenge/challengeoverview/challengeoverview.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeoverview/challengeoverview.component.ts @@ -1,11 +1,7 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { DOCUMENT } from '@angular/common'; -import { NGXLogger } from 'ngx-logger'; // import service -import { GlobalService } from '../../../services/global.service'; import { ChallengeService } from '../../../services/challenge.service'; -import { EndpointsService } from '../../../services/endpoints.service'; /** * Component Class @@ -28,15 +24,10 @@ export class ChallengeoverviewComponent implements OnInit { /** * Constructor. - * @param document Window document Injection. * @param challengeService ChallengeService Injection. */ constructor( - private challengeService: ChallengeService, - @Inject(DOCUMENT) private document: Document, - private globalService: GlobalService, - private endpointsService: EndpointsService, - private logger: NGXLogger + private challengeService: ChallengeService ) {} /** diff --git a/frontend_v2/src/app/components/challenge/challengeparticipate/challengeparticipate.component.ts b/frontend_v2/src/app/components/challenge/challengeparticipate/challengeparticipate.component.ts index e20470ac32..7f7438321d 100644 --- a/frontend_v2/src/app/components/challenge/challengeparticipate/challengeparticipate.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeparticipate/challengeparticipate.component.ts @@ -138,11 +138,12 @@ export class ChallengeparticipateComponent implements OnInit { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router Router Injection. * @param authService AuthService Injection. - * @param globalService GlobalService Injection. + * @param apiService ApiService Injection. + * @param router Router Injection. + * @param route ActivatedRoute Injection. * @param challengeService ChallengeService Injection. + * @param globalService GlobalService Injection. */ constructor( private authService: AuthService, diff --git a/frontend_v2/src/app/components/challenge/challengeparticipate/terms-and-conditions-modal/terms-and-conditions-modal.component.ts b/frontend_v2/src/app/components/challenge/challengeparticipate/terms-and-conditions-modal/terms-and-conditions-modal.component.ts index f7f4a09e94..75f5a993c2 100644 --- a/frontend_v2/src/app/components/challenge/challengeparticipate/terms-and-conditions-modal/terms-and-conditions-modal.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeparticipate/terms-and-conditions-modal/terms-and-conditions-modal.component.ts @@ -52,6 +52,10 @@ export class TermsAndConditionsModalComponent implements OnInit { */ denyCallback = () => {}; + /** + * Constructor. + * @param globalService GlobalService Injection. + */ constructor(private globalService: GlobalService) {} ngOnInit() { diff --git a/frontend_v2/src/app/components/challenge/challengephases/challengephases.component.ts b/frontend_v2/src/app/components/challenge/challengephases/challengephases.component.ts index f8d07ecb5d..3eae3e6fae 100644 --- a/frontend_v2/src/app/components/challenge/challengephases/challengephases.component.ts +++ b/frontend_v2/src/app/components/challenge/challengephases/challengephases.component.ts @@ -1,5 +1,4 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { DOCUMENT } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; import { ChallengeService } from '../../../services/challenge.service'; /** @@ -23,14 +22,9 @@ export class ChallengephasesComponent implements OnInit { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router GlobalService Injection. - * @param authService AuthService Injection. - * @param globalService GlobalService Injection. - * @param apiService Router Injection. * @param challengeService ChallengeService Injection. */ - constructor(private challengeService: ChallengeService, @Inject(DOCUMENT) private document: Document) {} + constructor(private challengeService: ChallengeService) {} /** * Component on intialized diff --git a/frontend_v2/src/app/components/challenge/challengephases/phasecard/phasecard.component.ts b/frontend_v2/src/app/components/challenge/challengephases/phasecard/phasecard.component.ts index 3f9b16b635..46a18b065a 100644 --- a/frontend_v2/src/app/components/challenge/challengephases/phasecard/phasecard.component.ts +++ b/frontend_v2/src/app/components/challenge/challengephases/phasecard/phasecard.component.ts @@ -1,8 +1,6 @@ -import { Component, OnInit, Input, Inject } from '@angular/core'; +import { Component, OnInit, Input } from '@angular/core'; import { GlobalService } from '../../../../services/global.service'; import { ChallengeService } from '../../../../services/challenge.service'; -import { ApiService } from '../../../../services/api.service'; -import { EndpointsService } from '../../../../services/endpoints.service'; /** * Component Class @@ -46,12 +44,11 @@ export class PhasecardComponent implements OnInit { /** * Constructor. * @param globalService GlobalService Injection. + * @param challengeService ChallengeService Injection. */ constructor( private globalService: GlobalService, - private challengeService: ChallengeService, - private apiService: ApiService, - private endpointsService: EndpointsService + private challengeService: ChallengeService ) {} /** diff --git a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html index 796a9092f8..9c6ef3f5ba 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html @@ -5,239 +5,279 @@
Challenge Settings
-
- -
-
- Edit challenge details: -
-
-
-
-
- - + + + + +
+
+
+
+ + +
+
+ + Upload New Image +
+
+
+ + {{ challenge['title'] }} + + + + + + + +
+
+
+ + Challenge Overview + + + + +
+
+
+
+ + Terms and Conditions + + + + +
+
+ + + Starts on: + {{ challenge['start_date'] | date: 'medium' }} {{ challenge['start_zone'] }} + + + + + + +
+ + + Ends on: + {{ challenge['end_date'] | date: 'medium' }} {{ challenge['end_zone'] }} + + + + + + +
+ + + {{ publishChallenge.state }} + + + +
+ Close Participation + +
+
+
+ + +
+
+ Banned emails Ids: +
+
+ + + + {{ email }} + cancel + + + + +
{{ message }}
+
+ Update +
+
+
+
+
+ + + +
+
+ Edit Phase
-
- - Upload New Image +
+
+
+ +
+ +
+ + Phase Details + + + +      + + Is Public + + + +      + + Submission Visibility + + + + +
+ +
-
- - {{ challenge['title'] }} - - - - - - - -
-
-
- - Challenge Overview - - - - + +
+
+ Edit Leaderboard
-
-
-
- - Terms and Conditions - - - - +
+
+
+ + +
+
+
+ + Leaderboard Visibility + + + +      +
+
+
- - - Starts on: - {{ challenge['start_date'] | date: 'medium' }} {{ challenge['start_zone'] }} - - - - - - -
- - - Ends on: - {{ challenge['end_date'] | date: 'medium' }} {{ challenge['end_zone'] }} - - - - - - -
- - - {{ publishChallenge.state }} - - - -
- Close Participation - -
-
-
- - -
-
- Edit phase details: -
-
-
-
-
- - + + + + + + +
+
+
+
Evaluation script
+
+
+
+
+ + + Evaluation Criteria + + + +    + + + Evaluation Script + + + + +
+
-
- - Is Public - - - -      - - Submission Visibility - - - - -
-
-
-
- - -
-
- Evaluation and Annotations -
-
-
-
- - - Evaluation Criteria - - - -    - - - Evaluation Script - - - - -
-
-
+
+
+
+
Submission worker actions
+
+
+
+
+ + + +
+
+
+
+
Submission worker logs
+
+
+
+
{{ log }}
+
+
+ + -
-
- Banned emails Ids: -
-
- - - - {{ email }} - cancel - - - - -
{{ message }}
-
- Update -
-
-
-
-
-
-
Submission worker actions
-
-
-
-
- - - -
-
-
-
-
Submission worker logs
-
-
-
-
{{ log }}
-
-
+
diff --git a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.scss b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.scss index 543e84f175..1984bb61be 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.scss +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.scss @@ -3,14 +3,31 @@ .challenge-card { .bottom-card-container { + padding: 0px 30px; .settings-section { - padding: 20px 30px; + padding: 50px; border: 1px solid; border-radius: 5px; min-height: 50px; color: rgb(101, 97, 97); } } + .row { + margin: 20px 0px 3% 0px; + } +} + +.row .col { + padding: 0px; +} + +input { + border-bottom: none; +} + +:host ::ng-deep .mat-tab-label .mat-tab-label-content{ + font-weight: $fw-light !important; + font-size: $fs-16 !important; } .phase-card { @@ -21,21 +38,29 @@ margin-left: 25px; } -@include screen-medium { +.worker-title { + margin-bottom: 2%; +} + +.upload-container { + text-align:center; +} + +@media (max-width: 992px) { .settings-section { - padding: 10px 20px !important; + padding: 50px !important; } } -@include screen-small-medium { - .settings-section { - padding: 10px 10px !important; +@media (min-width: 992px) { + .col-md-12 { + width: auto; } } -@include screen-small { +@media (max-width: 600px) { .settings-section { - padding: 10px 5px !important; + padding: 25px !important; } } @@ -48,7 +73,6 @@ } .challenge-detail { - margin-left: 25%; padding: 8px; } diff --git a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts index 96265fcc37..e4de5f05b1 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts @@ -30,87 +30,124 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { */ challenge: any; + /** + * Is challenge host + */ + isChallengeHost = false; + + /** + * To call the API inside modal for editing the challenge details + */ + apiCall: any; + /** * Challenge phase list */ phases = []; /** - * Phase selection type (radio button or select box) + * Phase split list */ - phaseSelectionType = 'selectBox'; + phaseSplits = []; /** - * Select box list type + * Challenge phases filtered */ - phaseSelectionListType = 'phase'; + filteredPhases = []; + /** + * Phase splits filtered + */ + filteredPhaseSplits = []; + /** * Currently selected phase */ selectedPhase: any = null; - + /** - * store worker logs + * Phase selection type (radio button or select box) */ - workerLogs = []; + phaseSelectionType = 'selectBox'; + + /** + * Select box list type + */ + phaseSelectionListType = 'phase'; /** - * An interval for fetching the leaderboard data in every 5 seconds + * If the submission is public */ - pollingInterval: any; + isSubmissionPublic : boolean = false; + + /** + * If the phase is public + */ + isPhasePublic : boolean = false; /** - * Is challenge host + * If leaderboard is public */ - isChallengeHost = false; + isLeaderboardPublic : boolean = false; /** - * To call the API inside modal for editing the challenge details + * Currently selected phase split */ - apiCall: any; + selectedPhaseSplit: any = null; /** - * Participants banned emails ids + * Phase selection type (radio button or select box) */ - bannedEmailIds: string[]; + phaseLeaderboardSelectionType = 'selectBox'; + + /** + * Select box list type + */ + phaseLeaderboardSelectionListType = 'settingsPhaseSplit'; /** - * Former participants banned emails ids + * If leaderboard of phase split is public + * 1 -> private + * 3 -> public */ - formerBannedEmailIds: string[]; + isPhaseSplitLeaderboardPublic: number = 1; + + /** + * store worker logs + */ + workerLogs = []; /** - * Email validation for the banned email ids + * An interval for fetching the leaderboard data in every 5 seconds */ - isValidationError = false; + pollingInterval: any; /** - * Email error message + * Participants banned emails ids */ - message: string; - + bannedEmailIds: string[]; + /** - * If the submission is public + * Former participants banned emails ids */ - isSubmissionPublic : boolean = false; + formerBannedEmailIds: string[]; /** - * If the phase is public + * Email validation for the banned email ids */ - isPhasePublic : boolean = false; + isValidationError = false; /** - * If leaderboard is public + * Email error message */ - isLeaderboardPublic : boolean = false; + message: string; /** * phase visibility state and it's icon */ phaseVisibility = { state: 'Private', - icon: 'fa fa-toggle-off', + icon: 'fa fa- text-darken-1', }; /** @@ -118,15 +155,23 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { */ submissionVisibility = { state: 'Private', - icon: 'fa fa-toggle-off', + icon: 'fa fa-toggle-off grey-text text-darken-1', }; + /** + * phase visibility state and it's icon + */ + leaderboardVisibility = { + state: 'Private', + icon: 'fa fa-toggle-off grey-text text-darken-1', + }; + /** * publish challenge state and it's icon */ publishChallenge = { state: 'Not Published', - icon: 'fa fa-toggle-off', + icon: 'fa fa-eye-slash red-text', }; /** @@ -161,14 +206,10 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { this.challenge = challenge; }); - this.challengeService.currentPhases.subscribe((phases) => { - this.phases = phases; - }); - this.challengeService.isChallengeHost.subscribe((status) => { this.isChallengeHost = status; }); - + this.challengeService.currentChallengePublishState.subscribe((publishChallenge) => { this.publishChallenge.state = publishChallenge.state; this.publishChallenge.icon = publishChallenge.icon; @@ -178,219 +219,427 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { this.fetchWorkerLogs(); this.startLoadingLogs(); } - } + + this.challengeService.currentPhases.subscribe((phases) => { + this.phases = phases; + for (let i = 0; i < this.phases.length; i++) { + if (this.phases[i].is_public === false) { + this.phases[i].showPrivate = true; + } + else { + this.phases[i].showPrivate = false; + } + } + this.filteredPhases = this.phases; + }); - updateView() { - this.bannedEmailIds = this.challenge.banned_email_ids || []; - this.formerBannedEmailIds = this.bannedEmailIds.concat(); // Creating deep copy + this.challengeService.currentPhaseSplit.subscribe((phaseSplits) => { + this.phaseSplits = phaseSplits; + for (let i = 0; i < this.phaseSplits.length; i++) { + if (this.phaseSplits[i].visibility !== 3) { + this.phaseSplits[i].showPrivate = true; + } + else { + this.phaseSplits[i].showPrivate = false; + } + } + this.filteredPhaseSplits = this.phaseSplits; + }); } + // Edit Challenge Details -> + /** - * Add banned email chip - * @param event current event + * Edit challenge title function */ - add(event: MatChipInputEvent): void { + editChallengeTitle() { const SELF = this; - const input = event.input; - const value = event.value; - SELF.isValidationError = false; - SELF.message = ''; - // Add our fruit - if ((value || '').trim()) { - if (!SELF.validateEmail(value.trim())) { - SELF.isValidationError = true; - SELF.message = 'Please enter a valid email!'; - } else { - SELF.bannedEmailIds.push(value.trim()); - } - } + SELF.apiCall = (params) => { + const BODY = JSON.stringify(params); + SELF.apiService + .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) + .subscribe( + (data) => { + SELF.challenge.title = data.title; + SELF.globalService.showToast('success', 'The challenge title is successfully updated!', 5); + }, + (err) => { + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + }, + () => {} + ); + }; - // Reset the input value - if (input && !SELF.isValidationError) { - input.value = ''; - } + const PARAMS = { + title: 'Edit Challenge Title', + content: '', + confirm: 'Submit', + deny: 'Cancel', + form: [ + { + name: 'editChallengeTitle', + isRequired: true, + label: 'title', + placeholder: 'Challenge Title', + type: 'text', + value: this.challenge.title, + }, + ], + confirmCallback: SELF.apiCall, + }; + SELF.globalService.showModal(PARAMS); } /** - * Called when a phase is selected (from child component) + * Delete challenge */ - phaseSelected() { + deleteChallenge() { const SELF = this; - return (phase) => { - SELF.selectedPhase = phase; - SELF.isPhasePublic = SELF.selectedPhase['is_public']; - SELF.isSubmissionPublic = SELF.selectedPhase['is_submission_public']; - SELF.isLeaderboardPublic = SELF.selectedPhase['leaderboard_public']; - if (SELF.isPhasePublic) { - SELF.phaseVisibility.state = 'Public'; - SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; - } - else { - SELF.phaseVisibility.state = 'Private'; - SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; - } - if (SELF.isSubmissionPublic) { - SELF.submissionVisibility.state = 'Public'; - SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text'; - } - else { - SELF.submissionVisibility.state = 'Private'; - SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; - } + const redirectTo = '/dashboard'; + + SELF.apiCall = () => { + const BODY = JSON.stringify({}); + SELF.apiService.postUrl(SELF.endpointsService.deleteChallengeURL(SELF.challenge.id), BODY).subscribe( + (data) => { + SELF.router.navigate([redirectTo]); + SELF.globalService.showToast('success', 'The Challenge is successfully deleted!', 5); + }, + (err) => { + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + }, + () => {} + ); }; - } - editPhaseDetails() { + const PARAMS = { + title: 'Delete Challenge', + content: '', + confirm: 'I understand consequences, delete the challenge', + deny: 'Cancel', + form: [ + { + name: 'challegenDeleteInput', + isRequired: true, + label: '', + placeholder: 'Please type in the name of the challenge to confirm', + type: 'text', + value: '', + }, + ], + confirmCallback: SELF.apiCall, + }; + SELF.globalService.showModal(PARAMS); + } + + /** + * Edit challenge image function + */ + editChallengeImage() { const SELF = this; SELF.apiCall = (params) => { const FORM_DATA: FormData = new FormData(); - for (const key in params) { - if (params[key]) { - FORM_DATA.append(key, params[key]); - } - } + FORM_DATA.append('image', params['image']); SELF.apiService .patchFileUrl( - SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.challenge.id, SELF.selectedPhase['id']), + SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), FORM_DATA ) .subscribe( (data) => { - SELF.selectedPhase = data; - SELF.challengeService.fetchPhases(SELF.challenge['id']); - SELF.challengeService.changePhaseSelected(true); - SELF.selectedPhase = false; - SELF.globalService.showToast('success', 'The challenge phase details are successfully updated!'); + SELF.challenge.image = data.image; + SELF.globalService.showToast('success', 'The Challenge image successfully updated!', 5); }, (err) => { + SELF.globalService.handleApiError(err, true); SELF.globalService.showToast('error', err); }, - () => {this.logger.info('PHASE-UPDATE-FINISHED')} + () => {} ); }; + /** + * Parameters of the modal + */ const PARAMS = { - title: 'Edit Challenge Phase Details', - name: SELF.selectedPhase['name'], - label: 'description', - description: SELF.selectedPhase['description'], - startDate: SELF.selectedPhase['start_date'], - endDate: SELF.selectedPhase['end_date'], - maxSubmissionsPerDay: SELF.selectedPhase['max_submissions_per_day'], - maxSubmissionsPerMonth: SELF.selectedPhase['max_submissions_per_month'], - maxSubmissions: SELF.selectedPhase['max_submissions'], - maxConcurrentSubmissionsAllowed: SELF.selectedPhase['max_concurrent_submissions_allowed'], - allowedSubmissionFileTypes: SELF.selectedPhase['allowed_submission_file_types'], + title: 'Edit Challenge Image', + content: '', confirm: 'Submit', deny: 'Cancel', + form: [ + { + name: 'Challenge Image', + isRequired: true, + label: 'image', + placeholder: '', + type: 'file', + value: '', + }, + ], confirmCallback: SELF.apiCall, }; - SELF.globalService.showEditPhaseModal(PARAMS); -} + SELF.globalService.showModal(PARAMS); + } /** - * Phase Visibility toggle function + * Edit challenge overview function */ - togglePhaseVisibility() { + + editChallengeOverview() { const SELF = this; - let togglePhaseVisibilityState, isPublic; - if (SELF.phaseVisibility.state === 'Public') { - togglePhaseVisibilityState = 'private'; - isPublic = false; - SELF.phaseVisibility.state = 'Private'; - SELF.phaseVisibility.icon = 'fa fa-toggle-off'; - } else { - togglePhaseVisibilityState = 'public'; - isPublic = true; - SELF.phaseVisibility.state = 'Public'; - SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; - } - const BODY: FormData = new FormData(); - BODY.append("is_public", isPublic); + + SELF.apiCall = (params) => { + const BODY = JSON.stringify(params); SELF.apiService - .patchFileUrl( - SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.selectedPhase['challenge'], SELF.selectedPhase['id']), - BODY - ) + .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) .subscribe( (data) => { - SELF.challengeService.fetchPhases(SELF.selectedPhase['challenge']); - SELF.challengeService.changePhaseSelected(true); - SELF.selectedPhase = false; - SELF.globalService.showToast( - 'success', - 'The phase was successfully made ' + togglePhaseVisibilityState, - 5 - ); + SELF.challenge.description = data.description; + SELF.globalService.showToast('success', 'The description is successfully updated!', 5); }, (err) => { SELF.globalService.handleApiError(err, true); SELF.globalService.showToast('error', err); - if (isPublic) { - SELF.phaseVisibility.state = 'Private'; - SELF.phaseVisibility.icon = 'fa fa-toggle-off'; - } else { - SELF.phaseVisibility.state = 'Public'; - SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; - } }, - () => this.logger.info('PHASE-VISIBILITY-UPDATE-FINISHED') + () => this.logger.info('EDIT-CHALLENGE-DESCRIPTION-FINISHED') ); + }; + + const PARAMS = { + title: 'Edit Challenge Description', + label: 'description', + isEditorRequired: true, + editorContent: this.challenge.description, + confirm: 'Submit', + deny: 'Cancel', + confirmCallback: SELF.apiCall, + }; + SELF.globalService.showModal(PARAMS); } /** - * Submission Visibility toggle function + * Edit terms and conditions of the challenge */ - toggleSubmissionVisibility() { + editTermsAndConditions() { const SELF = this; - if(SELF.isLeaderboardPublic == true) { - let toggleSubmissionVisibilityState, isSubmissionPublic; - if (SELF.submissionVisibility.state === 'Public') { - toggleSubmissionVisibilityState = 'private'; - isSubmissionPublic = false; - SELF.submissionVisibility.state = 'Private'; - SELF.submissionVisibility.icon = 'fa fa-toggle-off'; - } else { - toggleSubmissionVisibilityState = 'public'; - isSubmissionPublic = true; - SELF.submissionVisibility.state = 'Public'; - SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text'; - } - const BODY: FormData = new FormData(); - BODY.append("is_submission_public", isSubmissionPublic); + SELF.apiCall = (params) => { + const BODY = JSON.stringify(params); + SELF.apiService + .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) + .subscribe( + (data) => { + SELF.challenge.terms_and_conditions = data.terms_and_conditions; + this.updateView(); + SELF.globalService.showToast('success', 'The terms and conditions are successfully updated!', 5); + }, + (err) => { + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + }, + () => this.logger.info('EDIT-TERMS-AND-CONDITIONS-FINISHED') + ); + }; + + /** + * Parameters of the modal + */ + const PARAMS = { + title: 'Edit Terms And Conditions', + label: 'terms_and_conditions', + isEditorRequired: true, + editorContent: this.challenge.terms_and_conditions, + confirm: 'Submit', + deny: 'Cancel', + confirmCallback: SELF.apiCall, + }; + SELF.globalService.showModal(PARAMS); + } + + /** + * Edit challenge start and end date function + */ + challengeDateDialog() { + const SELF = this; + SELF.apiCall = (params) => { + if (new Date(params.start_date).valueOf() < new Date(params.end_date).valueOf()) { + const BODY = JSON.stringify({ + start_date: new Date(params.start_date).toISOString(), + end_date: new Date(params.end_date).toISOString(), + }); SELF.apiService - .patchFileUrl( - SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.selectedPhase['challenge'], SELF.selectedPhase['id']), - BODY - ) + .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) .subscribe( (data) => { - SELF.challengeService.fetchPhases(SELF.selectedPhase['challenge']); - SELF.challengeService.changePhaseSelected(true); - SELF.selectedPhase = false; - SELF.globalService.showToast( - 'success', - 'The submissions were successfully made ' + toggleSubmissionVisibilityState, - 5 - ); + SELF.challenge.start_date = data.start_date; + SELF.challenge.end_date = data.end_date; + SELF.globalService.showToast('success', 'The Challenge start and end date successfully updated!', 5); }, (err) => { SELF.globalService.handleApiError(err, true); SELF.globalService.showToast('error', err); - if (isSubmissionPublic) { - SELF.submissionVisibility.state = 'Private'; - SELF.submissionVisibility.icon = 'fa fa-toggle-off'; + }, + () => {} + ); + } else { + SELF.globalService.showToast('error', 'The challenge start date cannot be same or greater than end date.', 5); + } + }; + const PARAMS = { + title: 'Edit Challenge Start and End Date', + content: '', + confirm: 'Confirm', + deny: 'Cancel', + form: [ + { + isRequired: false, + label: 'start_date', + placeholder: 'Start Date and Time', + type: 'text', + value: moment(this.challenge.start_date).format('MM-DD-YYYY hh:mm a'), + }, + { + isRequired: false, + label: 'end_date', + placeholder: 'End Date and Time', + type: 'text', + value: moment(this.challenge.end_date).format('MM-DD-YYYY hh:mm a'), + }, + ], + isButtonDisabled: true, + confirmCallback: SELF.apiCall, + }; + SELF.globalService.showModal(PARAMS); + } + + /** + * Publish challenge click function + */ + togglePublishChallengeState() { + const SELF = this; + let toggleChallengePublishState, isPublished; + if (this.publishChallenge.state === 'Published') { + toggleChallengePublishState = 'private'; + isPublished = false; + } else { + toggleChallengePublishState = 'public'; + isPublished = true; + } + SELF.apiCall = () => { + const BODY = JSON.stringify({ + published: isPublished, + }); + SELF.apiService + .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) + .subscribe( + (data) => { + if (isPublished) { + this.publishChallenge.state = 'Published'; + this.publishChallenge.icon = 'fa fa-eye green-text'; + } else { + this.publishChallenge.state = 'Not Published'; + this.publishChallenge.icon = 'fa fa-eye-slash red-text'; + } + SELF.globalService.showToast( + 'success', + 'The challenge was successfully made ' + toggleChallengePublishState, + 5 + ); + }, + (err) => { + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + }, + () => this.logger.info('PUBLISH-CHALLENGE-UPDATE-FINISHED') + ); + }; + + const PARAMS = { + title: 'Make this challenge ' + toggleChallengePublishState + '?', + content: '', + confirm: "Yes, I'm sure", + deny: 'No', + confirmCallback: SELF.apiCall, + }; + SELF.globalService.showConfirm(PARAMS); + } + + /** + * Close participation function + */ + stopParticipation(event) { + event.preventDefault(); + const participationState = this.challenge['is_registration_open'] ? 'Close' : 'Open'; + const closeParticipationMsg = 'Participation is closed successfully'; + const openParticipationMsg = 'Participation is opened successfully'; + + this.apiCall = () => { + if (this.isChallengeHost && this.challenge['id'] !== null) { + const BODY = JSON.stringify({ + is_registration_open: !this.challenge['is_registration_open'], + }); + this.apiService + .patchUrl(this.endpointsService.editChallengeDetailsURL(this.challenge.creator.id, this.challenge.id), BODY) + .subscribe( + () => { + this.challenge['is_registration_open'] = !this.challenge['is_registration_open']; + if (this.challenge['is_registration_open']) { + this.globalService.showToast('success', openParticipationMsg, 5); } else { - SELF.submissionVisibility.state = 'Public'; - SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text'; + this.globalService.showToast('success', closeParticipationMsg, 5); } }, - () => this.logger.info('SUBMISSION-VISIBILITY-UPDATE-FINISHED') + (err) => { + this.globalService.handleApiError(err, true); + this.globalService.showToast('error', err); + }, + () => {} ); + } + }; + + const PARAMS = { + title: participationState + ' participation in the challenge?', + content: '', + confirm: "Yes, I'm sure", + deny: 'No', + confirmCallback: this.apiCall, + }; + this.globalService.showConfirm(PARAMS); + } + + // Banned Email Ids -> + + updateView() { + this.bannedEmailIds = this.challenge.banned_email_ids || []; + this.formerBannedEmailIds = this.bannedEmailIds.concat(); // Creating deep copy + } + + /** + * Add banned email chip + * @param event current event + */ + add(event: MatChipInputEvent): void { + const SELF = this; + const input = event.input; + const value = event.value; + SELF.isValidationError = false; + SELF.message = ''; + + // Add our fruit + if ((value || '').trim()) { + if (!SELF.validateEmail(value.trim())) { + SELF.isValidationError = true; + SELF.message = 'Please enter a valid email!'; + } else { + SELF.bannedEmailIds.push(value.trim()); + } } - else { - SELF.globalService.showToast('error', "Leaderboard is private, please make the leaderbaord public"); + + // Reset the input value + if (input && !SELF.isValidationError) { + input.value = ''; } } @@ -450,230 +699,309 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { ); } + // Edit Phase Details -> + /** - * Close participation function + * Called when a phase is selected (from child component) */ - stopParticipation(event) { - event.preventDefault(); - const participationState = this.challenge['is_registration_open'] ? 'Close' : 'Open'; - const closeParticipationMsg = 'Participation is closed successfully'; - const openParticipationMsg = 'Participation is opened successfully'; - - this.apiCall = () => { - if (this.isChallengeHost && this.challenge['id'] !== null) { - const BODY = JSON.stringify({ - is_registration_open: !this.challenge['is_registration_open'], - }); - this.apiService - .patchUrl(this.endpointsService.editChallengeDetailsURL(this.challenge.creator.id, this.challenge.id), BODY) - .subscribe( - () => { - this.challenge['is_registration_open'] = !this.challenge['is_registration_open']; - if (this.challenge['is_registration_open']) { - this.globalService.showToast('success', openParticipationMsg, 5); - } else { - this.globalService.showToast('success', closeParticipationMsg, 5); - } - }, - (err) => { - this.globalService.handleApiError(err, true); - this.globalService.showToast('error', err); - }, - () => {} - ); + phaseSelected() { + const SELF = this; + return (phase) => { + SELF.selectedPhase = phase; + SELF.isPhasePublic = SELF.selectedPhase['is_public']; + SELF.isSubmissionPublic = SELF.selectedPhase['is_submission_public']; + SELF.isLeaderboardPublic = SELF.selectedPhase['leaderboard_public']; + if (SELF.isPhasePublic) { + SELF.phaseVisibility.state = 'Public'; + SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; + } + else { + SELF.phaseVisibility.state = 'Private'; + SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } + if (SELF.isSubmissionPublic) { + SELF.submissionVisibility.state = 'Public'; + SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text'; + } + else { + SELF.submissionVisibility.state = 'Private'; + SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; } }; - - const PARAMS = { - title: participationState + ' participation in the challenge?', - content: '', - confirm: "Yes, I'm sure", - deny: 'No', - confirmCallback: this.apiCall, - }; - this.globalService.showConfirm(PARAMS); - } + } /** - * Edit challenge title function + * Edit Phase Details function */ - editChallengeTitle() { + editPhaseDetails() { const SELF = this; - SELF.apiCall = (params) => { - const BODY = JSON.stringify(params); + const FORM_DATA: FormData = new FormData(); + for (const key in params) { + if (params[key]) { + FORM_DATA.append(key, params[key]); + } + } SELF.apiService - .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) + .patchFileUrl( + SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.challenge.id, SELF.selectedPhase['id']), + FORM_DATA + ) .subscribe( (data) => { - SELF.challenge.title = data.title; - SELF.globalService.showToast('success', 'The challenge title is successfully updated!', 5); + for (var attrname in data) { + SELF.selectedPhase[attrname] = data[attrname]; + } + SELF.globalService.showToast('success', 'The challenge phase details are successfully updated!'); }, (err) => { - SELF.globalService.handleApiError(err, true); SELF.globalService.showToast('error', err); }, - () => {} + () => {this.logger.info('PHASE-UPDATE-FINISHED')} ); }; const PARAMS = { - title: 'Edit Challenge Title', - content: '', + title: 'Edit Challenge Phase Details', + name: SELF.selectedPhase['name'], + label: 'description', + description: SELF.selectedPhase['description'], + startDate: SELF.selectedPhase['start_date'], + endDate: SELF.selectedPhase['end_date'], + maxSubmissionsPerDay: SELF.selectedPhase['max_submissions_per_day'], + maxSubmissionsPerMonth: SELF.selectedPhase['max_submissions_per_month'], + maxSubmissions: SELF.selectedPhase['max_submissions'], + maxConcurrentSubmissionsAllowed: SELF.selectedPhase['max_concurrent_submissions_allowed'], + allowedSubmissionFileTypes: SELF.selectedPhase['allowed_submission_file_types'], confirm: 'Submit', deny: 'Cancel', - form: [ - { - name: 'editChallengeTitle', - isRequired: true, - label: 'title', - placeholder: 'Challenge Title', - type: 'text', - value: this.challenge.title, - }, - ], confirmCallback: SELF.apiCall, }; - SELF.globalService.showModal(PARAMS); - } + SELF.globalService.showEditPhaseModal(PARAMS); +} /** - * Edit terms and conditions of the challenge + * Phase Visibility toggle function */ - editTermsAndConditions() { + togglePhaseVisibility() { const SELF = this; - SELF.apiCall = (params) => { - const BODY = JSON.stringify(params); + let togglePhaseVisibilityState, isPublic; + if (SELF.phaseVisibility.state === 'Public') { + togglePhaseVisibilityState = 'private'; + isPublic = false; + SELF.phaseVisibility.state = 'Private'; + SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } else { + togglePhaseVisibilityState = 'public'; + isPublic = true; + SELF.phaseVisibility.state = 'Public'; + SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; + } + const BODY: FormData = new FormData(); + BODY.append("is_public", isPublic); SELF.apiService - .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) + .patchFileUrl( + SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.selectedPhase['challenge'], SELF.selectedPhase['id']), + BODY + ) .subscribe( (data) => { - SELF.challenge.terms_and_conditions = data.terms_and_conditions; - this.updateView(); - SELF.globalService.showToast('success', 'The terms and conditions are successfully updated!', 5); + SELF.selectedPhase['is_public'] = data.is_public; + SELF.selectedPhase['showPrivate'] = !data.is_public; + SELF.challengeService.changePhaseSelected(true); + if(!SELF.selectedPhase['is_public']) { + for(let i = 0; i < SELF.phaseSplits.length; i++) { + if(SELF.phaseSplits[i]['challenge_phase_name'] === SELF.selectedPhase['name'] && SELF.phaseSplits[i]['visibility'] == 3) { + let visibility: any = 1; + const BODY: FormData = new FormData(); + BODY.append("visibility", visibility); + SELF.apiService + .patchFileUrl( + SELF.endpointsService.particularChallengePhaseSplitUrl(SELF.phaseSplits[i]['id']), + BODY + ) + .subscribe( + (data) => { + SELF.phaseSplits[i]['visibility'] = data.visibility; + SELF.challengeService.changePhaseSplitSelected(true); + SELF.phaseSplits[i]['showPrivate'] = true; + if(SELF.selectedPhaseSplit === SELF.phaseSplits[i]) { + SELF.leaderboardVisibility.state = 'Private'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } + }, + (err) => { + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + }, + () => this.logger.info('Change leaderboard visibility after phase is updated') + ); + } + } + } + + SELF.globalService.showToast( + 'success', + 'The phase was successfully made ' + togglePhaseVisibilityState, + 5 + ); }, (err) => { - SELF.globalService.handleApiError(err, true); - SELF.globalService.showToast('error', err); - }, - () => this.logger.info('EDIT-TERMS-AND-CONDITIONS-FINISHED') - ); - }; - - /** - * Parameters of the modal - */ - const PARAMS = { - title: 'Edit Terms And Conditions', - label: 'terms_and_conditions', - isEditorRequired: true, - editorContent: this.challenge.terms_and_conditions, - confirm: 'Submit', - deny: 'Cancel', - confirmCallback: SELF.apiCall, - }; - SELF.globalService.showModal(PARAMS); - } - - - /** - * Delete challenge - */ - deleteChallenge() { - const SELF = this; - const redirectTo = '/dashboard'; - - SELF.apiCall = () => { - const BODY = JSON.stringify({}); - SELF.apiService.postUrl(SELF.endpointsService.deleteChallengeURL(SELF.challenge.id), BODY).subscribe( - (data) => { - SELF.router.navigate([redirectTo]); - SELF.globalService.showToast('success', 'The Challenge is successfully deleted!', 5); - }, - (err) => { - SELF.globalService.handleApiError(err, true); - SELF.globalService.showToast('error', err); - }, - () => {} - ); - }; - - const PARAMS = { - title: 'Delete Challenge', - content: '', - confirm: 'I understand consequences, delete the challenge', - deny: 'Cancel', - form: [ - { - name: 'challegenDeleteInput', - isRequired: true, - label: '', - placeholder: 'Please type in the name of the challenge to confirm', - type: 'text', - value: '', - }, - ], - confirmCallback: SELF.apiCall, - }; - SELF.globalService.showModal(PARAMS); + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + if (isPublic) { + SELF.phaseVisibility.state = 'Private'; + SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } else { + SELF.phaseVisibility.state = 'Public'; + SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; + } + }, + () => this.logger.info('PHASE-VISIBILITY-UPDATE-FINISHED') + ); } /** - * Edit challenge start and end date function + * Submission Visibility toggle function */ - challengeDateDialog() { + toggleSubmissionVisibility() { const SELF = this; - SELF.apiCall = (params) => { - if (new Date(params.start_date).valueOf() < new Date(params.end_date).valueOf()) { - const BODY = JSON.stringify({ - start_date: new Date(params.start_date).toISOString(), - end_date: new Date(params.end_date).toISOString(), - }); + if(SELF.isLeaderboardPublic == true) { + let toggleSubmissionVisibilityState, isSubmissionPublic; + if (SELF.submissionVisibility.state === 'Public') { + toggleSubmissionVisibilityState = 'private'; + isSubmissionPublic = false; + SELF.submissionVisibility.state = 'Private'; + SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } else { + toggleSubmissionVisibilityState = 'public'; + isSubmissionPublic = true; + SELF.submissionVisibility.state = 'Public'; + SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text'; + } + const BODY: FormData = new FormData(); + BODY.append("is_submission_public", isSubmissionPublic); SELF.apiService - .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) + .patchFileUrl( + SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.selectedPhase['challenge'], SELF.selectedPhase['id']), + BODY + ) .subscribe( (data) => { - SELF.challenge.start_date = data.start_date; - SELF.challenge.end_date = data.end_date; - SELF.globalService.showToast('success', 'The Challenge start and end date successfully updated!', 5); + SELF.selectedPhase['is_submission_public'] = data.is_submission_public; + SELF.globalService.showToast( + 'success', + 'The submissions were successfully made ' + toggleSubmissionVisibilityState, + 5 + ); }, (err) => { SELF.globalService.handleApiError(err, true); SELF.globalService.showToast('error', err); + if (isSubmissionPublic) { + SELF.submissionVisibility.state = 'Private'; + SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } else { + SELF.submissionVisibility.state = 'Public'; + SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text'; + } }, - () => {} + () => this.logger.info('SUBMISSION-VISIBILITY-UPDATE-FINISHED') ); + } + else { + SELF.globalService.showToast('error', "Leaderboard is private, please make the leaderbaord public"); + } + } + + // Edit Leaderboard Details -> + + /** + * This is called when a phase split is selected (from child components) + */ + phaseSplitSelected () { + const SELF = this; + return (phaseSplit) => { + SELF.selectedPhaseSplit = phaseSplit; + SELF.isPhaseSplitLeaderboardPublic = SELF.selectedPhaseSplit['visibility']; + if(SELF.isPhaseSplitLeaderboardPublic == 3) { + SELF.leaderboardVisibility.state = 'Public'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text'; + } + else { + SELF.leaderboardVisibility.state = 'Private'; + SELF.leaderboardVisibility.icon = 'fa fa fa-toggle-off grey-text text-darken-1'; + } + } + } + + /** + * Leadeboard Visibility toggle function + */ + toggleLeaderboardVisibility() { + const SELF = this; + let toggleLeaderboardVisibilityState, visibility, phaseIsPublic; + for(let i = 0; i < SELF.filteredPhases.length; i++) { + if(SELF.filteredPhases[i]['name'] === SELF.selectedPhaseSplit['challenge_phase_name']) { + phaseIsPublic = SELF.filteredPhases[i]['is_public']; + } + } + if(phaseIsPublic) { + if (SELF.leaderboardVisibility.state === 'Public') { + toggleLeaderboardVisibilityState = 'private'; + visibility = 1; + SELF.leaderboardVisibility.state = 'Private'; + SELF.leaderboardVisibility.icon = 'fa fa fa-toggle-off grey-text text-darken-1'; } else { - SELF.globalService.showToast('error', 'The challenge start date cannot be same or greater than end date.', 5); + toggleLeaderboardVisibilityState = 'public'; + visibility = 3; + SELF.leaderboardVisibility.state = 'Public'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text'; } - }; - const PARAMS = { - title: 'Edit Challenge Start and End Date', - content: '', - confirm: 'Confirm', - deny: 'Cancel', - form: [ - { - isRequired: false, - label: 'start_date', - placeholder: 'Start Date and Time', - type: 'text', - value: moment(this.challenge.start_date).format('MM-DD-YYYY hh:mm a'), + const BODY: FormData = new FormData(); + BODY.append("visibility", visibility); + SELF.apiService + .patchFileUrl(SELF.endpointsService.particularChallengePhaseSplitUrl(SELF.selectedPhaseSplit['id']),BODY) + .subscribe( + (data) => { + SELF.selectedPhaseSplit['visibility'] = data.visibility; + SELF.challengeService.changePhaseSplitSelected(true); + if (visibility == 3) { + SELF.selectedPhaseSplit['showPrivate'] = false; + SELF.leaderboardVisibility.state = 'Public'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text'; + } else { + SELF.selectedPhaseSplit['showPrivate'] = true; + SELF.leaderboardVisibility.state = 'Private'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } + SELF.globalService.showToast( + 'success', + 'The phase split was successfully made ' + toggleLeaderboardVisibilityState, + 5 + ); }, - { - isRequired: false, - label: 'end_date', - placeholder: 'End Date and Time', - type: 'text', - value: moment(this.challenge.end_date).format('MM-DD-YYYY hh:mm a'), + (err) => { + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + if (visibility == 3) { + SELF.leaderboardVisibility.state = 'Private'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; + } else { + SELF.leaderboardVisibility.state = 'Public'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text'; + } }, - ], - isButtonDisabled: true, - confirmCallback: SELF.apiCall, - }; - SELF.globalService.showModal(PARAMS); + () => this.logger.info('LEADERBOARD-VISIBILITY-UPDATE-FINISHED') + ); + } + else { + SELF.globalService.showToast('error', 'The phase is private, please make the phase public'); + } } + // Edit Evaluation Script and Criteria -> + /** * Edit evaluation criteria of the challenge */ @@ -758,90 +1086,7 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { SELF.globalService.showModal(PARAMS); } - /** - * Edit challenge image function - */ - editChallengeImage() { - const SELF = this; - SELF.apiCall = (params) => { - const FORM_DATA: FormData = new FormData(); - FORM_DATA.append('image', params['image']); - SELF.apiService - .patchFileUrl( - SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), - FORM_DATA - ) - .subscribe( - (data) => { - SELF.challenge.image = data.image; - SELF.globalService.showToast('success', 'The Challenge image successfully updated!', 5); - }, - (err) => { - SELF.globalService.handleApiError(err, true); - SELF.globalService.showToast('error', err); - }, - () => {} - ); - }; - - /** - * Parameters of the modal - */ - const PARAMS = { - title: 'Edit Challenge Image', - content: '', - confirm: 'Submit', - deny: 'Cancel', - form: [ - { - name: 'Challenge Image', - isRequired: true, - label: 'image', - placeholder: '', - type: 'file', - value: '', - }, - ], - confirmCallback: SELF.apiCall, - }; - SELF.globalService.showModal(PARAMS); - } - - /** - * Edit challenge overview function - */ - - editChallengeOverview() { - const SELF = this; - - SELF.apiCall = (params) => { - const BODY = JSON.stringify(params); - SELF.apiService - .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) - .subscribe( - (data) => { - SELF.challenge.description = data.description; - SELF.globalService.showToast('success', 'The description is successfully updated!', 5); - }, - (err) => { - SELF.globalService.handleApiError(err, true); - SELF.globalService.showToast('error', err); - }, - () => this.logger.info('EDIT-CHALLENGE-DESCRIPTION-FINISHED') - ); - }; - - const PARAMS = { - title: 'Edit Challenge Description', - label: 'description', - isEditorRequired: true, - editorContent: this.challenge.description, - confirm: 'Submit', - deny: 'Cancel', - confirmCallback: SELF.apiCall, - }; - SELF.globalService.showModal(PARAMS); - } + // Worker logs -> /** * API call to manage the worker from UI. @@ -893,61 +1138,12 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { SELF.fetchWorkerLogs(); }, 5000); } - - ngOnDestroy() { - clearInterval(this.pollingInterval); - } - + /** - * Publish challenge click function + * Component on destroyed. */ - togglePublishChallengeState() { - const SELF = this; - let toggleChallengePublishState, isPublished; - if (this.publishChallenge.state === 'Published') { - toggleChallengePublishState = 'private'; - isPublished = false; - } else { - toggleChallengePublishState = 'public'; - isPublished = true; - } - SELF.apiCall = () => { - const BODY = JSON.stringify({ - published: isPublished, - }); - SELF.apiService - .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) - .subscribe( - (data) => { - if (isPublished) { - this.publishChallenge.state = 'Published'; - this.publishChallenge.icon = 'fa fa-eye green-text'; - } else { - this.publishChallenge.state = 'Not Published'; - this.publishChallenge.icon = 'fa fa-eye-slash red-text'; - } - SELF.globalService.showToast( - 'success', - 'The challenge was successfully made ' + toggleChallengePublishState, - 5 - ); - }, - (err) => { - SELF.globalService.handleApiError(err, true); - SELF.globalService.showToast('error', err); - }, - () => this.logger.info('PUBLISH-CHALLENGE-UPDATE-FINISHED') - ); - }; - - const PARAMS = { - title: 'Make this challenge ' + toggleChallengePublishState + '?', - content: '', - confirm: "Yes, I'm sure", - deny: 'No', - confirmCallback: SELF.apiCall, - }; - SELF.globalService.showConfirm(PARAMS); + ngOnDestroy() { + clearInterval(this.pollingInterval); } } diff --git a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html index e633b57a7b..fc7a48e845 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html +++ b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html @@ -10,8 +10,6 @@
My Submissions
-
-
My Participated Team:  {{ participatedTeamName }}
diff --git a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.scss b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.scss index 8e33b90d0c..5528951325 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.scss +++ b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.scss @@ -2,7 +2,7 @@ @import './mixins.scss'; .ev-md-container { - padding: 0px 30px 30px 30px; + padding-bottom: 0px; .phase-title { margin-bottom: 20px; diff --git a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts index 53c0a44dd2..bef9851841 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts @@ -166,13 +166,14 @@ export class ChallengesubmissionsComponent implements OnInit, AfterViewInit { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router GlobalService Injection. * @param authService AuthService Injection. + * @param router Router Injection. + * @param route ActivatedRoute Injection. + * @param challengeService ChallengeService Injection. * @param globalService GlobalService Injection. * @param apiService Router Injection. + * @param windowService WindowService Injection. * @param endpointsService EndpointsService Injection. - * @param challengeService ChallengeService Injection. */ constructor( private authService: AuthService, @@ -226,6 +227,9 @@ export class ChallengesubmissionsComponent implements OnInit, AfterViewInit { if (this.phases[i].is_public === false) { this.phases[i].showPrivate = true; } + else { + this.phases[i].showPrivate = false; + } } this.filteredPhases = this.phases; }); @@ -246,7 +250,6 @@ export class ChallengesubmissionsComponent implements OnInit, AfterViewInit { SELF.submissionCount = 0; if (SELF.challenge['id'] && phase['id']) { SELF.fetchSubmissions(SELF.challenge['id'], phase['id']); - SELF.fetchSubmissionCounts(this.challenge['id'], phase['id']); } }; } @@ -262,6 +265,7 @@ export class ChallengesubmissionsComponent implements OnInit, AfterViewInit { API_PATH = SELF.endpointsService.challengeSubmissionURL(challenge, phase); SELF.apiService.getUrl(API_PATH).subscribe( (data) => { + SELF.submissionCount = data['count']; SELF.submissions = data['results']; let index = 0; SELF.submissions.forEach((submission) => { @@ -485,30 +489,7 @@ export class ChallengesubmissionsComponent implements OnInit, AfterViewInit { ); } } - - /** - * Fetch number of submissions for a challenge phase. - * @param challenge challenge id - * @param phase phase id - */ - fetchSubmissionCounts(challenge, phase) { - const API_PATH = this.endpointsService.challengeSubmissionCountURL(challenge, phase); - const SELF = this; - this.apiService.getUrl(API_PATH).subscribe( - (data) => { - if (data['participant_team_submission_count']) { - SELF.submissionCount = data['participant_team_submission_count']; - } - }, - (err) => { - SELF.globalService.handleApiError(err); - }, - () => { - this.logger.info('Fetched submission counts', challenge, phase); - } - ); - } - + /** * Display Edit Submission Modal. * @param submission Submission being edited diff --git a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts index 4acf2e5af9..f3cc067088 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts @@ -38,6 +38,11 @@ export class ChallengesubmitComponent implements OnInit { */ isSubmissionUsingCli: any; + /** + * If phase has been selected + */ + isPhaseSelected:boolean = false; + /** * Is user logged in */ @@ -213,12 +218,13 @@ export class ChallengesubmitComponent implements OnInit { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router Router Injection. * @param authService AuthService Injection. - * @param globalService GlobalService Injection. - * @param apiService ApiService Injection. + * @param router Router Injection. + * @param route ActivatedRoute Injection. * @param challengeService ChallengeService Injection. + * @param globalService GlobalService Injection. + * @param apiService Router Injection. + * @param endpointsService EndpointsService Injection. */ constructor( private authService: AuthService, @@ -260,6 +266,9 @@ export class ChallengesubmitComponent implements OnInit { if (phases[j].is_public === false) { this.phases[j].showPrivate = true; } + else { + this.phases[j].showPrivate = false; + } } }); @@ -472,16 +481,19 @@ export class ChallengesubmitComponent implements OnInit { const SELF = this; return (phase) => { SELF.selectedPhase = phase; + SELF.isPhaseSelected = true; SELF.isLeaderboardPublic = phase['leaderboard_public']; if (SELF.challenge['id'] && phase['id']) { SELF.getMetaDataDetails(SELF.challenge['id'], phase['id']); SELF.fetchRemainingSubmissions(SELF.challenge['id'], phase['id']); SELF.clearMetaAttributeValues(); SELF.submissionError = ''; - SELF.components['_results'].forEach((element) => { - element.value = ''; - element.message = ''; - }); + if(SELF.components) { + SELF.components['_results'].forEach((element) => { + element.value = ''; + element.message = ''; + }); + } } }; } diff --git a/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts b/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts index 5b90096306..30c072aa3f 100644 --- a/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts @@ -165,13 +165,14 @@ export class ChallengeviewallsubmissionsComponent implements OnInit, AfterViewIn /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router GlobalService Injection. * @param authService AuthService Injection. + * @param router Router Injection. + * @param route ActivatedRoute Injection. + * @param challengeService ChallengeService Injection. * @param globalService GlobalService Injection. * @param apiService Router Injection. + * @param windowService WindowService Injection. * @param endpointsService EndpointsService Injection. - * @param challengeService ChallengeService Injection. */ constructor( private authService: AuthService, @@ -224,6 +225,9 @@ export class ChallengeviewallsubmissionsComponent implements OnInit, AfterViewIn if (this.phases[i].is_public === false) { this.phases[i].showPrivate = true; } + else { + this.phases[i].showPrivate = false; + } } this.filteredPhases = this.phases; }); @@ -244,7 +248,6 @@ export class ChallengeviewallsubmissionsComponent implements OnInit, AfterViewIn SELF.submissionCount = 0; if (SELF.challenge['id'] && phase['id']) { SELF.fetchSubmissions(SELF.challenge['id'], phase['id']); - SELF.fetchSubmissionCounts(this.challenge['id'], phase['id']); } }; } @@ -273,6 +276,7 @@ export class ChallengeviewallsubmissionsComponent implements OnInit, AfterViewIn SELF.apiService.getUrl(API_PATH).subscribe( (data) => { if(name == SELF.filterSubmissionsQuery) { + SELF.submissionCount = data['count']; SELF.submissions = data['results']; let index = 0; SELF.submissions.forEach((submission) => { @@ -531,29 +535,6 @@ export class ChallengeviewallsubmissionsComponent implements OnInit, AfterViewIn SELF.globalService.showConfirm(PARAMS); } - /** - * Fetch number of submissions for a challenge phase. - * @param challenge challenge id - * @param phase phase id - */ - fetchSubmissionCounts(challenge, phase) { - const API_PATH = this.endpointsService.challengeSubmissionCountURL(challenge, phase); - const SELF = this; - this.apiService.getUrl(API_PATH).subscribe( - (data) => { - if (data['participant_team_submission_count']) { - SELF.submissionCount = data['participant_team_submission_count']; - } - }, - (err) => { - SELF.globalService.handleApiError(err); - }, - () => { - this.logger.info('Fetched submission counts', challenge, phase); - } - ); - } - /** * Modal to confirm the change of submission visibility * @param submission Selected submission diff --git a/frontend_v2/src/app/components/contact/contact.component.ts b/frontend_v2/src/app/components/contact/contact.component.ts index 5f9375005e..3b92dd4c71 100644 --- a/frontend_v2/src/app/components/contact/contact.component.ts +++ b/frontend_v2/src/app/components/contact/contact.component.ts @@ -1,11 +1,9 @@ -import { Component, OnInit, Inject } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ViewChildren, QueryList, AfterViewInit } from '@angular/core'; -import { DOCUMENT } from '@angular/common'; -import { Router, ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; import { NGXLogger } from 'ngx-logger'; import { InputComponent } from '../../components/utility/input/input.component'; -import { WindowService } from '../../services/window.service'; import { ApiService } from '../../services/api.service'; import { EndpointsService } from '../../services/endpoints.service'; import { GlobalService } from '../../services/global.service'; @@ -41,20 +39,15 @@ export class ContactComponent implements OnInit, AfterViewInit { componentlist: any; /** - * Constructor. - * @param document Window document Injection. - * @param route ActivatedRoute Injection. - * @param router Router Injection. + * Constructor.. * @param globalService GlobalService Injection. * @param apiService ApiService Injection. + * @param router Router Injection. * @param endpointsService EndpointsService Injection. */ constructor( - @Inject(DOCUMENT) private document: Document, - private windowService: WindowService, private globalService: GlobalService, private apiService: ApiService, - private route: ActivatedRoute, private router: Router, private endpointsService: EndpointsService, private logger: NGXLogger diff --git a/frontend_v2/src/app/components/home/featured-challenges/featured-challenges.component.ts b/frontend_v2/src/app/components/home/featured-challenges/featured-challenges.component.ts index 148d67c875..6e63bc100e 100644 --- a/frontend_v2/src/app/components/home/featured-challenges/featured-challenges.component.ts +++ b/frontend_v2/src/app/components/home/featured-challenges/featured-challenges.component.ts @@ -1,11 +1,9 @@ import { Component, OnInit } from '@angular/core'; -import { HttpClientModule } from '@angular/common/http'; -import { Router, ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; import { NGXLogger } from 'ngx-logger'; // import service import { GlobalService } from '../../../services/global.service'; -import { AuthService } from '../../../services/auth.service'; import { EndpointsService } from '../../../services/endpoints.service'; import { ApiService } from '../../../services/api.service'; @@ -31,19 +29,15 @@ export class FeaturedChallengesComponent implements OnInit { /** * Constructor. * @param endpointsService EndpointService Injection. - * @param route ActivatedRoute Injection. * @param router Router Injection. * @param globalService GlobalService Injection. * @param apiService ApiService Injection. - * @param authService AuthService Injection. */ constructor( - private apiService: ApiService, - private authService: AuthService, - private globalService: GlobalService, - private router: Router, - private route: ActivatedRoute, private endpointsService: EndpointsService, + private router: Router, + private globalService: GlobalService, + private apiService: ApiService, private logger: NGXLogger ) {} diff --git a/frontend_v2/src/app/components/home/home.component.ts b/frontend_v2/src/app/components/home/home.component.ts index 6e9c3fd99a..9e2885c1db 100644 --- a/frontend_v2/src/app/components/home/home.component.ts +++ b/frontend_v2/src/app/components/home/home.component.ts @@ -3,7 +3,6 @@ import { ApiService } from '../../services/api.service'; import { EndpointsService } from '../../services/endpoints.service'; import { AuthService } from '../../services/auth.service'; import { GlobalService } from '../../services/global.service'; -import { BehaviorSubject } from 'rxjs'; import { Router } from '@angular/router'; import { InputComponent } from '../../components/utility/input/input.component'; import { DOCUMENT } from '@angular/common'; @@ -69,11 +68,16 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy { components: QueryList; authServiceSubscription: any; - + /** - * Component constructor - * @param document Window document Injection. + * Constructor. + * @param apiService ApiServiceInjection + * @param endpointService EndPointServiceInjection. + * @param authService AuthServiceInjection. + * @param globalService GlobalService Injection. + * @param router Router Injection. */ + constructor( private apiService: ApiService, private endpointService: EndpointsService, diff --git a/frontend_v2/src/app/components/home/homemain/homemain.component.ts b/frontend_v2/src/app/components/home/homemain/homemain.component.ts index 3fcd4e2271..7f6e3782e5 100644 --- a/frontend_v2/src/app/components/home/homemain/homemain.component.ts +++ b/frontend_v2/src/app/components/home/homemain/homemain.component.ts @@ -1,8 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; import { AuthService } from '../../../services/auth.service'; -import { ApiService } from '../../../services/api.service'; -import { GlobalService } from '../../../services/global.service'; /** * Component Class @@ -20,17 +17,9 @@ export class HomemainComponent implements OnInit { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router Router Injection. - * @param globalService GlobalService Injection. - * @param apiService ApiService Injection. * @param authService AuthService Injection. */ constructor( - private router: Router, - private route: ActivatedRoute, - private apiService: ApiService, - private globalService: GlobalService, private authService: AuthService ) {} diff --git a/frontend_v2/src/app/components/nav/footer/footer.component.ts b/frontend_v2/src/app/components/nav/footer/footer.component.ts index 70d2c0818a..29f2db0097 100644 --- a/frontend_v2/src/app/components/nav/footer/footer.component.ts +++ b/frontend_v2/src/app/components/nav/footer/footer.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, Inject, Input } from '@angular/core'; import { ApiService } from '../../../services/api.service'; import { GlobalService } from '../../../services/global.service'; import { DOCUMENT } from '@angular/common'; -import { Router, ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; /** * Component Class @@ -34,18 +34,16 @@ export class FooterComponent implements OnInit { /** * Constructor. + * @param apiService ApiService Injection. * @param document Window document Injection. - * @param route ActivatedRoute Injection. - * @param router Router Injection. * @param globalService GlobalService Injection. - * @param apiService ApiService Injection. + * @param router Router Injection. */ constructor( private apiService: ApiService, @Inject(DOCUMENT) private document: Document, private globalService: GlobalService, - private router: Router, - private route: ActivatedRoute + private router: Router ) {} /** diff --git a/frontend_v2/src/app/components/nav/header-static/header-static.component.ts b/frontend_v2/src/app/components/nav/header-static/header-static.component.ts index 5d53f335b0..a35411f778 100644 --- a/frontend_v2/src/app/components/nav/header-static/header-static.component.ts +++ b/frontend_v2/src/app/components/nav/header-static/header-static.component.ts @@ -7,15 +7,12 @@ import { HostListener, ViewChild, ElementRef, - AfterViewInit, } from '@angular/core'; import { GlobalService } from '../../../services/global.service'; import { AuthService } from '../../../services/auth.service'; import { filter } from "rxjs/internal/operators"; -import { RouterModule, Router, ActivatedRoute, NavigationEnd } from '@angular/router'; +import { Router, NavigationEnd } from '@angular/router'; import { DOCUMENT } from '@angular/common'; -import { ApiService } from '../../../services/api.service'; -import { el } from '@angular/platform-browser/testing/src/browser_util'; /** * Component Class @@ -82,21 +79,17 @@ export class HeaderStaticComponent implements OnInit, OnDestroy { /** * Constructor. - * @param document Window document Injection. - * @param route ActivatedRoute Injection. - * @param router Router Injection. * @param globalService GlobalService Injection. - * @param authService AuthService Injection. - * @param apiService ApiService Injection. + * @param router Router Injection. * @param ref Angular Change Detector Injection. + * @param authService AuthService Injection. + * @param document Window document Injection. */ constructor( private globalService: GlobalService, - private route: ActivatedRoute, private router: Router, private ref: ChangeDetectorRef, public authService: AuthService, - private apiService: ApiService, @Inject(DOCUMENT) private document: Document ) { this.authState = authService.authState; diff --git a/frontend_v2/src/app/components/our-team/our-team.component.ts b/frontend_v2/src/app/components/our-team/our-team.component.ts index 0df1050411..bcac673717 100644 --- a/frontend_v2/src/app/components/our-team/our-team.component.ts +++ b/frontend_v2/src/app/components/our-team/our-team.component.ts @@ -1,12 +1,10 @@ import { Component, OnInit } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; import { NGXLogger } from 'ngx-logger'; // import service import { ApiService } from '../../services/api.service'; import { GlobalService } from '../../services/global.service'; import { EndpointsService } from '../../services/endpoints.service'; -import { AuthService } from '../../services/auth.service'; /** * Component Class @@ -19,19 +17,13 @@ import { AuthService } from '../../services/auth.service'; export class OurTeamComponent implements OnInit { /** * Constructor. - * @param endpointsService EndpointService Injection. - * @param route ActivatedRoute Injection. - * @param router Router Injection. - * @param globalService GlobalService Injection. * @param apiService ApiService Injection. - * @param authService AuthService Injection. + * @param globalService GlobalService Injection. + * @param endpointsService EndpointService Injection. */ constructor( private apiService: ApiService, - private authService: AuthService, private globalService: GlobalService, - private router: Router, - private route: ActivatedRoute, private endpointsService: EndpointsService, private logger: NGXLogger ) {} diff --git a/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.ts b/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.ts index 2de4884c82..036e2271ba 100644 --- a/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.ts +++ b/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.ts @@ -1,9 +1,7 @@ import { Component, OnInit, Input } from '@angular/core'; import { GlobalService } from '../../../../services/global.service'; -import { ApiService } from '../../../../services/api.service'; import { AuthService } from '../../../../services/auth.service'; -import { ChallengeService } from '../../../../services/challenge.service'; -import { Router, ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; /** * Component Class @@ -55,17 +53,7 @@ export class ChallengecardComponent implements OnInit { isLoggedIn = false; /** - * Tag list - */ - tags = ['Aritificial Intelligence', 'Machine Learning']; - - /** - * Challenge stars - */ - stars = { count: 0, is_starred: false }; - - /** - * Challenge stars + * Current Route */ routerPublic: Router; @@ -76,20 +64,14 @@ export class ChallengecardComponent implements OnInit { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router Router Injection. * @param globalService GlobalService Injection. * @param authService AuthService Injection. - * @param apiService ApiService Injection. - * @param challengeService ChallengeService Injection. + * @param router Router Injection. */ constructor( private globalService: GlobalService, - private apiService: ApiService, private authService: AuthService, - private challengeService: ChallengeService, - private router: Router, - private route: ActivatedRoute + private router: Router ) {} /** @@ -114,7 +96,6 @@ export class ChallengecardComponent implements OnInit { this.checkType(START_DATE, END_DATE, PRESENT); this.startDate = this.globalService.formatDate12Hour(START_DATE); this.endDate = this.globalService.formatDate12Hour(END_DATE); - this.fetchStars(); } /** @@ -136,30 +117,6 @@ export class ChallengecardComponent implements OnInit { } } - /** - * Fetch Stars for the current challenge card. - */ - fetchStars() { - this.challengeService.fetchStars(this.challenge['id'], (data) => { - this.stars = data; - }); - } - - /** - * Toggle stars for the current challenge card. - */ - starToggle() { - if (this.isLoggedIn) { - this.challengeService.starToggle( - this.challenge['id'], - (data, self) => { - self.stars = data; - }, - this - ); - } - } - /** * Challenge card is clicked (redirect). */ diff --git a/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.html b/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.html index 292e268082..5d81480557 100644 --- a/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.html +++ b/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.html @@ -4,6 +4,7 @@

All challenges

+
No challenges found.
@@ -12,9 +13,11 @@

All challenges

+
+
No challenges found.
@@ -24,9 +27,11 @@

All challenges

+
+
No challenges found.
@@ -36,6 +41,7 @@

All challenges

+
@@ -44,6 +50,7 @@

All challenges

Hosted challenges

+

You haven't hosted any challenge. Please @@ -57,8 +64,10 @@

Hosted challenges

+ +

No upcoming hosted challenges found. Please @@ -72,8 +81,10 @@

Hosted challenges

+ +

No past hosted challenges found.

@@ -84,9 +95,11 @@

Hosted challenges

+ +

No unapproved challenges found.

@@ -97,6 +110,7 @@

Hosted challenges

+
@@ -105,6 +119,7 @@

Hosted challenges

Participated challenges

+

You haven't participated in any challenge. Please @@ -118,8 +133,10 @@

Participated challenges

+
+

You haven't participated in any of the past challenges.

@@ -130,6 +147,7 @@

Participated challenges

+
diff --git a/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.ts b/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.ts index 329c033ae8..d6efda8c64 100644 --- a/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.ts +++ b/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.ts @@ -3,7 +3,7 @@ import { ApiService } from '../../../services/api.service'; import { GlobalService } from '../../../services/global.service'; import { AuthService } from '../../../services/auth.service'; import { EndpointsService } from '../../../services/endpoints.service'; -import { Router, ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; import { DOCUMENT } from '@angular/common'; /** @@ -179,12 +179,11 @@ export class ChallengelistComponent implements OnInit { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router Router Injection. - * @param globalService GlobalService Injection. - * @param authService AuthService Injection. * @param apiService ApiService Injection. + * @param authService AuthService Injection. + * @param globalService GlobalService Injection. * @param endpointsService EndpointsService Injection. + * @param router Router Injection. * @param document */ constructor( @@ -193,8 +192,7 @@ export class ChallengelistComponent implements OnInit { private globalService: GlobalService, private endpointsService: EndpointsService, private router: Router, - @Inject(DOCUMENT) private document, - private route: ActivatedRoute + @Inject(DOCUMENT) private document ) {} /** diff --git a/frontend_v2/src/app/components/publiclists/publiclists.component.ts b/frontend_v2/src/app/components/publiclists/publiclists.component.ts index 5bf8ba6653..2add1a954c 100644 --- a/frontend_v2/src/app/components/publiclists/publiclists.component.ts +++ b/frontend_v2/src/app/components/publiclists/publiclists.component.ts @@ -1,7 +1,4 @@ -import { Component, OnInit, Inject, OnDestroy } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; -import { DOCUMENT } from '@angular/common'; -import { GlobalService } from '../../services/global.service'; +import { Component } from '@angular/core'; import { AuthService } from '../../services/auth.service'; /** @@ -15,17 +12,9 @@ import { AuthService } from '../../services/auth.service'; export class PubliclistsComponent { /** * Constructor. - * @param document Window document Injection. - * @param route ActivatedRoute Injection. - * @param router Router Injection. * @param authService - * @param globalService GlobalService Injection. */ constructor( - private router: Router, - private route: ActivatedRoute, - @Inject(DOCUMENT) private document: Document, - public authService: AuthService, - private globalService: GlobalService + public authService: AuthService ) {} } diff --git a/frontend_v2/src/app/components/publiclists/teamlist/teamcard/teamcard.component.ts b/frontend_v2/src/app/components/publiclists/teamlist/teamcard/teamcard.component.ts index 9a1ea3f73b..6451dd108c 100644 --- a/frontend_v2/src/app/components/publiclists/teamlist/teamcard/teamcard.component.ts +++ b/frontend_v2/src/app/components/publiclists/teamlist/teamcard/teamcard.component.ts @@ -1,7 +1,4 @@ import { Component, OnInit, OnChanges, Input, Output, EventEmitter, SimpleChanges } from '@angular/core'; -import { GlobalService } from '../../../../services/global.service'; -import { ApiService } from '../../../../services/api.service'; -import { Router, ActivatedRoute } from '@angular/router'; import { AuthService } from '../../../../services/auth.service'; /** @@ -100,17 +97,10 @@ export class TeamcardComponent implements OnInit, OnChanges { /** * Constructor. - * @param route ActivatedRoute Injection. - * @param router Router Injection. - * @param globalService GlobalService Injection. - * @param apiService ApiService Injection. + * @param authService AuthService Injection. */ constructor( - private globalService: GlobalService, - private apiService: ApiService, - public authService: AuthService, - private router: Router, - private route: ActivatedRoute + public authService: AuthService ) {} /** diff --git a/frontend_v2/src/app/components/publiclists/teamlist/teamlist.component.html b/frontend_v2/src/app/components/publiclists/teamlist/teamlist.component.html index 6ce5711511..87365c698c 100644 --- a/frontend_v2/src/app/components/publiclists/teamlist/teamlist.component.html +++ b/frontend_v2/src/app/components/publiclists/teamlist/teamlist.component.html @@ -44,7 +44,7 @@
-