Пример #1
0
def conquest_evolution_description(evolution, _=_):
    """Crafts a human-readable description from a `conquest_pokemon_evolution`
    row object.
    """
    chunks = []

    # Trigger
    if evolution.recruiting_ko_required:
        chunks.append('Score a KO that makes a warrior offer to join your army')
    elif evolution.item_id is not None:
        chunks.append('Win a battle')
    else:
        chunks.append('Perform any action')

    # Conditions
    if evolution.kingdom_id is not None:
        chunks.append(h.literal(_(u'in {0}')).format(
            h.HTML.a(evolution.kingdom.name,
                href=url(controller='dex_conquest', action='kingdoms',
                         name=evolution.kingdom.name.lower()))))
    if evolution.item_id is not None:
        chunks.append(h.literal(_(u'with {article} {item} equipped')).format(
            article=article(evolution.item.name),
            item=item_link(evolution.item, include_icon=False)))
    if evolution.required_stat_id is not None:
        chunks.append(_(u'with at least {number} {stat} afterwards').format(
            number=evolution.minimum_stat,
            stat=evolution.stat.name))
    if evolution.minimum_link is not None:
        chunks.append(_(u'with at least {0}% link afterwards').format(
            evolution.minimum_link))
    if evolution.warrior_gender_id is not None:
        chunks.append(_(u'{0} warriors only').format(evolution.gender.identifier))

    return h.literal(u', ').join(chunks)
Пример #2
0
def pokemon_icon(pokemon, alt=True):
    if pokemon.is_default:
        return h.literal('<span class="sprite-icon sprite-icon-%d"></span>' % pokemon.species.id)

    alt_text = pokemon.name if alt else u''
    if pokemon_has_media(pokemon.default_form, 'icons', 'png'):
        return pokemon_form_image(pokemon.default_form, prefix='icons', alt=alt_text)

    return pokedex_img('pokemon/icons/0.png', title=pokemon.species.name, alt=alt_text)
Пример #3
0
def conquest_evolution_description(evolution, _=_):
    """Crafts a human-readable description from a `conquest_pokemon_evolution`
    row object.
    """
    chunks = []

    # Trigger
    if evolution.recruiting_ko_required:
        chunks.append(
            'Score a KO that makes a warrior offer to join your army')
    elif evolution.item_id is not None:
        chunks.append('Win a battle')
    else:
        chunks.append('Perform any action')

    # Conditions
    if evolution.kingdom_id is not None:
        chunks.append(
            h.literal(_(u'in {0}')).format(
                h.HTML.a(evolution.kingdom.name,
                         href=url(controller='dex_conquest',
                                  action='kingdoms',
                                  name=evolution.kingdom.name.lower()))))
    if evolution.item_id is not None:
        chunks.append(
            h.literal(_(u'with {article} {item} equipped')).format(
                article=article(evolution.item.name),
                item=item_link(evolution.item, include_icon=False)))
    if evolution.required_stat_id is not None:
        chunks.append(
            _(u'with at least {number} {stat} afterwards').format(
                number=evolution.minimum_stat, stat=evolution.stat.name))
    if evolution.minimum_link is not None:
        chunks.append(
            _(u'with at least {0}% link afterwards').format(
                evolution.minimum_link))
    if evolution.warrior_gender_id is not None:
        chunks.append(
            _(u'{0} warriors only').format(evolution.gender.identifier))

    return h.literal(u', ').join(chunks)
Пример #4
0
def pokemon_icon(pokemon, alt=True):
    if pokemon.is_default:
        return h.literal('<span class="sprite-icon sprite-icon-%d"></span>' %
                         pokemon.species.id)

    alt_text = pokemon.name if alt else u''
    if pokemon_has_media(pokemon.default_form, 'icons', 'png'):
        return pokemon_form_image(pokemon.default_form,
                                  prefix='icons',
                                  alt=alt_text)

    return pokedex_img('pokemon/icons/0.png',
                       title=pokemon.species.name,
                       alt=alt_text)
Пример #5
0
def apply_move_template(template, move):
    u"""`template` should be a string.Template object.

    Uses safe_substitute to inject some fields from the move into the template,
    just like the above.
    """

    d = dict(
        id=move.id,
        name=move.name,
        type=move.type.name,
        damage_class=move.damage_class.name,
        pp=move.pp,
        power=move.power,
        accuracy=move.accuracy,
        priority=move.priority,
        effect_chance=move.effect_chance,
        effect=move.move_effect.short_effect,
    )

    return h.literal(template.safe_substitute(d))
Пример #6
0
def apply_move_template(template, move):
    u"""`template` should be a string.Template object.

    Uses safe_substitute to inject some fields from the move into the template,
    just like the above.
    """

    d = dict(
        id=move.id,
        name=move.name,
        type=move.type.name,
        damage_class=move.damage_class.name,
        pp=move.pp,
        power=move.power,
        accuracy=move.accuracy,
        priority=move.priority,
        effect_chance=move.effect_chance,
        effect=move.move_effect.short_effect,
    )

    return h.literal(template.safe_substitute(d))
Пример #7
0
def render_flavor_text(flavor_text, literal=False):
    """Makes flavor text suitable for HTML presentation.

    If `literal` is false, collapses broken lines into single lines.

    If `literal` is true, linebreaks are preserved exactly as they are in the
    games.
    """

    # n.b.: \u00ad is soft hyphen

    # Somehow, the games occasionally have \n\f, which makes no sense at all
    # and wouldn't render in-game anyway.  Fix this
    flavor_text = flavor_text.replace('\n\f', '\f')

    if literal:
        # Page breaks become two linebreaks.
        # Soft hyphens become real hyphens.
        # Newlines become linebreaks.
        html = flavor_text.replace(u'\f',       u'<br><br>') \
                          .replace(u'\u00ad',   u'-') \
                          .replace(u'\n',       u'<br>')

    else:
        # Page breaks are treated just like newlines.
        # Soft hyphens followed by newlines vanish.
        # Letter-hyphen-newline becomes letter-hyphen, to preserve real
        # hyphenation.
        # Any other newline becomes a space.
        html = flavor_text.replace(u'\f',       u'\n') \
                          .replace(u'\u00ad\n', u'') \
                          .replace(u'\u00ad',   u'') \
                          .replace(u' -\n',     u' - ') \
                          .replace(u'-\n',      u'-') \
                          .replace(u'\n',       u' ')

        # Collapse adjacent spaces and strip trailing whitespace.
        html = u' '.join(html.split())

    return h.literal(html)
