diff --git a/pypjua/applications/pidf.py b/pypjua/applications/pidf.py index 51432c86..08a0bff7 100644 --- a/pypjua/applications/pidf.py +++ b/pypjua/applications/pidf.py @@ -1,301 +1,305 @@ """PIDF handling according to RFC3863 This module provides classes to parse and generate PIDF documents, and also uses the XML Application extensibility API to allow extensions to PIDF. Example usage: >>> from datetime import datetime >>> pidf = PIDF('pres:someone@example.com') >>> status = Status(basic=Basic('open')) >>> contact = Contact('im:someone@mobilecarrier.net') >>> contact.priority = "0.8" >>> tuple1 = Tuple('bs35r9', notes=[Note("Don't Disturb Please!"), Note("Ne derangez pas, s'il vous plait", lang="fr")], status=status) >>> tuple1.contact = contact >>> tuple1.timestamp = Timestamp(datetime(2008, 9, 11, 20, 42, 03)) >>> tuple2 = Tuple('eg92n8', status=Status(basic=Basic('open')), contact=Contact('mailto:someone@example.com')) >>> tuple2.contact.priority = "1.0" >>> pidf.notes.append(Note("I'll be in Tokyo next week")) >>> pidf.append(tuple1) >>> pidf.append(tuple2) >>> print pidf.toxml(pretty_print=True) open im:someone@mobilecarrier.net Don't Disturb Please! Ne derangez pas, s'il vous plait 2008-09-11T20:42:03+01:00 open mailto:someone@example.com I'll be in Tokyo next week """ import re import datetime from pypjua.applications import ParserError, XMLMeta, XMLApplication, XMLElement, XMLStringElement, ExtensibleXMLApplication, ExtensibleXMLListApplication, ExtensibleXMLElement __all__ = ['_namespace_', 'PIDFMeta', 'PIDFTopElement', 'TupleExtension', 'StatusExtension', 'Note', 'NoteList', 'Tuple', 'Status', 'Basic', 'Contact', 'Timestamp', 'PIDF'] _namespace_ = 'urn:ietf:params:xml:ns:pidf' class PIDFMeta(XMLMeta): pass # Mixin types for extenisibility class PIDFTopElement(object): pass class TupleExtension(object): pass class StatusExtension(object): pass class Timestamp(XMLElement): _xml_tag = 'timestamp' _xml_namespace = _namespace_ _xml_attrs = {} _xml_meta = PIDFMeta _timestamp_re = re.compile(r'(?P\d{4})-(?P\d{2})-(?P\d{2})T(?P\d{2}):(?P\d{2}):(?P\d{2})(\.(?P\d{1,}))?((?PZ)|((?P\+|-)(?P\d{2}):(?P\d{2})))') def __init__(self, timestamp=None): if timestamp is None: timestamp = datetime.datetime.now() self.timestamp = timestamp def _parse_element(self, element): self.timestamp = self.parse_timestamp(element.text) def _build_element(self, element, nsmap): element.text = self.format_timestamp(self.timestamp) @classmethod def utc_offset(cls): timediff = datetime.datetime.now() - datetime.datetime.utcnow() return int(round((timediff.days*86400 + timediff.seconds + timediff.microseconds/1000000.0)/60)) @classmethod def parse_timestamp(cls, stamp): + if stamp is None: + return None match = cls._timestamp_re.match(stamp) if match is None: raise ParserError("Timestamp %s is not in RFC3339 format" % stamp) dct = match.groupdict() if dct['UTC'] is not None: secoffset = 0 else: secoffset = int(dct['tzminute'])*60 + int(dct['tzhour'])*3600 if dct['tzsign'] == '-': secoffset *= -1 if dct['secfrac'] is not None: secfrac = dct['secfrac'][:6] secfrac += '0'*(6-len(secfrac)) secfrac = int(secfrac) else: secfrac = 0 dt = datetime.datetime(year=int(dct['year']), month=int(dct['month']), day=int(dct['day']), hour=int(dct['hour']), minute=int(dct['minute']), second=int(dct['second']), microsecond=secfrac) return dt - datetime.timedelta(seconds=secoffset) + datetime.timedelta(seconds=cls.utc_offset()*60) @classmethod def format_timestamp(cls, dt): + if dt is None: + return None minutes = cls.utc_offset() if minutes == 0: tzspec = 'Z' else: if minutes < 0: sign = '-' minutes *= -1 else: sign = '+' hours = minutes / 60 minutes = minutes % 60 tzspec = '%s%02d:%02d' % (sign, hours, minutes) return dt.replace(microsecond=0).isoformat()+tzspec def __str__(self): return str(self.timestamp) PIDFMeta.register(Timestamp) class Note(XMLStringElement): _xml_tag = 'note' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_lang = True _xml_attrs = {'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} PIDFMeta.register(Note) class NoteList(object): def __init__(self): self.__notes = {} def __getitem__(self, key): return self.__notes[key] def __delitem__(self, key): del self.__notes[key] def __iter__(self): return self.__notes.itervalues() def append(self, note): self.__notes[note.lang] = note class Basic(XMLStringElement): _xml_tag = 'basic' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_lang = False _xml_values = ('open', 'closed') PIDFMeta.register(Note) class Status(ExtensibleXMLElement): _xml_tag = 'status' _xml_namespace = _namespace_ _xml_attrs = {'id': {'id_attribute': True}} _xml_meta = PIDFMeta _xml_ext_type = StatusExtension def __init__(self, basic=None, **kwargs): ExtensibleXMLElement.__init__(self, **kwargs) self.basic = basic def _parse_element(self, element): self.basic = None for child in element: if child.tag == Basic.qname: self.basic = Basic.from_element(child, xml_meta=self._xml_meta) def _build_element(self, element, nsmap): if self.basic is not None: self.basic.to_element(parent=element, nsmap=nsmap) self._build_extensions(element, nsmap) PIDFMeta.register(Status) class Contact(XMLStringElement): _xml_tag = 'contact' _xml_namespace = _namespace_ _xml_attrs = {'priority': {}} _xml_meta = PIDFMeta _xml_lang = False PIDFMeta.register(Contact) class Tuple(ExtensibleXMLElement, PIDFTopElement): _xml_tag = 'tuple' _xml_namespace = _namespace_ _xml_attrs = {'id': {'id_attribute': True}} _xml_meta = PIDFMeta _xml_ext_type = TupleExtension def __init__(self, id, notes=[], status=None, contact=None, timestamp=None, **kwargs): ExtensibleXMLElement.__init__(self, **kwargs) self.id = id self.notes = NoteList() for note in notes: self.notes.append(note) self.status = status self.contact = contact self.timestamp = timestamp def _parse_element(self, element): self.notes = NoteList() self.status = None self.contact = None self.timestamp = None for child in element: if child.tag == Note.qname: self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) elif child.tag == Status.qname: self.status = Status.from_element(child, xml_meta=self._xml_meta) elif child.tag == Contact.qname: self.contact = Contact.from_element(child, xml_meta=self._xml_meta) elif child.tag == Timestamp.qname: self.timestamp = Timestamp.from_element(child, xml_meta=self._xml_meta) def _build_element(self, element, nsmap): if self.status is not None: self.status.to_element(parent=element, nsmap=nsmap) self._build_extensions(element, nsmap) if self.contact is not None: self.contact.to_element(parent=element, nsmap=nsmap) for note in self.notes: note.to_element(parent=element, nsmap=nsmap) if self.timestamp is not None: self.timestamp.to_element(parent=element, nsmap=nsmap) PIDFMeta.register(Tuple) class PIDF(ExtensibleXMLListApplication): accept_types = ['application/pidf+xml'] build_types = ['application/pidf+xml'] _xml_tag = 'presence' _xml_namespace = _namespace_ _xml_attrs = {'entity': {'id_attribute': True}} _xml_meta = PIDFMeta _xml_schema_file = 'pidf.xsd' _xml_nsmap = {None: _namespace_} _parser_opts = {'remove_blank_text': True} def __init__(self, entity, elems=[], notes=[]): self.entity = entity self[0:0] = elems self.notes = NoteList() for note in notes: self.notes.append(note) def _parse_element(self, element): self.notes = NoteList() for child in element: if child.tag == Note.qname: self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) else: child_cls = self._xml_meta.get(child.tag) if child_cls is not None: - self.append(chidl_cls.from_element(child, xml_meta=self._xml_meta)) + self.append(child_cls.from_element(child, xml_meta=self._xml_meta)) def _build_element(self, element, nsmap): for child in self: if isinstance(child, Tuple): child.to_element(parent=element, nsmap=nsmap) for note in self.notes: note.to_element(parent=element, nsmap=nsmap) for child in self: if not isinstance(child, Tuple): child.to_element(parent=element, nsmap=nsmap) def _before_add(self, value): if not isinstance(value, PIDFTopElement): raise TypeError("PIDF elements can only contain PIDFTopElement children, got %s instead" % value.__class__.__name__) return value PIDFMeta.register(PIDF) diff --git a/pypjua/applications/presdm.py b/pypjua/applications/presdm.py index 3665ab27..573bc48d 100644 --- a/pypjua/applications/presdm.py +++ b/pypjua/applications/presdm.py @@ -1,97 +1,112 @@ """Presence data-model elements handling according to RFC4479 This module provides an extension to PIDF (module pypjua.applications.pidf) to support the data module defined in RFC4479. """ from pypjua.applications import XMLExtension, XMLStringElement, ExtensibleXMLElement -from pypjua.applications.pidf import PIDFTopElement, PIDF, PIDFMeta +from pypjua.applications.pidf import PIDFTopElement, PIDF, PIDFMeta, NoteList, Note, Timestamp + +__all__ = ['_namespace_', + 'DeviceExtension', + 'PersonExtension', + 'DMNote', + 'DeviceID', + 'Device', + 'Person'] _namespace_ = 'urn:ietf:params:xml:ns:pidf:data-model' class DeviceExtension(object): pass class PersonExtension(object): pass +class DMNote(Note): + _xml_tag = 'note' + _xml_namespace = _namespace_ + _xml_meta = PIDFMeta + class DeviceID(XMLStringElement): _xml_tag = 'deviceID' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_lang = False class Device(ExtensibleXMLElement, PIDFTopElement): _xml_tag = 'device' _xml_namespace = _namespace_ _xml_attrs = {'id': {'id_attribute': True}} _xml_meta = PIDFMeta _xml_ext_type = DeviceExtension def __init__(self, id, deviceID=None, notes=[], timestamp=None, **kwargs): ExtensibleXMLElement.__init__(self, **kwargs) self.id = id self.deviceID = deviceID self.notes = NoteList() for note in notes: self.notes.append(note) self.timestamp = timestamp def _parse_element(self, element): self.deviceID = None self.notes = NoteList() self.timestamp = None for child in element: if child.tag == DeviceID.qname: self.deviceID = DeviceID.from_element(child, xml_meta=self._xml_meta) - elif child.tag == Note.qname: - self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) + elif child.tag == DMNote.qname: + self.notes.append(DMNote.from_element(child, xml_meta=self._xml_meta)) elif child.tag == Timestamp.qname: self.timestamp = Timestamp.from_element(child, xml_meta=self._xml_meta) def _build_element(self, element, nsmap): self._build_extensions(element, nsmap) if self.deviceID is not None: self.deviceID.to_element(parent=element, nsmap=nsmap) for note in self.notes: note.to_element(parent=element, nsmap=nsmap) if self.timestamp is not None: self.timestamp.to_element(parent=element, nsmap=nsmap) class Person(ExtensibleXMLElement, PIDFTopElement): _xml_tag = 'person' _xml_namespace = _namespace_ _xml_attrs = {'id': {'id_attribute': True}} _xml_meta = PIDFMeta _xml_ext_type = PersonExtension def __init__(self, id, notes=[], timestamp=None, **kwargs): ExtensibleXMLElement.__init__(self, **kwargs) self.id = id self.notes = NoteList() for note in notes: self.notes.append(note) self.timestamp = timestamp def _parse_element(self, element): self.notes = NoteList() self.timestamp = None for child in element: - if child.tag == Note.qname: - self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) + if child.tag == DMNote.qname: + self.notes.append(DMNote.from_element(child, xml_meta=self._xml_meta)) elif child.tag == Timestamp.qname: self.timestamp = Timestamp.from_element(child, xml_meta=self._xml_meta) def _build_element(self, element, nsmap): self._build_extensions(element, nsmap) for note in self.notes: note.to_element(parent=element, nsmap=nsmap) if self.timestamp is not None: self.timestamp.to_element(parent=element, nsmap=nsmap) class PresDMExtension(XMLExtension): _xml_ext_def = [(Device, []), - (Person, [])] + (Person, []), + (DeviceID, []), + (DMNote, [])] _xml_namespace = _namespace_ _xml_prefix = 'dm' _xml_schema_file = 'data-model.xsd' PIDF.registerExtension(PresDMExtension) diff --git a/pypjua/applications/rpid.py b/pypjua/applications/rpid.py index 6f82c775..b3b336ff 100644 --- a/pypjua/applications/rpid.py +++ b/pypjua/applications/rpid.py @@ -1,361 +1,436 @@ """RPID handling according to RFC4480 This module provides an extension to PIDF (module pypjua.applications.pidf) to support rich presence. """ -from pypjua.applications import XMLExtension, XMLListElement, XMLEmptyElement, XMLStringElement, XMLChoiceElement, XMLGenerator +from pypjua.applications import XMLExtension, XMLElement, XMLListElement, XMLEmptyElement, XMLStringElement, XMLChoiceElement from pypjua.applications.pidf import PIDFTopElement, PIDF, PIDFMeta, TupleExtension, Timestamp, Note, NoteList, Tuple from pypjua.applications.presdm import PersonExtension, DeviceExtension, Person, Device __all__ = ['_namespace_', - 'ActivityElement', - 'MoodElement', 'PlaceTypeElement', + 'PrivacyElement', + 'RPIDNote', 'Activities', 'Mood', 'PlaceIs', 'Audio', 'Video', 'Text', - 'Noisy', - 'Quiet', - 'TooBright', - 'Dark', - 'Uncomfortable', - 'Inappropriate', 'PlaceType', + 'Privacy', + 'Relationship', + 'ServiceClass', + 'Sphere', + 'StatusIcon', + 'TimeOffset', + 'UserInput', 'Class', - 'Ok', - 'Unknown', 'Other'] _namespace_ = 'urn:ietf:params:xml:ns:pidf:rpid' # Mixin types for extenisibility -class ActivityElement(object): pass -class MoodElement(object): pass class PlaceTypeElement(object): pass class PrivacyElement(object): pass -# Mixin types just for internal use -class AudioValueElement(object): pass -class VideoValueElement(object): pass -class TextValueElement(object): pass +class RPIDNote(Note): + _xml_tag = 'note' + _xml_namespace = _namespace_ + _xml_meta = PIDFMeta -class Activities(XMLListElement, PersonExtension): +class Activities(XMLChoiceElement, PersonExtension): _xml_tag = 'activities' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_attrs = {'id': {'id_attribute': True}, 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} + _xml_values = set(('appointment', 'away', 'breakfast', 'busy', 'dinner', + 'holiday', 'in transit', 'looking for work', 'meal', 'meeting', + 'on the phone', 'performance', 'permanent absence', 'playing', + 'presentation', 'shopping', 'sleeping', 'spectator', 'sterring', + 'travel', 'tv', 'vacation', 'working', 'worship', 'unknown')) + _xml_value_maps = {'in-transit': 'in transit', 'looking-for-work': 'looking for work', + 'on-the-phone': 'on the phone', 'permanent-absence': 'permanent absence'} + _xml_default_value = 'unknown' + _xml_allow_many = True + _xml_allow_other = True def __init__(self, id=None, since=None, until=None, notes=[], activities=[]): self.id = id self.since = since self.until = until self.notes = NoteList() - self[0:0] = activities + XMLChoiceElement.__init__(self, activities) def _parse_element(self, element): self.notes = NoteList() for child in element: - if child.tag == Note.qname: + if child.tag == RPIDNote.qname: self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) - else: - child_cls = self._xml_meta.get(child.tag) - if child_cls is not None: - self.append(child_cls.from_element(child, xml_meta=self._xml_meta)) + element.remove(child) + XMLChoiceElement._parse_element(self, element) def _build_element(self, element, nsmap): for note in self.notes: note.to_element(parent=element, nsmap=nsmap) - for child in self: - child.to_element(parent=element, nsmap=nsmap) - - def _before_add(self, value): - if not isinstance(value, ActivityElement): - raise TypeError("Activities elements can only contain ActivityElement children, got %s instead" % value.__class__.__name__) - return value + XMLChoiceElement._build_element(self, element, nsmap) -class ActivitiesGenerator(XMLGenerator): - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta - _xml_bases = (XMLEmptyElement, ActivityElement) - _xml_name_prefix = 'Activity' - _xml_names = ['appointment', 'away', 'breakfast', 'busy', 'dinner', - 'holiday', 'in-transit', 'looking-for-work', 'meal', 'meeting', - 'on-the-phone', 'performance', 'permanent-absence', 'playing', - 'presentation', 'shopping', 'sleeping', 'spectator', 'sterring', - 'travel', 'tv', 'vacation', 'working', 'worship'] - -class Mood(XMLListElement, PersonExtension): +class Mood(XMLChoiceElement, PersonExtension): _xml_tag = 'mood' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_attrs = {'id': {'id_attribute': True}, 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} + _xml_values = set(('afraid', 'amazed', 'angry', 'annoyed', 'anxious', 'ashamed', + 'bored', 'brave', 'calm', 'cold', 'confused', 'contended', + 'cranky', 'curious', 'depressed', 'disappointed', 'disgusted', + 'distracted', 'embarrassed', 'excited', 'flirtatious', + 'frustrated', 'grumpy', 'guilty', 'happy', 'hot', 'humbled', + 'humiliated', 'hungry', 'hurt', 'impressed', 'in awe', + 'in_love', 'indignant', 'interested', 'invisible', 'jealous', + 'lonely', 'mean', 'moody', 'nervous', 'neutral', 'offended', + 'playful', 'proud', 'relieved', 'remorseful', 'restless', + 'sad', 'sarcastic', 'serious', 'shocked', 'shy', 'sick', + 'sleepy', 'stressed', 'surprised', 'thirsty', 'worried', 'unknown')) + _xml_value_maps = {'in_awe': 'in awe', 'in_love': 'in love'} + _xml_default_value = 'unknown' + _xml_allow_many = True + _xml_allow_other = True def __init__(self, id=None, since=None, until=None, notes=[], moods=[]): self.id = id self.since = since self.until = until self.notes = NoteList() - self[0:0] = moods + XMLChoiceElement.__init__(self, moods) def _parse_element(self, element): self.notes = NoteList() for child in element: - if child.tag == Note.qname: + if child.tag == RPIDNote.qname: self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) - else: - child_cls = self._xml_meta.get(child.tag) - if child_cls is not None: - self.append(child_cls.from_element(child, xml_meta=self._xml_meta)) + element.remove(child) + XMLChoiceElement._parse_element(self, element) def _build_element(self, element, nsmap): for note in self.notes: note.to_element(parent=element, nsmap=nsmap) - for child in self: - child.to_element(parent=element, nsmap=nsmap) - - def _before_add(self, value): - if not isinstance(value, MoodElement): - raise TypeError("Mood elements can only contain MoodElement children, got %s instead" % value.__class__.__name__) - return value - -class MoodsGenerator(XMLGenerator): - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta - _xml_bases = (XMLEmptyElement, MoodElement) - _xml_name_prefix = 'Mood' - _xml_names = ['afraid', 'amazed', 'angry', 'annoyed', 'anxious', 'ashamed', - 'bored', 'brave', 'calm', 'cold', 'confused', 'contended', - 'cranky', 'curious', 'depressed', 'disappointed', 'disgusted', - 'distracted', 'embarrassed', 'excited', 'flirtatious', - 'frustrated', 'grumpy', 'guilty', 'happy', 'hot', 'humbled', - 'humiliated', 'hungry', 'hurt', 'impressed', 'in_awe', - 'in_love', 'indignant', 'interested', 'invisible', 'jealous', - 'lonely', 'mean', 'moody', 'nervous', 'neutral', 'offended', - 'playful', 'proud', 'relieved', 'remorseful', 'restless', - 'sad', 'sarcastic', 'serious', 'shocked', 'shy', 'sick', - 'sleepy', 'stressed', 'surprised', 'thirsty', 'worried'] - -class Noisy(XMLEmptyElement, AudioValueElement): - _xml_tag = 'noisy' - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta - -class Quiet(XMLEmptyElement, AudioValueElement): - _xml_tag = 'quiet' - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta - -class TooBright(XMLEmptyElement, VideoValueElement): - _xml_tag = 'toobright' - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta - -class Dark(XMLEmptyElement, VideoValueElement): - _xml_tag = 'dark' - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta - -class Uncomfortable(XMLEmptyElement, TextValueElement): - _xml_tag = 'uncomfortable' - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta - -class Inappropriate(XMLEmptyElement, TextValueElement): - _xml_tag = 'inappropriate' - _xml_namespace = _namespace_ - _xml_meta = PIDFMeta + XMLChoiceElement._build_element(self, element, nsmap) class Audio(XMLChoiceElement): _xml_tag = 'audio' _xml_namespace = _namespace_ _xml_meta = PIDFMeta - _xml_type = AudioValueElement + _xml_values = set(('noisy', 'ok', 'quiet', 'unknown')) + _xml_default_value = 'unknown' + _xml_allow_many = False + _xml_allow_other = False class Video(XMLChoiceElement): _xml_tag = 'video' _xml_namespace = _namespace_ _xml_meta = PIDFMeta - _xml_type = VideoValueElement + _xml_values = set(('toobright', 'ok', 'dark', 'unknown')) + _xml_default_value = 'unknown' + _xml_allow_many = False + _xml_allow_other = False class Text(XMLChoiceElement): _xml_tag = 'text' _xml_namespace = _namespace_ _xml_meta = PIDFMeta - _xml_type = TextValueElement + _xml_values = set(('uncomfortable', 'inappropriate', 'ok', 'unknown')) + _xml_default_value = 'unknown' + _xml_allow_many = False + _xml_allow_other = False class PlaceIs(XMLElement, PersonExtension): _xml_tag = 'place-is' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_attrs = {'id': {'id_attribute': True}, 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} def __init__(self, id=None, since=None, until=None, audio=None, video=None, text=None, notes=[]): self.id = id self.since = since self.until = until self.audio = audio self.video = video self.text = text self.notes = NoteList() def _parse_element(self, element): self.notes = NoteList() for child in element: - if child.tag == Note.qname: + if child.tag == RPIDNote.qname: self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) elif child.tag == Audio.qname: self.audio = Audio.from_element(child, xml_meta=self._xml_meta) elif child.tag == Video.qname: self.video = Video.from_element(child, xml_meta=self._xml_meta) elif child.tag == Text.qname: self.text = Text.from_element(child, xml_meta=self._xml_meta) def _build_element(self, element, nsmap): for note in self.notes: note.to_element(parent=element, nsmap=nsmap) - for child in self: - child.to_element(parent=element, nsmap=nsmap) class PlaceType(XMLListElement, PersonExtension): _xml_tag = 'place-type' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_attrs = {'id': {'id_attribute': True}, 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} def __init__(self, id=None, since=None, until=None, notes=[], placetypes=[]): self.id = id self.since = since self.until = until self.notes = NoteList() self[0:0] = placetypes def _parse_element(self, element): self.notes = NoteList() for child in element: - if child.tag == Note.qname: + if child.tag == RPIDNote.qname: self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) else: child_cls = self._xml_meta.get(child.tag) if child_cls is not None: self.append(child_cls.from_element(child, xml_meta=self._xml_meta)) def _build_element(self, element, nsmap): for note in self.notes: note.to_element(parent=element, nsmap=nsmap) for child in self: child.to_element(parent=element, nsmap=nsmap) def _before_add(self, value): if not isinstance(value, PlaceTypeElement): raise TypeError("PlaceType elements can only contain PlaceTypeElement children, got %s instead" % value.__class__.__name__) return value +class Other(XMLStringElement, PlaceTypeElement): + _xml_tag = 'other' + _xml_namespace = _namespace_ + _xml_meta = PIDFMeta + _xml_lang = True + class Privacy(XMLListElement, PersonExtension): _xml_tag = 'privacy' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_attrs = {'id': {'id_attribute': True}, 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} def __init__(self, id=None, since=None, until=None, notes=[], audio=False, text=False, video=False, privacy=[]): self.id = id self.since = since self.until = until self.notes = NoteList() self.audio = audio self.text = text self.video = video self[0:0] = privacy def _parse_element(self, element): self.notes = NoteList() self.audio = False self.text = False self.video = False for child in element: - if child.tag == Note.qname: + if child.tag == RPIDNote.qname: self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) elif child.tag == '{%s}audio' % _namespace_: self.audio = True elif child.tag == '{%s}text' % _namespace_: self.text = True elif child.tag == '{%s}video' % _namespace_: self.video = True else: child_cls = self._xml_meta.get(child.tag) if child_cls is not None: self.append(child_cls.from_element(child, xml_meta=self._xml_meta)) def _build_element(self, element, nsmap): for note in self.notes: note.to_element(parent=element, nsmap=nsmap) if len(self) == 0 and not self.audio and not self.text and not self.video: etree.SubElement(element, '{%s}unknown' % _namespace_, nsmap=nsmap) else: if self.audio: etree.SubElement(element, '{%s}audio' % _namespace_, nsmap=nsmap) if self.text: etree.SubElement(element, '{%s}text' % _namespace_, nsmap=nsmap) if self.video: etree.SubElement(element, '{%s}video' % _namespace_, nsmap=nsmap) for child in self: child.to_element(parent=element, nsmap=nsmap) def _before_add(self, value): if not isinstance(value, PrivacyElement): raise TypeError("Privacy elements can only contain PrivacyElement children, got %s instead" % value.__class__.__name__) return value -class Class(XMLStringElement, TupleExtension, PersonExtension, DeviceExtension): - _xml_tag = 'class' +class Relationship(XMLChoiceElement, TupleExtension): + _xml_tag = 'relationship' + _xml_namespace = _namespace_ + _xml_meta = PIDFMeta + _xml_values = set(('assistant', 'associate', 'family', 'friend', 'self', + 'supervisor', 'unknown')) + _xml_default_value = 'self' + _xml_allow_many = False + _xml_allow_other = True + + def __init__(self, relationship=None, notes=[]): + self.notes = NoteList() + Relationship.__init__(self, (relationship is None) and [] or [relationship]) + + def _parse_element(self, element): + self.notes = NoteList() + for child in element: + if child.tag == RPIDNote.qname: + self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) + element.remove(child) + XMLChoiceElement._parse_element(self, element) + + def _build_element(self, element, nsmap): + for note in self.notes: + note.to_element(parent=element, nsmap=nsmap) + XMLChoiceElement._build_element(self, element, nsmap) + +class ServiceClass(XMLChoiceElement, TupleExtension): + _xml_tag = 'service-class' + _xml_namespace = _namespace_ + _xml_meta = PIDFMeta + _xml_values = set(('courier', 'electronic', 'freight', 'in person', 'postal', 'unknown')) + _xml_default_value = 'unknown' + _xml_allow_many = False + _xml_allow_other = False + + def __init__(self, service_class=None, notes=[]): + self.notes = NoteList() + Relationship.__init__(self, (service_class is None) and [] or [service_class]) + + def _parse_element(self, element): + self.notes = NoteList() + for child in element: + if child.tag == RPIDNote.qname: + self.notes.append(Note.from_element(child, xml_meta=self._xml_meta)) + element.remove(child) + XMLChoiceElement._parse_element(self, element) + + def _build_element(self, element, nsmap): + for note in self.notes: + note.to_element(parent=element, nsmap=nsmap) + XMLChoiceElement._build_element(self, element, nsmap) + +class Sphere(XMLChoiceElement, PersonExtension): + _xml_tag = 'sphere' + _xml_namespace = _namespace_ + _xml_meta = PIDFMeta + _xml_attrs = {'id': {'id_attribute': True}, + 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, + 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} + _xml_values = set(('home', 'work', 'unknown')) + _xml_default_value = 'unknown' + _xml_allow_many = False + _xml_allow_other = False + + def __init__(self, sphere=None, id=None, since=None, until=None): + self.id = id + self.since = since + self.until = until + XMLChoiceElement.__init__(self, (sphere is None) and [] or [sphere]) + +class StatusIcon(XMLStringElement, TupleExtension, PersonExtension): + _xml_tag = 'status-icon' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_lang = False + _xml_attrs = {'id': {'id_attribute': True}, + 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, + 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}} + + def __init__(self, value=None, id=None, since=None, until=None): + self.id = id + self.since = since + self.until = until + XMLStringElement.__init__(self, value) -class Other(XMLStringElement, ActivityElement, MoodElement, PlaceTypeElement): - _xml_tag = 'other' +class TimeOffset(XMLStringElement, PersonExtension): + _xml_tag = 'time-offset' _xml_namespace = _namespace_ _xml_meta = PIDFMeta - _xml_lang = True + _xml_lang = False + _xml_attrs = {'id': {'id_attribute': True}, + 'since': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'from'}, + 'until': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp}, + 'description': {}} + + def __init__(self, value=None, description=None, id=None, since=None, until=None): + self.id = id + self.since = since + self.until = until + self.description = description + XMLStringElement.__init__(self, str(value)) -class Ok(XMLEmptyElement, AudioValueElement, VideoValueElement, TextValueElement): - _xml_tag = 'ok' +class UserInput(XMLStringElement, TupleExtension, PersonExtension, DeviceExtension): + _xml_tag = 'user-input' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_lang = False + _xml_values = ('active', 'idle') + _xml_attrs = {'id': {'id_attribute': True}, + 'last_input': {'parse': Timestamp.parse_timestamp, 'build': Timestamp.format_timestamp, 'xml_attribute': 'last-input'}, + 'idle_threshold': {'xml_attribute': 'idle-threshold'}} + + def __init__(self, value=None, id=None, last_input=None, idle_threshold=None): + self.id = id + self.last_input = since + self.idle_threshold = idle_threshold + XMLStringElement.__init__(self, value) -class Unknown(XMLEmptyElement, ActivityElement, MoodElement, AudioValueElement, VideoValueElement, TextValueElement): - _xml_tag = 'unknown' +class Class(XMLStringElement, TupleExtension, PersonExtension, DeviceExtension): + _xml_tag = 'class' _xml_namespace = _namespace_ _xml_meta = PIDFMeta _xml_lang = False class RPIDExtension(XMLExtension): _xml_ext_def = [(Activities, [(Person, {'attribute': 'activities'})]), (Mood, [(Person, {'attribute': 'mood'})]), - (PlaceIs, [(Person, {'attribute': 'placeis'})]), - (PlaceType, [(Person, {'attribute': 'placetype'})]), + (PlaceIs, [(Person, {'attribute': 'place_is'})]), + (PlaceType, [(Person, {'attribute': 'place_type'})]), (Privacy, [(Person, {'attribute': 'privacy'})]), - (Class, [(Tuple, {'attribute': 'class'}), - (Person, {'attribute': 'class'}), - (Device, {'attribute': 'class'})]), + (Relationship, [(Tuple, {'attribute': 'relationship'})]), + (ServiceClass, [(Tuple, {'attribute': 'service_class'})]), + (Sphere, [(Person, {'attribute': 'sphere'})]), + (StatusIcon, [(Tuple, {'attribute': 'status_icon'}), + (Person, {'attribute': 'status_icon'})]), + (TimeOffset, [(Person, {'attribute': 'time_offset'})]), + (UserInput, [(Tuple, {'attribute': 'user_input'}), + (Person, {'attribute': 'user_input'}), + (Device, {'attribute': 'user_input'})]), + (Class, [(Tuple, {'attribute': 'rpid_class'}), + (Person, {'attribute': 'rpid_class'}), + (Device, {'attribute': 'rpid_class'})]), + (RPIDNote, []), + (Audio, []), + (Video, []), + (Text, []), (Other, [])] _xml_namespace = _namespace_ _xml_prefix = 'rpid' _xml_schema_file = 'rpid.xsd' PIDF.registerExtension(RPIDExtension)