Mud/Water Sport last until the user leaves battle. #568
[zzz-pokedex.git] / pokedex / db / markdown.py
1 # encoding: utf8
2 u"""Implements the markup used for description and effect text in the database.
3
4 The language used is a variation of Markdown and Markdown Extra. There are
5 docs for each at http://daringfireball.net/projects/markdown/ and
6 http://michelf.com/projects/php-markdown/extra/ respectively.
7
8 Pokédex links are represented with the extended syntax `[name]{type}`, e.g.,
9 `[Eevee]{pokemon}`. The actual code that parses these is in spline-pokedex.
10 """
11 from __future__ import absolute_import
12
13 import markdown
14 import sqlalchemy.types
15
16 class MarkdownString(object):
17 """Wraps a Markdown string. Stringifies to the original text, but .as_html
18 will return an HTML rendering.
19
20 To add extensions to the rendering (which is necessary for rendering links
21 correctly, and which spline-pokedex does), you must append to this class's
22 `markdown_extensions` list. Yep, that's gross.
23 """
24
25 markdown_extensions = ['extra']
26
27 def __init__(self, source_text):
28 self.source_text = source_text
29 self._as_html = None
30
31 def __unicode__(self):
32 return self.source_text
33
34 @property
35 def as_html(self):
36 """Returns the string as HTML4."""
37
38 if self._as_html:
39 return self._as_html
40
41 md = markdown.Markdown(
42 extensions=self.markdown_extensions,
43 safe_mode='escape',
44 output_format='xhtml1',
45 )
46
47 self._as_html = md.convert(self.source_text)
48
49 return self._as_html
50
51 @property
52 def as_text(self):
53 """Returns the string in a plaintext-friendly form.
54
55 At the moment, this is just the original source text.
56 """
57 return self.source_text
58
59 def _markdownify_effect_text(move, effect_text):
60 effect_text = effect_text.replace(
61 u'$effect_chance',
62 unicode(move.effect_chance),
63 )
64
65 return MarkdownString(effect_text)
66
67 class MoveEffectProperty(object):
68 """Property that wraps move effects. Used like this:
69
70 MoveClass.effect = MoveEffectProperty('effect')
71
72 some_move.effect # returns a MarkdownString
73 some_move.effect.as_html # returns a chunk of HTML
74
75 This class attempts to detect if the wrapped property is a dict-based
76 association proxy, and will act like such a dict if so. Don't rely on it
77 for querying, of course.
78
79 This class also performs simple substitution on the effect, replacing
80 `$effect_chance` with the move's actual effect chance.
81 """
82
83 def __init__(self, effect_column):
84 self.effect_column = effect_column
85
86 def __get__(self, obj, cls):
87 prop = getattr(obj.move_effect, self.effect_column)
88 if isinstance(prop, dict):
89 # Looks like a dict proxy; markdownify everyone
90 newdict = dict(prop)
91 for key in newdict:
92 newdict[key] = _markdownify_effect_text(obj, newdict[key])
93 return newdict
94
95 # Otherwise, scalar prop. Boring
96 return _markdownify_effect_text(obj, prop)
97
98 class MarkdownColumn(sqlalchemy.types.TypeDecorator):
99 """Generic SQLAlchemy column type for Markdown text.
100
101 Do NOT use this for move effects! They need to know what move they belong
102 to so they can fill in, e.g., effect chances. Use the MoveEffectProperty
103 property class above.
104 """
105 impl = sqlalchemy.types.Unicode
106
107 def process_bind_param(self, value, dialect):
108 if not isinstance(value, basestring):
109 # Can't assign, e.g., MarkdownString objects yet
110 raise NotImplementedError
111
112 return unicode(value)
113
114 def process_result_value(self, value, dialect):
115 return MarkdownString(value)