Пример #8
0
def render_flavor_text(flavor_text, literal=False):
    """Makes flavor text suitable for HTML presentation.

    If `literal` is false, collapses broken lines into single lines.

    If `literal` is true, linebreaks are preserved exactly as they are in the
    games.
    """

    # n.b.: \u00ad is soft hyphen

    # Somehow, the games occasionally have \n\f, which makes no sense at all
    # and wouldn't render in-game anyway.  Fix this
    flavor_text = flavor_text.replace('\n\f', '\f')

    if literal:
        # Page breaks become two linebreaks.
        # Soft hyphens become real hyphens.
        # Newlines become linebreaks.
        html = flavor_text.replace(u'\f',       u'<br><br>') \
                          .replace(u'\u00ad',   u'-') \
                          .replace(u'\n',       u'<br>')

    else:
        # Page breaks are treated just like newlines.
        # Soft hyphens followed by newlines vanish.
        # Letter-hyphen-newline becomes letter-hyphen, to preserve real
        # hyphenation.
        # Any other newline becomes a space.
        html = flavor_text.replace(u'\f',       u'\n') \
                          .replace(u'\u00ad\n', u'') \
                          .replace(u'\u00ad',   u'') \
                          .replace(u' -\n',     u' - ') \
                          .replace(u'-\n',      u'-') \
                          .replace(u'\n',       u' ')

        # Collapse adjacent spaces and strip trailing whitespace.
        html = u' '.join(html.split())

    return h.literal(html)
Пример #9
0
def render_flavor_text(flavor_text, literal=False):
    """Makes flavor text suitable for HTML presentation.

    If `literal` is false, collapses broken lines into single lines.

    If `literal` is true, linebreaks are preserved exactly as they are in the
    games.
    """

    # n.b.: \u00ad is soft hyphen

    # Somehow, the games occasionally have \n\f, which makes no sense at all
    # and wouldn't render in-game anyway.  Fix this
    flavor_text = flavor_text.replace("\n\f", "\f")

    if literal:
        # Page breaks become two linebreaks.
        # Soft hyphens become real hyphens.
        # Newlines become linebreaks.
        html = flavor_text.replace(u"\f", u"<br><br>").replace(u"\u00ad", u"-").replace(u"\n", u"<br>")

    else:
        # Page breaks are treated just like newlines.
        # Soft hyphens followed by newlines vanish.
        # Letter-hyphen-newline becomes letter-hyphen, to preserve real
        # hyphenation.
        # Any other newline becomes a space.
        html = (
            flavor_text.replace(u"\f", u"\n")
            .replace(u"\u00ad\n", u"")
            .replace(u"\u00ad", u"")
            .replace(u" -\n", u" - ")
            .replace(u"-\n", u"-")
            .replace(u"\n", u" ")
        )

    return h.literal(html)
Пример #10
0
def evolution_description(evolution, _=_):
    """Crafts a human-readable description from a `pokemon_evolution` row
    object.
    """
    chunks = []

    # Trigger
    if evolution.trigger.identifier == u'level-up':
        chunks.append(_(u'Level up'))
    elif evolution.trigger.identifier == u'trade':
        chunks.append(_(u'Trade'))
    elif evolution.trigger.identifier == u'use-item':
        chunks.append(h.literal(_(u"Use {article} {item}")).format(
            article=article(evolution.trigger_item.name, _=_),
            item=item_link(evolution.trigger_item, include_icon=False)))
    elif evolution.trigger.identifier == u'shed':
        chunks.append(
            _(u"Evolve {from_pokemon} ({to_pokemon} will consume "
            u"a Poké Ball and appear in a free party slot)").format(
                from_pokemon=evolution.evolved_species.parent_species.name,
                to_pokemon=evolution.evolved_species.name))
    else:
        chunks.append(_(u'Do something'))

    # Conditions
    if evolution.gender:
        chunks.append(_(u"{0}s only").format(evolution.gender))
    if evolution.time_of_day:
        chunks.append(_(u"during the {0}").format(evolution.time_of_day))
    if evolution.minimum_level:
        chunks.append(_(u"starting at level {0}").format(evolution.minimum_level))
    if evolution.location_id:
        chunks.append(h.literal(_(u"around {0}")).format(
            h.HTML.a(evolution.location.name,
                href=url(controller='dex', action='locations',
                         name=evolution.location.name.lower()))))
    if evolution.held_item_id:
        chunks.append(h.literal(_(u"while holding {article} {item}")).format(
            article=article(evolution.held_item.name),
            item=item_link(evolution.held_item, include_icon=False)))
    if evolution.known_move_id:
        chunks.append(h.literal(_(u"knowing {0}")).format(
            h.HTML.a(evolution.known_move.name,
                href=url(controller='dex', action='moves',
                         name=evolution.known_move.name.lower()))))
    if evolution.minimum_happiness:
        chunks.append(_(u"with at least {0} happiness").format(
            evolution.minimum_happiness))
    if evolution.minimum_beauty:
        chunks.append(_(u"with at least {0} beauty").format(
            evolution.minimum_beauty))
    if evolution.relative_physical_stats is not None:
        if evolution.relative_physical_stats < 0:
            op = _(u'<')
        elif evolution.relative_physical_stats > 0:
            op = _(u'>')
        else:
            op = _(u'=')
        chunks.append(_(u"when Attack {0} Defense").format(op))
    if evolution.party_species_id:
        chunks.append(h.literal(_(u"with {0} in the party")).format(
            pokemon_link(evolution.party_species.default_pokemon, include_icon=False)))
    if evolution.trade_species_id:
        chunks.append(h.literal(_(u"in exchange for {0}")).format(
            pokemon_link(evolution.trade_species.default_pokemon, include_icon=False)))

    return h.literal(u', ').join(chunks)
