Exemple #1
0
    def test_index_page_feed_reader(self):
        url = reverse('homepage.views.index')
        response = self.client.get(url)
        eq_(response.status_code, 200)

        content = response.content
        if isinstance(content, str):
            content = unicode(content, 'utf-8')

        # because I know what's in test_rss20.xml I can
        # check for it here
        import feedparser
        assert settings.L10N_FEED_URL.startswith('file:///')
        parsed = feedparser.parse(settings.L10N_FEED_URL)
        entries = list(parsed.entries)
        first = entries[0]

        # because the titles are truncated in the template
        # we need to do the same here
        from django.template.defaultfilters import truncatewords
        ok_(truncatewords(first['title'], 8) in content)
        ok_('href="%s"' % first['link'] in content)

        second = parsed.entries[1]
        ok_(truncatewords(second['title'], 8) in content)
        ok_('href="%s"' % second['link'] in content)
Exemple #2
0
def favorite_bills_csv(request):
    '''Generate a csv of the user's favorited bills.
    '''
    # Get the faves.
    favorites = get_user_favorites(request.user.username)

    # Create a csv resposne.
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename="favorite_bills.csv"'

    # Start a CSV writer.
    fields = (settings.LEVEL_FIELD.title(), 'Bill Id', 'Sponsor', 'Title',
             'Session', 'Recent Action Date', 'Recent Action', 'Other Sponsors')
    writer = unicodecsv.DictWriter(response, fields, encoding='utf-8')
    writer.writeheader()

    # Write in each bill.
    for bill in favorites['bill']:
        bill = mdb.bills.find_one(bill['obj_id'])
        sponsors = (sp['name'] for sp in bill.sponsors_manager)
        row = (
            bill.metadata['name'],
            bill['bill_id'],
            next(sponsors),
            truncatewords(bill['title'], 25),
            bill.session_details()['display_name'],
            bill.most_recent_action()['date'],
            bill.most_recent_action()['action'],
            truncatewords(', '.join(sponsors), 40))
        writer.writerow(dict(zip(fields, row)))
    return response
Exemple #3
0
    def test_index_page_feed_reader(self):
        # clear the cache of the empty feed we have for other tests
        cache.clear()
        url = reverse('homepage')
        try:
            response = self.client.get(url)
        finally:
            cache.clear()
        self.assertEqual(response.status_code, 200)

        content = response.content
        if isinstance(content, six.binary_type):
            content = content.decode('utf-8')

        # because I know what's in test_rss20.xml I can
        # check for it here
        import feedparser
        assert settings.L10N_FEED_URL.startswith('file:///')
        parsed = feedparser.parse(settings.L10N_FEED_URL)
        entries = list(parsed.entries)
        first = entries[0]

        # because the titles are truncated in the template
        # we need to do the same here
        from django.template.defaultfilters import truncatewords
        self.assertIn(truncatewords(first['title'], 8), content)
        self.assertIn('href="%s"' % first['link'], content)

        second = parsed.entries[1]
        self.assertIn(truncatewords(second['title'], 8), content)
        self.assertIn('href="%s"' % second['link'], content)
Exemple #4
0
def start_poll(request):
    from poll_and_call.models import Issue, IssuePosition, RelatedBill as IssueByBill

    # get the bill & valence
    bill = get_object_or_404(Bill, id=request.GET.get("bill"))
    valence = (request.GET.get("position") == "support")

    # get the Issue
    try:
        ix = IssueByBill.objects.get(bill=bill).issue
    except IssueByBill.DoesNotExist:
        # no Issue yet, so create
        ix = Issue.objects.create(
            slug = "%d-%s-%d" % (bill.congress, bill.bill_type_slug, bill.number),
            title = "PLACEHOLDER",
            question = "PLACEHOLDER",
            introtext = "Weigh in on %s." % bill.display_number_with_congress_number,
            isopen = True,
            )
        IssueByBill.objects.create(issue=ix, bill=bill, valence=True)

    # update the Issue since the bill title may have changed
    ix.title = bill.title
    ix.question = "What is your position on %s?" % bill.title
    ix.save()

    # how to refer to the bill in the call script
    from django.template.defaultfilters import truncatewords
    title = bill.title_no_number
    if re.match(".* Act( of \d{4})?", title):
        title = "The " + title
    title = bill.display_number + ": " + title
    bt = truncatewords(title, 11)
    if "..." in bt:
        bt = truncatewords(title, 15)
        bt = u"%s (\u201C%s\u201D)" % (bill.display_number, bt.replace(bill.display_number + ": ", ""))

    # create and update the options
    for opt_valence, opt_verb in ((True, "support"), (False, "oppose")):
        try:
            p = ix.positions.get(valence=opt_valence)
        except:
            p = IssuePosition.objects.create(
                    text="PLACEHOLDER",
                    valence=opt_valence,
                    call_script="PLACEHOLDER",
                    )
            ix.positions.add(p)

        p.text = opt_verb.title()
        p.call_script = "I %s %s." % (opt_verb, bt)
        p.save()

    return HttpResponseRedirect(ix.get_absolute_url() + "/join/" + str(ix.positions.get(valence=valence).id))
Exemple #5
0
 def test_truncatewords(self):
     self.assertEqual(
         truncatewords('A sentence with a few words in it', 1), 'A ...')
     self.assertEqual(
         truncatewords('A sentence with a few words in it', 5),
         'A sentence with a few ...')
     self.assertEqual(
         truncatewords('A sentence with a few words in it', 100),
         'A sentence with a few words in it')
     self.assertEqual(
         truncatewords('A sentence with a few words in it',
         'not a number'), 'A sentence with a few words in it')
Exemple #6
0
 def item_description(self, item):
     """ Try getting the abstract if it exists. If not, strip tags from the
     body, and truncate the words to the MAX_WORDS limit.
     """
     MAX_WORDS = 50
     abstract = item.get_abstract()
     return truncatewords(abstract, MAX_WORDS)
Exemple #7
0
	def truncated(self, obj):
		text = truncatewords(unicode(obj), 10)
		
		if len(text) > 46:
			text = text[:46] + ' ...'
		
		return text
