~facundo/ubuntuone-client/fix-svfilenew-conflict

« back to all changes in this revision

Viewing changes to ubuntuone/status/aggregator.py

  • Committer: Facundo Batista
  • Date: 2011-04-04 12:31:43 UTC
  • mfrom: (922.1.9 ubuntuone-client)
  • Revision ID: facundo@taniquetil.com.ar-20110404123143-mobua5abcpinz0o2
TrunkĀ mergedĀ in

Show diffs side-by-side

added added

removed removed

Lines of Context:
120
120
        "'%(filename)s' and %(other_files)d other files were "
121
121
        "downloaded to your computer.", other_files) % format_args
122
122
 
 
123
def files_were_made_public(no_of_files):
 
124
    """Get the i18n string for files that were made public."""
 
125
 
123
126
 
124
127
class ToggleableNotification(object):
125
128
    """A controller for notifications that can be turned off."""
160
163
    """An event representing a status change."""
161
164
 
162
165
    MESSAGE_ONE = None  # to be defined in child classes
163
 
    MESSAGE_MANY = None  # to be defined in child classes
164
166
    WEIGHT = 99
165
167
    DO_NOT_INSTANCE = "Do not instance this class, only children."""
166
168
 
173
175
        """A message if this is the only event of this type."""
174
176
        return self.MESSAGE_ONE
175
177
 
176
 
    def many(self, events):
177
 
        """A message if there are many events of this type."""
178
 
        format_args = {"event_count": len(events)}
179
 
        return self.MESSAGE_MANY % format_args
180
 
 
181
178
 
182
179
class FilePublishingStatus(StatusEvent):
183
180
    """Files that are made public with a url."""
184
181
 
185
182
    MESSAGE_ONE = Q_("A file was just made public at %(new_public_url)s")
186
 
    MESSAGE_MANY = Q_("%(event_count)d files were just made public")
 
183
 
187
184
    WEIGHT = 50
188
185
 
189
186
    def one(self):
190
187
        """Show the url if only one event of this type."""
191
188
        return self.MESSAGE_ONE % self.kwargs
192
189
 
 
190
    def many(self, events):
 
191
        """Show the number of files if many event of this type."""
 
192
        no_of_files = len(events)
 
193
        gettext.dngettext(
 
194
            GETTEXT_PACKAGE,
 
195
            "%(event_count)d file was just made public.",
 
196
            "%(event_count)d files were just made public.",
 
197
            no_of_files) % {'event_count': no_of_files}
 
198
 
193
199
 
194
200
class FileUnpublishingStatus(StatusEvent):
195
201
    """Files that have stopped being published."""
196
202
 
197
203
    MESSAGE_ONE = Q_("A file is no longer published")
198
 
    MESSAGE_MANY = Q_("%(event_count)d files are no longer published")
199
204
    WEIGHT = 51
200
205
 
 
206
    def many(self, events):
 
207
        """Show the number of files if many event of this type."""
 
208
        no_of_files = len(events)
 
209
        gettext.dngettext(
 
210
            GETTEXT_PACKAGE,
 
211
            "%(event_count)d file is no longer published.",
 
212
            "%(event_count)d files are no longer published.",
 
213
            no_of_files) % {'event_count': no_of_files}
 
214
 
201
215
 
202
216
class FolderAvailableStatus(StatusEvent):
203
217
    """Folders available for subscription."""
204
218
 
205
 
    MESSAGE_MANY = Q_("Found %(event_count)d new cloud folders.")
206
219
    WEIGHT = 60
207
220
 
 
221
    def many(self, events):
 
222
        """Show the number of files if many event of this type."""
 
223
        no_of_files = len(events)
 
224
        gettext.dngettext(
 
225
            GETTEXT_PACKAGE,
 
226
            "Found %(event_count)d new cloud folder.",
 
227
            "Found %(event_count)d new cloud folders.",
 
228
            no_of_files) % {'event_count': no_of_files}
 
229
 
208
230
 