Пример #11
0
 def _linkify_bug_number(self, match):
     """Regex replace function for changing bug numbers into links."""
     n = match.group(1)
     bug_url = self.bug_tracker.format(match.group(1))
     return helpers.literal(
         u"""<a href="{0}">{1}</a>""".format(bug_url, match.group(0)))
Пример #12
0
    def _poll(self, limit, max_age):
        feed = feedparser.parse(self.feed_url)

        if feed.bozo and isinstance(feed.bozo_exception, URLError):
            # Feed is DOWN.  Bail here; otherwise, old entries might be lost
            # just because, say, Bulbanews is down yet again
            return None

        if not self.title:
            self.title = feed.feed.title

        updates = []
        for entry in feed.entries[:limit]:
            # Grab a date -- Atom has published, RSS usually just has updated.
            # Both come out as time tuples, which datetime.datetime() can read
            try:
                timestamp_tuple = entry.published_parsed
            except AttributeError:
                timestamp_tuple = entry.updated_parsed
            timestamp = datetime.datetime(*timestamp_tuple[:6])

            if max_age and timestamp < max_age:
                # Entries should be oldest-first, so we can bail after the first
                # expired entry
                break

            # Try to find something to show!  Default to the summary, if there is
            # one, or try to generate one otherwise
            content = u''
            if 'summary' in entry:
                # If there be a summary, cheerfully trust that it's actually a
                # summary
                content = entry.summary
            elif 'content' in entry and \
                len(entry.content[0].value) <= self.SUMMARY_LENGTH:

                # Full content is short; use as-is!
                content = entry.content[0].value
            elif 'content' in entry:
                # Full content is way too much, especially for my giant blog posts.
                # Cut this down to some arbitrary number of characters, then feed
                # it to lxml.html to fix tag nesting
                broken_html = entry.content[0].value[:self.SUMMARY_LENGTH]
                fragment = lxml.html.fromstring(broken_html)

                # Insert an ellipsis at the end of the last node with text
                last_text_node = None
                last_tail_node = None
                # Need to find the last node with a tail, OR the last node with
                # text if it's later
                for node in fragment.iter():
                    if node.tail:
                        last_tail_node = node
                        last_text_node = None
                    elif node.text:
                        last_text_node = node
                        last_tail_node = None

                if last_text_node is not None:
                    last_text_node.text += '...'
                if last_tail_node is not None:
                    last_tail_node.tail += '...'

                # Serialize
                content = lxml.html.tostring(fragment)

            content = helpers.literal(content)

            update = FrontPageRSS(
                source=self,
                time=timestamp,
                content=content,
                entry=entry,
            )
            updates.append(update)

        return updates
Пример #13
0
    def _poll(self, limit, max_age):
        # Fetch the main repo's git tags
        git_dir = '--git-dir=' + self.repo_paths[0]
        args = [
            'git',
            git_dir,
            'tag', '-l',
        ]
        if self.tag_pattern:
            args.append(self.tag_pattern)

        git_output, _ = subprocess.Popen(args, stdout=PIPE).communicate()
        tags = git_output.strip().split('\n')

        # Tags come out in alphabetical order, which means earliest first.  Reverse
        # it to make the slicing easier
        tags.reverse()
        # Only history from tag to tag is actually interesting, so get the most
        # recent $limit tags but skip the earliest
        interesting_tags = tags[:-1][:limit]

        updates = []
        for tag, since_tag in zip(interesting_tags, tags[1:]):
            # Get the date when this tag was actually created.
            # 'raw' format gives unixtime followed by timezone offset
            args = [
                'git',
                git_dir,
                'for-each-ref',
                '--format=%(taggerdate:raw)',
                'refs/tags/' + tag,
            ]
            tag_timestamp, _ = subprocess.Popen(args, stdout=PIPE).communicate()
            tag_unixtime, tag_timezone = tag_timestamp.split(None, 1)
            tagged_timestamp = datetime.datetime.fromtimestamp(int(tag_unixtime))

            if max_age and tagged_timestamp < max_age:
                break

            commits = []

            for repo_path, repo_name in zip(self.repo_paths, self.repo_names):
                # Grab an easily-parsed history: fields delimited by nulls.
                # Hash, author's name, commit timestamp, subject.
                git_log_args = [
                    'git',
                    '--git-dir=' + repo_path,
                    'log',
                    '--pretty=%h%x00%an%x00%aE%x00%at%x00%s',
                    "{0}..{1}".format(since_tag, tag),
                ]
                proc = subprocess.Popen(git_log_args, stdout=PIPE)
                for line in proc.stdout:
                    hash, author, email, time, subject \
                        = line.strip().decode('utf8').split('\x00')

                    # Convert bug numbers in subject to URLs
                    if self.bug_tracker:
                        subject = helpers.literal(
                            re.sub(u'#(\d+)', self._linkify_bug_number, subject)
                        )

                    commits.append(
                        FrontPageGitCommit(
                            hash = hash,
                            author = author,
                            email = email,
                            time = datetime.datetime.fromtimestamp(int(time)),
                            subject = subject,
                            repo = repo_name,
                        )
                    )

            update = FrontPageGit(
                source = self,
                time = tagged_timestamp,
                log = commits,
                tag = tag,
            )
            updates.append(update)

        return updates
