[CalendarServer-changes] [15073] CalendarServer/branches/release/CalendarServer-5.4-dev/ calendarserver/tools

source_changes at macosforge.org source_changes at macosforge.org
Thu Aug 27 12:07:18 PDT 2015


Revision: 15073
          http://trac.calendarserver.org//changeset/15073
Author:   cdaboo at apple.com
Date:     2015-08-27 12:07:18 -0700 (Thu, 27 Aug 2015)
Log Message:
-----------
Add missing-location fix option. Add unit tests.

Modified Paths:
--------------
    CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/calverify.py
    CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/test/test_calverify.py

Modified: CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/calverify.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/calverify.py	2015-08-27 19:06:30 UTC (rev 15072)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/calverify.py	2015-08-27 19:07:18 UTC (rev 15073)
@@ -2701,7 +2701,7 @@
             ot = time.time()
 
         # Check loop over uuid
-        UUIDDetails = collections.namedtuple("UUIDDetails", ("uuid", "rname", "purged",))
+        UUIDDetails = collections.namedtuple("UUIDDetails", ("uuid", "rname", "missing",))
         self.uuid_details = []
         if len(self.options["uuid"]) != 36:
             self.txn = self.store.newTransaction()
@@ -2768,16 +2768,16 @@
             if rows:
                 if not self.options["summary"]:
                     self.addSummaryBreak()
-                purged = yield self.missingLocation(rows, uuid)
+                missing = yield self.missingLocation(rows, uuid, rname)
             else:
-                purged = False
+                missing = False
 
-            self.uuid_details.append(UUIDDetails(uuid, rname, purged))
+            self.uuid_details.append(UUIDDetails(uuid, rname, missing))
 
             if not self.options["summary"]:
                 self.printSummary()
             else:
-                self.output.write(" - %s\n" % ("Bad Events" if purged else "OK",))
+                self.output.write(" - %s\n" % ("Bad Events" if missing else "OK",))
                 self.output.flush()
 
         if count == 0:
@@ -2786,7 +2786,7 @@
         if self.options["summary"]:
             table = tables.Table()
             table.addHeader(("GUID", "Name", "RID", "UID",))
-            purged = 0
+            missing = 0
             for item in sorted(self.uuid_details):
                 if not item.purged:
                     continue
@@ -2801,8 +2801,8 @@
                     ))
                     uuid = ""
                     rname = ""
-                    purged += 1
-            table.addFooter(("Total", "%d" % (purged,), "", "", "",))
+                    missing += 1
+            table.addFooter(("Total", "%d" % (missing,), "", "", "",))
             self.output.write("\n")
             table.printTable(os=self.output)
 
@@ -2811,9 +2811,9 @@
 
 
     @inlineCallbacks
-    def missingLocation(self, rows, uuid):
+    def missingLocation(self, rows, uuid, rname):
         """
-        Check each calendar resource by looking at any ORGANIER property value and verifying it is valid.
+        Check each calendar resource by looking at any ORGANIZER property value and verifying it is valid.
         """
 
         if not self.options["summary"]:
@@ -2858,10 +2858,7 @@
             if fail:
                 details.append(Details(resid, uid,))
                 if self.fix:
-                    # Add location value
-                    # TODO: locate organizer's copy of event
-                    #     Add LOCATION property to components
-                    #     Write it back to trigger scheduling
+                    yield self.fixCalendarData(cal, rname)
                     fixed += 1
 
             if self.options["verbose"] and not self.options["summary"]:
@@ -2909,7 +2906,54 @@
         returnValue(details)
 
 
+    @inlineCallbacks
+    def fixCalendarData(self, cal, rname):
+        """
+        Fix problems in calendar data using store APIs.
+        """
 
+        # Extract organizer (strip off urn:uuid:) and UID
+        organizer = cal.getOrganizer()[9:]
+        uid = cal.resourceUID()
+        _ignore_calendar, resid, _ignore_created, _ignore_modified = yield self.getCalendarForOwnerByUID(organizer, uid)
+
+        # Get the organizer's calendar object and data
+        homeID, calendarID = yield self.getAllResourceInfoForResourceID(resid)
+        home = yield self.txn.calendarHomeWithResourceID(homeID)
+        calendar = yield home.childWithID(calendarID)
+        calendarObj = yield calendar.objectResourceWithID(resid)
+
+        try:
+            component = yield calendarObj.componentForUser()
+        except InternalDataStoreError:
+            returnValue((False, "Failed parse: "))
+
+        # Add missing location to all components (need to dup component when modifying)
+        component = component.duplicate()
+        for comp in component.subcomponents():
+            if comp.name() != "VEVENT":
+                continue
+            location = comp.propertyValue("LOCATION")
+            if location is None:
+                comp.addProperty(Property("LOCATION", rname))
+
+        # Write out fix, commit and get a new transaction
+        result = True
+        message = ""
+        try:
+            yield calendarObj.setComponent(component)
+        except Exception, e:
+            print(e, component)
+            print(traceback.print_exc())
+            result = False
+            message = "Exception fix: "
+        yield self.txn.commit()
+        self.txn = self.store.newTransaction()
+
+        returnValue((result, message,))
+
+
+
 class EventSplitService(CalVerifyService):
     """
     Service which splits a recurring event at a specific date-time value.

Modified: CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/test/test_calverify.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/test/test_calverify.py	2015-08-27 19:06:30 UTC (rev 15072)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/calendarserver/tools/test/test_calverify.py	2015-08-27 19:07:18 UTC (rev 15073)
@@ -21,12 +21,12 @@
 
 from calendarserver.tools.calverify import BadDataService, \
     SchedulingMismatchService, DoubleBookingService, DarkPurgeService, \
-    EventSplitService
+    EventSplitService, MissingLocationService
 
 from pycalendar.datetime import PyCalendarDateTime
 
 from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, Deferred
 
 from twistedcaldav.config import config
 from twistedcaldav.ical import normalize_iCalStr
@@ -513,7 +513,7 @@
         """
 
         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": True,
