diff --git a/.gitignore b/.gitignore index a1b1f0d3a7..8b796207f5 100644 --- a/.gitignore +++ b/.gitignore @@ -71,5 +71,3 @@ out/ monitoring/prometheus/prometheus_db/* !monitoring/prometheus/prometheus_db/.gitkeep monitoring/grafana/grafana_db/grafana.db -monitoring/prometheus/django/* -!monitoring/prometheus/django/.gitkeep diff --git a/apps/challenges/aws_utils.py b/apps/challenges/aws_utils.py index acc1e8181f..d9fae7905c 100644 --- a/apps/challenges/aws_utils.py +++ b/apps/challenges/aws_utils.py @@ -1146,7 +1146,10 @@ def restart_workers(queryset): count = 0 failures = [] for challenge in queryset: - if challenge.is_docker_based: + if ( + challenge.is_docker_based + and not challenge.is_static_dataset_code_upload + ): response = "Sorry. This feature is not available for code upload/docker based challenges." failures.append( {"message": response, "challenge_pk": challenge.pk} diff --git a/apps/challenges/serializers.py b/apps/challenges/serializers.py index 6e28744f12..4fa6a24bee 100644 --- a/apps/challenges/serializers.py +++ b/apps/challenges/serializers.py @@ -103,6 +103,7 @@ class Meta: "allowed_submission_file_types", "default_submission_meta_attributes", "allowed_email_ids", + "is_submission_public", ) diff --git a/apps/jobs/views.py b/apps/jobs/views.py index 11eb837268..29b4850da0 100644 --- a/apps/jobs/views.py +++ b/apps/jobs/views.py @@ -486,18 +486,6 @@ def change_submission_data_and_visibility( if serializer.is_valid(): serializer.save() response_data = serializer.data - if ( - request.FILES.get("submission_input_file") - and challenge.is_static_dataset_code_upload - ): - message = { - "challenge_pk": challenge_pk, - "phase_pk": challenge_phase_pk, - "submission_pk": submission_pk, - "is_static_dataset_code_upload_submission": False, - } - # publish message in the queue - publish_submission_message(message) return Response(response_data, status=status.HTTP_200_OK) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -1260,9 +1248,38 @@ def update_submission(request, challenge_pk): if request.method == "PATCH": submission_pk = request.data.get("submission") + submission = get_submission_model(submission_pk) + # Update submission_input_file for is_static_dataset_code_upload submission evaluation + if ( + request.FILES.get("submission_input_file") + and submission.challenge_phase.challenge.is_static_dataset_code_upload + ): + serializer = SubmissionSerializer( + submission, + data=request.data, + context={ + "request": request, + }, + partial=True, + ) + if serializer.is_valid(): + serializer.save() + message = { + "challenge_pk": challenge_pk, + "phase_pk": submission.challenge_phase.pk, + "submission_pk": submission_pk, + "is_static_dataset_code_upload_submission": False, + } + # publish message in the queue + publish_submission_message(message) + response_data = serializer.data + return Response(response_data, status=status.HTTP_200_OK) + else: + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) submission_status = request.data.get("submission_status", "").lower() job_name = request.data.get("job_name", "").lower() - submission = get_submission_model(submission_pk) jobs = submission.job_name if job_name: jobs.append(job_name) diff --git a/docker-compose-staging.yml b/docker-compose-staging.yml index 27583d886f..4789017ebd 100644 --- a/docker-compose-staging.yml +++ b/docker-compose-staging.yml @@ -49,7 +49,6 @@ services: NODE_ENV: staging ports: - '80:80' - - '443:443' volumes: - /code/node_modules - /code/bower_components @@ -69,6 +68,7 @@ services: NODE_ENV: staging ports: - "9999:80" + - "443:443" volumes: - /code/node_modules logging: @@ -94,3 +94,61 @@ services: build: context: ./ dockerfile: docker/prod/code-upload-worker/Dockerfile + + prometheus: + hostname: prometheus + image: prom/prometheus:latest + user: "1000" + volumes: + - ./monitoring/prometheus/prometheus_staging.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' + - '--web.external-url=http://localhost:9090/prometheus' + ports: + - '9090:9090' + + grafana: + hostname: grafana + image: grafana/grafana:latest + user: "1000" + env_file: + - docker/prod/docker_staging.env + volumes: + - ./monitoring/grafana/grafana_db:/var/lib/grafana + depends_on: + - prometheus + ports: + - '3000:3000' + + statsd-exporter: + hostname: statsd + image: prom/statsd-exporter:latest + command: + - '--log.level=info' + - '--web.telemetry-path=/statsd/metrics' + ports: + - '9125:9125' + - '9102:9102' + + node_exporter: + hostname: node_exporter + image: prom/node-exporter + ports: + - '9100:9100' + + nginx-ingress: + image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/evalai-staging-nginx-ingress:${COMMIT_ID} + build: + context: ./ + dockerfile: docker/prod/nginx-ingress/Dockerfile + args: + MONITORING_ENV: staging + depends_on: + - prometheus + - grafana + ports: + - '80:80' + - '443:443' diff --git a/docker-compose.yml b/docker-compose.yml index df65f130f1..b3d7cab852 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,7 @@ services: depends_on: - db - sqs + - statsd-exporter volumes: - .:/code @@ -75,7 +76,6 @@ services: - ./monitoring/prometheus/prometheus_db:/etc/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - restart: unless-stopped ports: - '9090:9090' @@ -90,3 +90,19 @@ services: - prometheus ports: - '3000:3000' + + statsd-exporter: + hostname: statsd + image: prom/statsd-exporter:latest + command: + - '--log.level=info' + - '--web.telemetry-path=/statsd/metrics' + ports: + - '9125:9125' + - '9102:9102' + + node_exporter: + hostname: node_exporter + image: prom/node-exporter + ports: + - '9100:9100' diff --git a/docker/dev/django/uwsgi.ini b/docker/dev/django/uwsgi.ini index 6b38f6805c..3e79d7accc 100644 --- a/docker/dev/django/uwsgi.ini +++ b/docker/dev/django/uwsgi.ini @@ -8,4 +8,3 @@ vaccum = true python-autoreload = 1 buffer-size=32768 chmod-socket = 777 -lazy-apps = true diff --git a/docker/dev/docker.env b/docker/dev/docker.env index 51b5a31a3d..9352952bcf 100644 --- a/docker/dev/docker.env +++ b/docker/dev/docker.env @@ -36,4 +36,5 @@ SERVICE_DNS=localhost GF_SECURITY_ADMIN_USER=admin GF_SECURITY_ADMIN_PASSWORD=password -prometheus_multiproc_dir=/code/monitoring/prometheus/django +STATSD_ENDPOINT=statsd +STATSD_PORT=9125 diff --git a/docker/prod/docker_staging.env b/docker/prod/docker_staging.env index 1a5273bbff..e561b8a2be 100644 --- a/docker/prod/docker_staging.env +++ b/docker/prod/docker_staging.env @@ -55,3 +55,11 @@ EKS_NODEGROUP_ROLE_ARN=x SLACK_WEB_HOOK_URL=x ENVIRONMENT=staging SERVICE_DNS=x + +GF_SECURITY_ADMIN_USER=x +GF_SECURITY_ADMIN_PASSWORD=x +GF_SERVER_ROOT_URL=http://localhost:3000/grafana +GF_SERVER_SERVE_FROM_SUB_PATH=true + +STATSD_ENDPOINT=statsd +STATSD_PORT=9125 diff --git a/docker/prod/nginx-ingress/Dockerfile b/docker/prod/nginx-ingress/Dockerfile new file mode 100644 index 0000000000..0137e342f1 --- /dev/null +++ b/docker/prod/nginx-ingress/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:1.13-alpine +ARG MONITORING_ENV +COPY docker/prod/nginx-ingress/nginx_${MONITORING_ENV}.conf /etc/nginx/conf.d/default.conf +COPY /ssl /etc/ssl diff --git a/docker/prod/nginx-ingress/nginx_staging.conf b/docker/prod/nginx-ingress/nginx_staging.conf new file mode 100644 index 0000000000..b8e31a57ec --- /dev/null +++ b/docker/prod/nginx-ingress/nginx_staging.conf @@ -0,0 +1,47 @@ +upstream prometheus { + server prometheus:9090 fail_timeout=0; +} + +upstream grafana { + server grafana:3000 fail_timeout=0; +} + +server { + server_name monitoring-staging.eval.ai; + listen 80; + return 301 https://monitoring-staging.eval.ai$request_uri; +} + +server { + server_name monitoring-staging.eval.ai; + listen 443 ssl; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + ssl on; + ssl_certificate /etc/ssl/eval_ai.crt; + ssl_certificate_key /etc/ssl/eval_ai.key; + ssl_prefer_server_ciphers on; + # enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated. + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + location /prometheus { + 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; + } + + 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; + } + +} diff --git a/docker/prod/nodejs/nginx_staging.conf b/docker/prod/nodejs/nginx_staging.conf index b9af1864d4..8e7cdd02ce 100644 --- a/docker/prod/nodejs/nginx_staging.conf +++ b/docker/prod/nodejs/nginx_staging.conf @@ -2,6 +2,14 @@ upstream django_app { server django:8000 fail_timeout=0; } +upstream statsd_exporter { + server statsd:9102 fail_timeout=0; +} + +upstream node_exporter { + server node_exporter:9100 fail_timeout=0; +} + server { server_name staging-evalai.cloudcv.org evalai-staging.cloudcv.org; listen 80; @@ -53,6 +61,20 @@ server { proxy_pass http://django_app; } + location /statsd { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://statsd_exporter; + } + + location /node_exporter { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://node_exporter/metrics; + } + ssl on; ssl_certificate /etc/ssl/eval_ai.crt; ssl_certificate_key /etc/ssl/eval_ai.key; diff --git a/docker/prod/nodejs_v2/nginx_staging.conf b/docker/prod/nodejs_v2/nginx_staging.conf index 633655c2dc..74214c17a1 100644 --- a/docker/prod/nodejs_v2/nginx_staging.conf +++ b/docker/prod/nodejs_v2/nginx_staging.conf @@ -2,14 +2,22 @@ upstream django_app { server django:8000 fail_timeout=0; } +upstream statsd_exporter { + server statsd:9102 fail_timeout=0; +} + +upstream node_exporter { + server node_exporter:9100 fail_timeout=0; +} + server { - server_name beta-staging.eval.ai; + server_name staging.eval.ai; listen 80; - return 301 https://beta-staging.eval.ai$request_uri; + return 301 https://staging.eval.ai$request_uri; } server { - server_name beta-staging.eval.ai; + server_name staging.eval.ai; listen 443 ssl; location / { root /usr/share/nginx/html; @@ -17,11 +25,21 @@ server { try_files $uri $uri/ /index.html =404; } + client_max_body_size 200M; + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 256; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 9; + ssl on; ssl_certificate /etc/ssl/eval_ai.crt; ssl_certificate_key /etc/ssl/eval_ai.key; ssl_prefer_server_ciphers on; - #enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated. + # enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated. ssl_protocols TLSv1 TLSv1.1 TLSv1.2; access_log /var/log/nginx/access.log; @@ -33,4 +51,18 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://django_app; } + + location /statsd { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://statsd_exporter; + } + + location /node_exporter { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://node_exporter/metrics; + } } diff --git a/docs/source/faq(developers).md b/docs/source/faq(developers).md index e3d006a4ce..b3c597d205 100644 --- a/docs/source/faq(developers).md +++ b/docs/source/faq(developers).md @@ -258,3 +258,12 @@ rm -rf bower_components npm install bower install ``` + +#### Q. While trying to build EvalAI from the master branch and run the command docker-compose up: + +``` +ERROR: Service 'celery' failed to build: pull access denied for evalai_django, repository does not exist or may require 'docker login': denied: requested access to the resource is denied +``` + +Please make sure to clone EvalAI in its default directory with name evalai. This happens because the parent directory changes the name of docker images. +For instance, the image evalai_django gets renamed to evalai_dev_django if your directory is renamed to EvalAI_dev. diff --git a/evalai/urls.py b/evalai/urls.py index 8f94b949a2..9e09a5811d 100644 --- a/evalai/urls.py +++ b/evalai/urls.py @@ -92,7 +92,6 @@ schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc", ), - url(r"^api/prometheus/", include("django_prometheus.urls")), ] # DJANGO-SPAGHETTI-AND-MEATBALLS URLs available during development only. diff --git a/frontend_v2/src/app/app.component.html b/frontend_v2/src/app/app.component.html index 6be740b4ca..dd80668e1f 100644 --- a/frontend_v2/src/app/app.component.html +++ b/frontend_v2/src/app/app.component.html @@ -1,11 +1,11 @@
+ (currentRoutePath == '/' || currentRoutePath == '/auth/login' || currentRoutePath == '/auth/signup') + ? '' : (currentRoutePath == '/about' || currentRoutePath == '/contact' || currentRoutePath == '/our-team' || + currentRoutePath == '/get-involved' || currentRoutePath == '/privacy-policy') + ? 'page-wrap' : 'page-wrap-challenge' + " >
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 7bc11aea02..8b8d1edbc2 100644 --- a/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts +++ b/frontend_v2/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts @@ -343,6 +343,19 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit, OnD phaseSplitSelected(phaseSplit) { const SELF = this; SELF.selectedPhaseSplit = phaseSplit; + + const API_PATH = SELF.endpointsService.particularChallengePhaseSplitUrl(this.selectedPhaseSplit['id']); + SELF.apiService.getUrl(API_PATH).subscribe( + (data) => { + SELF.leaderboardPrecisionValue = data.leaderboard_decimal_precision; + SELF.setLeaderboardPrecisionValue = `1.${ SELF.leaderboardPrecisionValue }-${ SELF.leaderboardPrecisionValue }`; + }, + (err) => { + SELF.globalService.handleApiError(err); + }, + () => {} + ); + if (SELF.selectedPhaseSplit && SELF.router.url.endsWith('leaderboard/' + phaseSplit['id'])) { SELF.fetchLeaderboard(SELF.selectedPhaseSplit['id']); SELF.showLeaderboardByLatest = SELF.selectedPhaseSplit.show_leaderboard_by_latest_submission; @@ -620,7 +633,7 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit, OnD SELF.apiService.patchUrl(API_PATH, BODY).subscribe( (data) => { this.minusDisabled = SELF.leaderboardPrecisionValue === 0 ? true : false; - this.plusDisabled = SELF.leaderboardPrecisionValue === 5 ? true : false; + this.plusDisabled = SELF.leaderboardPrecisionValue === 20 ? true : false; }, (err) => { SELF.globalService.handleApiError(err, true); diff --git a/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.html b/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.html index 6af6dac21a..924da573c7 100644 --- a/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.html +++ b/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.html @@ -76,6 +76,7 @@ +
Submissions/day @@ -134,6 +135,43 @@ >
+
+ +
+
+
+ Max Concurrent Submissions Allowed + +
+
+ +
+
+
Allowed Submission File Types
+ +
+
+
diff --git a/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.ts b/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.ts index 043db1c87c..b74a9fab77 100644 --- a/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.ts +++ b/frontend_v2/src/app/components/challenge/challengephases/editphasemodal/editphasemodal.component.ts @@ -27,6 +27,11 @@ export class EditphasemodalComponent implements OnInit { * Challenge phase name */ name = ''; + + /** + * Challenge phase allowed submission file types + */ + allowedSubmissionFileTypes = ''; /** * Challenge phase description @@ -58,6 +63,11 @@ export class EditphasemodalComponent implements OnInit { */ maxSubmissions: number; + /** + * Challenge phase max concurrent submissions allowed + */ + maxConcurrentSubmissionsAllowed: number; + /** * If editor error message */ @@ -141,7 +151,9 @@ export class EditphasemodalComponent implements OnInit { * Constructor. * @param globalService GlobalService Injection. */ - constructor(private globalService: GlobalService) {} + constructor( + private globalService: GlobalService, + ) {} ngOnInit() { if (this.params) { @@ -172,6 +184,12 @@ export class EditphasemodalComponent implements OnInit { if (this.params['maxSubmissions']) { this.maxSubmissions = this.params['maxSubmissions']; } + if (this.params['maxConcurrentSubmissionsAllowed']) { + this.maxConcurrentSubmissionsAllowed = this.params['maxConcurrentSubmissionsAllowed']; + } + if (this.params['allowedSubmissionFileTypes']) { + this.allowedSubmissionFileTypes = this.params['allowedSubmissionFileTypes']; + } if (this.params['confirm']) { this.confirm = this.params['confirm']; } 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 d69aa6a310..796a9092f8 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.html @@ -106,16 +106,41 @@
Challenge Settings

- -
-
+
+ + +
+ +
+ + Is Public + + + +      + + Submission Visibility + + + + +
+
+
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 0de5a6dbb1..543e84f175 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.scss +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.scss @@ -13,6 +13,14 @@ } } +.phase-card { + width:50vw; +} + +.phase-button { + margin-left: 25px; +} + @include screen-medium { .settings-section { padding: 10px 20px !important; 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 924a462226..96265fcc37 100644 --- a/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts @@ -89,13 +89,44 @@ 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; + + /** + * phase visibility state and it's icon + */ + phaseVisibility = { + state: 'Private', + icon: 'fa fa-toggle-off', + }; + + /** + * submission visibility state and it's icon + */ + submissionVisibility = { + state: 'Private', + icon: 'fa fa-toggle-off', + }; /** * publish challenge state and it's icon */ publishChallenge = { state: 'Not Published', - icon: 'fa fa-eye-slash red-text', + icon: 'fa fa-toggle-off', }; /** @@ -188,49 +219,180 @@ export class ChallengesettingsComponent implements OnInit, OnDestroy { 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'; + } + }; + } - SELF.apiCall = (params) => { - const FORM_DATA: FormData = new FormData(); - for (const key in params) { - if (params[key]) { - FORM_DATA.append(key, params[key]); - } + editPhaseDetails() { + 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]); } + } + SELF.apiService + .patchFileUrl( + SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.challenge.id, SELF.selectedPhase['id']), + FORM_DATA + ) + .subscribe( + (data) => { + SELF.selectedPhase = data; + SELF.challengeService.fetchPhases(SELF.challenge['id']); + SELF.challengeService.changePhaseSelected(true); + SELF.selectedPhase = false; + SELF.globalService.showToast('success', 'The challenge phase details are successfully updated!'); + }, + (err) => { + SELF.globalService.showToast('error', err); + }, + () => {this.logger.info('PHASE-UPDATE-FINISHED')} + ); + }; + + const PARAMS = { + title: 'Edit Challenge Phase Details', + name: SELF.selectedPhase['name'], + label: 'description', + description: SELF.selectedPhase['description'], + startDate: SELF.selectedPhase['start_date'], + endDate: SELF.selectedPhase['end_date'], + maxSubmissionsPerDay: SELF.selectedPhase['max_submissions_per_day'], + maxSubmissionsPerMonth: SELF.selectedPhase['max_submissions_per_month'], + maxSubmissions: SELF.selectedPhase['max_submissions'], + maxConcurrentSubmissionsAllowed: SELF.selectedPhase['max_concurrent_submissions_allowed'], + allowedSubmissionFileTypes: SELF.selectedPhase['allowed_submission_file_types'], + confirm: 'Submit', + deny: 'Cancel', + confirmCallback: SELF.apiCall, + }; + SELF.globalService.showEditPhaseModal(PARAMS); +} + + /** + * Phase Visibility toggle function + */ + togglePhaseVisibility() { + const SELF = this; + let togglePhaseVisibilityState, isPublic; + if (SELF.phaseVisibility.state === 'Public') { + togglePhaseVisibilityState = 'private'; + isPublic = false; + SELF.phaseVisibility.state = 'Private'; + SELF.phaseVisibility.icon = 'fa fa-toggle-off'; + } else { + togglePhaseVisibilityState = 'public'; + isPublic = true; + SELF.phaseVisibility.state = 'Public'; + SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; + } + const BODY: FormData = new FormData(); + BODY.append("is_public", isPublic); + SELF.apiService + .patchFileUrl( + SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.selectedPhase['challenge'], SELF.selectedPhase['id']), + BODY + ) + .subscribe( + (data) => { + SELF.challengeService.fetchPhases(SELF.selectedPhase['challenge']); + SELF.challengeService.changePhaseSelected(true); + SELF.selectedPhase = false; + SELF.globalService.showToast( + 'success', + 'The phase was successfully made ' + togglePhaseVisibilityState, + 5 + ); + }, + (err) => { + SELF.globalService.handleApiError(err, true); + SELF.globalService.showToast('error', err); + if (isPublic) { + SELF.phaseVisibility.state = 'Private'; + SELF.phaseVisibility.icon = 'fa fa-toggle-off'; + } else { + SELF.phaseVisibility.state = 'Public'; + SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text'; + } + }, + () => this.logger.info('PHASE-VISIBILITY-UPDATE-FINISHED') + ); + } + + /** + * Submission Visibility toggle function + */ + 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'; + } 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 - .patchFileUrl( - SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.challenge.id, SELF.selectedPhase['id']), - FORM_DATA - ) + .patchFileUrl( + SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.selectedPhase['challenge'], SELF.selectedPhase['id']), + BODY + ) .subscribe( (data) => { - SELF.selectedPhase = data; - SELF.challengeService.fetchPhases(SELF.challenge['id']); - SELF.globalService.showToast('success', 'The challenge phase details are successfully updated!'); + SELF.challengeService.fetchPhases(SELF.selectedPhase['challenge']); + SELF.challengeService.changePhaseSelected(true); + SELF.selectedPhase = false; + 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'; + } else { + SELF.submissionVisibility.state = 'Public'; + SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text'; + } }, - () => {this.logger.info('PHASE-UPDATE-FINISHED')} + () => this.logger.info('SUBMISSION-VISIBILITY-UPDATE-FINISHED') ); - }; - - const PARAMS = { - title: 'Edit Challenge Phase Details', - name: SELF.selectedPhase['name'], - label: 'description', - description: SELF.selectedPhase['description'], - startDate: SELF.selectedPhase['start_date'], - endDate: SELF.selectedPhase['end_date'], - maxSubmissionsPerDay: SELF.selectedPhase['max_submissions_per_day'], - maxSubmissionsPerMonth: SELF.selectedPhase['max_submissions_per_month'], - maxSubmissions: SELF.selectedPhase['max_submissions'], - confirm: 'Submit', - deny: 'Cancel', - confirmCallback: SELF.apiCall, - }; - SELF.globalService.showEditPhaseModal(PARAMS); - }; - } + } + else { + SELF.globalService.showToast('error', "Leaderboard is private, please make the leaderbaord public"); + } + } /** * Remove banned email chip 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 5b61795a72..e633b57a7b 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html +++ b/frontend_v2/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html @@ -139,7 +139,7 @@
My Submissions
Show on leaderboard diff --git a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.html b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.html index 458ae03599..5d3e6b3e0b 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.html +++ b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.html @@ -83,6 +83,7 @@
Submission Guidelines
+
Submission limits

@@ -154,13 +155,14 @@
Submission Guidelines
-
+ +
Make Submission
-
+
    @@ -210,265 +212,10 @@
    Make Submission
-
-

Submission instructions

-
    -
  1. Install evalai-cli
    - $ pip install evalai{{ cliVersion }} - -
  2. -
  3. Add your EvalAI account token to evalai-cli
    - $ evalai set_token {{authToken}} - -
  4. -
  5. Make Submission
    - $ evalai challenge {{challengeId}} phase {{phaseId}} submit --file <submission_file_path> --large - -
  6. -
  7. -
    Use   --private  or  --public   flag in the submission - command to make the submission private or public respectively.
    -
    -
  8. -
  9. For more commands, please refer to evalai-cli documentation. -
  10. -
-
-
-
-
-
- -
{{ inputErrorMessage }}
-
-
- -
-
-
-
-
-

Select submission visibility:

- - -
- - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- {{ attribute.name }} - {{ attribute.description }}
- - -
-
-
-
-
-
- {{ attribute.name }} - {{ attribute.description }}
-
- - - - - -
-
-
-
-
-
- {{ attribute.name }} - {{ attribute.description }}
-
- - - -
-
-
-
-
-
- {{ attribute.name }} - {{ attribute.description }}
-
- - -
- -
-
-
-
-
-
-
-
- {{ submissionError }}

-
- -
-
-
-
- - - {{ selectedPhaseSubmissions.maxExceededMessage }} - - -
-
-
@@ -501,6 +248,268 @@
Make Submission
+ +
+
+

Submission instructions

+
    +
  1. Install evalai-cli
    + $ pip install evalai{{ cliVersion }} + +
  2. +
  3. Add your EvalAI account token to evalai-cli
    + $ evalai set_token {{authToken}} + +
  4. +
  5. Make Submission
    + $ evalai challenge {{challengeId}} phase {{phaseId}} submit --file <submission_file_path> --large + +
  6. +
  7. +
    Use   --private  or  --public   flag in the submission + command to make the submission private or public respectively.
    +
    +
  8. +
  9. For more commands, please refer to evalai-cli documentation. +
  10. +
+
+
+ +
+
+
+
+ +
{{ inputErrorMessage }}
+
+
+ +
+
+
+
+
+

Select submission visibility:

+ + +
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ {{ attribute.name }} - {{ attribute.description }}
+ + +
+
+
+
+
+
+ {{ attribute.name }} - {{ attribute.description }}
+
+ + + + + +
+
+
+
+
+
+ {{ attribute.name }} - {{ attribute.description }}
+
+ + + +
+
+
+
+
+
+ {{ attribute.name }} - {{ attribute.description }}
+
+ + +
+ +
+
+
+
+
+
+
+
+ {{ submissionError }}

+
+ +
+ +
+
+
+ + + {{ selectedPhaseSubmissions.maxExceededMessage }} + + +
+
+
diff --git a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.scss b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.scss index 57e9e0d982..25f420ea7d 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.scss +++ b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.scss @@ -5,6 +5,12 @@ padding: 10px 30px; } +.ev-challenge-view { + margin-top: 0px; + padding-top: 30px; + margin-bottom: 20px; +} + .row .row-lr-margin { margin-bottom: 20px; } @@ -56,7 +62,7 @@ th { padding-right: 36px; } ul { - padding-left: 70px; + padding-left: 60px; } } @@ -134,3 +140,8 @@ th { flex-direction: row; align-items: center; } + +.cli-card-container { + margin: 14.5px 0px; + padding: 0px 0px 0px 70px; +} 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 6a5746b031..4acf2e5af9 100644 --- a/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts +++ b/frontend_v2/src/app/components/challenge/challengesubmit/challengesubmit.component.ts @@ -44,15 +44,20 @@ export class ChallengesubmitComponent implements OnInit { isLoggedIn = false; /** - * Is submittion submitted + * Is submission submitted */ isSubmitted = false; /** - * Is submittion submitted + * Is submission submitted */ isPublicSubmission:boolean = true; + /** + * Is submission allowed by host + */ + isLeaderboardPublic:boolean = false; + /** * Challenge object */ @@ -467,6 +472,7 @@ export class ChallengesubmitComponent implements OnInit { const SELF = this; return (phase) => { SELF.selectedPhase = phase; + 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']); diff --git a/frontend_v2/src/app/components/nav/header-static/header-static.component.html b/frontend_v2/src/app/components/nav/header-static/header-static.component.html index c48a1dc74f..8c619e3ce2 100644 --- a/frontend_v2/src/app/components/nav/header-static/header-static.component.html +++ b/frontend_v2/src/app/components/nav/header-static/header-static.component.html @@ -9,7 +9,8 @@ />
  • - All Challenges + All Challenges
  • 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 e3f45c0dfd..5d53f335b0 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 @@ -11,6 +11,7 @@ import { } 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 { DOCUMENT } from '@angular/common'; import { ApiService } from '../../../services/api.service'; @@ -62,6 +63,16 @@ export class HeaderStaticComponent implements OnInit, OnDestroy { */ isLoggedIn: any = false; + /** + * Current name of tab which needs to be active + */ + tabHighlight: string = "allChallenges"; + + /** + * Returns true if the string is not a number + */ + isChallengeComponent : boolean = false; + /** * Inner width */ @@ -88,7 +99,7 @@ export class HeaderStaticComponent implements OnInit, OnDestroy { private apiService: ApiService, @Inject(DOCUMENT) private document: Document ) { - this.authState = authService.authState; + this.authState = authService.authState; } /** @@ -107,6 +118,27 @@ export class HeaderStaticComponent implements OnInit, OnDestroy { ngOnInit() { this.updateElements(); this.checkInnerWidth(); + + this.router.events + .pipe(filter(event => event instanceof NavigationEnd)) + .subscribe((event) => { + if(event) { + if(this.router.url.split('/')[length] == "all") { + this.tabHighlight = "allChallenges"; + this.globalService.changeTabActiveStatus("allChallenges"); + } + else if(this.router.url.split('/')[1] == "profile") { + this.tabHighlight = "profile"; + this.globalService.changeTabActiveStatus("profile"); + } + } + }); + this.isChallengeComponent = isNaN(parseInt(this.router.url.split('/')[length])); + + this.globalService.nameTabHighlight.subscribe((tabHighlight) => { + this.tabHighlight = tabHighlight; + }); + this.authServiceSubscription = this.authService.change.subscribe((authState) => { this.authState = authState; if (this.authService.isLoggedIn()) { diff --git a/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.scss b/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.scss index a427c2f91c..dee9eec866 100644 --- a/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.scss +++ b/frontend_v2/src/app/components/publiclists/challengelist/challengecard/challengecard.component.scss @@ -5,7 +5,7 @@ } .bg-img { - width: 100%; + object-fit: cover; height: 100%; margin: auto; } 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 4584fc91d2..329c033ae8 100644 --- a/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.ts +++ b/frontend_v2/src/app/components/publiclists/challengelist/challengelist.component.ts @@ -231,8 +231,10 @@ export class ChallengelistComponent implements OnInit { */ @HostListener('window:scroll', []) onWindowScroll(): void { - const RECT = this.document.getElementById('ongoing-challenges').getBoundingClientRect(); - this.isScrollbtnVisible = RECT.top < 0; + if(this.document.getElementById('ongoing-challenges')) { + const RECT = this.document.getElementById('ongoing-challenges').getBoundingClientRect(); + this.isScrollbtnVisible = RECT.top < 0; + } } /** diff --git a/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.html b/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.html index 0e7bb212cf..860294ec62 100644 --- a/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.html +++ b/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.html @@ -5,9 +5,7 @@
    - - Create Challenge - +

    Create Challenge


    @@ -43,9 +41,9 @@
    - Challenge Phases + Challenge Phases
    -
    +
    Name for Phase {{phase['id']}}
    +
  • diff --git a/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.scss b/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.scss index 8c41306b58..5bc6db6909 100644 --- a/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.scss +++ b/frontend_v2/src/app/components/template-challenge-create/template-challenge-create.component.scss @@ -13,6 +13,14 @@ } } +.challenge-phase { + margin-top: 2rem; +} + +::-webkit-calendar-picker-indicator:hover{ + cursor:pointer; +} + @media only screen and (max-width: $med-screen) { .web-container { width: 100%; @@ -43,7 +51,7 @@ p { } .web-container { - width: calc(100vw - 223px); + width: calc(100% - 223px); float: right; padding-top: 70px; overflow-x: hidden; 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 e24b401af4..6abdfe0c55 100644 --- a/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts +++ b/frontend_v2/src/app/components/utility/selectphase/selectphase.component.ts @@ -46,6 +46,11 @@ export class SelectphaseComponent implements OnInit, OnChanges { */ phaseVisibility = false; + /** + * If phase selected + */ + isPhaseSelected : boolean = false; + /** * Currently selected phase */ @@ -86,7 +91,15 @@ export class SelectphaseComponent implements OnInit, OnChanges { * Component on changes detected in Input. * @param change changes detected */ - ngOnChanges(change) {} + ngOnChanges(change) { + this.challengeService.isPhaseSelected.subscribe((isPhaseSelected) => { + this.isPhaseSelected = isPhaseSelected; + }); + if(this.isPhaseSelected == true) { + this.challengeService.changePhaseSelected(false); + this.phaseName = ''; + } + } /** * Select a particular phase. diff --git a/frontend_v2/src/app/components/utility/side-bar/side-bar.component.html b/frontend_v2/src/app/components/utility/side-bar/side-bar.component.html index 577e5af12b..e00acdb49f 100644 --- a/frontend_v2/src/app/components/utility/side-bar/side-bar.component.html +++ b/frontend_v2/src/app/components/utility/side-bar/side-bar.component.html @@ -3,25 +3,29 @@