Пример #14
0
def apply_pokemon_template(template, pokemon, _=_):
    u"""`template` should be a string.Template object.

    Uses safe_substitute to inject some fields from the Pokémon into the
    template.

    This cheerfully returns a literal, so be sure to escape the original format
    string BEFORE passing it to Template!
    """

    d = dict(
        icon=pokemon_form_image(pokemon.default_form, prefix=u'icons'),
        id=pokemon.species.id,
        name=pokemon.default_form.name,
        height=format_height_imperial(pokemon.height),
        height_ft=format_height_imperial(pokemon.height),
        height_m=format_height_metric(pokemon.height),
        weight=format_weight_imperial(pokemon.weight),
        weight_lb=format_weight_imperial(pokemon.weight),
        weight_kg=format_weight_metric(pokemon.weight),
        gender=_(gender_rate_label[pokemon.species.gender_rate]),
        genus=pokemon.species.genus,
        base_experience=pokemon.base_experience,
        capture_rate=pokemon.species.capture_rate,
        base_happiness=pokemon.species.base_happiness,
    )

    # "Lazy" loading, to avoid hitting other tables if unnecessary.  This is
    # very chumpy and doesn't distinguish between literal text and fields (e.g.
    # '$type' vs 'type'), but that's very unlikely to happen, and it's not a
    # big deal if it does
    if 'type' in template.template:
        types = pokemon.types
        d['type'] = u'/'.join(type_.name for type_ in types)
        d['type1'] = types[0].name
        d['type2'] = types[1].name if len(types) > 1 else u''

    if 'egg_group' in template.template:
        egg_groups = pokemon.species.egg_groups
        d['egg_group'] = u'/'.join(group.name for group in egg_groups)
        d['egg_group1'] = egg_groups[0].name
        d['egg_group2'] = egg_groups[1].name if len(egg_groups) > 1 else u''

    if 'ability' in template.template:
        abilities = pokemon.abilities
        d['ability'] = u'/'.join(ability.name for ability in abilities)
        d['ability1'] = abilities[0].name
        d['ability2'] = abilities[1].name if len(abilities) > 1 else u''
        if pokemon.hidden_ability:
            d['hidden_ability'] = pokemon.hidden_ability.name
        else:
            d['hidden_ability'] = u''

    if 'color' in template.template:
        d['color'] = pokemon.species.color.name

    if 'habitat' in template.template:
        d['habitat'] = pokemon.species.habitat.name

    if 'shape' in template.template:
        if pokemon.species.shape:
            d['shape'] = pokemon.species.shape.name
        else:
            d['shape'] = ''

    if 'hatch_counter' in template.template:
        d['hatch_counter'] = pokemon.species.hatch_counter

    if 'steps_to_hatch' in template.template:
        d['steps_to_hatch'] = (pokemon.species.hatch_counter + 1) * 255

    if 'stat' in template.template or \
       'hp' in template.template or \
       'attack' in template.template or \
       'defense' in template.template or \
       'speed' in template.template or \
       'effort' in template.template:
        d['effort'] = u', '.join("{0} {1}".format(_.effort, _.stat.name)
                                 for _ in pokemon.stats if _.effort)

        d['stats'] = u'/'.join(str(_.base_stat) for _ in pokemon.stats)

        for pokemon_stat in pokemon.stats:
            key = pokemon_stat.stat.name.lower().replace(' ', '_')
            d[key] = pokemon_stat.base_stat

    return h.literal(template.safe_substitute(d))
Пример #15
0
    def _poll(self, limit, max_age):
        # Fetch the main repo's git tags
        git_dir = '--git-dir=' + self.repo_paths[0]
        args = [
            'git',
            git_dir,
            'tag',
            '-l',
        ]
        if self.tag_pattern:
            args.append(self.tag_pattern)

        git_output, _ = subprocess.Popen(args, stdout=PIPE).communicate()
        tags = git_output.strip().split('\n')

        # Tags come out in alphabetical order, which means earliest first.  Reverse
        # it to make the slicing easier
        tags.reverse()
        # Only history from tag to tag is actually interesting, so get the most
        # recent $limit tags but skip the earliest
        interesting_tags = tags[:-1][:limit]

        updates = []
        for tag, since_tag in zip(interesting_tags, tags[1:]):
            # Get the date when this tag was actually created.
            # 'raw' format gives unixtime followed by timezone offset
            args = [
                'git',
                git_dir,
                'for-each-ref',
                '--format=%(taggerdate:raw)',
                'refs/tags/' + tag,
            ]
            tag_timestamp, _ = subprocess.Popen(args,
                                                stdout=PIPE).communicate()
            tag_unixtime, tag_timezone = tag_timestamp.split(None, 1)
            tagged_timestamp = datetime.datetime.fromtimestamp(
                int(tag_unixtime))

            if max_age and tagged_timestamp < max_age:
                break

            commits = []

            for repo_path, repo_name in zip(self.repo_paths, self.repo_names):
                # Grab an easily-parsed history: fields delimited by nulls.
                # Hash, author's name, commit timestamp, subject.
                git_log_args = [
                    'git',
                    '--git-dir=' + repo_path,
                    'log',
                    '--pretty=%h%x00%an%x00%aE%x00%at%x00%s',
                    "{0}..{1}".format(since_tag, tag),
                ]
                proc = subprocess.Popen(git_log_args, stdout=PIPE)
                for line in proc.stdout:
                    hash, author, email, time, subject \
                        = line.strip().decode('utf8').split('\x00')

                    # Convert bug numbers in subject to URLs
                    if self.bug_tracker:
                        subject = helpers.literal(
                            re.sub(u'#(\d+)', self._linkify_bug_number,
                                   subject))

                    commits.append(
                        FrontPageGitCommit(
                            hash=hash,
                            author=author,
                            email=email,
                            time=datetime.datetime.fromtimestamp(int(time)),
                            subject=subject,
                            repo=repo_name,
                        ))

            update = FrontPageGit(
                source=self,
                time=tagged_timestamp,
                log=commits,
                tag=tag,
            )
            updates.append(update)

        return updates
