diff --git a/CHANGELOG.md b/CHANGELOG.md index 18eece797..63e78b902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,16 @@ The format is based on [Keep a Changelog]. > - **Security**: in case of vulnerabilities. +## [0.4.2] - 2019-11-18 + +### Fixed + +- Fixed `IBMQBackendService.jobs()` to correctly return jobs when + both `start_datetime` and `end_datetime` are specified. Now, when the + two parameters are specified, the function will return the jobs after + (greater than or equal to) and before (less than or equal to) the + two given datetimes. (\#452) + ## [0.4.1] - 2019-11-14 ### Fixed @@ -27,7 +37,7 @@ The format is based on [Keep a Changelog]. optional parameters `start_datetime` and `end_datetime`. If one is specified, it is used to find jobs whose creation date is after (greater than) or before (less than) the given the date/time, - respectively. If both are specified, they are used to find jobs + respectively. If both are specified, they are used to find jobs whose creation date is between the two dates. (\#443) ## [0.4.0] - 2019-11-12 @@ -230,7 +240,8 @@ The format is based on [Keep a Changelog]. - Support for non-qobj format has been removed. (\#26, \#28) -[UNRELEASED]: https://github.com/Qiskit/qiskit-ibmq-provider/compare/0.4.1...HEAD +[UNRELEASED]: https://github.com/Qiskit/qiskit-ibmq-provider/compare/0.4.2...HEAD +[0.4.2]: https://github.com/Qiskit/qiskit-ibmq-provider/compare/0.4.1...0.4.2 [0.4.1]: https://github.com/Qiskit/qiskit-ibmq-provider/compare/0.4.0...0.4.1 [0.4.0]: https://github.com/Qiskit/qiskit-ibmq-provider/compare/0.3.3...0.4.0 [0.3.3]: https://github.com/Qiskit/qiskit-ibmq-provider/compare/0.3.2...0.3.3 diff --git a/qiskit/providers/ibmq/VERSION.txt b/qiskit/providers/ibmq/VERSION.txt index 267577d47..2b7c5ae01 100644 --- a/qiskit/providers/ibmq/VERSION.txt +++ b/qiskit/providers/ibmq/VERSION.txt @@ -1 +1 @@ -0.4.1 +0.4.2 diff --git a/qiskit/providers/ibmq/ibmqbackend.py b/qiskit/providers/ibmq/ibmqbackend.py index c4c333f4f..4be1ae456 100644 --- a/qiskit/providers/ibmq/ibmqbackend.py +++ b/qiskit/providers/ibmq/ibmqbackend.py @@ -276,9 +276,9 @@ def jobs( `_ can be used. start_datetime: filter by start date. This is used to find jobs - whose creation dates are after (greater than) this date/time. + whose creation dates are after (greater than or equal to) this date/time. end_datetime: filter by end date. This is used to find jobs - whose creation dates are before (less than) this date/time. + whose creation dates are before (less than or equal to) this date/time. db_filter: `loopback-based filter `_. This is an interface to a database ``where`` filter. Some @@ -296,12 +296,6 @@ def jobs( 'qasms.result.data.counts.11': {'gt': 400}} job_list = backend.jobs(limit=5, db_filter=cnts_filter) - Filter last five jobs from 30 days ago:: - - past_date = datetime.datetime.now() - datetime.timedelta(days=30) - date_filter = {'creationDate': {'lt': past_date.isoformat()}} - job_list = backend.jobs(limit=5, db_filter=date_filter) - Returns: list of IBMQJob instances diff --git a/qiskit/providers/ibmq/ibmqbackendservice.py b/qiskit/providers/ibmq/ibmqbackendservice.py index 9a52bb6b7..d6b16e781 100644 --- a/qiskit/providers/ibmq/ibmqbackendservice.py +++ b/qiskit/providers/ibmq/ibmqbackendservice.py @@ -126,9 +126,9 @@ def jobs( `_ can be used. start_datetime: filter by start date. This is used to find jobs - whose creation dates are after (greater than) this date/time. + whose creation dates are after (greater than or equal to) this date/time. end_datetime: filter by end date. This is used to find jobs - whose creation dates are before (less than) this date/time. + whose creation dates are before (less than or equal to) this date/time. db_filter: `loopback-based filter `_. This is an interface to a database ``where`` filter. Some @@ -146,12 +146,6 @@ def jobs( 'qasms.result.data.counts.11': {'gt': 400}} job_list = backend.jobs(limit=5, db_filter=cnts_filter) - Filter last five jobs from 30 days ago:: - - past_date = datetime.datetime.now() - datetime.timedelta(days=30) - date_filter = {'creationDate': {'lt': past_date.isoformat()}} - job_list = backend.jobs(limit=5, db_filter=date_filter) - Returns: list of IBMQJob instances @@ -187,18 +181,17 @@ def jobs( if job_name: api_filter['name'] = {"regexp": job_name} - # Create the date query according to the parameters specified. - if start_datetime or end_datetime: - date_filter = {'creationDate': {}} # type: ignore[var-annotated] - if start_datetime: - date_filter['creationDate'].update({'gt': start_datetime.isoformat()}) - if end_datetime: - date_filter['creationDate'].update({'lt': end_datetime.isoformat()}) - - api_filter.update(date_filter) + if start_datetime and end_datetime: + api_filter['creationDate'] = { + 'between': [start_datetime.isoformat(), end_datetime.isoformat()] + } + elif start_datetime: + api_filter['creationDate'] = {'gte': start_datetime.isoformat()} + elif end_datetime: + api_filter['creationDate'] = {'lte': end_datetime.isoformat()} if db_filter: - # status takes precedence over db_filter for same keys + # Argument filters takes precedence over db_filter for same keys api_filter = {**db_filter, **api_filter} # Retrieve the requested number of jobs, using pagination. The API diff --git a/test/ibmq/test_ibmq_job.py b/test/ibmq/test_ibmq_job.py index ba27d19e9..63d5b545d 100644 --- a/test/ibmq/test_ibmq_job.py +++ b/test/ibmq/test_ibmq_job.py @@ -313,39 +313,85 @@ def test_get_jobs_filter_job_status_backend_service(self, backend, provider): for job in job_list: self.assertTrue(job.status() is JobStatus.DONE) - @requires_device @requires_provider - def test_get_jobs_filter_job_start_datetime(self, backend, provider): + def test_get_jobs_filter_job_start_datetime(self, provider): """Test retrieving jobs created after a specified datetime.""" - past_date = datetime.now() - timedelta(days=10) + backend = provider.get_backend('ibmq_qasm_simulator') + past_month = datetime.now() - timedelta(days=30) + past_month_str = past_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') job_list = provider.backends.jobs(backend_name=backend.name(), - limit=5, skip=0, start_datetime=past_date) - for job in job_list: - self.assertTrue(job.creation_date() > str(past_date)) + limit=5, skip=0, start_datetime=past_month) + self.assertTrue(job_list) + for i, job in enumerate(job_list): + self.assertTrue(job.creation_date() >= past_month_str, + '{}) job creation_date {} is not ' + 'greater than or equal to past month: {}' + .format(i, job.creation_date(), past_month_str)) - @requires_device @requires_provider - def test_get_jobs_filter_job_end_datetime(self, backend, provider): + def test_get_jobs_filter_job_end_datetime(self, provider): """Test retrieving jobs created before a specified datetime.""" - date_today = datetime.now() + backend = provider.get_backend('ibmq_qasm_simulator') + past_month = datetime.now() - timedelta(days=30) + past_month_str = past_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') job_list = provider.backends.jobs(backend_name=backend.name(), - limit=5, skip=0, end_datetime=date_today) - for job in job_list: - self.assertTrue(job.creation_date() < str(date_today)) + limit=5, skip=0, end_datetime=past_month) + self.assertTrue(job_list) + for i, job in enumerate(job_list): + self.assertTrue(job.creation_date() <= past_month_str, + '{}) job creation_date {} is not ' + 'less than or equal to past month: {}' + .format(i, job.creation_date(), past_month_str)) - @requires_device @requires_provider - def test_get_jobs_filter_job_between_datetimes(self, backend, provider): + def test_get_jobs_filter_job_between_datetimes(self, provider): """Test retrieving jobs created between two specified datetimes.""" + backend = provider.get_backend('ibmq_qasm_simulator') date_today = datetime.now() - past_date = date_today - timedelta(days=10) + + past_month = date_today - timedelta(30) + past_month_str = past_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + past_two_month = date_today - timedelta(60) + past_two_month_str = past_two_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, skip=0, - start_datetime=past_date, end_datetime=date_today) - for job in job_list: - self.assertTrue(str(past_date) < job.creation_date() < str(date_today)) + start_datetime=past_two_month, end_datetime=past_month) + self.assertTrue(job_list) + for i, job in enumerate(job_list): + self.assertTrue((past_two_month_str <= job.creation_date() <= past_month_str), + '{}) job creation date {} is not ' + 'between past two month {} and past month {}' + .format(i, past_two_month_str, job.creation_date(), past_month_str)) + + @requires_provider + def test_get_jobs_filter_job_between_datetimes_not_overridden(self, provider): + """Test retrieving jobs created between two specified datetimes + and ensure `db_filter` does not override datetime arguments.""" + # pylint: disable=invalid-name + backend = provider.get_backend('ibmq_qasm_simulator') + date_today = datetime.now() + + past_two_month = date_today - timedelta(30) + past_two_month_str = past_two_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + past_three_month = date_today - timedelta(60) + past_three_month_str = past_three_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + + # Used for `db_filter`, should not override `start_datetime` and `end_datetime` arguments. + past_ten_days = date_today - timedelta(10) + + job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, skip=0, + start_datetime=past_three_month, + end_datetime=past_two_month, + db_filter={'creationDate': {'gt': past_ten_days}}) + self.assertTrue(job_list) + for i, job in enumerate(job_list): + self.assertTrue((past_three_month_str <= job.creation_date() <= past_two_month_str), + '{}) job creation date {} is not ' + 'between past three month {} and past two month {}' + .format(i, past_three_month_str, + job.creation_date(), past_two_month_str)) @requires_provider def test_get_jobs_filter_counts_backend(self, provider): @@ -399,25 +445,14 @@ def test_get_jobs_filter_counts_backend_service(self, provider): self.assertTrue(any(cresult.data.counts.to_dict()['0x0'] < 500 for cresult in result.results)) - @requires_device - def test_get_jobs_filter_date_backend(self, backend): - """Test retrieving jobs from a backend filtered by date.""" - date_today = datetime.now().isoformat() - my_filter = {'creationDate': {'lt': date_today}} - job_list = backend.jobs(limit=5, db_filter=my_filter) - - self.assertTrue(job_list) - self.log.info('found %s matching jobs', len(job_list)) - for i, job in enumerate(job_list): - self.log.info('match #%d: %s', i, job.creation_date()) - self.assertTrue(job.creation_date() < date_today) - - @requires_device @requires_provider - def test_get_jobs_filter_date_backend_service(self, backend, provider): + def test_get_jobs_filter_date_backend_service(self, provider): """Test retrieving jobs from backend service filtered by date.""" - date_today = datetime.now().isoformat() - my_filter = {'creationDate': {'lt': date_today}} + backend = provider.get_backend('ibmq_qasm_simulator') + date_today = datetime.now() + date_today_str = date_today.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + + my_filter = {'creationDate': {'lt': date_today.isoformat()}} job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, db_filter=my_filter) @@ -425,7 +460,9 @@ def test_get_jobs_filter_date_backend_service(self, backend, provider): self.log.info('found %s matching jobs', len(job_list)) for i, job in enumerate(job_list): self.log.info('match #%d: %s', i, job.creation_date()) - self.assertTrue(job.creation_date() < date_today) + self.assertTrue(job.creation_date() < date_today_str, + '{}) job.creation_date: {}, date_today: {}' + .format(i, job.creation_date(), date_today_str)) @requires_provider def test_double_submit_fails(self, provider):