diff --git a/apps/challenges/models.py b/apps/challenges/models.py index e511a825fa..ebce019d0c 100644 --- a/apps/challenges/models.py +++ b/apps/challenges/models.py @@ -429,6 +429,46 @@ def is_active(self): return False def save(self, *args, **kwargs): + from django.core.exceptions import ValidationError + + # Validate challenge dates + if self.challenge.start_date and self.challenge.end_date: + if self.challenge.start_date >= self.challenge.end_date: + raise ValidationError( + "Challenge start date must be before challenge end date" + ) + + # Validate challenge phase dates against challenge dates + if self.start_date and self.end_date and self.challenge: + # Phase start datetime < Phase end datetime + if self.start_date >= self.end_date: + raise ValidationError( + "Challenge phase start date must be before challenge phase end date" + ) + + # Phase start datetime >= Challenge start datetime + if self.challenge.start_date and self.start_date < self.challenge.start_date: + raise ValidationError( + "Challenge phase start date cannot be before challenge start date" + ) + + # Phase start datetime < Challenge end datetime + if self.challenge.end_date and self.start_date >= self.challenge.end_date: + raise ValidationError( + "Challenge phase start date must be before challenge end date" + ) + + # Phase end datetime > Challenge start datetime + if self.challenge.start_date and self.end_date <= self.challenge.start_date: + raise ValidationError( + "Challenge phase end date must be after challenge start date" + ) + + # Phase end datetime <= Challenge end datetime + if self.challenge.end_date and self.end_date > self.challenge.end_date: + raise ValidationError( + "Challenge phase end date cannot be after challenge end date" + ) # If the max_submissions_per_day is less than the # max_concurrent_submissions_allowed. diff --git a/docs/source/faq(developers).md b/docs/source/faq(developers).md index b3c597d205..5d79504dec 100644 --- a/docs/source/faq(developers).md +++ b/docs/source/faq(developers).md @@ -2,7 +2,7 @@ #### Q. How to start contributing? -EvalAI’s issue tracker is good place to start. If you find something that interests you, comment on the thread and we’ll help get you started. +EvalAI's issue tracker is good place to start. If you find something that interests you, comment on the thread and we'll help get you started. Alternatively, if you come across a new bug on the site, please file a new issue and comment if you would like to be assigned. Existing issues are tagged with one or more labels, based on the part of the website it touches, its importance etc., which can help you select one. #### Q. What are the technologies that EvalAI uses? @@ -21,136 +21,12 @@ Refer to [Markdown Guide](https://guides.github.com/features/mastering-markdown/ Coverage decreases when the existing test cases don't test the new code you wrote. If you click coverage, you can see exactly which all parts aren't covered and you can write new tests to test the parts. -#### Q. How to setup EvalAI using virtualenv? +#### Q. How to setup EvalAI? -We have removed the documentation for setting up using virtual environment since the project has grown and different developers face different dependency issues. We recommend to setup EvalAI using docker based environment. +We recommend setting up EvalAI using Docker-based environment for reliability and consistency across systems. Please refer to our [installation guide](https://evalai.readthedocs.io/en/latest/contribute.html). ### Common Errors during installation -#### Q. While using `pip install -r dev/requirement.txt` - -``` - Writing manifest file 'pip-egg-info/psycopg2.egg-info/SOURCES.txt' - Error: You need to install postgresql-server-dev-X.Y for building a server-side extension or - libpq-dev for building a client-side application. - ---------------------------------------- - Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-qIjU8G/psycopg2/ -``` - -Use the following commands in order to solve the error: - -1. `sudo apt-get install postgresql` -2. `sudo apt-get install python-psycopg2` -3. `sudo apt-get install libpq-dev` - -#### Q. While using `pip install -r dev/requirement.txt` - -``` -Command “python setup.py egg_info” failed with error code 1 in -/private/var/folders/c7/b45s17816zn_b1dh3g7yzxrm0000gn/T/pip-build- GM2AG/psycopg2/ -``` - -Firstly check that you have installed all the mentioned dependencies. -Then, Upgrade the version of postgresql to 10.1 in order to solve it. - -#### Q. Getting an import error - -``` -Couldn't import Django,"when using command python manage.py migrate -``` - -Firstly, check that you have activated the virtualenv. -Install python dependencies using the following commands on the command line - -``` -cd evalai -pip install -r requirements/dev.txt -``` - -#### Q. Getting Mocha Error - -``` -Can not load reporter “mocha”,it is not registered -``` - -Uninstall karma and then install - -``` -npm uninstall -g generator-karma && npm install -g generator-angular. -``` - -#### Q. While trying to execute `bower install` - -``` -bower: command not found -``` - -Execute the following command first : - -``` -npm install -g bower -``` - -#### Q. While trying to execute `gulp dev:runserver` - -``` -gulp: command not found -``` - -Execute the following command first - -``` -npm install -g gulp-cli -``` - -#### Q. While executing `gulp dev:runserver` - -``` -events.js:160 -throw er; // Unhandled 'error' event -^ -Error: Gem sass is not installed. -``` - -Execute the following command first : - -``` -gem install sass -``` - -#### Q. While trying to install `npm config set proxy http://proxy:port` on Ubuntu, I get the following error: - -``` -ubuntu@ubuntu-Inspiron-3521:~/Desktop/Python-2.7.14$ npm install -g angular-cli -npm ERR! Linux 4.4.0-21-generic -npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "install" "-g" "angular-cli" -npm ERR! node v4.2.6 -npm ERR! npm v3.5.2 -npm ERR! code ECONNRESET - -npm ERR! network tunneling socket could not be established, cause=getaddrinfo ENOTFOUND proxy proxy:80 -npm ERR! network This is most likely not a problem with npm itself -npm ERR! network and is related to network connectivity. -npm ERR! network In most cases you are behind a proxy or have bad network settings. -npm ERR! network -npm ERR! network If you are behind a proxy, please make sure that the -npm ERR! network 'proxy' config is set properly. See: 'npm help config' - -npm ERR! Please include the following file with any support request: -npm ERR! /home/ubuntu/Desktop/Python-2.7.14/npm-debug.log -``` - -To solve, execute the following commands: - -1. `npm config set registry=registry.npmjs.org` - -If the above does not work, try deleting them by following commands: - -1. `npm config delete proxy` -2. `npm config delete https-proxy` - -Then, start the installation process of frontend once more. - #### Q. While using docker, I am getting the following error on URL [http://localhost:8888/](http://localhost:8888/): ``` @@ -159,14 +35,6 @@ Cannot Get \ Try removing the docker containers and then building them again. -#### Q. Getting the following error while running `python manage.py seed`: - -``` -Starting the database seeder. Hang on... Exception while running run() in 'scripts.seed' Database successfully seeded -``` - -Change the python version to 2.7.x . The problem might be because of the python 3.0 version. - #### Q. Getting the following error while executing command `createdb evalai -U postgres`: ``` @@ -175,15 +43,6 @@ createdb: could not connect to database template1: FATAL: Peer authentication fa Try creating a new user and then grant all the privileges to it and then create a db. -#### Q. Getting the following error while executing `npm install`: - -``` -npm WARN generator-angular@0.16.0 requires a peer of generator- -karma@>=0.9.0 but none was installed. -``` - -Uninstall and then install karma again and also don't forget to clean the global as well as project npm cache. Then try again the step 8. - #### Q. While running the unit tests, I am getting the error similar to as shown below: ``` @@ -241,24 +100,6 @@ Exception while running run() in 'scripts.seed' Try clearing the postgres database manually and try again. -#### Q. Getting the following error while executing `gulp dev:runserver`: - -``` -/usr/lib//nodejs/gulp//bin/gulp.js:132 - gulpInst.start.apply(gulpInst, toRun)l - ^ -TypeError: Cannot read properly 'apply of undefined' -``` - -Execute the following command: - -``` -rm -rf node_modules/ -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: ``` diff --git a/tests/unit/challenges/test_models.py b/tests/unit/challenges/test_models.py index fef437059f..507e0adb78 100644 --- a/tests/unit/challenges/test_models.py +++ b/tests/unit/challenges/test_models.py @@ -261,3 +261,189 @@ def test__str__(self): "{0} : {1}".format(self.challenge_phase_split, self.submission), self.leaderboard_data.__str__(), ) + + +class ChallengePhaseDateValidationTestCase(BaseTestCase): + """Test date validation constraints for ChallengePhase""" + + def setUp(self): + super(ChallengePhaseDateValidationTestCase, self).setUp() + + def test_phase_start_date_before_challenge_start_date(self): + """Test that phase start date cannot be before challenge start date""" + from django.core.exceptions import ValidationError + + with self.assertRaises(ValidationError) as context: + ChallengePhase.objects.create( + name="Invalid Phase", + description="Phase with invalid start date", + leaderboard_public=False, + is_public=False, + start_date=timezone.now() - timedelta(days=3), # Before challenge start + end_date=timezone.now() + timedelta(days=1), + challenge=self.challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIn( + "Challenge phase start date cannot be before challenge start date", + str(context.exception), + ) + + def test_phase_end_date_after_challenge_end_date(self): + """Test that phase end date cannot be after challenge end date""" + from django.core.exceptions import ValidationError + + with self.assertRaises(ValidationError) as context: + ChallengePhase.objects.create( + name="Invalid Phase", + description="Phase with invalid end date", + leaderboard_public=False, + is_public=False, + start_date=timezone.now() - timedelta(days=1), + end_date=timezone.now() + timedelta(days=2), # After challenge end + challenge=self.challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIn( + "Challenge phase end date cannot be after challenge end date", + str(context.exception), + ) + + def test_phase_start_date_after_phase_end_date(self): + """Test that phase start date must be before phase end date""" + from django.core.exceptions import ValidationError + + with self.assertRaises(ValidationError) as context: + ChallengePhase.objects.create( + name="Invalid Phase", + description="Phase with start date after end date", + leaderboard_public=False, + is_public=False, + start_date=timezone.now() + timedelta(hours=2), + end_date=timezone.now() + timedelta(hours=1), # Before start + challenge=self.challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIn( + "Challenge phase start date must be before challenge phase end date", + str(context.exception), + ) + + def test_phase_start_date_equals_challenge_end_date(self): + """Test that phase start date cannot equal challenge end date""" + from django.core.exceptions import ValidationError + + with self.assertRaises(ValidationError) as context: + ChallengePhase.objects.create( + name="Invalid Phase", + description="Phase starting at challenge end", + leaderboard_public=False, + is_public=False, + start_date=self.challenge.end_date, # Equals challenge end + end_date=self.challenge.end_date + timedelta(hours=1), + challenge=self.challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIn( + "Challenge phase start date must be before challenge end date", + str(context.exception), + ) + + def test_phase_end_date_equals_challenge_start_date(self): + """Test that phase end date cannot equal challenge start date""" + from django.core.exceptions import ValidationError + + with self.assertRaises(ValidationError) as context: + ChallengePhase.objects.create( + name="Invalid Phase", + description="Phase ending at challenge start", + leaderboard_public=False, + is_public=False, + start_date=self.challenge.start_date - timedelta(hours=1), + end_date=self.challenge.start_date, # Equals challenge start + challenge=self.challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIn( + "Challenge phase end date must be after challenge start date", + str(context.exception), + ) + + def test_challenge_start_date_after_challenge_end_date(self): + """Test that challenge start date must be before challenge end date""" + from django.core.exceptions import ValidationError + + invalid_challenge = Challenge.objects.create( + title="Invalid Challenge", + description="Challenge with invalid dates", + terms_and_conditions="Terms", + submission_guidelines="Guidelines", + creator=self.challenge_host_team, + published=False, + enable_forum=True, + start_date=timezone.now() + timedelta(days=2), + end_date=timezone.now() + timedelta(days=1), # Before start + anonymous_leaderboard=False, + ) + + with self.assertRaises(ValidationError) as context: + ChallengePhase.objects.create( + name="Phase for Invalid Challenge", + description="Phase description", + leaderboard_public=False, + is_public=False, + start_date=timezone.now() + timedelta(days=1, hours=1), + end_date=timezone.now() + timedelta(days=1, hours=2), + challenge=invalid_challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIn( + "Challenge start date must be before challenge end date", + str(context.exception), + ) + + def test_valid_phase_dates_within_challenge_dates(self): + """Test that valid phase dates within challenge dates work correctly""" + try: + phase = ChallengePhase.objects.create( + name="Valid Phase", + description="Phase with valid dates", + leaderboard_public=False, + is_public=False, + start_date=timezone.now() - timedelta(days=1), + end_date=timezone.now() + timedelta(hours=12), + challenge=self.challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIsNotNone(phase) + self.assertEqual(phase.name, "Valid Phase") + except Exception as e: + self.fail(f"Valid phase dates should not raise exception: {e}") + + def test_phase_dates_equal_challenge_dates(self): + """Test that phase dates can equal challenge dates (edge case)""" + try: + phase = ChallengePhase.objects.create( + name="Phase Equal to Challenge", + description="Phase with same dates as challenge", + leaderboard_public=False, + is_public=False, + start_date=self.challenge.start_date, + end_date=self.challenge.end_date, + challenge=self.challenge, + max_submissions_per_day=100000, + max_submissions=100000, + ) + self.assertIsNotNone(phase) + self.assertEqual(phase.start_date, self.challenge.start_date) + self.assertEqual(phase.end_date, self.challenge.end_date) + except Exception as e: + self.fail(f"Phase dates equal to challenge dates should be valid: {e}") +