Exemple #8
0
 def test_non_string_input(self):
     # Filters shouldn't break if passed non-strings
     self.assertEqual(addslashes(123), '123')
     self.assertEqual(linenumbers(123), '1. 123')
     self.assertEqual(lower(123), '123')
     self.assertEqual(make_list(123), ['1', '2', '3'])
     self.assertEqual(slugify(123), '123')
     self.assertEqual(title(123), '123')
     self.assertEqual(truncatewords(123, 2), '123')
     self.assertEqual(upper(123), '123')
     self.assertEqual(urlencode(123), '123')
     self.assertEqual(urlize(123), '123')
     self.assertEqual(urlizetrunc(123, 1), '123')
     self.assertEqual(wordcount(123), 1)
     self.assertEqual(wordwrap(123, 2), '123')
     self.assertEqual(ljust('123', 4), '123 ')
     self.assertEqual(rjust('123', 4), ' 123')
     self.assertEqual(center('123', 5), ' 123 ')
     self.assertEqual(center('123', 6), ' 123  ')
     self.assertEqual(cut(123, '2'), '13')
     self.assertEqual(escape(123), '123')
     self.assertEqual(linebreaks_filter(123), '<p>123</p>')
     self.assertEqual(linebreaksbr(123), '123')
     self.assertEqual(removetags(123, 'a'), '123')
     self.assertEqual(striptags(123), '123')
Exemple #9
0
def AddBookListCache(mkey, book_list):
    cache_list = []
    dlist = []
    for book in book_list:
        try:
            book.writer
        except:
            continue
        dkey = book.writer.writer + ' ' + book.title
        if not dkey in dlist: 
            thumb_url = ''
            if hasattr(book, 'img') and book.img:
                name = book.img.name.rsplit('/')[-1]
                for blob in blobstore.BlobInfo.gql("WHERE filename = '%s'"  % (name.replace("'","''"),)):
                    thumb_url = images.get_serving_url(blob.key())
            dlist.append(dkey)
            cache_list.append({
               'id': book.id,
               'writer': {'id': book.writer.id, 'writer': book.writer.writer, 'count': book.writer.count},
               'subject': {'id': book.subject.id, 'subject': book.subject.subject, 'count': book.subject.count},
               'title': book.title,
               'content': truncatewords(striptags(book.content).strip('Annotation'), 80),
               'index': book.index,
               'thumb_url': thumb_url,
               'date': book.date.strftime('%Y-%m-%d %H:%M:%S') })
    cache.add('books:' + str(mkey), str(cache_list))
Exemple #10
0
 def format_result(self, request, item):
     context = RequestContext(request)
     return {
         'id': item.id,
         'title': item.title,
         'desc': cut(truncatewords(striptags(item.description), 15), '&nbsp;')
     }
Exemple #11
0
def get_blog_items():
    # c/o http://stackoverflow.com/questions/1208916/decoding-html-entities-with-python
    import re
    def _callback(matches):
        id = matches.group(1)
        try:
           return chr(int(id))
        except:
           return id
    def decode_unicode_references(data):
        return re.sub("&#(\d+)(;|(?=\s))", _callback, data)

    # Fetch from Atom feed.
    import feedparser
    feed = feedparser.parse("https://govtracknews.wordpress.com/feed/atom/")

    from django.template.defaultfilters import truncatewords, striptags

    # Yield dicts.
    for i, entry in enumerate(feed["entries"]):
        if i >= 2: return
        yield {
            "url": entry.link,
            "title": decode_unicode_references(entry.title),
            "published": datetime(*entry.updated_parsed[0:6]),
            "snippet": truncatewords(striptags(decode_unicode_references(entry.content[0].value)), 30)
        }
Exemple #12
0
def AddBookListCache(mkey, book_list, **kw):
    cache_list = []
    dlist = []
    for book in book_list:
        try:
            book.writer
        except:
            continue
        dkey = book.writer.writer + ' ' + book.title
        if not dkey in dlist: 
            # description content
            content = GetBookContent(book, 'content.opf')
            dlist.append(dkey)
            cache_list.append({
               'id': book.id,
               'counter': (kw.get('page_num', 1)-1) * kw.get('per_page', 0) + (len(cache_list)+1),
               'writer': {'id': book.writer.id, 'writer': book.writer.writer, 'count': book.writer.count},
               'subject': {'id': book.subject.id, 'subject': book.subject.subject, 'count': book.subject.count},
               'title': book.title,
               'tr_wrt': to_translit(book.writer.writer),
               'tr_subj': to_translit(book.subject.subject),
               'tr_titl': to_translit(book.title),
               'content': truncatewords(content, 80),
               'index': book.index,
               'date': book.date })
    cache.add('books:' + str(mkey), str(cache_list))
Exemple #13
0
 def __init__(self):
   FieldRenderer.__init__(self, 'subject', 'Subject',
       lambda c: """<a href="/%(id)d">%(subject)s</a> %(closed)s""" % {
           'id': c.key().id(),
           'subject': html.escape(defaultfilters.truncatewords(c.subject, 11)),
           'closed': library.closed_label(c),
         })
Exemple #14
0
    def serialize(self):
        '''
        Temporary method to take the place of TastyPie serialization
        functionality. Will remove later in place of TastyPie functionality,
        but too many special issues (e.g. thumbnails) to worry about
        doing "right" at the moment.
        '''
        thumb_small = get_cached_thumbnail(self.image, 'small') if self.image else None
        thumb_standard = get_cached_thumbnail(self.image, 'standard') if self.image else None

        return {
            'name': self.name,
            'description': truncatewords(self.description, 15),
            'location': {
                'address': self.location.address,
                'latitude': float(self.location.latitude) if self.location.latitude is not None else None,
                'longitude': float(self.location.longitude) if self.location.longitude is not None else None,
                'is_gecoded': self.location.latitude is not None and self.location.longitude is not None,
            } if self.location else None,
            'tags': [{
                'name': tag.name,
                'permalink': tag.get_absolute_url(),
            } for tag in self.tags.all()[:4]],
            'hours': [{
                'days': listing.days,
                'hours': listing.hours
            } for listing in self.hours],
            'image': self.image.url if self.image else '',
            # special fields only for JSON output
            'permalink': self.get_absolute_url(),
            'thumb_small': thumb_small.url if thumb_small else '',
            'thumb_standard': thumb_standard.url if thumb_standard else '',
        }
