Beispiel #1
0
class BlogPost(Document):
    _private_fields=['personal_thoughts']
    _public_fields=['author', 'content', 'comments']
    title = StringField()    
    content = StringField()
    author = EmbeddedDocumentField(Author)
    comments = ListField(EmbeddedDocumentField(Comment))
    deleted = BooleanField()   
Beispiel #2
0
    def setUp(self):
        class EmbeddedTestDocument(EmbeddedDocument):
            bandname = StringField()
        class SecondEmbeddedTestDocument(EmbeddedDocument):
            food = StringField()
        self.embeded_test_document = EmbeddedTestDocument
        self.second_embeded_test_document = EmbeddedTestDocument
        
        self.embedded_field = EmbeddedDocumentField(EmbeddedTestDocument)
        self.second_embedded_field = EmbeddedDocumentField(SecondEmbeddedTestDocument)

        class TestDocument(Document):
            the_doc = ListField([self.embedded_field, self.second_embedded_field])

        self.TestDoc = TestDocument
        self.testdoc = TestDocument()
Beispiel #3
0
class TaskList(Document):
    """A `TaskList` associated a creation date and updated_date with a list of
    `Action` instances.
    """
    actions = ListField(EmbeddedDocumentField(Action))
    created_date = DateTimeField(default=datetime.datetime.now)
    updated_date = DateTimeField(default=datetime.datetime.now)
    num_completed = IntField(default=0)
Beispiel #4
0
 def setUp(self):
     class EmbeddedTestDocument(EmbeddedDocument):
         bandname = StringField()
     self.embeded_test_document = EmbeddedTestDocument
     self.embedded_field = EmbeddedDocumentField(EmbeddedTestDocument)
     class TestDocument(Document):
         the_doc = ListField(self.embedded_field)
     self.TestDoc = TestDocument
     self.testdoc = TestDocument()
Beispiel #5
0
class Membership(PyvotalDocument):
    """Parsed response from api with membership info.
Use :meth:`PTracker.Membership <pyvotal.PTracker.Membership>` to create instances of this class.

Available fields:

+----------------------------------------+----------------------------------------+
|id                                      |Integer                                 |
+----------------------------------------+----------------------------------------+
|role                                    |String                                  |
+----------------------------------------+----------------------------------------+
|person                                  |:class:`~pyvotal.memberships.Person`    |
+----------------------------------------+----------------------------------------+
"""
    role = StringField()
    person = EmbeddedDocumentField(Person)

    _tagname = 'membership'

    @property
    def is_owner(self):
        """
        ``True`` if person is project owner.
        """
        return self.role == 'Owner'

    @property
    def is_member(self):
        """
        ``True`` if person is project member.
        """
        return self.role == 'Member'

    @property
    def is_viewer(self):
        """
        ``True`` if person is project viewer.
        """
        return self.role == 'Viewer'
Beispiel #6
0
class Conversation(BaseModel):
    _public_fields = [
        'created_at', 'location', 'message', 'interest', 'author',
        'replies_count', 'distance', 'parent'
    ]
    _key = 0x9c12cf6b

    id = IDField(_key)
    ext_id = OpaqueIdField(_key)
    created_at = DateTimeField()
    location = GeoPointField()
    message = StringField(max_length=2000)
    interest = EmbeddedDocumentField(Interest)
    author = EmbeddedDocumentField(User)
    replies_count = IntField()
    score = FloatField()
    distance = FloatField()

    #def __new__(cls, *args, **kwargs):
    #       cls.parent = EmbeddedDocumentField(cls)
    #       return object.__new__(cls, *args, **kwargs)

    def to_sqldict(self):
        data = BaseModel.to_sqldict(self)
        data['parent_id'] = self.parent.id if self.parent else None
        return data

    def to_dict(self):
        data = BaseModel.to_dict(self)
        if self.parent:
            data['parent'] = self.parent.to_dict()
        return data

    @staticmethod
    def parse(row, **kwargs):
        if not row:
            return None

        if len(kwargs) > 0:
            row.update(kwargs)

        obj = Conversation(**row)
        obj.author = User(id=row.get('user_id'),
                          name=row.get('username'),
                          avatar_url=row.get('avatar_url'))

        if row.get('lat') and row.get('lon'):
            obj.location = {'lat': row.get('lat'), 'lon': row.get('lon')}

        if row.get('interest_id') or row.get('interest_name'):
            obj.interest = Interest(id=row.get('interest_id'),
                                    name=row.get('interest_name'))

        if row.get('parent_id') or row.get('parent_name'):
            obj.parent = Conversation(id=row.get('parent_id'),
                                      name=row.get('parent_name'))

        return obj

    @staticmethod
    def parse_all(rows):
        records = []
        for row in rows:
            records.append(Conversation.parse(row))
        return records

    @staticmethod
    def find(interest_id, id):
        try:
            cursor = Conversation.cursor()
            cursor.execute(FIND, [id, interest_id])
            return Conversation.parse(cursor.fetchone())
        except Exception, e:
            Conversation.report_error(e, cursor)
            return None