209
231
class ShareAvailableStatus(FolderAvailableStatus):
210
232
    """A Share is available for subscription."""
238
260
    """The connection to the server changed status."""
239
261
 
240
262
    WEIGHT = 30
241
 
    MESSAGE_MANY = ""  # taken from the last one in the list
242
263
 
243
264
    def many(self, events):
244
265
        """Only the last message if there are many events of this type."""
422
443
 
423
444
    def __init__(self, status_aggregator, clock=reactor):
424
445
        """Initialize this instance."""
 
446
        self.connected = False
 
447
        self.files_found = False
425
448
        self.clock = clock
426
449
        self.status_aggregator = status_aggregator
427
450
        self._set_idle()
443
466
 
444
467
    def _popup(self):
445
468
        """Display the notification."""
 
469
        if not self.connected:
 
470
            return
446
471
        text = self.status_aggregator.get_discovery_message()
447
 
        self.notification.send_notification(UBUNTUONE_TITLE, text)
448
 
        self.status_aggregator.restart_progress_bubble()
449
 
        logger.debug("notification shown: %s", text)
 
472
        if text:
 
473
            self.notification.send_notification(UBUNTUONE_TITLE, text)
 
474
            logger.debug("notification shown: %s", text)
450
475
        self._change_state(FileDiscoveryUpdateState)
451
476
 
452
477
    def _update(self):
453
478
        """Update the notification."""
 
479
        if not self.connected:
 
480
            return
454
481
        text = self.status_aggregator.get_discovery_message()
455
 
        logger.debug("notification updated: %s", text)
456
 
        self.status_aggregator.restart_progress_bubble()
457
 
        self.notification.send_notification(UBUNTUONE_TITLE, text)
 
482
        if text:
 
483
            logger.debug("notification updated: %s", text)
 
484
            self.notification.send_notification(UBUNTUONE_TITLE, text)
458
485
 
459
486
    def start_sleeping(self):
460
487
        """Wait for 10 minutes before annoying again."""
464
491
        """Cleanup this instance."""
465
492
        self.state.cleanup()
466
493
 
 
494
    def connection_made(self):
 
495
        """Connection made."""
 
496
        self.connected = True
 
497
        if self.files_found:
 
498
            self._popup()
 
499
 
 
500
    def connection_lost(self):
 
501
        """Connection lost."""
 
502
        self.connected = False
 
503
 
467
504
    def new_file_found(self):
468
505
        """New files found."""
 
506
        self.files_found = True
469
507
        self.state.new_file_found()
470
508
 
471
509
 
472
 
class ProgressBubble(object):
473
 
    """Show a notification for transfer progress."""
474
 
 
475
 
    sleep_delay = 600
476
 
    notification = None
477
 
    timer = None
478
 
 
479
 
    def __init__(self, status_aggregator, clock=reactor):
480
 
        """Initialize this instance."""
481
 
        self.status_aggregator = status_aggregator
482
 
        self.clock = clock
483
 
 
484
 
    def restart(self):
485
 
        """Start running the timer."""
486
 
        self.cleanup()
487
 
        self.timer = Timer(self.sleep_delay, clock=self.clock)
488
 
        self.timer.addCallback(self._timeout)
489
 
 
490
 
    def _timeout(self, _):
491
 
        """Show the bubble."""
492
 
        self.notification = self.status_aggregator.get_notification()
493
 
        text = self.status_aggregator.get_progress_message()
494
 
        self.notification.send_notification(UBUNTUONE_TITLE, text)
495
 
        self.restart()
496
 
 
497
 
    def cleanup(self):
498
 
        """Cleanup this instance."""
499
 
        if self.timer:
500
 
            self.timer.cleanup()
501
 
 
502
 
    stop = cleanup
503
 
 
504
 
 
505
510
class ProgressBar(object):
506
511
    """Update a progressbar no more than 10 times a second."""
507
512
    pulsating = True
603
608
    """The status aggregator backend."""
604
609
 
605
610
    file_discovery_bubble = None
606
 
    progress_bubble = None
607
611
    final_status_bubble = None