Exemple #15
0
def markdown(
        text, heading_baselevel=1, filter_tags=True, truncate=False, disable_tags="",
        plain_preview=False, preset=None, plain=False):
    def wrap(result):
        if plain or plain_preview:
            return result
        else:
            script = """
            <script type='text/markdown' data-original-content>{}</script>
            """.format(json.dumps(html.escape(text)))
            return "<div>{}{}</div>".format(script, result)
    extensions = tuple(core_markdown.markdown_extensions) + (
            toc.TocExtension(baselevel=heading_baselevel, marker='[INHALT]'), )
    result = python_markdown.markdown(text, extensions=extensions)
    if preset == 'linkonly':
        result = bleach(result, disable_tags='all', except_for=('a',))
    elif filter_tags:
        disabled_tags = tuple(disable_tags.split(","))
        result = bleach(result, disabled_tags)
    if truncate:
        result = defaultfilters.truncatewords_html(result, truncate)
    if plain_preview:
        result = bleach(result, disable_tags="all")
        result = defaultfilters.truncatewords(result, plain_preview)
        result = django_html.conditional_escape(result)

    return safestring.mark_safe(wrap(result))
Exemple #16
0
def all_geojson(request):
    """
    Return a GeoJSON representation of all Programs with title, description and image-url properties.
    """

    try:
      programs = Program.objects.transform(4326).all().select_related()
    except Place.DoesNotExist:
      raise Http404

    features = []

    for program in programs:
        # truncatewords
        properties = dict(title=program.title, description=truncatewords(program.description,20), absolute_url=program.get_absolute_url(), map_icon='layer-0')
        if program.image:
            properties['image_url'] = program.image.url
        if program.icon:
            properties['map_icon'] = 'layer-%s' % program.icon.id
        geometry = simplejson.loads(program.geometry.geojson)
        feature = dict(type='Feature', geometry=geometry, properties=properties)
        features.append(feature)

    response = dict(type='FeatureCollection', features=features)
    return HttpResponse(simplejson.dumps(response), mimetype='application/json')
Exemple #17
0
def watch_pr():
    """
    Automatically create/update sandboxes for PRs opened by members of the watched
    organization on the watched repository
    """
    team_username_list = get_username_list_from_team(settings.WATCH_ORGANIZATION)

    for username in team_username_list:
        for pr in get_pr_list_from_username(username, settings.WATCH_FORK):
            pr_sub_domain = 'pr{number}.sandbox'.format(number=pr.number)

            instance, created = OpenEdXInstance.objects.get_or_create(
                sub_domain=pr_sub_domain,
                fork_name=pr.fork_name,
                branch_name=pr.branch_name,
            )
            truncated_title = truncatewords(pr.title, 4)
            instance.name = 'PR#{pr.number}: {truncated_title} ({pr.username}) - {i.reference_name}'\
                            .format(pr=pr, i=instance, truncated_title=truncated_title)
            instance.github_pr_number = pr.number
            instance.ansible_extra_settings = pr.extra_settings
            instance.save()

            if created:
                logger.info('New PR found, creating sandbox: %s', pr)
                provision_instance(instance.pk)
Exemple #18
0
 def serialize(self):
     '''
     Temporary method to take the place of TastyPie serialization
     functionality. Will remove later in place of TastyPie functionality,
     but too many special issues (e.g. thumbnails) to worry about
     doing "right" at the moment.
     '''
     return {
         'title': self.title,
         'description': truncatewords(self.description, 15),
         'dstart': str(self.dstart),
         'dexpires': str(self.dexpires),
         'dstart_str': self.dstart_str,
         'dexpires_str': self.dexpires_str,
         'points': self.points,
         'place': {
             'name': self.place.name,
             'location': {
                 'address': self.place.location.address,
                 'latitude': float(self.place.location.latitude) if self.place.location.latitude is not None else None,
                 'longitude': float(self.place.location.longitude) if self.place.location.longitude is not None else None,
                 'is_gecoded': self.place.location.latitude is not None and self.place.location.longitude is not None,
             } if self.place.location else None,
             'permalink': self.place.get_absolute_url(),
         } if self.place else None,
         'total_available': self.total_available,
         'total_sold': self.total_sold,
         'tags': [{
             'name': tag.name,
             'permalink': tag.get_absolute_url()
         } for tag in self.tags.all()],
         # special fields only for JSON output
         'permalink': self.get_absolute_url(),
     }
Exemple #19
0
 def test_string_representation_from_title(self):
     """ Tests that the string representation of an Html Asset is autogenerated from the Body field when no title is present """ 
     asset = HtmlAsset()
     asset.save()
     body = "Eboney Brown's kids range from age one to age nine, so it helps that her daycare center, Early Excellence, is just a short walk from Wyatt-Edison Charter School, where her older kids go. Early Excellence, which welcomes guests with a mural of a white Denver skyline set against a backdrop of blue mountains, serves families from as far away as Thornton and Green Valley Ranch. And many of those families, says Early Excellence director Jennifer Luke, are caught in a cycle of poverty and depend daily on regional transportation. \"I know they can't put a bus on every corner,\" says Luke, who knows many parents who combine public transportation with long walks - year round, no matter the weather."
     translation = HtmlAssetTranslation(body=body, asset=asset)
     translation.save()
     self.assertEqual(unicode(asset), truncatewords(striptags(asset.body), 4))
 def get_description_txt(self, obj):
     # from django.contrib.markup.templatetags.markup import markdown
     request = self.context['request']
     lim = request.QUERY_PARAMS.get('word', WORD_LIMIT)
     text = obj.content
     # if obj.description is not None:
     #     text = obj.description
     return defaultfilters.truncatewords(defaultfilters.striptags(text), int(lim))
Exemple #21
0
 def label_for_value(self, value):
     key = self.rel.get_related_field().name
     obj = self.rel.to._default_manager.get(**{key: value})
     try:
         img_url = obj.get_display_url()
     except:
         return '<br /><strong>%s</strong> ' % ('No display size defined')
     return '<br /><img src="%s" /> <br /><strong>%s</strong> ' % (img_url,escape(truncatewords(obj, 14)))        
Exemple #22
0
    def generate_summary(self, content):
        config = self.get_config()
        cleaned = striptags(content)
        if config["summary_count_type"] == "char":
            return truncatechars(cleaned, config["summary_count"])

        else:
            return truncatewords(cleaned, config["summary_count"])
Exemple #23
0
 def summary_or_description(self):
     '''for summary on list pages, with fallback to truncated description'''
     if self.summary:
         return self.summary.strip()
     elif self.description:
         _description = striptags(self.description.strip())
         return truncatewords(_description, 25)
     return ''
