Merge lp:~gmb/launchpad/bugzilla-3.4-dbtime into lp:launchpad

Proposed by Graham Binns
Status: Merged
Merged at revision: not available
Proposed branch: lp:~gmb/launchpad/bugzilla-3.4-dbtime
Merge into: lp:launchpad
Diff against target: None lines
To merge this branch: bzr merge lp:~gmb/launchpad/bugzilla-3.4-dbtime
Reviewer Review Type Date Requested Status
Celso Providelo (community) code Approve
Review via email: mp+10464@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

This branch adds the ability to get the time from the remote server to
the BugzillaAPI ExternalBugTracker.

I've added a local implementation of the API method that we call so that
we can test properly without having to connect to an actual instance as
well as the implementation of getCurrentDBTime() itself. I've made some
minor changes to TestBugzillaXMLRPCTransport.time() to make it a little
more readable.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt
  lib/lp/bugs/externalbugtracker/bugzilla.py
  lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt
  lib/lp/bugs/tests/externalbugtracker.py

Revision history for this message
Celso Providelo (cprov) wrote :

Hi Graham,

The change is flawless!

Thanks for improving the testing infrastructure readability while you were there.

r=me

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt 2009-08-19 13:12:30 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt 2009-08-20 15:43:37 +0000
@@ -75,3 +75,39 @@
75 ...75 ...
76 BugTrackerAuthenticationError: http://thiswillfail.example.com:76 BugTrackerAuthenticationError: http://thiswillfail.example.com:
77 Fault 300: The username or password you entered is not valid.77 Fault 300: The username or password you entered is not valid.
78
79
80Getting the server time
81-----------------------
82
83To be able to accurately sync with a bug tracker, we need to be able to
84check the time on the remote server. We use BugzillaAPI.getCurrentDBTime()
85to get the current time on the remote server.
86
87 # There's no way to create a UTC timestamp without monkey-patching
88 # the TZ environment variable. Rather than do that, we create our
89 # own datetime and work with that.
90 >>> from datetime import datetime
91 >>> remote_time = datetime(2009, 8, 19, 17, 2, 2)
92
93 >>> test_transport.local_datetime = remote_time
94 >>> bugzilla.getCurrentDBTime()
95 CALLED Bugzilla.time()
96 datetime.datetime(2009, 8, 19, 17, 2, 2, tzinfo=<UTC>)
97
98If the remote system is in a different timezone, getCurrentDBTime() will
99convert its time to UTC before returning it.
100
101 >>> test_transport.utc_offset = 60**2
102 >>> test_transport.timezone = 'CET'
103 >>> bugzilla.getCurrentDBTime()
104 CALLED Bugzilla.time()
105 datetime.datetime(2009, 8, 19, 16, 2, 2, tzinfo=<UTC>)
106
107This works whether the UTC offset is positive or negative.
108
109 >>> test_transport.utc_offset = -5 * 60**2
110 >>> test_transport.timezone = 'US/Eastern'
111 >>> bugzilla.getCurrentDBTime()
112 CALLED Bugzilla.time()
113 datetime.datetime(2009, 8, 19, 22, 2, 2, tzinfo=<UTC>)
78114
=== modified file 'lib/lp/bugs/externalbugtracker/bugzilla.py'
--- lib/lp/bugs/externalbugtracker/bugzilla.py 2009-08-19 13:19:22 +0000
+++ lib/lp/bugs/externalbugtracker/bugzilla.py 2009-08-20 15:43:37 +0000
@@ -430,6 +430,34 @@
430 self.baseurl,430 self.baseurl,
431 "Fault %s: %s" % (fault.faultCode, fault.faultString))431 "Fault %s: %s" % (fault.faultCode, fault.faultString))
432432
433 def getCurrentDBTime(self):
434 """See `IExternalBugTracker`."""
435 time_dict = self.xmlrpc_proxy.Bugzilla.time()
436
437 # Convert the XML-RPC DateTime we get back into a regular Python
438 # datetime.
439 server_db_timetuple = time.strptime(
440 str(time_dict['db_time']), '%Y%m%dT%H:%M:%S')
441 server_db_datetime = datetime(*server_db_timetuple[:6])
442
443 # The server's DB time is the one that we want to use. However,
444 # this may not be in UTC, so we need to convert it. Since we
445 # can't guarantee that the timezone data returned by the server
446 # is sane, we work out the server's offset from UTC by looking
447 # at the difference between the web_time and the web_time_utc
448 # values.
449 server_web_time = time.strptime(
450 str(time_dict['web_time']), '%Y%m%dT%H:%M:%S')
451 server_web_datetime = datetime(*server_web_time[:6])
452 server_web_time_utc = time.strptime(
453 str(time_dict['web_time_utc']), '%Y%m%dT%H:%M:%S')
454 server_web_datetime_utc = datetime(*server_web_time_utc[:6])
455
456 server_utc_offset = server_web_datetime - server_web_datetime_utc
457 server_utc_datetime = server_db_datetime - server_utc_offset
458
459 return server_utc_datetime.replace(tzinfo=pytz.timezone('UTC'))
460
433461
434class BugzillaLPPlugin(BugzillaAPI):462class BugzillaLPPlugin(BugzillaAPI):
435 """An `ExternalBugTracker` to handle Bugzillas using the LP Plugin."""463 """An `ExternalBugTracker` to handle Bugzillas using the LP Plugin."""
436464
=== modified file 'lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt'
--- lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt 2009-08-18 16:39:17 +0000
+++ lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt 2009-08-19 23:17:09 +0000
@@ -576,3 +576,36 @@
576 Traceback (most recent call last):576 Traceback (most recent call last):
577 ...577 ...
578 Fault: <Fault 300: 'The username or password you entered is not valid.'>578 Fault: <Fault 300: 'The username or password you entered is not valid.'>
579
580
581Getting the current time
582------------------------
583
584The Bugzilla.time() method allows us to retrieve a dict of the time on
585the remote server.
586
587 >>> time_dict = server.Bugzilla.time()
588 >>> for key in sorted(time_dict):
589 ... print "%s: %s" % (key, time_dict[key])
590 db_time: 20080501T01:01:01
591 tz_name: UTC
592 tz_offset: +0000
593 tz_short_name: UTC
594 web_time: 20080501T01:01:01
595 web_time_utc: 20080501T01:01:01
596
597If the remote server is in a different timezone, the db_time and
598web_time items will be in the server's local timezone whilst
599web_time_utc will be in UTC.
600
601 >>> bugzilla_transport.utc_offset = 60**2
602 >>> bugzilla_transport.timezone = 'CET'
603 >>> time_dict = server.Bugzilla.time()
604 >>> for key in sorted(time_dict):
605 ... print "%s: %s" % (key, time_dict[key])
606 db_time: 20080501T01:01:01
607 tz_name: CET
608 tz_offset: +0100
609 tz_short_name: CET
610 web_time: 20080501T01:01:01
611 web_time_utc: 20080501T00:01:01
579612
=== modified file 'lib/lp/bugs/tests/externalbugtracker.py'
--- lib/lp/bugs/tests/externalbugtracker.py 2009-08-18 16:41:18 +0000
+++ lib/lp/bugs/tests/externalbugtracker.py 2009-08-20 15:43:37 +0000
@@ -522,7 +522,9 @@
522 # what BugZilla will return.522 # what BugZilla will return.
523 local_time = xmlrpclib.DateTime(local_datetime.timetuple())523 local_time = xmlrpclib.DateTime(local_datetime.timetuple())
524524
525 utc_date_time = local_datetime - timedelta(seconds=self.utc_offset)525 utc_offset_delta = timedelta(seconds=self.utc_offset)
526 utc_date_time = local_datetime - utc_offset_delta
527
526 utc_time = xmlrpclib.DateTime(utc_date_time.timetuple())528 utc_time = xmlrpclib.DateTime(utc_date_time.timetuple())
527 return {529 return {
528 'local_time': local_time,530 'local_time': local_time,
@@ -757,7 +759,10 @@
757759
758 # Map namespaces onto method names.760 # Map namespaces onto method names.
759 methods = {761 methods = {
760 'Bugzilla': ['version'],762 'Bugzilla': [
763 'time',
764 'version',
765 ],
761 'Test': ['login_required'],766 'Test': ['login_required'],
762 'User': ['login'],767 'User': ['login'],
763 }768 }
@@ -794,6 +799,24 @@
794 300,799 300,
795 "The username or password you entered is not valid.")800 "The username or password you entered is not valid.")
796801
802 def time(self):
803 """Return a dict of the local time and associated data."""
804 # We cheat slightly by calling the superclass to get the time
805 # data. We do this the old fashioned way because XML-RPC
806 # Transports don't support new-style classes.
807 time_dict = TestBugzillaXMLRPCTransport.time(self)
808 offset_hours = (self.utc_offset / 60) / 60
809 offset_string = '+%02d00' % offset_hours
810
811 return {
812 'db_time': time_dict['local_time'],
813 'tz_name': time_dict['tz_name'],
814 'tz_offset': offset_string,
815 'tz_short_name': time_dict['tz_name'],
816 'web_time': time_dict['local_time'],
817 'web_time_utc': time_dict['utc_time'],
818 }
819
797820
798class TestMantis(Mantis):821class TestMantis(Mantis):
799 """Mantis ExternalSystem for use in tests.822 """Mantis ExternalSystem for use in tests.