Ticket #1636: 0001-Journal-entry-sharing.patch
File 0001-Journal-entry-sharing.patch, 10.7 KB (added by erikos, 13 years ago) |
---|
-
src/jarabe/journal/model.py
From f6dd32244133525a0966662f1b13deef0367a412 Mon Sep 17 00:00:00 2001 From: Simon Schampijer <simon@schampijer.de> Date: Tue, 8 Feb 2011 12:18:59 -0500 Subject: [PATCH] Journal entry sharing http://wiki.sugarlabs.org/go/Features/Journal_Entry_Sharing --- src/jarabe/journal/model.py | 182 +++++++++++++++++++++++++++++++++++++++---- 1 files changed, 166 insertions(+), 16 deletions(-) diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py index 320e577..4228768 100644
a b import errno 20 20 from datetime import datetime 21 21 import time 22 22 import shutil 23 import tempfile 23 24 from stat import S_IFLNK, S_IFMT, S_IFDIR, S_IFREG 24 25 import re 25 26 from operator import itemgetter 27 import json 28 from gettext import gettext as _ 26 29 27 30 import gobject 28 31 import dbus 29 import gconf30 32 import gio 31 33 32 34 from sugar import dispatch … … PROPERTIES = ['activity', 'activity_id', 'buddies', 'bundle_id', 46 48 MIN_PAGES_TO_CACHE = 3 47 49 MAX_PAGES_TO_CACHE = 5 48 50 51 JOURNAL_METADATA_DIR = '.Sugar-Metadata' 52 49 53 _datastore = None 50 54 created = dispatch.Signal() 51 55 updated = dispatch.Signal() … … class InplaceResultSet(BaseResultSet): 295 299 files = self._file_list[offset:offset + limit] 296 300 297 301 entries = [] 298 for file_path, stat, mtime_, size_ in files: 299 metadata = _get_file_metadata(file_path, stat) 302 for file_path, stat, mtime_, metadata, size_ in files: 303 if metadata is None: 304 metadata = _get_file_metadata(file_path, stat) 300 305 metadata['mountpoint'] = self._mount_point 301 306 entries.append(metadata) 302 307 … … class InplaceResultSet(BaseResultSet): 324 329 325 330 def _scan_a_file(self): 326 331 full_path = self._pending_files.pop(0) 332 metadata = None 327 333 328 334 try: 329 335 stat = os.lstat(full_path) … … class InplaceResultSet(BaseResultSet): 365 371 366 372 if self._regex is not None and \ 367 373 not self._regex.match(full_path): 368 return 374 filename = os.path.basename(full_path) 375 dir_path = os.path.dirname(full_path) 376 metadata = _get_file_metadata_from_json( \ 377 dir_path, filename, preview=False) 378 add_to_list = False 379 if metadata is not None: 380 for f in ['fulltext', 'title', 381 'description', 'tags']: 382 if f in metadata and \ 383 self._regex.match(metadata[f]): 384 add_to_list = True 385 break 386 if not add_to_list: 387 return 369 388 370 389 if self._date_start is not None and stat.st_mtime < self._date_start: 371 390 return … … class InplaceResultSet(BaseResultSet): 378 397 if mime_type not in self._mime_types: 379 398 return 380 399 381 file_info = (full_path, stat, int(stat.st_mtime), stat.st_size) 400 file_info = (full_path, stat, int(stat.st_mtime), metadata, 401 stat.st_size) 382 402 self._file_list.append(file_info) 383 403 384 404 return … … class InplaceResultSet(BaseResultSet): 401 421 402 422 403 423 def _get_file_metadata(path, stat): 404 client = gconf.client_get_default() 424 """Returns the metadata from the corresponding file 425 on the external device or does create the metadata 426 based on the file properties. 427 428 """ 429 filename = os.path.basename(path) 430 dir_path = os.path.dirname(path) 431 metadata = _get_file_metadata_from_json(dir_path, filename, preview=True) 432 if metadata: 433 return metadata 434 405 435 return {'uid': path, 406 436 'title': os.path.basename(path), 407 437 'timestamp': stat.st_mtime, … … def _get_file_metadata(path, stat): 409 439 'mime_type': gio.content_type_guess(filename=path), 410 440 'activity': '', 411 441 'activity_id': '', 412 'icon-color': client.get_string('/desktop/sugar/user/color'),442 'icon-color': '#000000,#ffffff', 413 443 'description': path} 414 444 415 445 446 def _get_file_metadata_from_json(dir_path, filename, preview=False): 447 """Returns the metadata from the json file and the preview 448 stored on the external device. 449 450 """ 451 metadata = None 452 metadata_path = os.path.join(dir_path, JOURNAL_METADATA_DIR, 453 filename + '.metadata') 454 if os.path.exists(metadata_path): 455 try: 456 metadata = json.load(open(metadata_path)) 457 except ValueError: 458 logging.debug("Could not read metadata for file %r on" \ 459 "external device.", filename) 460 else: 461 metadata['uid'] = os.path.join(dir_path, filename) 462 if preview: 463 preview_path = os.path.join(dir_path, JOURNAL_METADATA_DIR, 464 filename + '.preview') 465 if os.path.exists(preview_path): 466 try: 467 metadata['preview'] = dbus.ByteArray(open(preview_path).read()) 468 except: 469 logging.debug("Could not read preview for file %r on" \ 470 "external device.", filename) 471 else: 472 if metadata and 'preview' in metadata: 473 del(metadata['preview']) 474 return metadata 475 476 416 477 def _get_datastore(): 417 478 global _datastore 418 479 if _datastore is None: … … def delete(object_id): 519 580 """ 520 581 if os.path.exists(object_id): 521 582 os.unlink(object_id) 583 dir_path = os.path.dirname(object_id) 584 filename = os.path.basename(object_id) 585 old_files = [os.path.join(dir_path, JOURNAL_METADATA_DIR, 586 filename + '.metadata'), 587 os.path.join(dir_path, JOURNAL_METADATA_DIR, 588 filename + '.preview')] 589 for old_file in old_files: 590 if os.path.exists(old_file): 591 try: 592 os.unlink(old_file) 593 except: 594 pass 522 595 deleted.send(None, object_id=object_id) 523 596 else: 524 597 _get_datastore().delete(object_id) … … def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): 556 629 file_path, 557 630 transfer_ownership) 558 631 else: 559 if not os.path.exists(file_path):560 raise ValueError('Entries without a file cannot be copied to ' 561 'removable devices')632 object_id = _write_entry_on_external_device(metadata, file_path) 633 634 return object_id 562 635 563 file_name = _get_file_name(metadata['title'], metadata['mime_type'])564 file_name = _get_unique_file_name(metadata['mountpoint'], file_name)565 636 637 def _write_entry_on_external_device(metadata, file_path): 638 """This creates and updates an entry copied from the 639 DS to external storage device. Besides copying the 640 associated file a hidden file for the preview and one 641 for the metadata are stored. We make sure that the 642 metadata and preview file are in the same directory 643 as the data file. 644 645 This function handles renames of an entry on the 646 external device and avoids name collisions. Renames are 647 handled failsafe. 648 649 """ 650 if 'uid' in metadata and os.path.exists(metadata['uid']): 651 file_path = metadata['uid'] 652 653 if not file_path or not os.path.exists(file_path): 654 raise ValueError('Entries without a file cannot be copied to ' 655 'removable devices') 656 657 if metadata['title'] == '': 658 metadata['title'] = _('Untitled') 659 file_name = get_file_name(metadata['title'], metadata['mime_type']) 660 661 destination_path = os.path.join(metadata['mountpoint'], file_name) 662 if destination_path != file_path: 663 file_name = get_unique_file_name(metadata['mountpoint'], file_name) 566 664 destination_path = os.path.join(metadata['mountpoint'], file_name) 665 clean_name, extension_ = os.path.splitext(file_name) 666 metadata['title'] = clean_name 667 668 metadata_copy = metadata.copy() 669 del metadata_copy['mountpoint'] 670 if 'uid' in metadata_copy: 671 del metadata_copy['uid'] 672 673 metadata_dir_path = os.path.join(metadata['mountpoint'], 674 JOURNAL_METADATA_DIR) 675 if not os.path.exists(metadata_dir_path): 676 os.mkdir(metadata_dir_path) 677 678 if 'preview' in metadata_copy: 679 preview = metadata_copy['preview'] 680 preview_fname = file_name + '.preview' 681 preview_path = os.path.join(metadata['mountpoint'], 682 JOURNAL_METADATA_DIR, preview_fname) 683 metadata_copy['preview'] = preview_fname 684 685 (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) 686 os.write(fh, preview) 687 os.close(fh) 688 os.rename(fn, preview_path) 689 690 metadata_path = os.path.join(metadata['mountpoint'], 691 JOURNAL_METADATA_DIR, 692 file_name + '.metadata') 693 (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) 694 os.write(fh, json.dumps(metadata_copy)) 695 os.close(fh) 696 os.rename(fn, metadata_path) 697 698 if os.path.dirname(destination_path) == os.path.dirname(file_path): 699 old_file_path = file_path 700 if old_file_path != destination_path: 701 os.rename(file_path, destination_path) 702 old_fname = os.path.basename(file_path) 703 old_files = [os.path.join(metadata['mountpoint'], 704 JOURNAL_METADATA_DIR, 705 old_fname + '.metadata'), 706 os.path.join(metadata['mountpoint'], 707 JOURNAL_METADATA_DIR, 708 old_fname + '.preview')] 709 for ofile in old_files: 710 if os.path.exists(ofile): 711 try: 712 os.unlink(ofile) 713 except: 714 pass 715 else: 567 716 shutil.copy(file_path, destination_path) 568 object_id = destination_path 569 created.send(None, object_id=object_id) 717 718 object_id = destination_path 719 created.send(None, object_id=object_id) 570 720 571 721 return object_id 572 722 573 723 574 def _get_file_name(title, mime_type):724 def get_file_name(title, mime_type): 575 725 file_name = title 576 726 577 727 extension = mime.get_primary_extension(mime_type) … … def _get_file_name(title, mime_type): 596 746 return file_name 597 747 598 748 599 def _get_unique_file_name(mount_point, file_name):749 def get_unique_file_name(mount_point, file_name): 600 750 if os.path.exists(os.path.join(mount_point, file_name)): 601 751 i = 1 602 752 name, extension = os.path.splitext(file_name)