def get_breadcrumb(path, name=False):
    exp = re.compile('([A-Za-z0-9-_]+/)')
    path = path.split('?')[0]
    sub_path = exp.findall(path)
    out = u'<div data-role="navbar"><ul class="breadcrumbs">'
    i = 0;
    path = "/"
    last_sub = ''

    for page in sub_path:
        path +=page #on reconstruit l'arborecence a chaque boucle
        out +='<li><a data-theme="c" href="%(path)s"' % {'path':path} #on forme la liste des boutons
        
        if i == len(sub_path)-1:
            out +=' class="ui-btn-active"' 
        
        if i ==0:
            out +=' class="ui-btn-home"><span class="hidden">%s</span><span class="home-icon">&nbsp;</span></a></li>' % _('Home')
        else:
            try:
                pk = int(page[0:-1])
                obj_type = ContentType.objects.get(app_label=sub_path[i-2][0:-1], model=sub_path[i-1][0:-1])
                obj = obj_type.get_object_for_this_type(id=pk)
                out += '>%(page)s</a></li>' % {'page': '%s' %  truncatewords(obj, 2)}
            except:
                if i == len(sub_path)-1 and name:
                    out += '>%(page)s</a></li>' % {'page': name}
                else:
                    out += '>%(page)s</a></li>' % {'page': page.replace('/', '')}
        i +=1    
        last_sub =page
    out+='</ul></div>'
 
    #on parcour l'arborecence
   #for page in sub_path:
   #    path +=page #on reconstruit l'arborecence a chaque boucle
   #    out +='<li><a data-theme="c" href="%(path)s"' % {'path':path} #on forme la liste des boutons
   #    
   #    if i == len(sub_path)-1:
   #        # on active le dernier lien
   #        out +=' class="ui-btn-active"' 
   #        
   #    if i>0 and name != '':
   #        try : # on test si le path courant est un entier
   #            _interger=int(sub_path[i].replace('/','')) #on essaie
   #            page = unicode(name) # si ca marche, on met le nom de la page en tant que paramètre obtenu
   #        except ValueError:
   #            pass;# sino, on continu normalement
   #                    
   #    if i ==0:
   #        out +=' class="ui-btn-home"><span class="hidden">%s</span><span class="home-icon">&nbsp;</span></a></li>' % _('Home')
   #    else:
   #        out +='>%(page)s</a></li>' % {'page': page.replace('/', '')} # on fini la liste
   #    i +=1    
   #    last_sub =page
   #out+='</ul></div>'

    return out
Exemple #25
0
def get_article_abstract(context, article, words):
    """ Gets the abstract of the article using the user's IP address. """
    abstract = article.get_abstract()

    if abstract in ["", settings.BLANK_ARTICLE_TEXT]:
        ip = get_ip(context['request'])
        abstract = article.get_body(ip=ip)

    return truncatewords(strip_tags(abstract), words)
Exemple #26
0
 def save(self, *args, **kwargs):
     """
     Store summary if none was given
     and created formatted version of body text.
     """
     if not self.summary:
         self.summary = truncatewords(self.body, 50)
     self.body_formatted = sanetize_text(self.body)
     super(Article, self).save()
    def get_clean_content(self, words=10):
        """
        Return self.content with html tags stripped and truncate after a
        "words" number of words with use of django template filter
        'truncatewords'.

        :param words: Number of words to truncate after
        """
        return truncatewords(strip_tags(self.content), words)
Exemple #28
0
 def summary(self, obj):
     return format_html('<h3><a href="{}">#{} &mdash; {}: {}</a></h3><p><strong>Created: {} | Location: {}</strong></p><p style="font-weight: normal;">{}</p>',
                     reverse('admin:vincent_incidentreport_change', args=[obj.pk]),
                     obj.pk,
                     obj.get_scope_display(),
                     obj.get_nature_display(),
                     template_localtime(obj.created).strftime("%-I:%M:%S %p, %b %-d"),
                     obj.polling_location,
                     truncatewords(obj.description, 60))
Exemple #29
0
def ultimas_respuestas(pregunta, respuestas):
    if respuestas:
        retorno = '<ul>'
        for r in respuestas:
            if r.id_pregunta == str(pregunta):
                retorno += '<li>' + truncatewords(r.contenido, 20) + "</li>\n"
        return mark_safe(retorno+'</ul>')
    else:
        return ''
Exemple #30
0
 def construct_row(inst, fields):
     row = {}
     for f in fields:
         attr = getattr(inst, f)
         if callable(attr):
             row[f] = attr()
         else:
             row[f] = attr
         row[f] = truncatewords(striptags(unicode(row[f])), 5)
     return row
Exemple #31
0
def truncatewords(value, length):
    # Jinja2 has it's own ``truncate`` filter that supports word
    # boundaries and more stuff, but cannot deal with HTML.
    from django.template.defaultfilters import truncatewords
    return truncatewords(value, int(length))
Exemple #32
0
 def info(self, obj):
     return truncatewords(obj.description, 10)
Exemple #33
0
    def get_teaser(self):
        if self.teaser_html:
            return self.teaser_html

        return truncatewords(self.text_html, 100)
Exemple #34
0
 def get_short_description(self):
     desc = truncatewords(self.short_description, 25) + '...'
     return desc
Exemple #35
0
 def short_detail(self):
     return truncatewords(self.detail, 15)
 def short_description(self):
     return truncatewords(self.description, 10)
Exemple #37
0
def _format_description(description):
    if description:
        return truncatewords(striptags(description), 12)
    return ''
Exemple #38
0
 def item_description(self, item):
     return truncatewords(item.content, 30)
Exemple #39
0
 def reason_truncated(self, obj):
     return truncatewords(striptags(obj.reason), 15)
Exemple #40
0
 def short_summary(self):
     return truncatewords(self.summary, 10)
 def search_terms_(self, instance: SearchQuery) -> str:
     """Return truncated version of search_terms."""
     raw = instance.search_terms
     # take first five words, and further truncate to 50 chars if necessary
     return truncatechars(truncatewords(raw, 5), 50)
Exemple #42
0
 def item_description(self, item):
     '''описание для объекта'''
     return truncatewords(
         item.body,
         30)  # используем встроенный шаблонный фильтр truncatewords,