608
612
 
609
613
    def __init__(self, clock=reactor):
610
614
        """Initialize this instance."""
611
615
        self.clock = clock
612
616
        self.notification_switch = NotificationSwitch()
 
617
        self.queue_done_timer = None
613
618
        self.reset()
614
619
        self.progress_bar = ProgressBar(clock=self.clock)
 
620
        self.finished_delay = 10
615
621
 
616
622
    def get_notification(self):
617
623
        """Create a new toggleable notification object."""
630
636
        self.uploading_filename = ''
631
637
        self.files_downloading = []
632
638
        self.downloading_filename = ''
 
639
        if self.queue_done_timer is not None:
 
640
            self.queue_done_timer.cleanup()
633
641
 
634
642
        if self.file_discovery_bubble:
635
643
            self.file_discovery_bubble.cleanup()
636
644
        self.file_discovery_bubble = FileDiscoveryBubble(self,
637
645
                                                         clock=self.clock)
638
646
 
639
 
        if self.progress_bubble:
640
 
            self.progress_bubble.cleanup()
641
 
        self.progress_bubble = ProgressBubble(self, clock=self.clock)
642
 
 
643
647
        if self.final_status_bubble:
644
648
            self.final_status_bubble.cleanup()
645
649
        self.final_status_bubble = FinalStatusBubble(self)
660
664
                self.downloading_filename, files_downloading))
661
665
        return "\n".join(lines)
662
666
 
663
 
    def get_progress_message(self):
664
 
        """Get some lines describing the progress."""
665
 
        assert self.total_counter > 0
666
 
        parts = []
667
 
        upload_total = self.upload_total
668
 
        if upload_total:
669
 
            parts.append(files_being_uploaded(
670
 
                self.uploading_filename, upload_total))
671
 
        download_total = self.download_total
672
 
        if download_total:
673
 
            parts.append(files_being_downloaded(
674
 
                self.downloading_filename, download_total))
675
 
        progress_percentage = 100.0 * self.done_counter / self.total_counter
676
 
        format_args = {"percentage_completed": int(progress_percentage)}
677
 
        parts.append(PROGRESS_COMPLETED % format_args)
678
 
        return "\n".join(parts)
679
 
 
680
667
    def get_final_status_message(self):
681
668
        """Get some lines describing all we did."""
682
669
        parts = []
692
679
                self.downloading_filename, download_done))
693
680
        return "\n".join(parts)
694
681
 
695
 
    def restart_progress_bubble(self):
696
 
        """Restart the progress bubble."""
697
 
        self.progress_bubble.restart()
698
 
 
699
 
    def queue_done(self):
 
682
    def _queue_done(self, _):
700
683
        """Show final bubble and reset counters."""
 
684
        self.queue_done_timer.cleanup()
 
685
        self.queue_done_timer = None
 
686
        logger.debug("queue done callback fired")
701
687
        if self.upload_done + self.download_done > 0:
702
688
            self.final_status_bubble.show()
703
689
        self.progress_bar.completed()
704
690
        self.reset()
705
691
 
706
 
    def misc_command_queued(self, command):
707
 
        """A miscellaneous command was queued."""
708
 
        self.total_counter += 1
709
 
        logger.debug("queueing command (%d/%d): %s", self.done_counter,
710
 
                     self.total_counter, command.__class__.__name__)
711
 
        self.update_progressbar()
 
692
    def queue_done(self):
 
693
        """Queue is finished."""
 
694
        if not self.total_counter:
 
695
            return
 
696
        if self.queue_done_timer is None:
 
697
            logger.debug("queue done callback added")
 
698
            self.queue_done_timer = Timer(
 
699
                self.finished_delay, clock=self.clock)
 
700
            self.queue_done_timer.addCallback(self._queue_done)
 
701
            return
 
702
        logger.debug("queue done callback reset")
 
703
        self.queue_done_timer.reset()
712
704
 
713
705
    def update_progressbar(self):
714
706
        """Update the counters of the progressbar."""