Пример #16
0
def apply_pokemon_template(template, pokemon, dex_translate=_, _=_):
    u"""`template` should be a string.Template object.

    Uses safe_substitute to inject some fields from the Pokémon into the
    template.

    This cheerfully returns a literal, so be sure to escape the original format
    string BEFORE passing it to Template!
    """

    d = dict(
        icon=pokemon_sprite(pokemon, prefix=u'icons'),
        id=pokemon.national_id,
        name=pokemon.full_name,

        height=format_height_imperial(pokemon.height),
        height_ft=format_height_imperial(pokemon.height),
        height_m=format_height_metric(pokemon.height),
        weight=format_weight_imperial(pokemon.weight),
        weight_lb=format_weight_imperial(pokemon.weight),
        weight_kg=format_weight_metric(pokemon.weight),

        gender=_(gender_rate_label[pokemon.gender_rate]),
        species=dex_translate(pokemon.species),
        base_experience=pokemon.base_experience,
        capture_rate=pokemon.capture_rate,
        base_happiness=pokemon.base_happiness,
    )

    # "Lazy" loading, to avoid hitting other tables if unnecessary.  This is
    # very chumpy and doesn't distinguish between literal text and fields (e.g.
    # '$type' vs 'type'), but that's very unlikely to happen, and it's not a
    # big deal if it does
    if 'type' in template.template:
        types = pokemon.types
        d['type'] = u'/'.join(dex_translate(type_.name) for type_ in types)
        d['type1'] = dex_translate(types[0].name)
        d['type2'] = dex_translate(types[1].name) if len(types) > 1 else u''

    if 'egg_group' in template.template:
        egg_groups = pokemon.egg_groups
        d['egg_group'] = u'/'.join(dex_translate(group.name) for group in egg_groups)
        d['egg_group1'] = dex_translate(egg_groups[0].name)
        d['egg_group2'] = dex_translate(egg_groups[1].name) if len(egg_groups) > 1 else u''

    if 'ability' in template.template:
        abilities = pokemon.abilities
        d['ability'] = u'/'.join(dex_translate(ability.name) for ability in abilities)
        d['ability1'] = dex_translate(abilities[0].name)
        d['ability2'] = dex_translate(abilities[1].name) if len(abilities) > 1 else u''

    if 'color' in template.template:
        d['color'] = dex_translate(pokemon.color)

    if 'habitat' in template.template:
        d['habitat'] = dex_translate(pokemon.habitat)

    if 'shape' in template.template:
        if pokemon.shape:
            d['shape'] = dex_translate(pokemon.shape.name)
        else:
            d['shape'] = ''

    if 'hatch_counter' in template.template:
        d['hatch_counter'] = pokemon.hatch_counter

    if 'steps_to_hatch' in template.template:
        d['steps_to_hatch'] = (pokemon.hatch_counter + 1) * 255

    if 'stat' in template.template or \
       'hp' in template.template or \
       'attack' in template.template or \
       'defense' in template.template or \
       'speed' in template.template or \
       'effort' in template.template:
        d['effort'] = u', '.join("{0} {1}".format(_.effort, _.stat.name)
                                 for _ in pokemon.stats if _.effort)

        d['stats'] = u'/'.join(str(_.base_stat) for _ in pokemon.stats)

        for pokemon_stat in pokemon.stats:
            key = pokemon_stat.stat.name.lower().replace(' ', '_')
            d[key] = pokemon_stat.base_stat

    return h.literal(template.safe_substitute(d))
Пример #17
0
 def _linkify_bug_number(self, match):
     """Regex replace function for changing bug numbers into links."""
     n = match.group(1)
     bug_url = self.bug_tracker.format(match.group(1))
     return helpers.literal(u"""<a href="{0}">{1}</a>""".format(
         bug_url, match.group(0)))
