Merge branch 'master' of stigma:platforma
[redakcja.git] / apps / api / handlers / manage_handlers.py
index b3e2760..b94ca72 100644 (file)
@@ -1,20 +1,25 @@
 # -*- encoding: utf-8 -*-
 
 # -*- encoding: utf-8 -*-
 
+import logging
+log = logging.getLogger('platforma.api.manage')
+
 __author__= "Łukasz Rekucki"
 __date__ = "$2009-09-25 15:49:50$"
 __doc__ = "Module documentation."
 
 __author__= "Łukasz Rekucki"
 __date__ = "$2009-09-25 15:49:50$"
 __doc__ = "Module documentation."
 
-from piston.handler import BaseHandler, AnonymousBaseHandler
+from piston.handler import BaseHandler
+from wlrepo import UpdateException
 
 from api.utils import hglibrary
 
 from api.utils import hglibrary
-from explorer.models import PullRequest
+from api.models import PullRequest
 from api.response import *
 from api.response import *
+import datetime
 
 class PullRequestListHandler(BaseHandler):
     allowed_methods = ('GET',)
 
     def read(self, request):
 
 class PullRequestListHandler(BaseHandler):
     allowed_methods = ('GET',)
 
     def read(self, request):
-        if request.user.has_perm('explorer.book.can_share'):
+        if request.user.has_perm('change_pullrequest'):
             return PullRequest.objects.all()
         else:
             return PullRequest.objects.filter(commiter=request.user)
             return PullRequest.objects.all()
         else:
             return PullRequest.objects.filter(commiter=request.user)
@@ -29,7 +34,7 @@ class PullRequestHandler(BaseHandler):
     def update(self, request, prq_id):
         """Change the status of request"""
 
     def update(self, request, prq_id):
         """Change the status of request"""
 
-        if not request.user.has_perm('explorer.document.can_share'):
+        if not request.user.has_perm('change_pullrequest'):
             return AccessDenied().django_response("Insufficient priviliges")
         
         prq = PullRequest.objects.get(id=prq_id)
             return AccessDenied().django_response("Insufficient priviliges")
         
         prq = PullRequest.objects.get(id=prq_id)
@@ -37,36 +42,84 @@ class PullRequestHandler(BaseHandler):
         if not prq:
             return EntityNotFound().django_response()
 
         if not prq:
             return EntityNotFound().django_response()
 
-
         action = request.PUT.get('action', None)
 
         action = request.PUT.get('action', None)
 
-        if action == 'accept' and prq.status == 'N':
-            return self.accept_merge(prq)
+        if action == 'accept':
+            return self.accept_merge(request.user, prq)
         elif action == 'reject' and prq.status in ['N', 'R']:
         elif action == 'reject' and prq.status in ['N', 'R']:
-            return self.reject_merge(prq)
+            return self.reject_merge(request.user, prq)
         else:
             return BadRequest().django_response()
 
 
     @hglibrary
         else:
             return BadRequest().django_response()
 
 
     @hglibrary
-    def accept_merge(self, prq, lib):        
-        doc = lib.document( prq.document )
-        udoc = doc.take( prq.comitter.username )
-        success, changed = udoc.share(prq.comment)
-
-        if not success:
-            return EntityConflict().django_response()
-
-        doc = doc.latest()
-
-        prq.status = 'A'
-        prq.merged_revisions = unicode(doc.revision)
-        prq.save()
-        
-        return SuccessAllOk().django_response({
-            'status': prq.status
-        })
-
+    def accept_merge(self, user, prq, lib):
+        if prq.status not in ['N', 'E']:
+            return BadRequest().django_response({
+                'reason': 'invalid-state',
+                'message': "This pull request is alredy resolved. Can't accept."
+            })
+            
+        src_doc = lib.document_for_rev( prq.source_revision )
+
+        lock = lib.lock()
+        try:
+            if not src_doc.up_to_date():
+                # This revision is no longer up to date, thus
+                # it needs to be updated, before push:
+                #
+                #  Q: where to put the updated revision ?
+                #  A: create a special user branch named prq-#prqid
+                prq_doc = src_doc.take("$prq-%d" % prq.id)
+
+                # This could be not the first time we try this,
+                # so the prq_doc could already be there
+                # and up to date
+
+                try:
+                    prq_doc = prq_doc.update(user.username)
+                    prq.source_revision = str(prq_doc.revision)
+                    src_doc = prq_doc
+                except UpdateException, e:
+                    # this can happen only if the merge program
+                    # is misconfigured - try returning an entity conflict
+                    # TODO: put some useful infor here
+                    prq.status = 'E'
+                    prq.save()
+                    return EntityConflict().django_response({
+                        'reason': 'update-failed',
+                        'message': e.message })                
+
+            # check if there are conflicts
+            if src_doc.has_conflict_marks():
+                prq.status = 'E'
+                prq.save()
+                # Now, the user must resolve the conflict
+                return EntityConflict().django_response({
+                    "reason": "unresolved-conflicts",
+                    "message": "There are conflict in the document. Resolve the conflicts retry accepting."
+                })
+
+            # So, we have an up-to-date, non-conflicting document
+            changed = src_doc.share(prq.comment)
+
+            if not changed:
+                # this is actually very bad, but not critical
+                log.error("Unsynched pull request: %d" % prq.id)                
+
+            # sync state with repository
+            prq.status = 'A'
+            prq.merged_revision = str(src_doc.shared().revision)
+            prq.merged_timestamp = datetime.now()
+            prq.save()
+
+            return SuccessAllOk().django_response({
+                'status': prq.status,
+                'merged_into': prq.merged_revision,
+                'merged_at': prq.merged_timestamp
+            })
+        finally:
+            lock.release()
     
     def reject_merge(self, prq, lib):
         prq.status = 'R'
     
     def reject_merge(self, prq, lib):
         prq.status = 'R'