@@ -557,7 +557,7 @@
         """
 
         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": True,
@@ -629,7 +629,7 @@
         """
 
         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -669,7 +669,7 @@
         """
 
         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -1420,7 +1420,7 @@
         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
         sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
         sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -1487,7 +1487,7 @@
         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
         sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
         sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -1578,7 +1578,7 @@
         self.assertNotEqual(sync_token_old3, sync_token_new3)
 
         # Re-scan after changes to make sure there are no errors
-        self.commit()
+        yield self.commit()
         options["fix"] = False
         calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
         yield calverify.doAction()
@@ -1696,7 +1696,7 @@
 
         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -1747,7 +1747,7 @@
 
         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -1805,7 +1805,7 @@
         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
 
         # Re-scan after changes to make sure there are no errors
-        self.commit()
+        yield self.commit()
         options["fix"] = False
         calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
         yield calverify.doAction()
@@ -1923,7 +1923,7 @@
 
         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -1972,7 +1972,7 @@
 
         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -2023,7 +2023,7 @@
         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
 
         # Re-scan after changes to make sure there are no errors
-        self.commit()
+        yield self.commit()
         options["fix"] = False
         options["uuid"] = CalVerifyMismatchTestsBase.uuidl1
         calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2432,7 +2432,7 @@
 
         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -2594,7 +2594,7 @@
         """
 
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -2641,7 +2641,7 @@
         """
 
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -2680,7 +2680,7 @@
         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
 
         # Re-scan after changes to make sure there are no errors
-        self.commit()
+        yield self.commit()
         options["fix"] = False
         options["uuid"] = self.uuidl1
         calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2700,7 +2700,7 @@
         """
 
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -2739,7 +2739,7 @@
         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
 
         # Re-scan after changes to make sure there are no errors
-        self.commit()
+        yield self.commit()
         options["fix"] = False
         options["uuid"] = self.uuidl1
         calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