Beispiel #7
0
        try:
            cursor = Conversation.cursor()
            if after:
                cursor.execute(FIND_ALL_BY_SUBSCRIPTION_AFTER,
                               [user_id, user_id, after, limit, offset])
            else:
                cursor.execute(FIND_ALL_BY_SUBSCRIPTION,
                               [user_id, user_id, limit, offset])

            return Conversation.parse_all(cursor.fetchall())
        except Exception, e:
            Conversation.report_error(e, cursor)
            return None

    @staticmethod
    def countAllBySubscription(user_id, after=None):
        try:
            cursor = Conversation.cursor()
            if after:
                cursor.execute(COUNT_ALL_BY_SUBSCRIPTION_AFTER,
                               [user_id, user_id, after])
            else:
                cursor.execute(COUNT_ALL_BY_SUBSCRIPTION, [user_id, user_id])
            return cursor.fetchone()['count']
        except Exception, e:
            Conversation.report_error(e, cursor)
            return 0


Conversation.parent = EmbeddedDocumentField(Conversation)
Beispiel #8
0
class Post(BlogDocument):
    """A post or a page"""
    title = StringField(default='')
    # Formatted for display.
    body = StringField(default='')
    # Input from MarsEdit or migrate_from_wordpress.
    original = StringField(default='')
    # Plain text.
    plain = StringField(default='')
    # Plain-text excerpt.
    summary = StringField(default='')
    author = StringField(default='')
    type = StringField(choices=('post', 'page'), default='post')
    status = StringField(choices=('publish', 'draft', 'redirect'),
                         default='publish')
    meta_description = StringField(default='')
    tags = SortedListField(StringField())
    categories = SortedListField(EmbeddedDocumentField(EmbeddedCategory))
    slug = StringField(default='')
    guest_access_tokens = ListField(EmbeddedDocumentField(GuestAccessToken))
    wordpress_id = IntField()  # legacy id from WordPress
    pub_date = DateTimeField()
    mod = DateTimeField()
    # Post was moved, this is its new slug.
    redirect = StringField(default=None)

    def __init__(self, *args, **kwargs):
        super(Post, self).__init__(*args, **kwargs)
        if not self.mod.tzinfo:
            self.mod = utc_tz.localize(self.mod)

    @classmethod
    def from_metaweblog(cls, struct, post_type='post', is_edit=False):
        """Receive metaWeblog RPC struct and initialize a Post.
           Used both by migrate_from_wordpress and when receiving a new or
           edited post from MarsEdit.
        """
        title = struct.get('title', '')

        meta_description = struct.get('mt_excerpt', '')
        if len(meta_description) > 155:
            raise ValueError("Description is %d chars, max 155" %
                             len(meta_description))

        if 'mt_keywords' in struct:
            tags = [
                tag.strip() for tag in struct['mt_keywords'].split(',')
                if tag.strip()
            ]
        else:
            tags = None

        slug = (slugify.slugify(struct['wp_slug'])
                if struct.get('wp_slug') else slugify.slugify(title))

        description = struct.get('description', '')
        status = (struct.get('post_status') or struct.get('page_status')
                  or 'publish')

        if 'date_modified_gmt' in struct:
            tup = struct['date_modified_gmt'].timetuple()
            mod = utc_tz.localize(datetime.datetime(*tup[0:6]))
        else:
            mod = datetime.datetime.utcnow()

        body = markup.markup(description)

        rv = cls(
            title=title,
            # Format for display
            body=body,
            plain=plain.plain(body),
            summary=summarize.summarize(body, 200),
            original=description,
            meta_description=meta_description,
            tags=tags,
            slug=slug,
            type=post_type,
            status=status,
            wordpress_id=struct.get('postid'),
            mod=mod)

        if not is_edit and 'date_created_gmt' in struct:
            # TODO: can fail if two posts created in same second, add random
            #   suffix to ObjectId
            date_created = datetime.datetime.strptime(
                struct['date_created_gmt'].value, "%Y%m%dT%H:%M:%S")
            rv.id = ObjectId.from_datetime(date_created)

        return rv

    def to_metaweblog(self, application):
        # We're kind of throwing fieldnames at the wall and seeing what sticks,
        # MarsEdit expects different names in the responses to different API
        # calls.

        if self.status == 'publish':
            url = absolute(application.reverse_url('post', self.slug))
        else:
            url = absolute(application.reverse_url('draft', self.slug))

        rv = {
            'title':
            self.title,
            # Note we're returning the original, not the display version
            'description':
            self.original,
            'link':
            url,
            'permaLink':
            url,
            'categories':
            [cat.to_metaweblog(application) for cat in self['categories']],
            'mt_keywords':
            ','.join(self['tags']),
            'dateCreated':
            self.local_date_created(application),
            'date_created_gmt':
            self.date_created,
            'postid':
            str(self.id),
            'id':
            str(self.id),
            'status':
            self.status,
            'wp_slug':
            self.slug,
            'mt_excerpt':
            self.meta_description,
        }

        if self.type == 'post':
            rv['post_id'] = str(self.id)
            rv['post_status'] = self.status
        elif self.type == 'page':
            rv['page_id'] = str(self.id)
            rv['page_status'] = self.status

        return rv

    def to_python(self):
        dct = super(Post, self).to_python()

        # Avoid bug where metaWeblog_editPost() sets categories to []
        if not self.categories:
            dct.pop('categories', None)

        # TODO: for other models, too?
        if 'id' in dct:
            dct['_id'] = dct.pop('id')
        return dct

    @property
    def date_created(self):
        if self.pub_date:
            return utc_tz.localize(self.pub_date)
        else:
            return super(Post, self).date_created

    def local_date_created(self, application):
        dc = self.date_created
        tz = application.settings['tz']
        return tz.normalize(dc.astimezone(tz))

    def local_short_date(self, application):
        dc = self.local_date_created(application)
        return '%s/%s/%s' % (dc.month, dc.day, dc.year)

    def local_long_date(self, application):
        dc = self.local_date_created(application)
        return '%s %s, %s' % (dc.strftime('%b'), dc.day, dc.year)

    def local_time_of_day(self, application):
        dc = self.local_date_created(application)
        return '%d:%02d %s' % (dc.hour % 12, dc.minute, dc.strftime('%p'))

    @property
    def last_modified(self):
        return max(self.date_created, self.mod)

    @property
    def display_summary(self):
        return self.meta_description if self.meta_description else self.summary

    def has_guest_access_token(self, token):
        """Is the given ObjectId a valid access token?"""
        assert isinstance(token, ObjectId)
        for token_object in self.guest_access_tokens:
            if token_object.token == token:
                return True

        return False
 class TestDoc(Document):
     language = fields.StringField(choices=['en', 'de'])
     other = EmbeddedDocumentField(Other)