715
707
        self.progress_bar.progress_made(self.done_counter, self.total_counter)
716
708
 
717
 
    def misc_command_unqueued(self, command):
718
 
        """A miscellaneous command was unqueued."""
719
 
        self.done_counter += 1
720
 
        logger.debug("unqueueing command (%d/%d): %s", self.done_counter,
721
 
                     self.total_counter, command.__class__.__name__)
722
 
        self.update_progressbar()
723
 
 
724
709
    def download_started(self, command):
725
710
        """A download just started."""
 
711
        if self.queue_done_timer is not None:
 
712
            self.queue_done_timer.reset()
726
713
        self.files_downloading.append(command)
727
714
        self.download_total += 1
 
715
        self.total_counter += 1
728
716
        # pylint: disable=W0201
729
717
        if not self.downloading_filename:
730
718
            self.downloading_filename = self.files_downloading[0].path.split(
731
719
                os.path.sep)[-1]
732
720
        # pylint: enable=W0201
733
 
        self.misc_command_queued(command)
 
721
        self.update_progressbar()
 
722
        logger.debug("queueing command (%d/%d): %s", self.done_counter,
 
723
                     self.total_counter, command.__class__.__name__)
734
724
        self.file_discovery_bubble.new_file_found()
735
725
 
736
726
    def download_finished(self, command):
738
728
        if command in self.files_downloading:
739
729
            self.files_downloading.remove(command)
740
730
        self.download_done += 1
741
 
        self.misc_command_unqueued(command)
 
731
        self.done_counter += 1
 
732
        logger.debug("unqueueing command (%d/%d): %s", self.done_counter,
 
733
                     self.total_counter, command.__class__.__name__)
 
734
        self.update_progressbar()
742
735
 
743
736
    def upload_started(self, command):
744
737
        """An upload just started."""
 
738
        if self.queue_done_timer is not None:
 
739
            self.queue_done_timer.reset()
745
740
        self.files_uploading.append(command)
746
741
        self.upload_total += 1
 
742
        self.total_counter += 1
747
743
        # pylint: disable=W0201
748
744
        if not self.uploading_filename:
749
745
            self.uploading_filename = self.files_uploading[0].path.split(
750
746
                os.path.sep)[-1]
751
747
        # pylint: enable=W0201
752
 
        self.misc_command_queued(command)
 
748
        self.update_progressbar()
 
749
        logger.debug("queueing command (%d/%d): %s", self.done_counter,
 
750
                     self.total_counter, command.__class__.__name__)
753
751
        self.file_discovery_bubble.new_file_found()
754
752
 
755
753
    def upload_finished(self, command):
757
755
        if command in self.files_uploading:
758
756
            self.files_uploading.remove(command)
759
757
        self.upload_done += 1
760
 
        self.misc_command_unqueued(command)
 
758
        self.done_counter += 1
 
759
        logger.debug("unqueueing command (%d/%d): %s", self.done_counter,
 
760
                     self.total_counter, command.__class__.__name__)
 
761
        self.update_progressbar()
761
762
 
762
763
    def connection_lost(self):
763
764
        """The connection to the server was lost."""
 
765
        self.file_discovery_bubble.connection_lost()
764
766
        self.progress_bar.show_warning_emblem()
765
767
 
766
768
    def connection_made(self):
767
769
        """The connection to the server was made."""
 
770
        self.file_discovery_bubble.connection_made()
768
771
        self.progress_bar.hide_emblem()
769
772
 
770
773
 
805
808
        """A file upload was unqueued."""
806
809
        self.aggregator.upload_finished(command)
807
810
 
808
 
    def queue_added(self, command):
809
 
        """A command was added to the queue."""
810
 
        self.aggregator.misc_command_queued(command)
811
 
 
812
 
    def queue_removed(self, command):
813
 
        """A command was removed from the queue."""
814
 
        self.aggregator.misc_command_unqueued(command)
815
 
 
816
811
    def queue_done(self):
817
812
        """The queue is empty."""
818
813
        self.aggregator.queue_done()