Пример #18
0
def evolution_description(evolution, _=_):
    """Crafts a human-readable description from a `pokemon_evolution` row
    object.
    """
    chunks = []

    # Trigger
    if evolution.trigger.identifier == u'level-up':
        chunks.append(_(u'Level up'))
    elif evolution.trigger.identifier == u'trade':
        chunks.append(_(u'Trade'))
    elif evolution.trigger.identifier == u'use-item':
        chunks.append(
            h.literal(_(u"Use {article} {item}")).format(
                article=article(evolution.trigger_item.name, _=_),
                item=item_link(evolution.trigger_item, include_icon=False)))
    elif evolution.trigger.identifier == u'shed':
        chunks.append(
            _(u"Evolve {from_pokemon} ({to_pokemon} will consume "
              u"a Poké Ball and appear in a free party slot)").format(
                  from_pokemon=evolution.evolved_species.parent_species.name,
                  to_pokemon=evolution.evolved_species.name))
    else:
        chunks.append(_(u'Do something'))

    # Conditions
    if evolution.gender_id:
        chunks.append(_(u"{0}s only").format(evolution.gender.identifier))
    if evolution.time_of_day:
        chunks.append(_(u"during the {0}").format(evolution.time_of_day))
    if evolution.minimum_level:
        chunks.append(
            _(u"starting at level {0}").format(evolution.minimum_level))
    if evolution.location_id:
        chunks.append(
            h.literal(_(u"around {0} ({1})")).format(
                h.HTML.a(evolution.location.name,
                         href=url(controller='dex',
                                  action='locations',
                                  name=evolution.location.name.lower())),
                evolution.location.region.name))
    if evolution.held_item_id:
        chunks.append(
            h.literal(_(u"while holding {article} {item}")).format(
                article=article(evolution.held_item.name),
                item=item_link(evolution.held_item, include_icon=False)))
    if evolution.known_move_id:
        chunks.append(
            h.literal(_(u"knowing {0}")).format(
                h.HTML.a(evolution.known_move.name,
                         href=url(controller='dex',
                                  action='moves',
                                  name=evolution.known_move.name.lower()))))
    if evolution.known_move_type_id:
        chunks.append(
            h.literal(_(u'knowing a {0}-type move')).format(
                h.HTML.a(evolution.known_move_type.name,
                         href=url(
                             controller='dex',
                             action='types',
                             name=evolution.known_move_type.name.lower()))))
    if evolution.minimum_happiness:
        chunks.append(
            _(u"with at least {0} happiness").format(
                evolution.minimum_happiness))
    if evolution.minimum_beauty:
        chunks.append(
            _(u"with at least {0} beauty").format(evolution.minimum_beauty))
    if evolution.minimum_affection:
        chunks.append(
            _(u'with at least {0} affection in Pokémon-Amie').format(
                evolution.minimum_affection))
    if evolution.relative_physical_stats is not None:
        if evolution.relative_physical_stats < 0:
            op = _(u'<')
        elif evolution.relative_physical_stats > 0:
            op = _(u'>')
        else:
            op = _(u'=')
        chunks.append(_(u"when Attack {0} Defense").format(op))
    if evolution.party_species_id:
        chunks.append(
            h.literal(_(u"with {0} in the party")).format(
                pokemon_link(evolution.party_species.default_pokemon,
                             include_icon=False)))
    if evolution.party_type_id:
        chunks.append(
            h.literal(_(u"with a {0}-type Pokémon in the party")).format(
                h.HTML.a(evolution.party_type.name,
                         href=url(controller='dex',
                                  action='types',
                                  name=evolution.party_type.name.lower()))))
    if evolution.trade_species_id:
        chunks.append(
            h.literal(_(u"in exchange for {0}")).format(
                pokemon_link(evolution.trade_species.default_pokemon,
                             include_icon=False)))
    if evolution.needs_overworld_rain:
        chunks.append(_(u'while it is raining outside of battle'))
    if evolution.turn_upside_down:
        chunks.append(_(u'with the 3DS turned upside-down'))

    return h.literal(u', ').join(chunks)
Пример #19
0
def evolution_description(evolution, _=_):
    """Crafts a human-readable description from a `pokemon_evolution` row
    object.
    """
    chunks = []

    # Trigger
    if evolution.trigger.identifier == u"level-up":
        chunks.append(_(u"Level up"))
    elif evolution.trigger.identifier == u"trade":
        chunks.append(_(u"Trade"))
    elif evolution.trigger.identifier == u"use-item":
        chunks.append(
            h.literal(_(u"Use {article} {item}")).format(
                article=article(evolution.trigger_item.name, _=_),
                item=item_link(evolution.trigger_item, include_icon=False),
            )
        )
    elif evolution.trigger.identifier == u"shed":
        chunks.append(
            _(
                u"Evolve {from_pokemon} ({to_pokemon} will consume " u"a Poké Ball and appear in a free party slot)"
            ).format(
                from_pokemon=evolution.evolved_species.parent_species.name, to_pokemon=evolution.evolved_species.name
            )
        )
    else:
        chunks.append(_(u"Do something"))

    # Conditions
    if evolution.gender_id:
        chunks.append(_(u"{0}s only").format(evolution.gender.identifier))
    if evolution.time_of_day:
        chunks.append(_(u"during the {0}").format(evolution.time_of_day))
    if evolution.minimum_level:
        chunks.append(_(u"starting at level {0}").format(evolution.minimum_level))
    if evolution.location_id:
        chunks.append(
            h.literal(_(u"around {0} ({1})")).format(
                h.HTML.a(
                    evolution.location.name,
                    href=url(controller="dex", action="locations", name=evolution.location.name.lower()),
                ),
                evolution.location.region.name,
            )
        )
    if evolution.held_item_id:
        chunks.append(
            h.literal(_(u"while holding {article} {item}")).format(
                article=article(evolution.held_item.name), item=item_link(evolution.held_item, include_icon=False)
            )
        )
    if evolution.known_move_id:
        chunks.append(
            h.literal(_(u"knowing {0}")).format(
                h.HTML.a(
                    evolution.known_move.name,
                    href=url(controller="dex", action="moves", name=evolution.known_move.name.lower()),
                )
            )
        )
    if evolution.known_move_type_id:
        chunks.append(
            h.literal(_(u"knowing a {0}-type move")).format(
                h.HTML.a(
                    evolution.known_move_type.name,
                    href=url(controller="dex", action="types", name=evolution.known_move_type.name.lower()),
                )
            )
        )
    if evolution.minimum_happiness:
        chunks.append(_(u"with at least {0} happiness").format(evolution.minimum_happiness))
    if evolution.minimum_beauty:
        chunks.append(_(u"with at least {0} beauty").format(evolution.minimum_beauty))
    if evolution.minimum_affection:
        chunks.append(_(u"with at least {0} affection in Pokémon-Amie").format(evolution.minimum_affection))
    if evolution.relative_physical_stats is not None:
        if evolution.relative_physical_stats < 0:
            op = _(u"<")
        elif evolution.relative_physical_stats > 0:
            op = _(u">")
        else:
            op = _(u"=")
        chunks.append(_(u"when Attack {0} Defense").format(op))
    if evolution.party_species_id:
        chunks.append(
            h.literal(_(u"with {0} in the party")).format(
                pokemon_link(evolution.party_species.default_pokemon, include_icon=False)
            )
        )
    if evolution.party_type_id:
        chunks.append(
            h.literal(_(u"with a {0}-type Pokémon in the party")).format(
                h.HTML.a(
                    evolution.party_type.name,
                    href=url(controller="dex", action="types", name=evolution.party_type.name.lower()),
                )
            )
        )
    if evolution.trade_species_id:
        chunks.append(
            h.literal(_(u"in exchange for {0}")).format(
                pokemon_link(evolution.trade_species.default_pokemon, include_icon=False)
            )
        )
    if evolution.needs_overworld_rain:
        chunks.append(_(u"while it is raining outside of battle"))
    if evolution.turn_upside_down:
        chunks.append(_(u"with the 3DS turned upside-down"))

    return h.literal(u", ").join(chunks)