Beispiel #10
0
class SingleTask(Document):
    """A `SingleTask` associates a creation date with an `Action` instance.
    """
    action = EmbeddedDocumentField(Action)
    created_date = DateTimeField(default=datetime.datetime.now)
class Customer(User):
    date_made = DateTimeField(required=True)
    first_name = StringField(max_length=20, required=True)
    last_name = StringField(max_length=30, required=True)
    orders = ListField(EmbeddedDocumentField(Order))
class Order(EmbeddedDocument):
    date_made = DateTimeField(required=True)
    date_changed = DateTimeField()
    line_items = ListField(EmbeddedDocumentField(Product))
    total = FloatField()
Beispiel #13
0
class Project(PyvotalDocument):
    """Parsed response from api with project info.
Use :meth:`PTracker.Project <pyvotal.PTracker.Project>` to create instances of this class.

Available fields:

+----------------------------------+----------------------------------------------+
|id                                |Integer                                       |
+----------------------------------+----------------------------------------------+
|name                              |String                                        |
+----------------------------------+----------------------------------------------+
|iteraton_length                   |Integer                                       |
+----------------------------------+----------------------------------------------+
|week_start_day                    |String                                        |
+----------------------------------+----------------------------------------------+
|pont_scale                        |String                                        |
+----------------------------------+----------------------------------------------+
|account                           |String                                        |
+----------------------------------+----------------------------------------------+
|first_iteration_start_time        |datetime                                      |
+----------------------------------+----------------------------------------------+
|current_iteration_number          |Integer                                       |
+----------------------------------+----------------------------------------------+
|enable_tasks                      |Boolean                                       |
+----------------------------------+----------------------------------------------+
|velocity_scheme                   |String                                        |
+----------------------------------+----------------------------------------------+
|current_velocity                  |Integer                                       |
+----------------------------------+----------------------------------------------+
|initial_velocity                  |Integer                                       |
+----------------------------------+----------------------------------------------+
|number_of_done_iterations_to_show |Integer                                       |
+----------------------------------+----------------------------------------------+
|labels                            |String                                        |
+----------------------------------+----------------------------------------------+
|allow_attachments                 |Boolean                                       |
+----------------------------------+----------------------------------------------+
|public                            |Boolean                                       |
+----------------------------------+----------------------------------------------+
|use_https                         |Boolean                                       |
+----------------------------------+----------------------------------------------+
|bugs_and_chores_are_estimatable   |Boolean                                       |
+----------------------------------+----------------------------------------------+
|commit_mode                       |Boolean                                       |
+----------------------------------+----------------------------------------------+
|last_activity_at                  |datetime                                      |
+----------------------------------+----------------------------------------------+
|integrations                      |list of :class:`~pyvotal.projects.Integration`|
+----------------------------------+----------------------------------------------+

Note: you should check field values for ``None`` as some of them may be missing.

"""
    name = StringField()
    iteration_length = IntField()
    week_start_day = StringField()
    point_scale = StringField()
    account = StringField()
    first_iteration_start_time = PyDateTimeField()
    current_iteration_number = IntField()
    enable_tasks = PyBooleanField()
    velocity_scheme = StringField()
    current_velocity = IntField()
    initial_velocity = IntField()
    number_of_done_iterations_to_show = IntField()
    labels = StringField()
    allow_attachments = PyBooleanField()
    public = PyBooleanField()
    use_https = PyBooleanField()
    bugs_and_chores_are_estimatable = PyBooleanField()
    commit_mode = PyBooleanField()
    last_activity_at = PyDateTimeField()

    integrations = ListField(EmbeddedDocumentField(Integration))

    xml_exclude = ['integrations']

    _tagname = 'project'

    @property
    def memberships(self):
        """:class:`~pyvotal.memberships.MembershipManager` to manipulate project`s memberships."""
        if self.id is None:
            raise PyvotalException("Project does not have id")
        if not getattr(self, '_memberships', None):
            self._memberships = MembershipManager(self.client, self.id)

        return self._memberships

    @property
    def iterations(self):
        """:class:`~pyvotal.iterations.IterationManager` to manipulate project`s iterations."""
        if self.id is None:
            raise PyvotalException("Project does not have id")
        if not getattr(self, '_iterations', None):
            self._iterations = IterationManager(self.client, self.id)

        return self._iterations

    @property
    def stories(self):
        """:class:`~pyvotal.stories.StoryManager` to manipulate project`s stories."""
        if self.id is None:
            raise PyvotalException("Project does not have id")
        if not getattr(self, '_stories', None):
            self._stories = StoryManager(self.client, self.id, self)

        return self._stories

    def _contribute_to_xml(self, etree):
        if getattr(self, "no_owner", False):
            elem = SubElement(etree, "no_owner")
            elem.text = str(True).lower()
            elem.attrib = {'type': 'boolean'}
