Revision 96efed67

b/snf-astakos-app/astakos/im/models.py
2091 2091

  
2092 2092
    ### Deactivation calls
2093 2093

  
2094
    def unset_modified(self):
2095
        self.is_modified = False
2096
        self.save()
2097

  
2094 2098
    def deactivate(self):
2095 2099
        self.deactivation_date = datetime.now()
2096 2100
        self.is_active = False
2101
        self.save()
2097 2102

  
2098 2103
    def reactivate(self):
2099 2104
        self.deactivation_date = None
2100 2105
        self.is_active = True
2106
        self.save()
2101 2107

  
2102 2108
    def terminate(self):
2103 2109
        self.deactivation_reason = 'TERMINATED'
......
2233 2239
class ProjectMembershipManager(ForUpdateManager):
2234 2240

  
2235 2241
    def any_accepted(self):
2236
        q = (Q(state=ProjectMembership.ACCEPTED) |
2237
             Q(state=ProjectMembership.PROJECT_DEACTIVATED))
2242
        q = self.model.Q_ACTUALLY_ACCEPTED
2238 2243
        return self.filter(q)
2239 2244

  
2240 2245
    def actually_accepted(self):
......
2258 2263
    LEAVE_REQUESTED     =   5
2259 2264
    # User deactivation
2260 2265
    USER_SUSPENDED      =   10
2261
    # Project deactivation
2262
    PROJECT_DEACTIVATED =   100
2263 2266

  
2264 2267
    REMOVED             =   200
2265 2268

  
......
2267 2270
                                 ACCEPTED,
2268 2271
                                 LEAVE_REQUESTED,
2269 2272
                                 USER_SUSPENDED,
2270
                                 PROJECT_DEACTIVATED])
2273
                                 ])
2271 2274

  
2272 2275
    ACCEPTED_STATES     =   set([ACCEPTED,
2273 2276
                                 LEAVE_REQUESTED,
2274 2277
                                 USER_SUSPENDED,
2275
                                 PROJECT_DEACTIVATED])
2278
                                 ])
2276 2279

  
2277 2280
    ACTUALLY_ACCEPTED   =   set([ACCEPTED, LEAVE_REQUESTED])
2278 2281

  
......
2304 2307
        ACCEPTED            : _('Accepted'),
2305 2308
        LEAVE_REQUESTED     : _('Leave Requested'),
2306 2309
        USER_SUSPENDED      : _('Suspended'),
2307
        PROJECT_DEACTIVATED : _('Accepted'), # sic
2308 2310
        REMOVED             : _('Pending removal'),
2309 2311
        }
2310 2312

  
......
2313 2315
        ACCEPTED            : _('Accepted member'),
2314 2316
        LEAVE_REQUESTED     : _('Requested to leave'),
2315 2317
        USER_SUSPENDED      : _('Suspended member'),
2316
        PROJECT_DEACTIVATED : _('Accepted member'), # sic
2317 2318
        REMOVED             : _('Pending removal'),
2318 2319
        }
2319 2320

  
......
2368 2369
        now = datetime.now()
2369 2370
        self.acceptance_date = now
2370 2371
        self._set_history_item(reason='ACCEPT', date=now)
2371
        if self.project.is_approved():
2372
            self.state = self.ACCEPTED
2373
            self.is_pending = True
2374
        else:
2375
            self.state = self.PROJECT_DEACTIVATED
2376

  
2372
        self.state = self.ACCEPTED
2373
        self.is_pending = True
2377 2374
        self.save()
2378 2375

  
2379 2376
    def can_leave(self):
......
2515 2512

  
2516 2513
        return (sub_list, add_list)
2517 2514

  
2515
    def is_fully_applied(self, project=None):
2516
        if project is None:
2517
            project = self.project
2518
        if project.is_deactivated():
2519
            return self.application is None
2520
        else:
2521
            return self.application_id == project.application_id
2522

  
2518 2523
    def set_sync(self):
2519 2524
        if not self.is_pending:
2520 2525
            m = _("%s: attempt to sync a non pending membership") % (self,)
......
2523 2528
        state = self.state
2524 2529
        if state in self.ACTUALLY_ACCEPTED:
2525 2530
            pending_application = self.pending_application
2526
            if pending_application is None:
2527
                m = _("%s: attempt to sync an empty pending application") % (
2528
                    self,)
2529
                raise AssertionError(m)
2530 2531

  
2531 2532
            self.application = pending_application
2532
            self.is_active = True
2533
            self.is_active = (self.application is not None)
2533 2534

  
2534 2535
            self.pending_application = None
2535 2536
            self.pending_serial = None
......
2537 2538
            # project.application may have changed in the meantime,
2538 2539
            # in which case we stay PENDING;
2539 2540
            # we are safe to check due to select_for_update
2540
            if self.application == self.project.application:
2541
                self.is_pending = False
2542
            self.save()
2543

  
2544
        elif state == self.PROJECT_DEACTIVATED:
2545
            if self.pending_application:
2546
                m = _("%s: attempt to sync in state '%s' "
2547
                      "with a pending application") % (self, state)
2548
                raise AssertionError(m)
2549

  
2550
            self.application = None
2551
            self.is_active = False
2552
            self.pending_serial = None
2553
            self.is_pending = False
2541
            self.is_pending = not self.is_fully_applied()
2554 2542
            self.save()
2555 2543

  
2556 2544
        elif state == self.REMOVED:
......
2566 2554
            raise AssertionError(m)
2567 2555

  
2568 2556
        state = self.state
2569
        if state in [self.ACCEPTED, self.LEAVE_REQUESTED,
2570
                     self.PROJECT_DEACTIVATED, self.REMOVED]:
