From 667c3740f8783b071962d30db13e9b0e0d367719 Mon Sep 17 00:00:00 2001 From: Aviral Jain Date: Sat, 24 Jul 2021 22:05:17 +0530 Subject: [PATCH 01/22] Deployment: Add docker compose file for monitoring containers(#3537) * shift monitoring containers to new docker-compose file * remove prometheus pushgateway service --- docker-compose-monitoring.yml | 32 ++++++++++++++++++++++++++++++++ docker-compose.yml | 31 ------------------------------- 2 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 docker-compose-monitoring.yml 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.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' From 31a83748e63372631e6f39c8d1a1dcddfd7015df Mon Sep 17 00:00:00 2001 From: savish28 <32800267+savish28@users.noreply.github.com> Date: Sat, 24 Jul 2021 22:06:28 +0530 Subject: [PATCH 02/22] Code Upload Worker: Refactor static code upload challenge workers for logs, wait time and exception(#3536) * Fix timelimit and add logs code upload worker * Submission worker: Add exception incase submission file not present * Revert suggested changes: time_delta and none case * Remove submission monitoring code upload worker * Remove submission container black changes --- .../workers/code_upload_submission_worker.py | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/scripts/workers/code_upload_submission_worker.py b/scripts/workers/code_upload_submission_worker.py index a90b66acba..da9a18c142 100755 --- a/scripts/workers/code_upload_submission_worker.py +++ b/scripts/workers/code_upload_submission_worker.py @@ -2,6 +2,7 @@ import logging import os import signal +import sys import yaml import time @@ -25,7 +26,16 @@ def exit_gracefully(self, signum, frame): self.kill_now = True +formatter = logging.Formatter( + "[%(asctime)s] %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S" +) + +handler = logging.StreamHandler(sys.stdout) +handler.setFormatter(formatter) + logger = logging.getLogger(__name__) +logger.addHandler(handler) +logger.setLevel(logging.INFO) AUTH_TOKEN = os.environ.get("AUTH_TOKEN", "auth_token") EVALAI_API_SERVER = os.environ.get( @@ -527,21 +537,6 @@ def cleanup_submission( ) -def is_submission_evaluation_done(container_state, container_state_map): - """Function to check if submission container is successfully terminated and sidecar-container is running - Arguments: - container_state {[AWS EKS API Container state object]} -- State of the container - container_state_map {[dict]} -- Map of container_name to container_state - """ - if ( - container_state.terminated.reason == "Completed" - and container_state_map.get("sidecar-container") - and container_state_map.get("sidecar-container").terminated is None - ): - return True - return False - - def update_failed_jobs_and_send_logs( api_instance, core_v1_api_instance, @@ -587,16 +582,6 @@ def update_failed_jobs_and_send_logs( logger.exception( "Exception while reading Job logs {}".format(e) ) - elif ( - container_name == "submission" - and is_submission_evaluation_done( - container_state, container_state_map - ) - ): - clean_submission = True - submission_error = ( - "submission.json/submission.csv file not found." - ) except Exception as e: logger.exception("Exception while reading Job {}".format(e)) clean_submission = True @@ -675,7 +660,7 @@ def main(): while True: # Equal distribution of queue messages among submission worker and code upload worker if challenge.get("is_static_dataset_code_upload"): - time.sleep(2.1) + time.sleep(0.5) message = evalai.get_message_from_sqs_queue() message_body = message.get("body") if message_body: @@ -684,6 +669,7 @@ def main(): ) and not message_body.get( "is_static_dataset_code_upload_submission" ): + time.sleep(35) continue api_instance = get_api_object( cluster_name, cluster_endpoint, challenge, evalai From b980dd35fdff154d28fbecfab13ce35730188ea0 Mon Sep 17 00:00:00 2001 From: savish28 <32800267+savish28@users.noreply.github.com> Date: Sat, 24 Jul 2021 22:07:52 +0530 Subject: [PATCH 03/22] Backend: Add multi AWS key support for submission and code upload worker container(#3530) * Aws: Add multi key support submission and code upload container * Refactor task definations from aws_utils * Add line * Spell: defination -> definition Co-authored-by: Rishabh Jain --- apps/challenges/aws_utils.py | 537 +++------------------------- apps/challenges/task_definitions.py | 488 +++++++++++++++++++++++++ 2 files changed, 528 insertions(+), 497 deletions(-) create mode 100644 apps/challenges/task_definitions.py 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/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 +}} +""" From dc5b97be8910290ae39313c552157d99c250b525 Mon Sep 17 00:00:00 2001 From: Gautam Jajoo Date: Sat, 24 Jul 2021 22:09:43 +0530 Subject: [PATCH 04/22] Frontend_V2: Add edit section for leaderboard(#3524) * add leaderboard section * add leaderboard visibility * add visibility functionality * update phase split * update private badge * add private label * fix the label after fetching splits * minor fix -> private label * fix two values of variable in selectphase component * add empty leaderboard schema function * remove services and update the details without fetching * add else statement to make the changes after update * remove console statement and fix model * add grey-text to toggle off button * remove console * update label for selected phase/phaseSplit * remvoe leaderboard schema and fix indentation * remove : from card heading Co-authored-by: Rishabh Jain --- .../challengeleaderboard.component.ts | 3 + .../challengesettings.component.html | 76 +++++--- .../challengesettings.component.ts | 174 ++++++++++++++++-- .../challengesubmissions.component.ts | 3 + .../challengesubmit.component.ts | 3 + .../challengeviewallsubmissions.component.ts | 3 + .../selectphase/selectphase.component.html | 45 ++++- .../selectphase/selectphase.component.ts | 43 ++++- .../src/app/services/challenge.service.ts | 33 ++-- 9 files changed, 324 insertions(+), 59 deletions(-) diff --git a/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts b/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts index 8b8d1edbc2..02d61306c5 100644 --- a/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts @@ -271,6 +271,9 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit, OnD this.phaseSplits[i].showPrivate = true; this.showPrivateIds.push(this.phaseSplits[i].id); } + else { + this.phaseSplits[i].showPrivate = false; + } } this.filteredPhaseSplits = this.phaseSplits; setTimeout(() => { 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..edb5b1f233 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html @@ -9,7 +9,7 @@
Challenge Settings
- Edit challenge details: + Edit challenge details

@@ -101,42 +101,72 @@
Challenge Settings
- Edit phase details: + Edit phase details

-
- - Is Public - - - -      - - Submission Visibility - - - - + + Phase Details + + + +      + + Is Public + + + +      + + Submission Visibility + + + + +
+
+
+
+ + +
+
+ Edit leaderboard details +
+
+
+
+
+ + +
+
+
+ + Leaderboard Visibility + + + +      +
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..5f60443037 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts @@ -35,6 +35,22 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { */ phases = []; + /** + * Phase split list + */ + phaseSplits = []; + + /** + * Challenge phases filtered + */ + filteredPhases = []; + + /** + * Phase splits filtered + */ + filteredPhaseSplits = []; + + /** * Phase selection type (radio button or select box) */ @@ -49,6 +65,21 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { * Currently selected phase */ selectedPhase: any = null; + + /** + * Phase selection type (radio button or select box) + */ + phaseLeaderboardSelectionType = 'selectBox'; + + /** + * Select box list type + */ + phaseLeaderboardSelectionListType = 'settingsPhaseSplit'; + + /** + * Currently selected phase split + */ + selectedPhaseSplit: any = null; /** * store worker logs @@ -105,12 +136,19 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { */ isLeaderboardPublic : boolean = false; + /** + * If leaderboard of phase split is public + * 1 -> private + * 3 -> public + */ + isPhaseSplitLeaderboardPublic: number = 1; + /** * phase visibility state and it's icon */ phaseVisibility = { state: 'Private', - icon: 'fa fa-toggle-off', + icon: 'fa fa- text-darken-1', }; /** @@ -118,15 +156,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', }; /** @@ -163,6 +209,15 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { 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; }); this.challengeService.isChallengeHost.subscribe((status) => { @@ -174,6 +229,19 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { this.publishChallenge.icon = publishChallenge.icon; }); + 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; + }); + if (!this.challenge["remote_evaluation"]) { this.fetchWorkerLogs(); this.startLoadingLogs(); @@ -212,6 +280,83 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { } } + /** + * 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; + 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 { + toggleLeaderboardVisibilityState = 'public'; + visibility = 3; + SELF.leaderboardVisibility.state = 'Public'; + SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text'; + } + 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 + ); + }, + (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'; + } + }, + () => this.logger.info('LEADERBOARD-VISIBILITY-UPDATE-FINISHED') + ); + } + /** * Called when a phase is selected (from child component) */ @@ -257,10 +402,9 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { ) .subscribe( (data) => { - SELF.selectedPhase = data; - SELF.challengeService.fetchPhases(SELF.challenge['id']); - SELF.challengeService.changePhaseSelected(true); - SELF.selectedPhase = false; + for (var attrname in data) { + SELF.selectedPhase[attrname] = data[attrname]; + } SELF.globalService.showToast('success', 'The challenge phase details are successfully updated!'); }, (err) => { @@ -299,7 +443,7 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { togglePhaseVisibilityState = 'private'; isPublic = false; SELF.phaseVisibility.state = 'Private'; - SELF.phaseVisibility.icon = 'fa fa-toggle-off'; + SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; } else { togglePhaseVisibilityState = 'public'; isPublic = true; @@ -315,9 +459,9 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { ) .subscribe( (data) => { - SELF.challengeService.fetchPhases(SELF.selectedPhase['challenge']); + SELF.selectedPhase['is_public'] = data.is_public; + SELF.selectedPhase['showPrivate'] = !data.is_public; SELF.challengeService.changePhaseSelected(true); - SELF.selectedPhase = false; SELF.globalService.showToast( 'success', 'The phase was successfully made ' + togglePhaseVisibilityState, @@ -329,7 +473,7 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { SELF.globalService.showToast('error', err); if (isPublic) { SELF.phaseVisibility.state = 'Private'; - SELF.phaseVisibility.icon = 'fa fa-toggle-off'; + 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'; @@ -350,7 +494,7 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { toggleSubmissionVisibilityState = 'private'; isSubmissionPublic = false; SELF.submissionVisibility.state = 'Private'; - SELF.submissionVisibility.icon = 'fa fa-toggle-off'; + SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1'; } else { toggleSubmissionVisibilityState = 'public'; isSubmissionPublic = true; @@ -366,9 +510,7 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { ) .subscribe( (data) => { - SELF.challengeService.fetchPhases(SELF.selectedPhase['challenge']); - SELF.challengeService.changePhaseSelected(true); - SELF.selectedPhase = false; + SELF.selectedPhase['is_submission_public'] = data.is_submission_public; SELF.globalService.showToast( 'success', 'The submissions were successfully made ' + toggleSubmissionVisibilityState, @@ -380,7 +522,7 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { SELF.globalService.showToast('error', err); if (isSubmissionPublic) { SELF.submissionVisibility.state = 'Private'; - SELF.submissionVisibility.icon = 'fa fa-toggle-off'; + 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'; 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..0feb9e7678 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts @@ -226,6 +226,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; }); 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..7d27a7c228 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts @@ -260,6 +260,9 @@ export class ChallengesubmitComponent implements OnInit { if (phases[j].is_public === false) { this.phases[j].showPrivate = true; } + else { + this.phases[j].showPrivate = false; + } } }); 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..eb399d26fc 100644 --- a/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts @@ -224,6 +224,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; }); diff --git a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.html b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.html index 96d50034ca..cdeb130a9f 100644 --- a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.html +++ b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.html @@ -39,15 +39,50 @@ Select phase - {{ phaseName }} + {{ phaseName }} + + + + Private + + + + + {{ key.name }} +     + - + Private + + + + + + + + Select Phase Split + + + + Phase: {{ phaseName }}, Split:  {{ settingsSplitName }}   + + + + + Private + + + - - {{ key.name }} + + + Phase:{{ key.challenge_phase_name }}, Split: {{ key.dataset_split_name }}   + Private @@ -67,7 +102,7 @@ Phase: {{ phaseName }}, Split:  {{ splitName }}   - + Private diff --git a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts index 6abdfe0c55..199d60bc11 100644 --- a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts +++ b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts @@ -26,6 +26,11 @@ export class SelectphaseComponent implements OnInit, OnChanges { */ @Input() selectedPhaseSplitUrlChange: any; + /** + * Selected phase split + */ + @Input() phaseSplitSelected: any; + /** * Phase selection type (radio button or select box) */ @@ -61,11 +66,21 @@ export class SelectphaseComponent implements OnInit, OnChanges { */ splitName = ''; + /** + * Selected split name + */ + settingsSplitName = ''; + /** * Selected phase split */ selectedPhaseSplit = ''; + /** + * If phase selected + */ + isPhaseSplitSelected : boolean = false; + /** * Challenge object */ @@ -95,17 +110,28 @@ export class SelectphaseComponent implements OnInit, OnChanges { this.challengeService.isPhaseSelected.subscribe((isPhaseSelected) => { this.isPhaseSelected = isPhaseSelected; }); - if(this.isPhaseSelected == true) { + + if(this.isPhaseSelected == true && this.phaseSelected) { this.challengeService.changePhaseSelected(false); - this.phaseName = ''; + this.selectPhase(this.selectedPhase); } - } + + this.challengeService.isPhaseSplitSelected.subscribe((isPhaseSplitSelected) => { + this.isPhaseSplitSelected = isPhaseSplitSelected; + }); + + if(this.phaseSplitSelected && this.isPhaseSplitSelected) { + this.challengeService.changePhaseSplitSelected(false); + this.selectSettingsPhaseSplit(this.selectedPhaseSplit, this.phaseSelectionType, this.phaseSelectionListType); + } + } /** * Select a particular phase. * @param phase phase to be selected. */ selectPhase(phase) { + this.selectedPhase = phase; this.phaseName = phase.name; this.phaseVisibility = phase.showPrivate; this.phaseSelected(phase); @@ -117,6 +143,17 @@ export class SelectphaseComponent implements OnInit, OnChanges { * @param phaseSelectionType phase selection type (radio button or select box). * @param phaseSelectionListType phase selection list type (phase or phase split) */ + + selectSettingsPhaseSplit(phaseSplit, phaseSelectionType, phaseSelectionListType) { + this.phaseSelectionType = phaseSelectionType; + this.phaseSelectionListType = phaseSelectionListType; + this.selectedPhaseSplit = phaseSplit; + this.phaseName = phaseSplit.challenge_phase_name; + this.settingsSplitName = phaseSplit.dataset_split_name; + this.phaseVisibility = phaseSplit.showPrivate; + this.phaseSplitSelected(phaseSplit); + } + selectPhaseSplit(phaseSplit, phaseSelectionType, phaseSelectionListType) { this.phaseSelectionType = phaseSelectionType; this.phaseSelectionListType = phaseSelectionListType; diff --git a/frontend_v2/src/app/services/challenge.service.ts b/frontend_v2/src/app/services/challenge.service.ts index 942b3317b8..91a1bdd648 100644 --- a/frontend_v2/src/app/services/challenge.service.ts +++ b/frontend_v2/src/app/services/challenge.service.ts @@ -33,11 +33,12 @@ export class ChallengeService { currentHostTeam = this.hostTeamSource.asObservable(); private challengeHostSource = new BehaviorSubject(false); isChallengeHost = this.challengeHostSource.asObservable(); - private phaseSelected = new BehaviorSubject(false); - isPhaseSelected = this.phaseSelected.asObservable(); private challengePublishSource = new BehaviorSubject(this.defaultPublishChallenge); currentChallengePublishState = this.challengePublishSource.asObservable(); - + private phaseSelected = new BehaviorSubject(false); + isPhaseSelected = this.phaseSelected.asObservable(); + private phaseSplitSelected = new BehaviorSubject(false); + isPhaseSplitSelected = this.phaseSplitSelected.asObservable(); /** * Constructor. * @param globalService GlobalService Injection. @@ -68,14 +69,6 @@ export class ChallengeService { this.challengeHostSource.next(isChallengeHost); } - /** - * Update the status for selectPhase component after details are updated - * @param selectedPhase new updated phase details status - */ - changePhaseSelected(selectedPhase: boolean) { - this.phaseSelected.next(selectedPhase); - } - /** * Update challenge publish state and icon for current challenge. * @param publishChallenge new challenge publish status and icon. @@ -108,6 +101,14 @@ export class ChallengeService { this.phasesSource.next(phases); } + /** + * Update the status for selectPhase component after details are updated + * @param selectedPhase new updated phase details status + */ + changePhaseSelected(selectedPhase: boolean) { + this.phaseSelected.next(selectedPhase); + } + /** * Update user's participation status for current Challenge. * @param participationStatus new participation status. @@ -124,6 +125,14 @@ export class ChallengeService { this.phaseSplitSource.next(phaseSplits); } + /** + * Update the status for selectPhase component after details are updated + * @param selectedPhase new updated phase details status + */ + changePhaseSplitSelected(selectedPhaseSplit: boolean) { + this.phaseSplitSelected.next(selectedPhaseSplit); + } + /** * Update current Host Team. * @param hostTeam new host team. @@ -301,7 +310,7 @@ export class ChallengeService { * Fetch Phase Splits * @param id id of the challenge */ - private fetchPhaseSplits(id) { + fetchPhaseSplits(id) { const API_PATH = this.endpointsService.challengePhaseSplitURL(id); const SELF = this; this.apiService.getUrl(API_PATH).subscribe( From 9eb4de327700b61d3c861dd5348eb8f4059306db Mon Sep 17 00:00:00 2001 From: Gautam Jajoo Date: Sat, 24 Jul 2021 22:10:14 +0530 Subject: [PATCH 05/22] Frontend_V2: Fix buttons on team card (#3544) Co-authored-by: Rishabh Jain --- .../publiclists/teamlist/teamlist.component.html | 2 +- .../publiclists/teamlist/teamlist.component.scss | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) 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 @@
- +
@@ -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

+
From c6448a5320e40a24e52840944d69ec547c9f095d Mon Sep 17 00:00:00 2001 From: Gautam Jajoo Date: Sat, 31 Jul 2021 19:48:02 +0530 Subject: [PATCH 13/22] Frontend_V2: Fix headings on my-submissions(#3549) Co-authored-by: Rishabh Jain --- .../challengesubmissions/challengesubmissions.component.html | 2 -- .../challengesubmissions/challengesubmissions.component.scss | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) 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; From 774cc8599513c5683475cfae88292efa0b89fb8b Mon Sep 17 00:00:00 2001 From: Aviral Jain Date: Sat, 31 Jul 2021 19:48:58 +0530 Subject: [PATCH 14/22] Backend: Switch to http for prometheus and grafana(#3558) * switch to http for prometheus and grafana * expose tcp and udp ports for statsd-exporter * remove sample.txt file --- docker-compose-staging.yml | 3 ++- docker/prod/nginx-ingress/nginx_staging.conf | 4 ++-- sample.txt | 0 3 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 sample.txt diff --git a/docker-compose-staging.yml b/docker-compose-staging.yml index 1a4e7b617d..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: diff --git a/docker/prod/nginx-ingress/nginx_staging.conf b/docker/prod/nginx-ingress/nginx_staging.conf index 02c21b792f..d65f61b1a0 100644 --- a/docker/prod/nginx-ingress/nginx_staging.conf +++ b/docker/prod/nginx-ingress/nginx_staging.conf @@ -38,14 +38,14 @@ 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 { diff --git a/sample.txt b/sample.txt deleted file mode 100644 index e69de29bb2..0000000000 From ef62e183b987e5e881acf7d9f15c9bd922dcb00d Mon Sep 17 00:00:00 2001 From: savish28 <32800267+savish28@users.noreply.github.com> Date: Sat, 31 Jul 2021 19:51:16 +0530 Subject: [PATCH 15/22] Code Upload Worker: Bug fixes for printing logs, reading jobs and prevent pod failures(#3560) Co-authored-by: Rishabh Jain --- .../workers/code_upload_submission_worker.py | 88 ++++++++++++------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/scripts/workers/code_upload_submission_worker.py b/scripts/workers/code_upload_submission_worker.py index da9a18c142..196222bfb4 100755 --- a/scripts/workers/code_upload_submission_worker.py +++ b/scripts/workers/code_upload_submission_worker.py @@ -36,6 +36,7 @@ def exit_gracefully(self, signum, frame): logger = logging.getLogger(__name__) logger.addHandler(handler) logger.setLevel(logging.INFO) +logger.propagate = False AUTH_TOKEN = os.environ.get("AUTH_TOKEN", "auth_token") EVALAI_API_SERVER = os.environ.get( @@ -487,6 +488,7 @@ def read_job(api_instance, job_name): api_response = api_instance.read_namespaced_job(job_name, namespace) except ApiException as e: logger.exception("Exception while reading Job with error {}".format(e)) + return None return api_response @@ -550,38 +552,60 @@ def update_failed_jobs_and_send_logs( clean_submission = False try: job_def = read_job(api_instance, job_name) - controller_uid = job_def.metadata.labels["controller-uid"] - pod_label_selector = "controller-uid=" + controller_uid - pods_list = core_v1_api_instance.list_namespaced_pod( - namespace="default", - label_selector=pod_label_selector, - timeout_seconds=10, - ) - container_state_map = {} - for container in pods_list.items[0].status.container_statuses: - container_state_map[container.name] = container.state - for container_name, container_state in container_state_map.items(): - if container_name in ["agent", "submission"]: - if container_state.terminated is not None: - if container_state.terminated.reason == "Error": - pod_name = pods_list.items[0].metadata.name - try: - pod_log_response = ( - core_v1_api_instance.read_namespaced_pod_log( - name=pod_name, - namespace="default", - _return_http_data_only=True, - _preload_content=False, - container=container_name, - ) - ) - pod_log = pod_log_response.data.decode("utf-8") - clean_submission = True - submission_error = pod_log - except client.rest.ApiException as e: - logger.exception( - "Exception while reading Job logs {}".format(e) - ) + if job_def: + controller_uid = job_def.metadata.labels["controller-uid"] + pod_label_selector = "controller-uid=" + controller_uid + pods_list = core_v1_api_instance.list_namespaced_pod( + namespace="default", + label_selector=pod_label_selector, + timeout_seconds=10, + ) + # Prevents monitoring when Job created with pending pods state (not assigned to node) + if pods_list.items[0].status.container_statuses: + container_state_map = {} + for container in pods_list.items[0].status.container_statuses: + container_state_map[container.name] = container.state + for ( + container_name, + container_state, + ) in container_state_map.items(): + if container_name in ["agent", "submission"]: + if container_state.terminated is not None: + if container_state.terminated.reason == "Error": + pod_name = pods_list.items[0].metadata.name + try: + pod_log_response = core_v1_api_instance.read_namespaced_pod_log( + name=pod_name, + namespace="default", + _return_http_data_only=True, + _preload_content=False, + container=container_name, + ) + pod_log = pod_log_response.data.decode( + "utf-8" + ) + clean_submission = True + submission_error = pod_log + except client.rest.ApiException as e: + logger.exception( + "Exception while reading Job logs {}".format( + e + ) + ) + else: + logger.info( + "Job pods in pending state, waiting for node assignment for submission {}".format( + submission_pk + ) + ) + else: + logger.exception( + "Exception while reading Job {}, does not exist.".format( + job_name + ) + ) + clean_submission = True + submission_error = "Submission Job Failed." except Exception as e: logger.exception("Exception while reading Job {}".format(e)) clean_submission = True From caf8dd8e9c3565e2118d4ab8910971be3b6a06cd Mon Sep 17 00:00:00 2001 From: Ram Ramrakhya Date: Mon, 2 Aug 2021 21:54:51 +0530 Subject: [PATCH 16/22] Frontend: Add metadata for leaderboard metrics(#3561) * [Frontend] Add Leaderboard metric order arrows * Review fix --- frontend/src/css/modules/leaderboard.scss | 81 ++++++++++++------- frontend/src/js/controllers/challengeCtrl.js | 26 ++++++ .../src/views/web/challenge/leaderboard.html | 3 +- 3 files changed, 79 insertions(+), 31 deletions(-) diff --git a/frontend/src/css/modules/leaderboard.scss b/frontend/src/css/modules/leaderboard.scss index fc403ea560..48a6e0c765 100644 --- a/frontend/src/css/modules/leaderboard.scss +++ b/frontend/src/css/modules/leaderboard.scss @@ -1,61 +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; - background-color: #f2f2f2; + 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 { @@ -67,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 6a790ddca1..ff58821393 100644 --- a/frontend/src/js/controllers/challengeCtrl.js +++ b/frontend/src/js/controllers/challengeCtrl.js @@ -876,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; diff --git a/frontend/src/views/web/challenge/leaderboard.html b/frontend/src/views/web/challenge/leaderboard.html index b1e5c3eec0..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)}} From 8b2fad8f4a254a5ab2311ef43ef271e29024229a Mon Sep 17 00:00:00 2001 From: Ram Ramrakhya Date: Wed, 4 Aug 2021 07:29:11 +0530 Subject: [PATCH 17/22] Backend: Add cron job to stop workers for unapproved challenges(#3559) * Add cron to terminate workers for unapproved challenges * Fix tests Co-authored-by: Rishabh Jain --- apps/challenges/serializers.py | 1 + apps/challenges/views.py | 12 +++- .../monitoring/auto_start_or_stop_workers.py | 56 ++++++++++++++---- tests/unit/challenges/test_views.py | 57 +++++++++++++++++++ tests/unit/participants/test_views.py | 6 ++ 5 files changed, 120 insertions(+), 12 deletions(-) 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/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/scripts/monitoring/auto_start_or_stop_workers.py b/scripts/monitoring/auto_start_or_stop_workers.py index 194095cab1..55a1b978e8 100644 --- a/scripts/monitoring/auto_start_or_stop_workers.py +++ b/scripts/monitoring/auto_start_or_stop_workers.py @@ -36,8 +36,20 @@ def stop_worker(challenge_id): return response -def get_all_challenges(): - all_challenge_endpoint = "{}/api/challenges/challenge/present".format( +def delete_worker(challenge_id): + delete_worker_endpoint = ( + "{}/api/challenges/{}/manage_worker/delete/".format( + evalai_endpoint, challenge_id + ) + ) + response = requests.put( + delete_worker_endpoint, headers=authorization_header + ) + return response + + +def get_challenges(): + all_challenge_endpoint = "{}/api/challenges/challenge/all".format( evalai_endpoint ) response = requests.get( @@ -46,20 +58,37 @@ def get_all_challenges(): return response.json() -def start_or_stop_workers_for_active_challenges(response): +def is_unapproved_challenge(workers, approved_by_admin, created_at): + current_date = utc.localize(datetime.now()) + return ( + workers is not None + and not approved_by_admin + and (current_date - created_at).days >= 3 + ) + + +def start_or_stop_workers_for_challenges(response): for challenge in response["results"]: challenge_id = challenge["id"] workers = challenge["workers"] + approved_by_admin = challenge["approved_by_admin"] is_docker_based = challenge["is_docker_based"] challenge_start_date = parser.parse(challenge["start_date"]) challenge_end_date = parser.parse(challenge["end_date"]) + created_at = parser.parse(challenge["created_at"]) current_date = utc.localize(datetime.now()) print( - "Start submission worker for challenge id {}".format(challenge_id) + "Start/Stop submission worker for challenge id {}".format( + challenge_id + ) ) if not is_docker_based: - if workers is None and challenge_start_date <= current_date: + if ( + workers is None + and challenge_start_date <= current_date + and challenge_end_date > current_date + ): response = start_worker(challenge_id) if not response.ok: print( @@ -67,11 +96,16 @@ def start_or_stop_workers_for_active_challenges(response): challenge_id ) ) - if workers is not None and challenge_end_date <= current_date: - response = stop_worker(challenge_id) + # Delete workers for challenges uploaded in last 3 days that are unapproved or inactive challenges + if ( + workers is not None and challenge_end_date < current_date + ) or is_unapproved_challenge( + workers, approved_by_admin, created_at + ): + response = delete_worker(challenge_id) if not response.ok: print( - "ERROR: Stop worker failed for challenge id {}!".format( + "ERROR: Delete worker failed for challenge id {}!".format( challenge_id ) ) @@ -80,13 +114,13 @@ def start_or_stop_workers_for_active_challenges(response): def start_job(): - response = get_all_challenges() - start_or_stop_workers_for_active_challenges(response) + response = get_challenges() + start_or_stop_workers_for_challenges(response) next_page = response["next"] while next_page is not None: response = execute_get_request(next_page) - start_or_stop_workers_for_active_challenges(response) + start_or_stop_workers_for_challenges(response) next_page = response["next"] diff --git a/tests/unit/challenges/test_views.py b/tests/unit/challenges/test_views.py index e2d7a4d846..32210fd59d 100644 --- a/tests/unit/challenges/test_views.py +++ b/tests/unit/challenges/test_views.py @@ -174,6 +174,9 @@ def test_get_challenge(self): "cli_version": self.challenge.cli_version, "remote_evaluation": self.challenge.remote_evaluation, "workers": self.challenge.workers, + "created_at": "{0}{1}".format( + self.challenge.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] @@ -323,6 +326,9 @@ def test_get_particular_challenge(self): "cli_version": self.challenge.cli_version, "remote_evaluation": self.challenge.remote_evaluation, "workers": self.challenge.workers, + "created_at": "{0}{1}".format( + self.challenge.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } response = self.client.get(self.url, {}) self.assertEqual(response.data, expected) @@ -399,6 +405,9 @@ def test_update_challenge_when_user_is_its_creator(self): "cli_version": self.challenge.cli_version, "remote_evaluation": self.challenge.remote_evaluation, "workers": self.challenge.workers, + "created_at": "{0}{1}".format( + self.challenge.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } response = self.client.put( self.url, {"title": new_title, "description": new_description} @@ -501,6 +510,9 @@ def test_particular_challenge_partial_update(self): "cli_version": self.challenge.cli_version, "remote_evaluation": self.challenge.remote_evaluation, "workers": self.challenge.workers, + "created_at": "{0}{1}".format( + self.challenge.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } response = self.client.patch(self.url, self.partial_update_data) self.assertEqual(response.data, expected) @@ -552,6 +564,9 @@ def test_particular_challenge_update(self): "cli_version": self.challenge.cli_version, "remote_evaluation": self.challenge.remote_evaluation, "workers": self.challenge.workers, + "created_at": "{0}{1}".format( + self.challenge.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } response = self.client.put(self.url, self.data) self.assertEqual(response.data, expected) @@ -1064,6 +1079,9 @@ def test_get_past_challenges(self): "cli_version": self.challenge3.cli_version, "remote_evaluation": self.challenge3.remote_evaluation, "workers": self.challenge3.workers, + "created_at": "{0}{1}".format( + self.challenge3.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] response = self.client.get(self.url, {}, format="json") @@ -1117,6 +1135,9 @@ def test_get_present_challenges(self): "cli_version": self.challenge2.cli_version, "remote_evaluation": self.challenge2.remote_evaluation, "workers": self.challenge2.workers, + "created_at": "{0}{1}".format( + self.challenge2.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] response = self.client.get(self.url, {}, format="json") @@ -1170,6 +1191,9 @@ def test_get_future_challenges(self): "cli_version": self.challenge4.cli_version, "remote_evaluation": self.challenge4.remote_evaluation, "workers": self.challenge4.workers, + "created_at": "{0}{1}".format( + self.challenge4.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] response = self.client.get(self.url, {}, format="json") @@ -1222,6 +1246,9 @@ def test_get_all_challenges(self): "cli_version": self.challenge4.cli_version, "remote_evaluation": self.challenge4.remote_evaluation, "workers": self.challenge4.workers, + "created_at": "{0}{1}".format( + self.challenge4.created_at.isoformat(), "Z" + ).replace("+00:00", ""), }, { "id": self.challenge3.pk, @@ -1263,6 +1290,9 @@ def test_get_all_challenges(self): "cli_version": self.challenge3.cli_version, "remote_evaluation": self.challenge3.remote_evaluation, "workers": self.challenge3.workers, + "created_at": "{0}{1}".format( + self.challenge3.created_at.isoformat(), "Z" + ).replace("+00:00", ""), }, { "id": self.challenge2.pk, @@ -1304,6 +1334,9 @@ def test_get_all_challenges(self): "cli_version": self.challenge2.cli_version, "remote_evaluation": self.challenge2.remote_evaluation, "workers": self.challenge2.workers, + "created_at": "{0}{1}".format( + self.challenge2.created_at.isoformat(), "Z" + ).replace("+00:00", ""), }, ] response = self.client.get(self.url, {}, format="json") @@ -1406,6 +1439,9 @@ def test_get_featured_challenges(self): "cli_version": self.challenge3.cli_version, "remote_evaluation": self.challenge3.remote_evaluation, "workers": self.challenge3.workers, + "created_at": "{0}{1}".format( + self.challenge3.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] response = self.client.get(self.url, {}, format="json") @@ -1535,6 +1571,9 @@ def test_get_challenge_by_pk_when_user_is_challenge_host(self): "cli_version": self.challenge3.cli_version, "remote_evaluation": self.challenge3.remote_evaluation, "workers": self.challenge3.workers, + "created_at": "{0}{1}".format( + self.challenge3.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } response = self.client.get(self.url, {}) @@ -1600,6 +1639,9 @@ def test_get_challenge_by_pk_when_user_is_participant(self): "cli_version": self.challenge4.cli_version, "remote_evaluation": self.challenge4.remote_evaluation, "workers": self.challenge4.workers, + "created_at": "{0}{1}".format( + self.challenge4.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } self.client.force_authenticate(user=self.user1) @@ -1721,6 +1763,9 @@ def test_get_challenge_when_host_team_is_given(self): "cli_version": self.challenge2.cli_version, "remote_evaluation": self.challenge2.remote_evaluation, "workers": self.challenge2.workers, + "created_at": "{0}{1}".format( + self.challenge2.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] @@ -1774,6 +1819,9 @@ def test_get_challenge_when_participant_team_is_given(self): "cli_version": self.challenge2.cli_version, "remote_evaluation": self.challenge2.remote_evaluation, "workers": self.challenge2.workers, + "created_at": "{0}{1}".format( + self.challenge2.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] @@ -1827,6 +1875,9 @@ def test_get_challenge_when_mode_is_participant(self): "cli_version": self.challenge2.cli_version, "remote_evaluation": self.challenge2.remote_evaluation, "workers": self.challenge2.workers, + "created_at": "{0}{1}".format( + self.challenge2.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] @@ -1878,6 +1929,9 @@ def test_get_challenge_when_mode_is_host(self): "cli_version": self.challenge.cli_version, "remote_evaluation": self.challenge.remote_evaluation, "workers": self.challenge.workers, + "created_at": "{0}{1}".format( + self.challenge.created_at.isoformat(), "Z" + ).replace("+00:00", ""), }, { "id": self.challenge2.pk, @@ -1919,6 +1973,9 @@ def test_get_challenge_when_mode_is_host(self): "cli_version": self.challenge2.cli_version, "remote_evaluation": self.challenge2.remote_evaluation, "workers": self.challenge2.workers, + "created_at": "{0}{1}".format( + self.challenge2.created_at.isoformat(), "Z" + ).replace("+00:00", ""), }, ] diff --git a/tests/unit/participants/test_views.py b/tests/unit/participants/test_views.py index ce4b08fcc9..90d8ab4708 100644 --- a/tests/unit/participants/test_views.py +++ b/tests/unit/participants/test_views.py @@ -815,6 +815,9 @@ def test_get_teams_and_corresponding_challenges_for_a_participant(self): "cli_version": self.challenge1.cli_version, "remote_evaluation": self.challenge1.remote_evaluation, "workers": self.challenge1.workers, + "created_at": "{0}{1}".format( + self.challenge1.created_at.isoformat(), "Z" + ).replace("+00:00", ""), }, "participant_team": { "id": self.participant_team.id, @@ -883,6 +886,9 @@ def test_get_participant_team_challenge_list(self): "cli_version": self.challenge1.cli_version, "remote_evaluation": self.challenge1.remote_evaluation, "workers": self.challenge1.workers, + "created_at": "{0}{1}".format( + self.challenge1.created_at.isoformat(), "Z" + ).replace("+00:00", ""), } ] From 7116eb1c21fe479e0186bd2d2d0f96f1132c5b44 Mon Sep 17 00:00:00 2001 From: Gautam Jajoo Date: Thu, 5 Aug 2021 19:44:50 +0530 Subject: [PATCH 18/22] Frontend_V2: Remove submission count api call from submissions page(#3562) * remove submission count api call * remove space Co-authored-by: Rishabh Jain --- .../challengesubmissions.component.ts | 27 ++----------------- .../challengeviewallsubmissions.component.ts | 25 +---------------- 2 files changed, 3 insertions(+), 49 deletions(-) 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 0feb9e7678..f1c00fd0f2 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts @@ -249,7 +249,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']); } }; } @@ -265,6 +264,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) => { @@ -488,30 +488,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/challengeviewallsubmissions/challengeviewallsubmissions.component.ts b/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts index eb399d26fc..d1c42d3e76 100644 --- a/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts @@ -247,7 +247,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']); } }; } @@ -276,6 +275,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) => { @@ -534,29 +534,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 From 27d0dd1f3f1ab5ac5bd6525c186fe6a66447ae53 Mon Sep 17 00:00:00 2001 From: savish28 <32800267+savish28@users.noreply.github.com> Date: Sun, 8 Aug 2021 02:33:21 +0530 Subject: [PATCH 19/22] Worker: Limit pod logs for stdout for code upload challenges(#3564) * Limit pod logs for stdout * Update limit from 500 to 1000 Co-authored-by: Rishabh Jain --- scripts/workers/code_upload_submission_worker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/workers/code_upload_submission_worker.py b/scripts/workers/code_upload_submission_worker.py index 196222bfb4..660738ffbd 100755 --- a/scripts/workers/code_upload_submission_worker.py +++ b/scripts/workers/code_upload_submission_worker.py @@ -584,6 +584,7 @@ def update_failed_jobs_and_send_logs( pod_log = pod_log_response.data.decode( "utf-8" ) + pod_log = pod_log[-1000:] clean_submission = True submission_error = pod_log except client.rest.ApiException as e: From c509e3855ede16006f0ea30dbb51fdfef6aae88b Mon Sep 17 00:00:00 2001 From: Ram Ramrakhya Date: Mon, 9 Aug 2021 04:14:53 +0530 Subject: [PATCH 20/22] Frontend+Backend: Add feature to cancel submissions(#3456) * Add submission cancel feature * Rebase changes * Remove unused code * Fix reviews * Fix review Co-authored-by: Rishabh Jain --- apps/jobs/views.py | 37 ++++++++++++++++-- frontend/src/js/controllers/challengeCtrl.js | 39 ++++++++++++++++++- .../web/challenge/cancel-submission.html | 16 ++++++++ .../views/web/challenge/my-submission.html | 3 ++ .../update-submission-visibility.html | 2 +- scripts/workers/submission_worker.py | 8 ++++ 6 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 frontend/src/views/web/challenge/cancel-submission.html 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/frontend/src/js/controllers/challengeCtrl.js b/frontend/src/js/controllers/challengeCtrl.js index ff58821393..8fbce15eb1 100644 --- a/frontend/src/js/controllers/challengeCtrl.js +++ b/frontend/src/js/controllers/challengeCtrl.js @@ -2030,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 @@ +
+ +
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/scripts/workers/submission_worker.py b/scripts/workers/submission_worker.py index be22b23a8b..f3992d101a 100644 --- a/scripts/workers/submission_worker.py +++ b/scripts/workers/submission_worker.py @@ -348,6 +348,14 @@ def extract_submission_data(submission_id): # for message corresponding to which submission entry # does not exist return None + # Ignore submissions with status cancelled + if submission.status == Submission.CANCELLED: + logger.info( + "{} Submission {} was cancelled by the user".format( + SUBMISSION_LOGS_PREFIX, submission_id + ) + ) + return None if submission.challenge_phase.challenge.is_static_dataset_code_upload: input_file = submission.submission_input_file From 797e1e7eb65ebe84a67b292a73477ed4088b321e Mon Sep 17 00:00:00 2001 From: Gautam Jajoo Date: Thu, 12 Aug 2021 21:37:51 +0530 Subject: [PATCH 21/22] Frontend_V2: Refactor code to remove unused services and imports(#3547) * remove unused services and imports * add no-unused-variable in tslint Co-authored-by: Rishabh Jain --- .../app/components/about/about.component.ts | 2 +- .../src/app/components/auth/auth.component.ts | 6 ++-- .../auth/login/login.component.html | 16 +++++----- .../components/auth/login/login.component.ts | 29 +++++-------------- .../auth/signup/signup.component.ts | 14 ++------- .../verify-email/verify-email.component.ts | 7 +---- .../challenge-create.component.ts | 15 ++++------ .../challenge/challenge.component.ts | 20 ++----------- .../challengediscuss.component.ts | 3 +- .../challengeevaluation.component.ts | 14 --------- .../challengeleaderboard.component.ts | 10 +++---- .../challengeoverview.component.ts | 11 +------ .../challengeparticipate.component.ts | 7 +++-- .../terms-and-conditions-modal.component.ts | 4 +++ .../challengephases.component.ts | 10 ++----- .../phasecard/phasecard.component.ts | 9 ++---- .../challengesubmissions.component.ts | 7 +++-- .../challengesubmit.component.ts | 9 +++--- .../challengeviewallsubmissions.component.ts | 7 +++-- .../components/contact/contact.component.ts | 15 +++------- .../featured-challenges.component.ts | 14 +++------ .../src/app/components/home/home.component.ts | 11 ++++++- .../home/homemain/homemain.component.ts | 11 ------- .../components/nav/footer/footer.component.ts | 10 +++---- .../header-static/header-static.component.ts | 15 +++------- .../components/our-team/our-team.component.ts | 12 ++------ .../challengelist/challengelist.component.ts | 12 ++++---- .../publiclists/publiclists.component.ts | 15 ++-------- .../teamlist/teamcard/teamcard.component.ts | 14 ++------- .../teamlist/teamlist.component.ts | 1 - .../templatechallengelist.component.ts | 14 ++++----- .../template-challenge-create.component.ts | 8 ++--- .../utility/input/input.component.ts | 2 +- .../utility/modal/modal.component.ts | 2 +- frontend_v2/tslint.json | 2 ++ 35 files changed, 113 insertions(+), 245 deletions(-) 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 @@ - +
{}; + /** + * 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/challengesubmissions/challengesubmissions.component.ts b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts index f1c00fd0f2..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, 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 7d27a7c228..f18cfb5697 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts @@ -213,12 +213,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, 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 d1c42d3e76..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, 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 42dde6c28c..9efd12a4cd 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'; @@ -32,6 +31,16 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy { components: QueryList; authServiceSubscription: any; + + /** + * 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/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.ts b/frontend_v2/src/app/components/publiclists/teamlist/teamlist.component.ts index 45ccf1811e..cf197bacec 100644 --- a/frontend_v2/src/app/components/publiclists/teamlist/teamlist.component.ts +++ b/frontend_v2/src/app/components/publiclists/teamlist/teamlist.component.ts @@ -226,7 +226,6 @@ export class TeamlistComponent implements OnInit, OnDestroy { templatePhases = null; /** - * Constructor. * @param route ActivatedRoute Injection. * @param router Router Injection. diff --git a/frontend_v2/src/app/components/publiclists/templatechallengelist/templatechallengelist.component.ts b/frontend_v2/src/app/components/publiclists/templatechallengelist/templatechallengelist.component.ts index a1fe325542..7daa9fd72d 100644 --- a/frontend_v2/src/app/components/publiclists/templatechallengelist/templatechallengelist.component.ts +++ b/frontend_v2/src/app/components/publiclists/templatechallengelist/templatechallengelist.component.ts @@ -1,9 +1,7 @@ -import { Component, HostListener, Inject, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ApiService } from '../../../services/api.service'; import { GlobalService } from '../../../services/global.service'; import { AuthService } from '../../../services/auth.service'; -import { Router, ActivatedRoute } from '@angular/router'; -import { DOCUMENT } from '@angular/common'; /** @@ -36,12 +34,14 @@ export class TemplateChallengeListComponent implements OnInit { */ templateChallengesFetchPath = 'challenges/get_all_challenge_templates/' - + /** + * Constructor. + * @param authService AuthService Injection. + * @param globalService GlobalService Injection. + * @param apiService ApiService Injection. + */ constructor( public authService: AuthService, - private router: Router, - private route: ActivatedRoute, - @Inject(DOCUMENT) private document, private globalService: GlobalService, private apiService: ApiService ) {} diff --git a/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.ts b/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.ts index a8ed8f92ea..fce08e9f17 100644 --- a/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.ts +++ b/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.ts @@ -1,11 +1,9 @@ -import { Component, OnInit, ViewChildren, QueryList, Inject } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { AuthService } from '../../services/auth.service'; -import { ApiService } from '../../services/api.service'; import { GlobalService } from '../../services/global.service'; import { ChallengeService } from '../../services/challenge.service'; import { Router, ActivatedRoute } from '@angular/router'; -import { DOCUMENT } from '@angular/common'; /** * Component Class @@ -78,9 +76,7 @@ export class TemplateChallengeCreateComponent implements OnInit { private router: Router, private route: ActivatedRoute, private challengeService: ChallengeService, - @Inject(DOCUMENT) private document, - private globalService: GlobalService, - private apiService: ApiService + private globalService: GlobalService ) {} ngOnInit() { diff --git a/frontend_v2/src/app/components/utility/input/input.component.ts b/frontend_v2/src/app/components/utility/input/input.component.ts index 4647c9e0cc..71b3b1bc4c 100644 --- a/frontend_v2/src/app/components/utility/input/input.component.ts +++ b/frontend_v2/src/app/components/utility/input/input.component.ts @@ -1,4 +1,4 @@ -import { Injectable, Component, OnInit, Input, Inject } from '@angular/core'; +import { Component, OnInit, Input, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { GlobalService } from '../../../services/global.service'; diff --git a/frontend_v2/src/app/components/utility/modal/modal.component.ts b/frontend_v2/src/app/components/utility/modal/modal.component.ts index fc0e5fbb5f..89639303c0 100644 --- a/frontend_v2/src/app/components/utility/modal/modal.component.ts +++ b/frontend_v2/src/app/components/utility/modal/modal.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Input } from '@angular/core'; -import { ViewChildren, QueryList, AfterViewInit } from '@angular/core'; +import { ViewChildren, QueryList } from '@angular/core'; import { GlobalService } from '../../../services/global.service'; import { InputComponent } from '../input/input.component'; import { ChallengeService } from '../../../services/challenge.service'; diff --git a/frontend_v2/tslint.json b/frontend_v2/tslint.json index 2c7cc88920..b66f76a483 100644 --- a/frontend_v2/tslint.json +++ b/frontend_v2/tslint.json @@ -36,6 +36,8 @@ "no-switch-case-fall-through": true, "no-unnecessary-initializer": true, "no-unused-expression": true, + "no-unused-variable": true, + "noUnusedLocals": true, "no-use-before-declare": true, "no-var-keyword": true, "object-literal-sort-keys": false, From c9b05b58414c05a0a3576361503cedb75fac5cde Mon Sep 17 00:00:00 2001 From: Gautam Jajoo Date: Fri, 13 Aug 2021 22:31:53 +0530 Subject: [PATCH 22/22] Frontend_V2: Add Tab view on the settings page(#3551) * add tab view to settings tab * fix worker title * change tab-heading * add :host -> does not change styles when coming from other compoenent * merge phase and leaderboard tab * show defauly options in select phase * add comments * fix private label for small screen size * fix spacing issues * add evaluation script heading * add check for empty arrays * make phase splits private when phase is private Co-authored-by: Rishabh Jain --- .../components/challenge/challenge.module.ts | 2 + .../challengeleaderboard.component.html | 2 +- .../challengesettings.component.html | 516 +++---- .../challengesettings.component.scss | 42 +- .../challengesettings.component.ts | 1188 +++++++++-------- .../challengesubmit.component.ts | 16 +- .../selectphase/selectphase.component.html | 17 +- .../selectphase/selectphase.component.scss | 11 + .../selectphase/selectphase.component.ts | 29 + 9 files changed, 981 insertions(+), 842 deletions(-) diff --git a/frontend_v2/src/app/components/challenge/challenge.module.ts b/frontend_v2/src/app/components/challenge/challenge.module.ts index 0294ab3a50..2ef886e3c6 100644 --- a/frontend_v2/src/app/components/challenge/challenge.module.ts +++ b/frontend_v2/src/app/components/challenge/challenge.module.ts @@ -11,6 +11,7 @@ import { MatRadioModule, MatDialogModule, MatSliderModule, + MatTabsModule, } from '@angular/material'; // import components @@ -66,6 +67,7 @@ import { ChallengeanalyticsComponent } from './challengeanalytics/challengeanaly MatRadioModule, MatDialogModule, MatSliderModule, + MatTabsModule, ], exports: [ ChallengeComponent, diff --git a/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html b/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html index d8ab8bc653..30cd029661 100644 --- a/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html +++ b/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html @@ -20,7 +20,7 @@
Leaderboard
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 -
-
-
-
-
- -
-
- - Phase Details - - - -      - - Is Public - - - -      - - Submission Visibility - - - - -
-
-
-
+ - -
-
- Edit leaderboard details -
-
-
-
-
- - + + + +
+
+
+
Evaluation script
+
+
+
+
+ + + Evaluation Criteria + + + +    + + + Evaluation Script + + + + +
+
-
-
- - Leaderboard Visibility - - - -      + +
+
+
+
Submission worker actions
+
+
+
+
+ + + +
+
+
+
+
Submission worker logs
+
+
+
+
{{ log }}
+
-
-
-
-
- - -
-
- Evaluation and Annotations -
-
-
-
- - - Evaluation Criteria - - - -    - - - Evaluation Script - - - - -
-
-
+ + -
-
- 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 5f60443037..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,6 +30,16 @@ 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 */ @@ -50,21 +60,40 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { */ filteredPhaseSplits = []; + /** + * Currently selected phase + */ + selectedPhase: any = null; /** * Phase selection type (radio button or select box) */ phaseSelectionType = 'selectBox'; + /** + * Select box list type + */ + phaseSelectionListType = 'phase'; + /** - * Select box list type + * If the submission is public */ - phaseSelectionListType = 'phase'; + isSubmissionPublic : boolean = false; + + /** + * If the phase is public + */ + isPhasePublic : boolean = false; /** - * Currently selected phase + * If leaderboard is public */ - selectedPhase: any = null; + isLeaderboardPublic : boolean = false; + + /** + * Currently selected phase split + */ + selectedPhaseSplit: any = null; /** * Phase selection type (radio button or select box) @@ -77,9 +106,11 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { phaseLeaderboardSelectionListType = 'settingsPhaseSplit'; /** - * Currently selected phase split + * If leaderboard of phase split is public + * 1 -> private + * 3 -> public */ - selectedPhaseSplit: any = null; + isPhaseSplitLeaderboardPublic: number = 1; /** * store worker logs @@ -91,16 +122,6 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { */ pollingInterval: any; - /** - * Is challenge host - */ - isChallengeHost = false; - - /** - * To call the API inside modal for editing the challenge details - */ - apiCall: any; - /** * Participants banned emails ids */ @@ -120,28 +141,6 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { * Email error message */ message: string; - - /** - * If the submission is public - */ - isSubmissionPublic : boolean = false; - - /** - * If the phase is public - */ - isPhasePublic : boolean = false; - - /** - * If leaderboard is public - */ - isLeaderboardPublic : boolean = false; - - /** - * If leaderboard of phase split is public - * 1 -> private - * 3 -> public - */ - isPhaseSplitLeaderboardPublic: number = 1; /** * phase visibility state and it's icon @@ -207,6 +206,20 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { this.challenge = challenge; }); + this.challengeService.isChallengeHost.subscribe((status) => { + this.isChallengeHost = status; + }); + + this.challengeService.currentChallengePublishState.subscribe((publishChallenge) => { + this.publishChallenge.state = publishChallenge.state; + this.publishChallenge.icon = publishChallenge.icon; + }); + + if (!this.challenge["remote_evaluation"]) { + this.fetchWorkerLogs(); + this.startLoadingLogs(); + } + this.challengeService.currentPhases.subscribe((phases) => { this.phases = phases; for (let i = 0; i < this.phases.length; i++) { @@ -220,15 +233,6 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { this.filteredPhases = this.phases; }); - this.challengeService.isChallengeHost.subscribe((status) => { - this.isChallengeHost = status; - }); - - this.challengeService.currentChallengePublishState.subscribe((publishChallenge) => { - this.publishChallenge.state = publishChallenge.state; - this.publishChallenge.icon = publishChallenge.icon; - }); - this.challengeService.currentPhaseSplit.subscribe((phaseSplits) => { this.phaseSplits = phaseSplits; for (let i = 0; i < this.phaseSplits.length; i++) { @@ -241,298 +245,401 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { } this.filteredPhaseSplits = this.phaseSplits; }); - - if (!this.challenge["remote_evaluation"]) { - this.fetchWorkerLogs(); - this.startLoadingLogs(); - } } - updateView() { - this.bannedEmailIds = this.challenge.banned_email_ids || []; - this.formerBannedEmailIds = this.bannedEmailIds.concat(); // Creating deep copy - } + // 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); } /** - * This is called when a phase split is selected (from child components) + * Delete challenge */ - phaseSplitSelected () { + deleteChallenge() { 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'; - } - } + 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); } /** - * Leadeboard Visibility toggle function + * Edit challenge image function */ - toggleLeaderboardVisibility() { + editChallengeImage() { const SELF = this; - let toggleLeaderboardVisibilityState, visibility; - 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 { - toggleLeaderboardVisibilityState = 'public'; - visibility = 3; - SELF.leaderboardVisibility.state = 'Public'; - SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text'; - } - const BODY: FormData = new FormData(); - BODY.append("visibility", visibility); + SELF.apiCall = (params) => { + const FORM_DATA: FormData = new FormData(); + FORM_DATA.append('image', params['image']); SELF.apiService - .patchFileUrl( - SELF.endpointsService.particularChallengePhaseSplitUrl(SELF.selectedPhaseSplit['id']), - BODY - ) + .patchFileUrl( + SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), + FORM_DATA + ) .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 - ); + 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); - 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'; - } }, - () => this.logger.info('LEADERBOARD-VISIBILITY-UPDATE-FINISHED') + () => {} ); - } + }; - /** - * Called when a phase is selected (from child component) - */ - 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'; - } + /** + * 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); + } - editPhaseDetails() { + /** + * Edit challenge overview function + */ + + editChallengeOverview() { 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]); - } - } + const BODY = JSON.stringify(params); SELF.apiService - .patchFileUrl( - SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.challenge.id, SELF.selectedPhase['id']), - FORM_DATA - ) + .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY) .subscribe( (data) => { - for (var attrname in data) { - SELF.selectedPhase[attrname] = data[attrname]; - } - SELF.globalService.showToast('success', 'The challenge phase details are successfully updated!'); + 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('PHASE-UPDATE-FINISHED')} + () => this.logger.info('EDIT-CHALLENGE-DESCRIPTION-FINISHED') ); }; const PARAMS = { - title: 'Edit Challenge Phase Details', - name: SELF.selectedPhase['name'], + title: 'Edit Challenge Description', 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'], + isEditorRequired: true, + editorContent: this.challenge.description, confirm: 'Submit', deny: 'Cancel', confirmCallback: SELF.apiCall, }; - SELF.globalService.showEditPhaseModal(PARAMS); -} + SELF.globalService.showModal(PARAMS); + } /** - * Phase Visibility toggle function + * Edit terms and conditions of the challenge */ - togglePhaseVisibility() { + editTermsAndConditions() { 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 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.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.selectedPhase['is_public'] = data.is_public; - SELF.selectedPhase['showPrivate'] = !data.is_public; - SELF.challengeService.changePhaseSelected(true); - SELF.globalService.showToast( - 'success', - 'The phase was successfully made ' + togglePhaseVisibilityState, - 5 - ); + 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); - 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') + () => 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); + } + /** - * Submission Visibility toggle function + * Edit challenge start and end date function */ - toggleSubmissionVisibility() { + challengeDateDialog() { 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 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.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.selectedPhase['is_submission_public'] = data.is_submission_public; - 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 grey-text text-darken-1'; + }, + () => {} + ); + } 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 = ''; } } @@ -592,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); + 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('EDIT-TERMS-AND-CONDITIONS-FINISHED') + () => this.logger.info('PHASE-VISIBILITY-UPDATE-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); } /** - * Edit challenge start and end date function + * Submission Visibility toggle 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(), - }); + toggleSubmissionVisibility() { + 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 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 */ @@ -900,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. @@ -1035,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/challengesubmit/challengesubmit.component.ts b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts index f18cfb5697..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 */ @@ -476,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/utility/selectphase/selectphase.component.html b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.html index cdeb130a9f..74845ecbd2 100644 --- a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.html +++ b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.html @@ -3,6 +3,7 @@ Select phase - + - {{ phaseName }} + {{ phaseName }} - + Private @@ -64,13 +65,13 @@ Select Phase Split - + Phase: {{ phaseName }}, Split:  {{ settingsSplitName }}   - + Private @@ -78,7 +79,7 @@ - + Phase:{{ key.challenge_phase_name }}, Split: {{ key.dataset_split_name }}   @@ -102,13 +103,13 @@ Phase: {{ phaseName }}, Split:  {{ splitName }}   - + Private - + Phase:{{ key.challenge_phase_name }}, Split: {{ key.dataset_split_name }}   diff --git a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.scss b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.scss index 142e73be80..7728cd91b2 100644 --- a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.scss +++ b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.scss @@ -28,6 +28,17 @@ padding-right: 0px; } +@media (max-width: 400px) { + .mat-form-field { + margin-right: 40px; + display: block; + } + .col-xs-12 { + padding-bottom: 5px; + width: 80%; + } +} + ::ng-deep .mat-select-placeholder { padding-bottom: 10px; } diff --git a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts index 199d60bc11..0936782f59 100644 --- a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts +++ b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts @@ -16,6 +16,16 @@ export class SelectphaseComponent implements OnInit, OnChanges { */ @Input() phases: any; + /** + * Phase Splits list for settings tab + */ + @Input() settingsPhaseSplits: any; + + /** + * Phase Splits list + */ + @Input() phaseSplits: any; + /** * Selected phase callback */ @@ -86,6 +96,11 @@ export class SelectphaseComponent implements OnInit, OnChanges { */ challenge: any; + /** + * Select default radio option if same as phase id + */ + radioSelected: number; + /** * Constructor. * @param globalService GlobalService Injection. @@ -100,6 +115,20 @@ export class SelectphaseComponent implements OnInit, OnChanges { this.challengeService.currentChallenge.subscribe((challenge) => { this.challenge = challenge; }); + + if(Array.isArray(this.phases) && this.phases.length) { + this.selectedPhase = this.phases[0]; + this.radioSelected = this.phases[0].id; + this.selectPhase(this.phases[0]); + } + else if(Array.isArray(this.settingsPhaseSplits) && this.settingsPhaseSplits.length) { + this.selectedPhaseSplit = this.settingsPhaseSplits[0]; + this.selectSettingsPhaseSplit(this.settingsPhaseSplits[0], "selectBox", "settingsPhaseSplit"); + } + else if(Array.isArray(this.phaseSplits) && this.phaseSplits.length) { + this.selectedPhaseSplit = this.phaseSplits[0]; + this.selectPhaseSplit(this.phaseSplits[0], "selectBox", "phaseSplit"); + } } /**