@@ -2759,7 +2759,7 @@
         """
 
         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
-        self.commit()
+        yield self.commit()
 
         options = {
             "ical": False,
@@ -2798,7 +2798,7 @@
         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
 
         # Re-scan after changes to make sure there are no errors
-        self.commit()
+        yield self.commit()
         options["fix"] = False
         options["uuid"] = self.uuidl1
         calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
@@ -3127,3 +3127,218 @@
         self.assertTrue("%(now_fwd10)s" % self.subs in result)
         self.assertTrue("%(now_fwd11)s *" % self.subs in result)
         self.assertTrue("%(now_fwd12)s" % self.subs in result)
+
+
+
+class CalVerifyMissingLocations(CalVerifyMismatchTestsBase):
+    """
+    Tests calverify for events.
+    """
+
+    subs = {
+        "year": nowYear,
+        "month": nowMonth,
+        "uuid1": CalVerifyMismatchTestsBase.uuid1,
+        "uuid2": CalVerifyMismatchTestsBase.uuid2,
+        "uuid3": CalVerifyMismatchTestsBase.uuid3,
+        "uuidl1": CalVerifyMismatchTestsBase.uuidl1,
+    }
+
+    # Valid event
+    VALID_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:VALID_ICS
+TRANSP:OPAQUE
+SUMMARY:VALID_ICS
+DTSTART:%(year)s%(month)02d08T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:urn:uuid:%(uuid1)s
+ATTENDEE:urn:uuid:%(uuid1)s
+ATTENDEE:urn:uuid:%(uuid2)s
+ATTENDEE:urn:uuid:%(uuidl1)s
+LOCATION:Location 1
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % subs
+
+    # Invalid event
+    INVALID_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:INVALID_ICS
+TRANSP:OPAQUE
+SUMMARY:INVALID_ICS
+DTSTART:%(year)s%(month)02d08T120000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:urn:uuid:%(uuid1)s
+ATTENDEE:urn:uuid:%(uuid1)s
+ATTENDEE:urn:uuid:%(uuid2)s
+ATTENDEE:urn:uuid:%(uuidl1)s
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % subs
+
+    allEvents = {
+        "invite1.ics"      : (VALID_ICS, CalVerifyMismatchTestsBase.metadata,),
+        "invite2.ics"      : (INVALID_ICS, CalVerifyMismatchTestsBase.metadata,),
+    }
+
+    requirements = {
+        CalVerifyMismatchTestsBase.uuid1 : {
+            "calendar" : allEvents,
+            "inbox" : {},
+        },
+        CalVerifyMismatchTestsBase.uuid2 : {
+            "calendar" : allEvents,
+            "inbox" : {},
+        },
+        CalVerifyMismatchTestsBase.uuid3 : {
+            "calendar" : {},
+            "inbox" : {},
+        },
+        CalVerifyMismatchTestsBase.uuidl1 : {
+            "calendar" : allEvents,
+            "inbox" : {},
+        },
+    }
+
+    @inlineCallbacks
+    def test_scanMissingLocations(self):
+        """
+        MissingLocationService.doAction without fix for missing locations. Make sure it detects
+        as much as it can. Make sure sync-token is not changed.
+        """
+
+        sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
+        yield self.commit()
+
+        options = {
+            "ical": False,
+            "badcua": False,
+            "mismatch": False,
+            "nobase64": False,
+            "double": False,
+            "dark-purge": False,
+            "missing-location": True,
+            "fix": False,
+            "verbose": False,
+            "details": False,
+            "summary": False,
+            "days": 365,
+            "uid": "",
+            "uuid": self.uuidl1,
+            "tzid": "utc",
+            "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+            "no-organizer": False,
+            "invalid-organizer": False,
+            "disabled-organizer": False,
+        }
+        output = StringIO()
+        calverify = MissingLocationService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+        self.assertEqual(
+            sorted([i.uid for i in calverify.results["Bad Events"]]),
+            ["INVALID_ICS", ]
+        )
+        self.assertEqual(calverify.results["Number of bad events"], 1)
+        self.assertTrue("Fix bad events" not in calverify.results)
+
+        sync_token_newl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
+        self.assertEqual(sync_token_oldl1, sync_token_newl1)
+
+
+    @inlineCallbacks
+    def test_fixMissingLocations(self):
+        """
+        MissingLocationService.doAction with fix for missing locations. Make sure it detects
+        as much as it can. Make sure sync-token is changed.
+        """
+
+        self.patch(config.Scheduling.Options, "AttendeeRefreshBatchDelaySeconds", 0)
+
+        # Make sure location is in each users event
+        for uid in (self.uuid1, self.uuid2, self.uuidl1,):
+            calobj = yield self.calendarObjectUnderTest(home=uid, calendar_name="calendar", name="invite2.ics")
+            caldata = yield calobj.componentForUser()
+            self.assertTrue("LOCATION:" not in str(caldata))
+        yield self.commit()
+
+        sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
+        yield self.commit()
+
+        options = {
+            "ical": False,
+            "badcua": False,
+            "mismatch": False,
+            "nobase64": False,
+            "double": False,
+            "dark-purge": False,
+            "missing-location": True,
+            "fix": True,
+            "verbose": False,
+            "details": False,
+            "summary": False,
+            "days": 365,
+            "uid": "",
+            "uuid": self.uuidl1,
+            "tzid": "utc",
+            "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+            "no-organizer": False,
+            "invalid-organizer": False,
+            "disabled-organizer": False,
+        }
+        output = StringIO()
+        calverify = MissingLocationService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+        self.assertEqual(
+            sorted([i.uid for i in calverify.results["Bad Events"]]),
+            ["INVALID_ICS", ]
+        )
+        self.assertEqual(calverify.results["Number of bad events"], 1)
+        self.assertEqual(calverify.results["Fix bad events"], 1)
+
+        sync_token_newl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
+        self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
+        yield self.commit()
+
+        # Need to wait for organizer inbox item to appear to verify that auto-accept process call has completed
+        cobjs = []
+        while len(cobjs) != 1:
+            d = Deferred()
+            reactor.callLater(1.0, lambda: d.callback(None))
+            yield d
+            calendar = yield self.calendarUnderTest(home=self.uuid1, name="inbox")
+            cobjs = yield calendar.calendarObjects()
+            yield self.commit()
+
+        # Re-scan after changes to make sure there are no errors
+        options["fix"] = False
+        options["uuid"] = self.uuidl1
+        calverify = MissingLocationService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+        self.assertEqual(len(calverify.results["Bad Events"]), 0)
+        self.assertTrue("Fix bad events" not in calverify.results)
+
+        # Make sure location is in each users event
+        for uid in (self.uuid1, self.uuid2, self.uuidl1,):
+            calobj = yield self.calendarObjectUnderTest(home=uid, calendar_name="calendar", name="invite2.ics")
+            caldata = yield calobj.componentForUser()
+            self.assertTrue("LOCATION:" in str(caldata))
+        yield self.commit()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150827/979f9ddb/attachment-0001.html>


More information about the calendarserver-changes mailing list