2557
        if state in [self.ACCEPTED, self.LEAVE_REQUESTED, self.REMOVED]:
2571 2558
            self.pending_application = None
2572 2559
            self.pending_serial = None
2573 2560
            self.save()
......
2620 2607
    qh_ack_serials(list(serials_to_ack))
2621 2608
    return len(memberships)
2622 2609

  
2610
def _pre_sync_projects(projects):
2611
    for project in projects:
2612
        objects = project.projectmembership_set
2613
        memberships = objects.actually_accepted().select_for_update()
2614
        for membership in memberships:
2615
            if not membership.is_fully_applied(project):
2616
                membership.is_pending = True
2617
                membership.save()
2618

  
2623 2619
def pre_sync_projects(sync=True):
2624
    ACCEPTED = ProjectMembership.ACCEPTED
2625
    LEAVE_REQUESTED = ProjectMembership.LEAVE_REQUESTED
2626
    PROJECT_DEACTIVATED = ProjectMembership.PROJECT_DEACTIVATED
2627 2620
    objs = Project.objects
2628 2621

  
2629 2622
    modified = list(objs.modified_projects().select_for_update())
2630
    if sync:
2631
        for project in modified:
2632
            objects = project.projectmembership_set
2633

  
2634
            memberships = objects.actually_accepted().select_for_update()
2635
            for membership in memberships:
2636
                membership.is_pending = True
2637
                membership.save()
2638

  
2639 2623
    reactivating = list(objs.reactivating_projects().select_for_update())
2640
    if sync:
2641
        for project in reactivating:
2642
            objects = project.projectmembership_set
2643

  
2644
            q = objects.filter(state=PROJECT_DEACTIVATED)
2645
            memberships = q.select_for_update()
2646
            for membership in memberships:
2647
                membership.is_pending = True
2648
                if membership.leave_request_date is None:
2649
                    membership.state = ACCEPTED
2650
                else:
2651
                    membership.state = LEAVE_REQUESTED
2652
                membership.save()
2653

  
2654 2624
    deactivating = list(objs.deactivating_projects().select_for_update())
2655
    if sync:
2656
        for project in deactivating:
2657
            objects = project.projectmembership_set
2658 2625

  
2659
            # Note: we keep a user-level deactivation
2660
            # (e.g. USER_SUSPENDED) intact
2661
            memberships = objects.actually_accepted().select_for_update()
2662
            for membership in memberships:
2663
                membership.is_pending = True
2664
                membership.state = PROJECT_DEACTIVATED
2665
                membership.save()
2626
    if sync:
2627
        _pre_sync_projects(modified)
2628
        _pre_sync_projects(reactivating)
2629
        _pre_sync_projects(deactivating)
2666 2630

  
2667 2631
#    transaction.commit()
2668 2632
    return (modified, reactivating, deactivating)
......
2694 2658
                logger.warning("Excluded from sync: %s" % uuid)
2695 2659
                continue
2696 2660

  
2697
        if membership.state in ACTUALLY_ACCEPTED:
2698
            membership.pending_application = membership.project.application
2661
        project = membership.project
2662
        if (membership.state in ACTUALLY_ACCEPTED and
2663
            not project.is_deactivated()):
2664
            membership.pending_application = project.application
2699 2665

  
2700 2666
        membership.pending_serial = serial
2701 2667
        membership.get_diff_quotas(sub_quota, add_quota)
......
2726 2692
    logger.error("Failed: %s" % r)
2727 2693
    raise SyncError(m)
2728 2694

  
2695
def _post_sync_projects(projects, action):
2696
    for project in projects:
2697
        objects = project.projectmembership_set
2698
        memberships = objects.actually_accepted().select_for_update()
2699
        for membership in memberships:
2700
            if not membership.is_fully_applied(project):
2701
                break
2702
        else:
2703
            action(project)
2704

  
2729 2705
def post_sync_projects():
2730
    PROJECT_DEACTIVATED = ProjectMembership.PROJECT_DEACTIVATED
2731
    Q_ACTUALLY_ACCEPTED = ProjectMembership.Q_ACTUALLY_ACCEPTED
2732 2706
    objs = Project.objects
2733 2707

  
2734 2708
    modified = objs.modified_projects().select_for_update()
2735
    for project in modified:
2736
        objects = project.projectmembership_set
2737
        q = objects.filter(Q_ACTUALLY_ACCEPTED & Q(is_pending=True))
2738
        memberships = list(q.select_for_update())
2739
        if not memberships:
2740
            project.is_modified = False
2741
            project.save()
2709
    _post_sync_projects(modified, lambda p: p.unset_modified())
2742 2710

  
2743 2711
    reactivating = objs.reactivating_projects().select_for_update()
2744
    for project in reactivating:
2745
        objects = project.projectmembership_set
2746
        q = objects.filter(Q(state=PROJECT_DEACTIVATED) | Q(is_pending=True))
2747
        memberships = list(q.select_for_update())
2748
        if not memberships:
2749
            project.reactivate()
2750
            project.save()
2712
    _post_sync_projects(reactivating, lambda p: p.reactivate())
2751 2713

  
2752 2714
    deactivating = objs.deactivating_projects().select_for_update()
2753
    for project in deactivating:
2754
        objects = project.projectmembership_set
2755
        q = objects.filter(Q_ACTUALLY_ACCEPTED | Q(is_pending=True))
2756
        memberships = list(q.select_for_update())
2757
        if not memberships:
2758
            project.deactivate()
2759
            project.save()
2715
    _post_sync_projects(deactivating, lambda p: p.deactivate())
2760 2716

  
2761 2717
    transaction.commit()
2762 2718

  

Also available in: Unified diff