david / django-portablecontacts (http://portablecontacts.net/)
Support for PortableContacts in Django.
Clone this repository (size: 65.7 KB): HTTPS / SSH
$ hg clone http://code.welldev.org/django-portablecontacts
| commit 3: | c314c0dd2388 |
| parent 2: | d759edee4b66 |
| branch: | default |
Sorting, work in progress, find a way to order by complex fields and properties.
21 months ago
Changed (Δ6.4 KB):
raw changeset »
portablecontacts/tests.py (256 lines added, 4 lines removed)
portablecontacts/views.py (6 lines added, 0 lines removed)
Up to file-list portablecontacts/tests.py:
| … | … | @@ -465,11 +465,247 @@ XML representations:: |
465 |
465 |
</entry> |
466 |
466 |
</response> |
467 |
467 |
|
468 |
Sorting |
|
469 |
------- |
|
470 |
||
471 |
Sorting allows requests to specify the order in which contacts are returned. |
|
472 |
||
473 |
sortBy, specifies the field name whose value SHALL be used to order the |
|
474 |
returned Contacts. The sort order is determine by the sortOrder parameter. |
|
475 |
If sortBy is a Singular Field, contacts are sorted according to that field's |
|
476 |
value; if it's a Plural Field, contacts are sorted by the Value |
|
477 |
(or Major Value, if it's a Complex Field) of the field marked with |
|
478 |
"primary": "true", if any, or else the first value in the list, if any, |
|
479 |
or else they are sorted last if the given contact has no data for the given |
|
480 |
field:: |
|
481 |
||
482 |
>>> response = c.get("/portablecontacts/@me/@all/", {'sortBy': 'givenName'}) |
|
483 |
>>> response.status_code |
|
484 |
200 |
|
485 |
>>> print response.content |
|
486 |
{ |
|
487 |
"itemsPerPage": 100, |
|
488 |
"startIndex": 0, |
|
489 |
"totalResults": 3, |
|
490 |
"entry": [ |
|
491 |
{ |
|
492 |
"displayName": "Minimal Contact", |
|
493 |
"id": 1 |
|
494 |
}, |
|
495 |
{ |
|
496 |
"preferredUsername": "david", |
|
497 |
"displayName": "David Larlet", |
|
498 |
"id": 3, |
|
499 |
"name": { |
|
500 |
"givenName": "David", |
|
501 |
"familyName": "Larlet" |
|
502 |
} |
|
503 |
}, |
|
504 |
{ |
|
505 |
"relationships": [ |
|
506 |
"friend" |
|
507 |
], |
|
508 |
"organizations": [ |
|
509 |
{ |
|
510 |
"name": "Burns Worldwide", |
|
511 |
"title": "Head Bee Guy" |
|
512 |
} |
|
513 |
], |
|
514 |
"phoneNumbers": [ |
|
515 |
{ |
|
516 |
"type": "work", |
|
517 |
"value": "KLONDIKE5" |
|
518 |
}, |
|
519 |
{ |
|
520 |
"type": "mobile", |
|
521 |
"value": "650-123-4567" |
|
522 |
} |
|
523 |
], |
|
524 |
"displayName": "Mork Hashimoto", |
|
525 |
"name": { |
|
526 |
"givenName": "Mork", |
|
527 |
"familyName": "Hashimoto" |
|
528 |
}, |
|
529 |
"tags": [ |
|
530 |
"plaxo guy" |
|
531 |
], |
|
532 |
"emails": [ |
|
533 |
{ |
|
534 |
"type": "work", |
|
535 |
"primary": true, |
|
536 |
"value": "mhashimoto-04@plaxo.com" |
|
537 |
}, |
|
538 |
{ |
|
539 |
"type": "home", |
|
540 |
"value": "mhashimoto-04@plaxo.com" |
|
541 |
} |
|
542 |
], |
|
543 |
"photos": [ |
|
544 |
{ |
|
545 |
"type": "thumbnail", |
|
546 |
"value": "http://sample.site.org/photos/12345.jpg" |
|
547 |
} |
|
548 |
], |
|
549 |
"ims": [ |
|
550 |
{ |
|
551 |
"type": "aim", |
|
552 |
"value": "plaxodev8" |
|
553 |
} |
|
554 |
], |
|
555 |
"accounts": [ |
|
556 |
{ |
|
557 |
"domain": "plaxo.com", |
|
558 |
"userid": "2706" |
|
559 |
} |
|
560 |
], |
|
561 |
"urls": [ |
|
562 |
{ |
|
563 |
"type": "work", |
|
564 |
"value": "http://www.seeyellow.com" |
|
565 |
}, |
|
566 |
{ |
|
567 |
"type": "home", |
|
568 |
"value": "http://www.angryalien.com" |
|
569 |
} |
|
570 |
], |
|
571 |
"id": 2, |
|
572 |
"addresses": [ |
|
573 |
{ |
|
574 |
"locality": "Springfield", |
|
575 |
"country": "USA", |
|
576 |
"region": "VT", |
|
577 |
"formatted": "742 Evergreen Terrace\nSuite 123\nSpringfield VT 12345 USA", |
|
578 |
"streetAddress": "742 Evergreen Terrace\nSuite 123", |
|
579 |
"postalCode": "12345", |
|
580 |
"type": "home" |
|
581 |
} |
|
582 |
] |
|
583 |
} |
|
584 |
] |
|
585 |
} |
|
586 |
||
587 |
sortOrder, the order in which the sortBy parameter is applied. Allowed values |
|
588 |
are ascending and descending. If a value for sortBy is provided and no |
|
589 |
sortOrder is specifies, the sortOrder SHALL default to ascending. Sort order |
|
590 |
is expected to be case-insensitive Unicode alphabetic sort order, with no |
|
591 |
specific locale implied:: |
|
592 |
||
593 |
>>> response = c.get("/portablecontacts/@me/@all/", {'sortBy': 'givenName', 'sortOrder': 'descending'}) |
|
594 |
>>> response.status_code |
|
595 |
200 |
|
596 |
>>> print response.content |
|
597 |
{ |
|
598 |
"itemsPerPage": 100, |
|
599 |
"startIndex": 0, |
|
600 |
"totalResults": 3, |
|
601 |
"entry": [ |
|
602 |
{ |
|
603 |
"relationships": [ |
|
604 |
"friend" |
|
605 |
], |
|
606 |
"organizations": [ |
|
607 |
{ |
|
608 |
"name": "Burns Worldwide", |
|
609 |
"title": "Head Bee Guy" |
|
610 |
} |
|
611 |
], |
|
612 |
"phoneNumbers": [ |
|
613 |
{ |
|
614 |
"type": "work", |
|
615 |
"value": "KLONDIKE5" |
|
616 |
}, |
|
617 |
{ |
|
618 |
"type": "mobile", |
|
619 |
"value": "650-123-4567" |
|
620 |
} |
|
621 |
], |
|
622 |
"displayName": "Mork Hashimoto", |
|
623 |
"name": { |
|
624 |
"givenName": "Mork", |
|
625 |
"familyName": "Hashimoto" |
|
626 |
}, |
|
627 |
"tags": [ |
|
628 |
"plaxo guy" |
|
629 |
], |
|
630 |
"emails": [ |
|
631 |
{ |
|
632 |
"type": "work", |
|
633 |
"primary": true, |
|
634 |
"value": "mhashimoto-04@plaxo.com" |
|
635 |
}, |
|
636 |
{ |
|
637 |
"type": "home", |
|
638 |
"value": "mhashimoto-04@plaxo.com" |
|
639 |
} |
|
640 |
], |
|
641 |
"photos": [ |
|
642 |
{ |
|
643 |
"type": "thumbnail", |
|
644 |
"value": "http://sample.site.org/photos/12345.jpg" |
|
645 |
} |
|
646 |
], |
|
647 |
"ims": [ |
|
648 |
{ |
|
649 |
"type": "aim", |
|
650 |
"value": "plaxodev8" |
|
651 |
} |
|
652 |
], |
|
653 |
"accounts": [ |
|
654 |
{ |
|
655 |
"domain": "plaxo.com", |
|
656 |
"userid": "2706" |
|
657 |
} |
|
658 |
], |
|
659 |
"urls": [ |
|
660 |
{ |
|
661 |
"type": "work", |
|
662 |
"value": "http://www.seeyellow.com" |
|
663 |
}, |
|
664 |
{ |
|
665 |
"type": "home", |
|
666 |
"value": "http://www.angryalien.com" |
|
667 |
} |
|
668 |
], |
|
669 |
"id": 2, |
|
670 |
"addresses": [ |
|
671 |
{ |
|
672 |
"locality": "Springfield", |
|
673 |
"country": "USA", |
|
674 |
"region": "VT", |
|
675 |
"formatted": "742 Evergreen Terrace\nSuite 123\nSpringfield VT 12345 USA", |
|
676 |
"streetAddress": "742 Evergreen Terrace\nSuite 123", |
|
677 |
"postalCode": "12345", |
|
678 |
"type": "home" |
|
679 |
} |
|
680 |
] |
|
681 |
}, |
|
682 |
{ |
|
683 |
"preferredUsername": "david", |
|
684 |
"displayName": "David Larlet", |
|
685 |
"id": 3, |
|
686 |
"name": { |
|
687 |
"givenName": "David", |
|
688 |
"familyName": "Larlet" |
|
689 |
} |
|
690 |
}, |
|
691 |
{ |
|
692 |
"displayName": "Minimal Contact", |
|
693 |
"id": 1 |
|
694 |
} |
|
695 |
] |
|
696 |
} |
|
697 |
||
468 |
698 |
|
469 |
699 |
Pagination |
470 |
700 |
---------- |
471 |
701 |
|
472 |
Just the first entry:: |
|
702 |
The pagination parameters can be used together to "page through" a large |
|
703 |
number of results in manageable chunks. |
|
704 |
||
705 |
For instance, on an initial query, specifying startIndex=0&count=1 will return |
|
706 |
only the first result. The total number of possible results is indicated by |
|
707 |
the totalResults field of results, so the client knows how many "pages" of |
|
708 |
results exist:: |
|
473 |
709 |
|
474 |
710 |
>>> response = c.get("/portablecontacts/@me/@all/", {'startIndex':0, 'count':1}) |
475 |
711 |
>>> response.status_code |
| … | … | @@ -487,13 +723,29 @@ Just the first entry:: |
487 |
723 |
] |
488 |
724 |
} |
489 |
725 |
|
490 |
A |
|
726 |
A subsequent query of startIndex=2&count=1 will return the next next result, |
|
727 |
and so on:: |
|
491 |
728 |
|
492 |
>>> response = c.get("/portablecontacts/@me/@all/", {'startIndex': |
|
729 |
>>> response = c.get("/portablecontacts/@me/@all/", {'startIndex':2, 'count':1}) |
|
493 |
730 |
>>> response.status_code |
494 |
731 |
200 |
495 |
732 |
>>> print response.content |
496 |
||
733 |
{ |
|
734 |
"itemsPerPage": 1, |
|
735 |
"startIndex": 2, |
|
736 |
"totalResults": 3, |
|
737 |
"entry": [ |
|
738 |
{ |
|
739 |
"preferredUsername": "david", |
|
740 |
"displayName": "David Larlet", |
|
741 |
"id": 3, |
|
742 |
"name": { |
|
743 |
"givenName": "David", |
|
744 |
"familyName": "Larlet" |
|
745 |
} |
|
746 |
} |
|
747 |
] |
|
748 |
} |
|
497 |
749 |
|
498 |
750 |
""" |
499 |
751 |
Up to file-list portablecontacts/views.py:
| … | … | @@ -15,6 +15,12 @@ def retrieve(request, id=None, format='j |
15 |
15 |
options = {'indent': True} |
16 |
16 |
if id is None: |
17 |
17 |
contacts = request.user.contact_set.all() |
18 |
||
19 |
# sorting |
|
20 |
sortBy = request.GET.get('sortBy', 'id') |
|
21 |
sortOrder = request.GET.get('sortOrder', 'ascending') |
|
22 |
contacts = contacts.order_by('%s%s' % (sortOrder=='descending' and '-' or '', sortBy)) |
|
23 |
||
18 |
24 |
# pagination |
19 |
25 |
startIndex = int(request.GET.get('startIndex', 0)) |
20 |
26 |
count = int(request.GET.get('count', 100)) |