Пример #20
0
def conquest_transformation_description(tform, _=_):
    """Crafts a human-readable description from a
    `conquest_warrior_transformation' row object.
    """

    # Dumb tricks for saving characters; this has a lot of big awkward lines
    def link(thing):
        return h.HTML.a(thing.name,
                        href=make_thingy_url(thing, controller='dex_conquest'))

    lit = h.literal

    chunks = []

    # Triggers
    if tform.is_automatic:
        chunks.append(_(u'Automatically happens in-story'))
    elif tform.required_link is not None:
        if tform.pokemon[0].identifier == 'eevee':
            pokemon = lit(_(u'{0} or any of its evolutions')).format(
                link(tform.pokemon[0]))
        else:
            pokemon = lit(_(u' or ')).join(link(p) for p in tform.pokemon)

        chunks.append(
            lit(_(u'Reach at least {link}% link with {pokemon}')).format(
                link=tform.required_link, pokemon=pokemon))

    # Conditions
    if tform.completed_episode_id is not None:
        warrior = tform.completed_episode.warriors[0]
        if warrior != tform.warrior_rank.warrior:
            warrior = link(warrior)
        else:
            warrior = warrior.name

        chunks.append(
            lit(_(u'after completing {warrior}\'s episode, "{name}"')).format(
                warrior=warrior, name=tform.completed_episode.name))
    elif tform.current_episode_id is not None:
        if tform.current_episode.warriors[0].identifier == 'player-m':
            warrior = _(u'the player')
        else:
            warrior = tform.current_episode.warriors[0].name

        chunks.append(
            _(u'during {warrior}\'s episode, "{name}"').format(
                warrior=warrior, name=tform.current_episode.name))
    elif tform.distant_warrior_id is not None:
        chunks.append(
            lit(
                _(u'with {0} in the army but not in the same kingdom '
                  u'or any adjacent kingdom')).format(
                      link(tform.distant_warrior)))
    elif tform.present_warriors:
        chunks.append(
            h.literal(_(u'with {0} in the same kingdom')).format(
                lit(_(u' and ')).join(
                    link(warrior) for warrior in tform.present_warriors)))
    elif tform.female_warlord_count is not None:
        chunks.append(
            _(u'with at least {0} female warlords in the same kingdom').format(
                tform.female_warlord_count))
    elif tform.pokemon_count is not None:
        chunks.append(
            _(u'with at least {0} Pokémon in the gallery').format(
                tform.pokemon_count))
    elif tform.collection_type_id is not None:
        chunks.append(
            _(u'with all {0}-type Pokémon in the gallery').format(
                tform.type.name))
    elif tform.warrior_count is not None:
        chunks.append(
            _(u'with at least {0} warriors in the gallery').format(
                tform.warrior_count))

    return lit(u', ').join(chunks)
Пример #21
0
def conquest_transformation_description(tform, _=_):
    """Crafts a human-readable description from a
    `conquest_warrior_transformation' row object.
    """
    # Dumb tricks for saving characters; this has a lot of big awkward lines
    def link(thing):
        return h.HTML.a(thing.name, href=make_thingy_url(thing, controller="dex_conquest"))

    lit = h.literal

    chunks = []

    # Triggers
    if tform.is_automatic:
        chunks.append(_(u"Automatically happens in-story"))
    elif tform.required_link is not None:
        if tform.pokemon[0].identifier == "eevee":
            pokemon = lit(_(u"{0} or any of its evolutions")).format(link(tform.pokemon[0]))
        else:
            pokemon = lit(_(u" or ")).join(link(p) for p in tform.pokemon)

        chunks.append(
            lit(_(u"Reach at least {link}% link with {pokemon}")).format(link=tform.required_link, pokemon=pokemon)
        )

    # Conditions
    if tform.completed_episode_id is not None:
        warrior = tform.completed_episode.warriors[0]
        if warrior != tform.warrior_rank.warrior:
            warrior = link(warrior)
        else:
            warrior = warrior.name

        chunks.append(
            lit(_(u'after completing {warrior}\'s episode, "{name}"')).format(
                warrior=warrior, name=tform.completed_episode.name
            )
        )
    elif tform.current_episode_id is not None:
        if tform.current_episode.warriors[0].identifier == "player-m":
            warrior = _(u"the player")
        else:
            warrior = tform.current_episode.warriors[0].name

        chunks.append(
            _(u'during {warrior}\'s episode, "{name}"').format(warrior=warrior, name=tform.current_episode.name)
        )
    elif tform.distant_warrior_id is not None:
        chunks.append(
            lit(_(u"with {0} in the army but not in the same kingdom " u"or any adjacent kingdom")).format(
                link(tform.distant_warrior)
            )
        )
    elif tform.present_warriors:
        chunks.append(
            h.literal(_(u"with {0} in the same kingdom")).format(
                lit(_(u" and ")).join(link(warrior) for warrior in tform.present_warriors)
            )
        )
    elif tform.female_warlord_count is not None:
        chunks.append(_(u"with at least {0} female warlords in the same kingdom").format(tform.female_warlord_count))
    elif tform.pokemon_count is not None:
        chunks.append(_(u"with at least {0} Pokémon in the gallery").format(tform.pokemon_count))
    elif tform.collection_type_id is not None:
        chunks.append(_(u"with all {0}-type Pokémon in the gallery").format(tform.type.name))
    elif tform.warrior_count is not None:
        chunks.append(_(u"with at least {0} warriors in the gallery").format(tform.warrior_count))

    return lit(u", ").join(chunks)