Exemple #43
0
class Course(PhotosMixin, ToolsMixin, FilesMixin, models.Model):
    name = models.CharField(max_length=64)
    slug = property(lambda self: slugify(self.name))
    active = models.BooleanField(
        default=True)  # only used with the reshedule view
    _ht = "Used for the events page."
    short_name = models.CharField(max_length=64,
                                  null=True,
                                  blank=True,
                                  help_text=_ht)
    get_short_name = lambda self: self.short_name or self.name
    subjects = models.ManyToManyField(Subject)
    no_discount = models.BooleanField(default=False)

    @cached_property
    def first_room(self):
        return (self.courseroomtime_set.all() or [self])[0].room

    presentation = models.BooleanField("Evaluate Presentation", default=True)
    visuals = models.BooleanField("Evaluate Visuals", default=True)
    content = models.BooleanField("Evaluate Content", default=True)

    _ht = "The dashboard (/admin/) won't bug you to reschedule until after this date"
    reschedule_on = models.DateField(default=datetime.date.today,
                                     help_text=_ht)
    first_date = property(lambda self: self.active_sessions[0].first_date)
    last_date = property(lambda self: self.active_sessions[-1].last_date)

    @property
    def as_json(self):
        image = get_thumbnail(get_override(self.first_photo, 'landscape_crop'),
                              "298x199",
                              crop="center")
        out = {
            'id':
            self.pk,
            'name':
            self.name,
            'subject_names': [s.name for s in self.subjects.all()],
            'subject_ids': [s.pk for s in self.subjects.all()],
            'url':
            self.get_absolute_url(),
            'im': {},
            'next_time':
            time.mktime(self.first_date.timetuple())
            if self.active_sessions else 0,
            'fee':
            self.fee,
            'active_sessions': [s.as_json for s in self.active_sessions],
            'past_session_count':
            len(self.archived_sessions),
            'short_description':
            self.get_short_description(),
            'requirements':
            self.requirements,
            'no_discount':
            self.no_discount,
        }
        if image and image.exists():
            out['im'] = {
                'width': image.width,
                'height': image.height,
                'url': image.url
            }
        out['enrolled_status'] = "Enroll" if out[
            'active_sessions'] else "Details"
        return out

    fee = models.IntegerField(null=True, blank=True, default=0)
    fee_notes = models.CharField(max_length=256, null=True, blank=True)
    requirements = models.CharField(max_length=256, null=True, blank=True)
    prerequisites = models.CharField(max_length=256, null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    short_description = models.TextField(null=True, blank=True)
    get_short_description = lambda self: self.short_description or truncatewords(
        striptags(self.description), 40)
    safety = models.BooleanField(default=False)
    room = models.ForeignKey(Room)

    def get_location_string(self):
        if self.first_room != self.room:
            s = "This class meets in the %s and then moves to the %s after a half hour lecture."
            return s % (self.first_room.name.lower(), self.room.name.lower())
        return "This class meets in the %s." % (self.room.name.lower())

    _ht = "If true, this class will not raise conflict warnings for events in the same room."
    no_conflict = models.BooleanField(default=False, help_text=_ht)
    max_students = models.IntegerField(default=16)

    objects = CourseManager()
    __unicode__ = lambda self: self.name
    get_absolute_url = lambda self: reverse("course:detail",
                                            args=[self.pk, self.slug])
    get_admin_url = lambda self: "/admin/course/course/%s/" % self.id

    @cached_method
    def get_tools(self):
        criterion_ids = Criterion.objects.filter(courses=self).values_list(
            "id", flat=True)
        return Tool.objects.filter(
            permission__criteria__id__in=criterion_ids).distinct()

    @cached_property
    def active_sessions(self):
        # sessions haven't ended yet (and maybe haven't started)
        first_date = datetime.datetime.now() - datetime.timedelta(0.5)
        return list(self.sessions.filter(last_date__gte=first_date))

    @property
    def archived_sessions(self):
        # opposite of active_sessions
        first_date = datetime.datetime.now() - datetime.timedelta(0.5)
        last_year = first_date - datetime.timedelta(365)
        if settings.DEBUG:
            last_year = first_date - datetime.timedelta(365 * 4)
        sessions = self.session_set.filter(last_date__lt=first_date,
                                           last_date__gte=last_year)
        return list(sessions.order_by("-first_date"))

    sessions = lambda self: Session.objects.filter(course=self, active=True)
    sessions = cached_property(sessions, name="sessions")
    last_session = lambda self: (list(self.sessions) or [None])[-1]

    def save(self, *args, **kwargs):
        super(Course, self).save(*args, **kwargs)
        #this has to be repeated in the admin because of how that works
        subjects = self.subjects.all()
        for subject in subjects:
            if subject.parent and not (subject.parent in subjects):
                self.subjects.add(subject.parent)

        for session in self.active_sessions:
            session.sessionproduct.update()
        if not settings.TESTING:
            reset_classes_json("Classes reset during course save")

    #! inherited from section, may not be necessary
    def get_notes(self):
        notes = []
        if self.requirements:
            notes.append(('Requirements', self.requirements))
        if self.fee_notes:
            notes.append(('Fee Notes', self.fee_notes))
        if self.safety:
            notes.append((
                'Safety',
                "This class has a 20 minute safety session before the first session."
            ))
        if self.prerequisites:
            notes.append(('Prerequisites', self.prerequisites))
        return notes

    @property
    def list_users(self):
        return list(set([s.user for s in self.active_sessions]))

    class Meta:
        ordering = ("name", )
 def admin_get_abridged_feedback(self, num_words=20):
     return truncatewords(self.feedback, num_words)
Exemple #45
0
 def save(self, *args, **kwargs):
     if len(self.title) < 15:
         self.slug = slugify(self.title)
     else:
         self.slug = slugify(truncatewords(self.title, 3))
     super(News, self).save(*args, **kwargs)
def main(options):
    """
    Parse rolls.
    """

    # Setup XML processors
    vote_processor = VoteProcessor()
    option_processor = VoteOptionProcessor()
    voter_processor = VoterProcessor()
    voter_processor.PERSON_CACHE = dict(
        (x.pk, x) for x in Person.objects.all())

    # The pattern which the roll file matches
    # Filename contains info which should be placed to DB
    # along with info extracted from the XML file
    re_path = re.compile('data/us/(\d+)/rolls/([hs])(\w+)-(\d+)\.xml')

    chamber_mapping = {'s': CongressChamber.senate, 'h': CongressChamber.house}

    if options.filter:
        files = glob.glob(options.filter)
        log.info('Parsing rolls matching %s' % options.filter)
    elif options.congress:
        files = glob.glob('data/us/%s/rolls/*.xml' % options.congress)
        log.info('Parsing rolls of only congress#%s' % options.congress)
    else:
        files = glob.glob('data/us/*/rolls/*.xml')
    log.info('Processing votes: %d files' % len(files))
    total = len(files)
    progress = Progress(total=total, name='files', step=10)

    def log_delete_qs(qs):
        if qs.count() == 0: return
        print "Deleting obsoleted records: ", qs
        #if qs.count() > 3:
        #    print "Delete skipped..."
        #    return
        qs.delete()

    seen_obj_ids = set()
    had_error = False

    for fname in files:
        progress.tick()

        match = re_path.search(fname)

        try:
            existing_vote = Vote.objects.get(
                congress=match.group(1),
                chamber=chamber_mapping[match.group(2)],
                session=match.group(3),
                number=match.group(4))
        except Vote.DoesNotExist:
            existing_vote = None

        if not File.objects.is_changed(
                fname
        ) and not options.force and existing_vote != None and not existing_vote.missing_data:
            seen_obj_ids.add(existing_vote.id)
            continue

        try:
            tree = etree.parse(fname)

            ## Look for votes with VP tie breakers.
            #if len(tree.xpath("/roll/voter[@VP='1']")) == 0:
            #    had_error = True # prevent delete at the end
            #    continue

            # Process role object
            for roll_node in tree.xpath('/roll'):
                vote = vote_processor.process(Vote(), roll_node)
                if existing_vote: vote.id = existing_vote.id
                match = re_path.search(fname)
                vote.congress = int(match.group(1))
                vote.chamber = chamber_mapping[match.group(2)]
                vote.session = match.group(3)
                vote.number = int(match.group(4))

                # Get related bill & amendment.

                for bill_node in roll_node.xpath("bill"):
                    related_bill_num = bill_node.get("number")
                    if 9 <= vote.congress <= 42 and vote.session in ('1', '2'):
                        # Bill numbering from the American Memory colletion is different. The number combines
                        # the session, bill number, and a 0 or 5 for regular or 'half' numbering. Prior to
                        # the 9th congress numbering seems to be wholly assigned by us and not related to
                        # actual numbering, so we skip matching those bills.
                        related_bill_num = "%d%04d%d" % (int(
                            vote.session), int(bill_node.get("number")), 0)
                    try:
                        vote.related_bill = Bill.objects.get(
                            congress=bill_node.get("session"),
                            bill_type=BillType.by_xml_code(
                                bill_node.get("type")),
                            number=related_bill_num)
                    except Bill.DoesNotExist:
                        if vote.congress >= 93:
                            vote.missing_data = True

                for amdt_node in roll_node.xpath("amendment"):
                    if amdt_node.get(
                            "ref"
                    ) == "regular" and vote.related_bill is not None:
                        try:
                            vote.related_amendment = Amendment.objects.get(
                                congress=vote.related_bill.congress,
                                amendment_type=AmendmentType.by_slug(
                                    amdt_node.get("number")[0]),
                                number=amdt_node.get("number")[1:])
                        except Amendment.DoesNotExist:
                            if vote.congress >= 93:
                                print "Missing amendment", fname
                                vote.missing_data = True
                    elif amdt_node.get("ref") == "bill-serial":
                        # It is impossible to associate House votes with amendments just from the House
                        # vote XML because the amendment-num might correspond either with the A___ number
                        # or with the "An amendment, numbered ____" number from the amendment purpose,
                        # and there's really no way to figure out which. Maybe we can use the amendment
                        # sponsor instead?
                        #vote.related_amendment = Amendment.objects.get(bill=vote.related_bill, sequence=amdt_node.get("number"))
                        # Instead, we set related_amendment from the amendment parser. Here, we have to
                        # preserve the related_amendment if it is set.
                        if existing_vote:
                            vote.related_amendment = existing_vote.related_amendment

                # clean up some question text and use the question_details field

                if vote.category in (
                        VoteCategory.passage, VoteCategory.passage_suspension,
                        VoteCategory.veto_override) and vote.related_bill:
                    # For passage votes, set the question to the bill title and put the question
                    # details in the details field.
                    vote.question = vote.related_bill.title
                    vote.question_details = vote.vote_type + " in the " + vote.get_chamber_display(
                    )

                elif vote.category == VoteCategory.amendment and vote.related_amendment:
                    # For votes on amendments, make a better title/explanation.
                    vote.question = vote.related_amendment.title
                    vote.question_details = vote.vote_type + " in the " + vote.get_chamber_display(
                    )

                elif vote.related_bill and vote.question.startswith(
                        "On the Cloture Motion " +
                        vote.related_bill.display_number):
                    vote.question = "Cloture on " + vote.related_bill.title
                elif vote.related_bill and vote.question.startswith(
                        "On Cloture on the Motion to Proceed " +
                        vote.related_bill.display_number):
                    vote.question = "Cloture on " + vote.related_bill.title
                    vote.question_details = "On Cloture on the Motion to Proceed in the " + vote.get_chamber_display(
                    )
                elif vote.related_bill and vote.question.startswith(
                        "On the Motion to Proceed " +
                        vote.related_bill.display_number):
                    vote.question = "Motion to Proceed on " + vote.related_bill.title

                elif vote.related_amendment and vote.question.startswith(
                        "On the Cloture Motion " +
                        vote.related_amendment.get_amendment_type_display() +
                        " " + str(vote.related_amendment.number)):
                    vote.question = "Cloture on " + vote.related_amendment.title
                    vote.question_details = vote.vote_type + " in the " + vote.get_chamber_display(
                    )

                # weird House foratting of bill numbers ("H RES 123 Blah blah")
                if vote.related_bill:
                    vote.question = re.sub(
                        "(On [^:]+): " +
                        vote.related_bill.display_number.replace(
                            ". ", " ").replace(".", " ").upper() + " .*",
                        r"\1: " + truncatewords(vote.related_bill.title, 15),
                        vote.question)

                vote.save()

                seen_obj_ids.add(vote.id)  # don't delete me later

                # Process roll options, overwrite existing options where possible.
                seen_option_ids = set()
                roll_options = {}
                for option_node in roll_node.xpath('./option'):
                    option = option_processor.process(VoteOption(),
                                                      option_node)
                    option.vote = vote
                    if existing_vote:
                        try:
                            option.id = VoteOption.objects.filter(
                                vote=vote, key=option.key
                            )[0].id  # get is better, but I had the database corruption problem
                        except IndexError:
                            pass
                    option.save()
                    roll_options[option.key] = option
                    seen_option_ids.add(option.id)
                log_delete_qs(
                    VoteOption.objects.filter(vote=vote).exclude(
                        id__in=seen_option_ids)
                )  # may cascade and delete the Voters too?

                # Process roll voters, overwriting existing voters where possible.
                if existing_vote:
                    existing_voters = dict(
                        Voter.objects.filter(vote=vote).values_list(
                            "person", "id"))
                seen_voter_ids = set()
                voters = list()
                for voter_node in roll_node.xpath('./voter'):
                    voter = voter_processor.process(roll_options, Voter(),
                                                    voter_node)
                    voter.vote = vote
                    voter.created = vote.created

                    # for VP votes, load the actual person & role...
                    if voter.voter_type == VoterType.vice_president:
                        try:
                            r = PersonRole.objects.get(
                                role_type=RoleType.vicepresident,
                                startdate__lte=vote.created,
                                enddate__gte=vote.created)
                            voter.person_role = r
                            voter.person = r.person
                        except PersonRole.DoesNotExist:
                            # overlapping roles? missing data?
                            log.error(
                                'Could not resolve vice president in %s' %
                                fname)

                    if existing_vote and voter.person:
                        try:
                            voter.id = existing_voters[voter.person.id]
                        except KeyError:
                            pass

                    voters.append(voter)

                    if voter.voter_type == VoterType.unknown and not vote.missing_data:
                        vote.missing_data = True
                        vote.save()

                # pre-fetch the role of each voter
                load_roles_at_date(
                    [x.person for x in voters if x.person != None],
                    vote.created)
                for voter in list(voters):
                    if voter.voter_type != VoterType.vice_president:
                        voter.person_role = voter.person.role
                    # If we couldn't match a role for this person on the date of the vote, and if the voter was Not Voting,
                    # and we're looking at historical data, then this is probably a data error --- the voter wasn't even in office.
                    if voter.person_role is None:
                        if vote.source == VoteSource.keithpoole and voter.option.key == "0":
                            # Drop this record.
                            voters.remove(voter)
                        else:
                            log.error("%s: Could not find role for %s on %s." %
                                      (fname, voter.person, vote.created))
                            vote.missing_data = True
                            vote.save()

                # save all of the records (inserting/updating)
                for voter in voters:
                    voter.save()
                    seen_voter_ids.add(voter.id)

                # remove obsolete voter records
                log_delete_qs(
                    Voter.objects.filter(vote=vote).exclude(
                        id__in=seen_voter_ids)
                )  # possibly already deleted by cascade above

                # pre-calculate totals
                vote.calculate_totals()

                if not options.disable_events:
                    vote.create_event()

            File.objects.save_file(fname)

        except Exception, ex:
            log.error('Error in processing %s' % fname, exc_info=ex)
            had_error = True
Exemple #47
0
 def __unicode__(self):
     return remove_uncomplete_latex(truncatewords(self.text, 10))
Exemple #48
0
 def text_excerpt(self, obj):
     return truncatewords(obj.text, 10)
Exemple #49
0
def style_repo_description(var):
    truncated_desc = truncatewords(var, 20)
    return truncated_desc
Exemple #50
0
 def __str__(self):
     return truncatewords(self.text, 30)
Exemple #51
0
def main(DAYS_BACK=None, custom=None, word_trim=None):
    if not word_trim:
        word_trim = 100
    # Loop through number of DAYS_BACK ...
    main_string = ''
    outstring = ''
    alpha_list = []
    alpha_list_out = []
    for i in xrange(DAYS_BACK):
        # Get the date for the day's XML feed URL ...
        i_days_ago = TODAY - datetime.timedelta(days=i)
        i_days_ago_string = i_days_ago.strftime('%Y%m%d')

        # Parse the day's XML feed ...
        # https://www.legacy.com/services/notices.asp?Affiliate=registerguard&Password=LEGACY_PW&RunDate=20140101
        raw_doc = urllib2.urlopen(
            'https://www.legacy.com/services/notices.asp?Affiliate=registerguard&Password=%s&RunDate=%s'
            % (LEGACY_PW, i_days_ago_string)).read()
        doc = objectify.parse(StringIO(raw_doc))
        tree = doc.getroot()

        # Munging strings used in the final product/output ...
        legacy_site_date_page_url = 'http://www.legacy.com/obituaries/registerguard/obituary-search.aspx?daterange=0&specificdate=%s&countryid=1&stateid=48&affiliateid=1765&entriesperpage=50' % (
            i_days_ago.strftime("%Y%m%d"))
        pre_string = u'\t<div class="obituary_date">\n\t\t<a href="%s" target="_blank"><h1>%s</h1></a>\n' % (
            legacy_site_date_page_url, i_days_ago.strftime('%A, %B %-d, %Y'))

        days_obits = tree[
            '{urn:schemas-microsoft-com:xml-sql}query'].getchildren()
        if days_obits:
            print 'Obits today! %s' % i_days_ago_string
            for e in days_obits:
                if e.MaidenName:
                    maiden_name = u'(%s)' % e.MaidenName
                else:
                    maiden_name = ''

                main_string += u'\t\t<div class="obituary_item">\n'

                # Check for an image
                image_url = getattr(e, 'ImageUrl', '')
                if image_url:
                    main_string += u'\t\t\t<a href="{0}&utm_source=RG_Obituaries&utm_medium=email&utm_campaign=RG_Obituaries_{1}&utm_content=image" target="_blank"><img src="{2}" alt="{3} {4}" /></a>\n'.format(
                        e.DisplayURL,
                        TODAY.strftime('%Y-%m-%d'),
                        image_url,
                        e.FirstName,
                        e.LastName,
                    )

                main_string += u'\t\t\t<a href="%s&utm_source=RG_Obituaries&utm_medium=email&utm_campaign=RG_Obituaries_%s&utm_content=headline" target="_blank"><h2>%s %s %s %s %s %s %s %s</h2></a>\n' % \
                    (
                        e.DisplayURL,
                        TODAY.strftime('%Y-%m-%d'),
                        e.NamePrefix,
                        e.NameAdditionalPrefix,
                        e.FirstName,
                        e.MiddleName,
                        maiden_name,
                        e.LastName,
                        e.NameSuffix,
                        e.NameAdditionalSuffix,
                    )

                # Process the tribute text
                #
                # Split on '<br><br>', lop off first chunk
                graf_list = e.NoticeText.text.split('<br><br>')[1:]
                untrimmed_text = u' '.join(graf_list)
                trimmed_text = truncatewords(untrimmed_text, word_trim)
                main_string += u'\t\t\t<p>{0}</p>\n'.format(trimmed_text, )

                main_string += u'\t\t</div> <!-- /.obituary_item -->\n'

                # Clean up date for alpha_list ...
                date_obj = datetime.datetime.strptime(e.DateCompleted.text,
                                                      '%Y-%m-%dT00:00:00')
                formatted_date_str = date_obj.strftime('%B %d, %Y')
                # Gather names here for alpha_list ...
                alpha_list.append([
                    e.LastName, e.FirstName, formatted_date_str, e.DisplayURL
                ])

        else:
            print 'Aw man, no Obits today! %s' % i_days_ago_string

            main_string += u'\t\t<div class="obituary_item">\n'
            main_string += u'\t\t\t<h2>No new obituaries added.</h2>\n'
            main_string += u'\t\t</div> <!-- /.obituary_item -->\n'

        main_string += u'\t</div> <!-- /.obituary_date -->\n'
        daily_string = pre_string + main_string
        outstring += daily_string
        # Clean out main_string for next loop/day's use ...
        main_string = u''

    if custom:
        outfile = open(os.path.join(OUTFILE_PATH, OUTFILE_DATE_CUSTOM), 'w')
    else:
        outfile = open(os.path.join(OUTFILE_PATH, OUTFILE_DATE), 'w')

    outstring = u'<div class="obituaries_by_date">\n{0}\n</div> <!-- /.obituaries_by_date -->'.format(
        outstring.rstrip())
    outfile.write(outstring.encode('cp1252'))
    outfile.close()
    '''
    alpha_list_item:
    ['Zaninovich',
    'Martin',
    'July 19, 2014',
    'http://www.legacy.com/Link.asp?I=LS000171779531'],
    '''
    alpha_list.sort()
    for alpha_list_item in alpha_list:
        alpha_list_out.append(
            '<li><a href="%s">%s, %s</a><br><small style="text-transform:uppercase; color: #666; letter-spacing:.05em; font-family:arial,san-serif;">%s</small></li>'
            % (alpha_list_item[3], alpha_list_item[0], alpha_list_item[1],
               alpha_list_item[2]))
    alpha_string_out = '<ul class="li2">\n' + '\n'.join(
        alpha_list_out) + '</ul>\n'

    if custom:
        outfile_alpha = open(os.path.join(OUTFILE_PATH, OUTFILE_ALPHA_CUSTOM),
                             'w')
    else:
        outfile_alpha = open(os.path.join(OUTFILE_PATH, OUTFILE_ALPHA), 'w')
    outfile_alpha.write(alpha_string_out.encode('cp1252'))
    outfile_alpha.close()
Exemple #52
0
 def __unicode__(self):
     return truncatewords(self.text, 30)
Exemple #53
0
 def info(self, obj):
     return truncatewords(obj.body, 10)
Exemple #54
0
 def item_description(self, item):
     return truncatewords(item.body, 30)
Exemple #55
0
 def get_change_message(self, obj):
     return truncatewords(obj.get_change_message(), 23)
Exemple #56
0
 def truncated_title(self):
     """
     This PR's title truncated to 4 words
     """
     return truncatewords(self.title, 4)
Exemple #57
0
 def value_for_grid(self):
     return truncatewords(striptags(self.value), 10)
Exemple #58
0
 def short_lyric(self):
     return truncatewords(self.lyric, 25)
Exemple #59
0
def get_breadcrumb(path, name=False):
    exp = re.compile('([A-Za-z0-9-_]+/)')
    path = path.split('?')[0]
    sub_path = exp.findall(path)
    out = u'<div data-role="navbar"><ul class="breadcrumbs">'
    i = 0
    path = "/"
    last_sub = ''

    for page in sub_path:
        path += page  #on reconstruit l'arborecence a chaque boucle
        out += '<li><a data-theme="c" href="%(path)s"' % {
            'path': path
        }  #on forme la liste des boutons

        if i == len(sub_path) - 1:
            out += ' class="ui-btn-active"'

        if i == 0:
            out += ' class="ui-btn-home"><span class="hidden">%s</span><span class="home-icon">&nbsp;</span></a></li>' % _(
                'Home')
        else:
            try:
                pk = int(page[0:-1])
                obj_type = ContentType.objects.get(app_label=sub_path[i -
                                                                      2][0:-1],
                                                   model=sub_path[i - 1][0:-1])
                obj = obj_type.get_object_for_this_type(id=pk)
                out += '>%(page)s</a></li>' % {
                    'page': '%s' % truncatewords(obj, 2)
                }
            except:
                if i == len(sub_path) - 1 and name:
                    out += '>%(page)s</a></li>' % {'page': name}
                else:
                    out += '>%(page)s</a></li>' % {
                        'page': page.replace('/', '')
                    }
        i += 1
        last_sub = page
    out += '</ul></div>'

    #on parcour l'arborecence
    #for page in sub_path:
    #    path +=page #on reconstruit l'arborecence a chaque boucle
    #    out +='<li><a data-theme="c" href="%(path)s"' % {'path':path} #on forme la liste des boutons
    #
    #    if i == len(sub_path)-1:
    #        # on active le dernier lien
    #        out +=' class="ui-btn-active"'
    #
    #    if i>0 and name != '':
    #        try : # on test si le path courant est un entier
    #            _interger=int(sub_path[i].replace('/','')) #on essaie
    #            page = unicode(name) # si ca marche, on met le nom de la page en tant que paramètre obtenu
    #        except ValueError:
    #            pass;# sino, on continu normalement
    #
    #    if i ==0:
    #        out +=' class="ui-btn-home"><span class="hidden">%s</span><span class="home-icon">&nbsp;</span></a></li>' % _('Home')
    #    else:
    #        out +='>%(page)s</a></li>' % {'page': page.replace('/', '')} # on fini la liste
    #    i +=1
    #    last_sub =page
    #out+='</ul></div>'

    return out
Exemple #60
0
 def item_description(self, item):
     value = (markdown.markdown(item.body))
     return truncatewords(value, 30)