Beispiel #14
0
class Story(PyvotalDocument):
    """Parsed response from api with story info. Use :meth:`PTracker.Story <pyvotal.PTracker.Story>` to create instances of this class.

Available fields:

+----------------------------------------+----------------------------------------+
|id                                      |Integer                                 |
+----------------------------------------+----------------------------------------+
|project_id                              |Integer                                 |
+----------------------------------------+----------------------------------------+
|story_type                              |String                                  |
+----------------------------------------+----------------------------------------+
|url                                     |String                                  |
+----------------------------------------+----------------------------------------+
|estimate                                |Integer                                 |
+----------------------------------------+----------------------------------------+
|current_state                           |String                                  |
+----------------------------------------+----------------------------------------+
|description                             |String                                  |
+----------------------------------------+----------------------------------------+
|name                                    |String                                  |
+----------------------------------------+----------------------------------------+
|requested_by                            |String                                  |
+----------------------------------------+----------------------------------------+
|owned_by                                |String                                  |
+----------------------------------------+----------------------------------------+
|created_at                              |datetime                                |
+----------------------------------------+----------------------------------------+
|accepted_at                             |datetime                                |
+----------------------------------------+----------------------------------------+
|labels                                  |String                                  |
+----------------------------------------+----------------------------------------+
|notes                                   |list of :class:`Note`                   |
+----------------------------------------+----------------------------------------+
|attachments                             |list of :class:`Attachment`             |
+----------------------------------------+----------------------------------------+

Story could also have more fields depending on existing integrations in project.
To get them use :attr:`Project.integrations`

    """
    project_id = IntField()
    story_type = StringField()
    url = StringField()
    estimate = IntField()
    current_state = StringField()
    description = StringField()
    name = StringField()
    requested_by = StringField()
    owned_by = StringField()
    created_at = PyDateTimeField()
    accepted_at = PyDateTimeField()
    labels = StringField()

    notes = ListField(EmbeddedDocumentField(Note))

    attachments = ListField(EmbeddedDocumentField(Attachment))

    xml_exclude = ['attachments', 'notes']

    _tagname = 'story'

    @property
    def tasks(self):
        """:class:`~pyvotal.tasks.TaskManager` to manipulate story`s tasks."""
        if self.id is None:
            raise PyvotalException("Story does not have id")
        if not getattr(self, '_tasks', None):
            self._tasks = TaskManager(self.client, self.project_id, self.id)

        return self._tasks

    def add_attachment(self, name, fobj):
        """Adds attachment to story.

        :param name: Attachment file name.
        :param fobj: File-like object to upload.

        ::

          from pyvotal import PTracker

          ptracker = PTracker(token='token')

          project = ptracker.projects.get(1231)
          story = projects.stories.get(1232)

          story.add_attachment("hosts", open("/etc/hosts"))"""

        url = 'projects/%s/stories/%s/attachments' % (self.project_id, self.id)
        self.client.post(url, None, files={'Filedata': (name, fobj)})

    def add_note(self, text):
        """Adds note to story.

        :param text: Text to be added to story as note.
        :return: Created :class:`~pyvotal.stories.Note`.

        ::

          from pyvotal import PTracker

          ptracker = PTracker(token='token')

          project = ptracker.projects.get(1231)
          story = projects.stories.get(1232)

          note = story.add_note("Usefull info")
          print note.id, note.noted_at """

        url = 'projects/%s/stories/%s/notes/' % (self.project_id, self.id)
        data = "<note><text>%s</text></note>" % text
        etree = self.client.post(url, data)
        obj = Note()
        obj._from_etree(etree)
        return obj

    def save(self):
        """Saves changes to existing story back to pivotal.

::

  from pyvotal import PTracker

  ptracker = PTracker(token='token')
  project = ptracker.projects.get(1231)

  story = projects.stories.get(1232)
  story.estimate = 3
  story.save()"""

        url = 'projects/%s/stories/%s' % (self.project_id, self.id)
        data = self._to_xml(excludes=['id', 'url'])
        self.client.put(url, data)
        # FIXME return new story

    def move(self, move, target_id):
        url = 'projects/%s/stories/%s/moves' % (self.project_id, self.id)
        kwargs = dict()
        kwargs['move[move]'] = move
        kwargs['move[target]'] = target_id
        data = self.client.post(url, "", params=kwargs)

        obj = Story()
        obj._from_etree(data)
        obj.client = self.client
        return obj

    def move_after(self, story):
        """
        Moves story after given.

        :param story: Story id or :class:`~pyvotal.stories.Story`.
        :return: Updated  :class:`~pyvotal.stories.Story`.

        ::

          from pyvotal import PTracker

          ptracker = PTracker(token='token')
          project = ptracker.projects.get(1231)

          story = projects.stories.get(1232)
          story.move_after(1233)"""

        if isinstance(story, Story):
            story_id = story.id
        else:
            story_id = story
        return self.move('after', story_id)

    def move_before(self, story):
        """
        Moves story before given.

        :param story: Story id or :class:`~pyvotal.stories.Story`.
        :return: Updated  :class:`~pyvotal.stories.Story`.

        ::

          from pyvotal import PTracker

          ptracker = PTracker(token='token')
          project = ptracker.projects.get(1231)

          story = projects.stories.get(1232)
          story.move_before(another_story)"""

        if isinstance(story, Story):
            story_id = story.id
        else:
            story_id = story
        return self.move('before', story_id)

    @property
    def project(self):
        return self._project

    @project.setter
    def project(self, value):
        self._project = value
        if value is not None:
            for integration in self._project.integrations:
                self._fields[integration.field_name] = StringField()