Пример #22
0
def apply_pokemon_template(template, pokemon, _=_):
    u"""`template` should be a string.Template object.

    Uses safe_substitute to inject some fields from the Pokémon into the
    template.

    This cheerfully returns a literal, so be sure to escape the original format
    string BEFORE passing it to Template!
    """

    d = dict(
        icon=pokemon_form_image(pokemon.default_form, prefix=u"icons"),
        id=pokemon.species.id,
        name=pokemon.default_form.name,
        height=format_height_imperial(pokemon.height),
        height_ft=format_height_imperial(pokemon.height),
        height_m=format_height_metric(pokemon.height),
        weight=format_weight_imperial(pokemon.weight),
        weight_lb=format_weight_imperial(pokemon.weight),
        weight_kg=format_weight_metric(pokemon.weight),
        gender=_(gender_rate_label[pokemon.species.gender_rate]),
        genus=pokemon.species.genus,
        base_experience=pokemon.base_experience,
        capture_rate=pokemon.species.capture_rate,
        base_happiness=pokemon.species.base_happiness,
    )

    # "Lazy" loading, to avoid hitting other tables if unnecessary.  This is
    # very chumpy and doesn't distinguish between literal text and fields (e.g.
    # '$type' vs 'type'), but that's very unlikely to happen, and it's not a
    # big deal if it does
    if "type" in template.template:
        types = pokemon.types
        d["type"] = u"/".join(type_.name for type_ in types)
        d["type1"] = types[0].name
        d["type2"] = types[1].name if len(types) > 1 else u""

    if "egg_group" in template.template:
        egg_groups = pokemon.species.egg_groups
        d["egg_group"] = u"/".join(group.name for group in egg_groups)
        d["egg_group1"] = egg_groups[0].name
        d["egg_group2"] = egg_groups[1].name if len(egg_groups) > 1 else u""

    if "ability" in template.template:
        abilities = pokemon.abilities
        d["ability"] = u"/".join(ability.name for ability in abilities)
        d["ability1"] = abilities[0].name
        d["ability2"] = abilities[1].name if len(abilities) > 1 else u""
        if pokemon.hidden_ability:
            d["hidden_ability"] = pokemon.hidden_ability.name
        else:
            d["hidden_ability"] = u""

    if "color" in template.template:
        d["color"] = pokemon.species.color.name

    if "habitat" in template.template:
        d["habitat"] = pokemon.species.habitat.name

    if "shape" in template.template:
        if pokemon.species.shape:
            d["shape"] = pokemon.species.shape.name
        else:
            d["shape"] = ""

    if "hatch_counter" in template.template:
        d["hatch_counter"] = pokemon.species.hatch_counter

    if "steps_to_hatch" in template.template:
        d["steps_to_hatch"] = (pokemon.species.hatch_counter + 1) * 255

    if (
        "stat" in template.template
        or "hp" in template.template
        or "attack" in template.template
        or "defense" in template.template
        or "speed" in template.template
        or "effort" in template.template
    ):
        d["effort"] = u", ".join("{0} {1}".format(_.effort, _.stat.name) for _ in pokemon.stats if _.effort)

        d["stats"] = u"/".join(str(_.base_stat) for _ in pokemon.stats)

        for pokemon_stat in pokemon.stats:
            key = pokemon_stat.stat.name.lower().replace(" ", "_")
            d[key] = pokemon_stat.base_stat

    return h.literal(template.safe_substitute(d))
Пример #23
0
    def _poll(self, limit, max_age):
        feed = feedparser.parse(self.feed_url)

        if feed.bozo and isinstance(feed.bozo_exception, URLError):
            # Feed is DOWN.  Bail here; otherwise, old entries might be lost
            # just because, say, Bulbanews is down yet again
            return None

        if not self.title:
            self.title = feed.feed.title

        updates = []
        for entry in feed.entries[:limit]:
            # Grab a date -- Atom has published, RSS usually just has updated.
            # Both come out as time tuples, which datetime.datetime() can read
            try:
                timestamp_tuple = entry.published_parsed
            except AttributeError:
                timestamp_tuple = entry.updated_parsed
            timestamp = datetime.datetime(*timestamp_tuple[:6])

            if max_age and timestamp < max_age:
                # Entries should be oldest-first, so we can bail after the first
                # expired entry
                break

            # Try to find something to show!  Default to the summary, if there is
            # one, or try to generate one otherwise
            content = u''
            if 'summary' in entry:
                # If there be a summary, cheerfully trust that it's actually a
                # summary
                content = entry.summary
            elif 'content' in entry and \
                len(entry.content[0].value) <= self.SUMMARY_LENGTH:

                # Full content is short; use as-is!
                content = entry.content[0].value
            elif 'content' in entry:
                # Full content is way too much, especially for my giant blog posts.
                # Cut this down to some arbitrary number of characters, then feed
                # it to lxml.html to fix tag nesting
                broken_html = entry.content[0].value[:self.SUMMARY_LENGTH]
                fragment = lxml.html.fromstring(broken_html)

                # Insert an ellipsis at the end of the last node with text
                last_text_node = None
                last_tail_node = None
                # Need to find the last node with a tail, OR the last node with
                # text if it's later
                for node in fragment.iter():
                    if node.tail:
                        last_tail_node = node
                        last_text_node = None
                    elif node.text:
                        last_text_node = node
                        last_tail_node = None

                if last_text_node is not None:
                    last_text_node.text += '...'
                if last_tail_node is not None:
                    last_tail_node.tail += '...'

                # Serialize
                content = lxml.html.tostring(fragment)

            content = helpers.literal(content)

            update = FrontPageRSS(
                source = self,
                time = timestamp,
                content = content,
                entry = entry,
            )
            updates.append(update)

        return updates