def zipme(self): """Zip the Campaing into a cmpgn file.""" log.info("Zipping %s" % self) if not os.path.exists('build'): os.makedirs('build') with zipfile.ZipFile(os.path.join('build', '%s.cmpgn'%self.name), 'w') as zipme: zipme.writestr('content.xml', self.content_xml.encode('utf-8')) zipme.writestr('properties.xml', self.properties_xml) md5s = [] # record added assets for name, asset in self.assets: if asset.md5 in md5s: continue # dont zip the same file twice log.debug("adding asset %s: %s" % (name, asset)) md5s.append(asset.md5) zipme.writestr('assets/%s' % asset.md5, jenv().get_template('md5.template').render(name=os.path.splitext(os.path.basename(asset.fp))[0], extension='png', md5=asset.md5)) zipme.writestr('assets/%s.png' % asset.md5, asset.bytes)
def zipme(self): """Zip the token into a rptok file.""" log.info("Zipping %s" % self) if not os.path.exists('build'): os.makedirs('build') with zipfile.ZipFile(os.path.join('build', '%s.rptok'%self.name), 'w') as zipme: zipme.writestr('content.xml', self.content_xml) zipme.writestr('properties.xml', self.properties_xml) # default image for the token, right now it's a brown bear # zip the xml file named with the md5 containing the asset properties for asset in self.assets.values(): zipme.writestr('assets/%s' % asset.md5, jenv().get_template('md5.template').render(name=asset.fp, extension='png', md5=asset.md5)) # zip the img itself zipme.writestr('assets/%s.png' % asset.md5, asset.bytes) # build thumbnails zipme.writestr('thumbnail', self.icon.thumbnail(50,50).getvalue()) zipme.writestr('thumbnail_large', self.icon.thumbnail(500,500).getvalue())
def zipme(self): """Zip the token into a rptok file.""" filename = os.path.join('build', '%s.rptok'%(self.name.replace(":","_"))) # don't compress to avoid technical issue when sharing files # the gain is very small anyway with zipfile.ZipFile(filename, "w", zipfile.ZIP_STORED) as zipme: zipme.writestr('content.xml', self.content_xml.encode('utf-8')) zipme.writestr('properties.xml', self.properties_xml.encode('utf-8')) # default image for the token, right now it's a brown bear # zip the xml file named with the md5 containing the asset properties for name, asset in self.assets.iteritems(): zipme.writestr('assets/%s' % asset.md5, jenv().get_template('md5.template').render(name=name, extension='png', md5=asset.md5).encode("utf-8")) zipme.writestr('assets/%s.png' % asset.md5, asset.bytes) # build thumbnails zipme.writestr('thumbnail', self.img.thumbnail(50,50).getvalue()) #dont include the large thumbnail, it will double the token size for no benefit #zipme.writestr('thumbnail_large', self.img.thumbnail(500,500).getvalue()) return filename
def content_xml(self): return jenv().get_template('zone_content.template').render( zone=self) or u''
def main(): parser = argparse.ArgumentParser(description='DnD 5e token builder') parser.add_argument('--verbose', '-v', action='count') parser.add_argument('--max-token', '-m', type=int) parser.add_argument('--delivery', '-d', action="store_true", default=False) global args args = parser.parse_args() if not os.path.exists('build'): os.makedirs('build') localMonsters = [] tob = '../open5e/legacy-source-content/monsters/tome-of-beasts/' sources = [ r'../5e-database/5e-SRD-Monsters-volo.json', r'../5e-database/5e-SRD-Monsters.json', ] sources += [os.path.join(dp, f) for dp, dn, filenames in os.walk(tob) for f in filenames if os.path.splitext(f)[1] == '.rst' and 'index' not in f] for f in sources: with codecs.open(f, 'r', encoding='utf8') as mfile: if f.endswith('json'): localMonsters += json.load(mfile) if f.endswith('rst'): localMonsters += [loadFromRst(mfile)] mLog = logging.getLogger() mLog.setLevel(logging.DEBUG) mLog.handlers[-1].setLevel(logging.WARNING-(args.verbose or 0)*10) fh = logging.FileHandler(os.path.join('build', 'tokens.log'), mode="w") # mode w will erase previous logs fh.setLevel(logging.DEBUG) fh.setFormatter(logging.Formatter('%(name)s : %(levelname)s : %(message)s')) mLog.addHandler(fh) # generate the lib addon token addon = LibToken('Lib:Addon5e') fromFile = lambda path: jenv().get_template(path).render().encode("utf-8") params = {'group': 'zLib', 'prefix': 'a5e'} addon.add(macros.Macro(addon, '', 'onCampaignLoad', ''' [h: defineFunction( "%(prefix)s.jget", "jget@this" )] [h: defineFunction( "%(prefix)s.debug", "debug@this" )] [h: defineFunction( "%(prefix)s.output", "output@this" )] [h: defineFunction( "%(prefix)s.rollDice", "rollDice@this",0,0)] ''' % params, **params)) addon.add(macros.Macro(addon, '', 'debug', '''[h: props = getPropertyNames()] [foreach(name, props, "<br>"), code: { [name]: [getProperty(name)]: [getRawProperty(name)]}] ''', **params)) addon.add(macros.Macro(addon, '', 'output', fromFile("output.mtmacro"), **params)) addon.add(macros.Macro(addon, '', 'rollDice', fromFile("rollDice.mtmacro"), **params)) addon.add(macros.Macro(addon, '', 'jget', ''' [h: '<!-- Like json.get, but will adapt if the requested reference cannot be made. By default, returns 0, or returns a default named (as a third parameter). -->'] [h: object = arg(0)] [h: key = arg(1)] [h, if( argCount() > 2 ): default = arg(2); default = 0] [h, if( json.type( object ) == "OBJECT" ), code: { [h: macro.return = if( json.contains( object, key ), json.get( object, key ), default )] };{ [if( json.type( object ) == "ARRAY" && isNumber( key ) ), code: { [h, if( json.length( object ) > key ): macro.return = json.get( object, key ) ; macro.return = default )] };{ [h: macro.return = default ] }] }] ''', **params)) params = {'group': 'dnd5e'} addon.add(macros.Macro(addon, '', 'Description', fromFile('description.mtmacro'), **params)) addon.add(macros.Macro(addon, '', 'CastSpell', fromFile('castSpell.mtmacro'), **params)) addon.add(macros.Macro(addon, '', 'NPCAttack', fromFile('npcAttack.mtmacro'), **params)) addon.add(macros.Macro(addon, '', 'Init', fromFile('init.mtmacro'), **params)) addon.add(macros.Macro(addon, '', 'getNPCInitBonus', '''[h, macro("getNPCSkills@Lib:Addon5e"):0] [h: jskills = macro.return] [h: initb = json.get(jskills, "Initiative")] [h, if (initb==""), code: {[h: initb=getProperty("bdex")]}] [h:macro.return=initb]''', **params)) # "Perception +5, Initiative +3" => {"Perception": 5, "Initiative": 3} addon.add(macros.Macro(addon, '', 'getNPCSkills', r'''<!-- Fetch skill bonuses--> <!-- Depending on the source (SRD, tome of the beast, MM) format of skill data may change--> <!-- Method 1: fetch skill entries like "skills: Perception +5, Stealth +4"--> [h: id = strfind(getProperty("skills"), "((\\w+) \\+(\\d+))")] [h: jskills = "{}"] [h: find = getFindCount(id)] [h, while (find != 0), code: { [h: sname = getGroup(id, find, 2)] [h: svalue = getGroup(id, find, 3)] [h: jskills = json.set(jskills, sname, svalue)] [h: find = find - 1] }] [h: all_skills= getLibProperty("all_skills", "Lib:Addon5e")] <!-- Most of the token don't specify a modifier for all skills--> <!-- for all skills missing a modifier, use the default one which is the attribute modifier --> [h, foreach(skill, all_skills), code: { [Attribute = json.get(all_skills, skill)] [att_ = lower(substring(Attribute, 0, 3))] [modifier = json.get(jskills, skill)] [default_mod = getProperty("b"+att_)] [no_mod = json.isEmpty(modifier) ] [if (no_mod): jskills = json.set(jskills, skill , default_mod)] }] [h: macro.return = jskills]''', **params)) # "Wis +3, Con +2" => {"Wis": 2, "Con": 2} addon.add(macros.Macro(addon, '', 'getNPCSaves', r'''[h: id = strfind(getProperty("saves"), "((\\w+) \\+(\\d+))")] [h: jsaves= "{}"] [h: find = getFindCount(id)] <!-- parse the prop "saves" which may contain some save modifiers--> <!-- "Wis +3, Con +2" => "Wis": 2, "Con": 2 --> [h, while (find != 0), code: { [h: sname = getGroup(id, find, 2)] [h: svalue = getGroup(id, find, 3)] [h: jsaves = json.set(jsaves, sname, svalue)] [h: find = find - 1] }] <!-- Most of the token don't specify a modifier for all saves --> <!-- for all saves missing a modifier, use the default one which is the attribute modifier --> [h, foreach(Attribute, getLibProperty("attributes", "Lib:Addon5e")), code: { [Att = substring(Attribute, 0, 3)] [att_ = lower(Att)] [modifier = json.get(jsaves, Att)] [default_mod = getProperty("b"+att_)] [no_mod = json.isEmpty(modifier) ] [if (no_mod): jsaves = json.set(jsaves, Att ,default_mod)] }] [h: macro.return = jsaves]''', **params)) addon.add(macros.Macro(addon, '', 'SaveMe', fromFile('saveme.mtmacro'), **params)) addon.add(macros.Macro(addon, '', 'CheckMe', fromFile('checkme.mtmacro'), **params)) params = {'group': 'aMenu'} # TODO: control panel is currently empty but it is a customized panel where I can add whatever macro, it act as a campaign panel # but is fully customizable, it's a html form # see http://forums.rptools.net/viewtopic.php?f=20&t=23208&p=236662&hilit=amsave#p236662 addon.add(macros.Macro(addon, '', 'ControlPanel', '''[dialog("A5e Panel", "width=215; height=700; temporary=0; input=1"): {[r,macro("cpanel@this"):0]}]''', **params)) params = {'group': 'Format'} addon.add(macros.Macro(addon, '', 'cpanel', fromFile('cpanel.mtmacro'), **params)) addon.add(macros.Macro(addon, '', 'HTMLMacroButton','''[h:bgColor = arg(1)] [h,if(argCount() > 5): shadow = arg(5); shadow = "")] [h,if(argCount() > 6): toolTip = arg(6); toolTip = "")] [h,if(argCount() > 7): args = arg(7); args = "[]")] [h,if(argCount() > 8): libType = arg(8); libType = "@this")] [h,if(argCount() > 9): output = arg(9); output = "none")] [h:btnformat = strformat("padding:1px; border-width:1pt; border-style:solid; border-color:black; text-align:center; white-space:nowrap; background-image:url(%{shadow}); background-color:%{bgColor};")] <td width='[r:arg(0)]%'> <table width='100%' cellpadding='0' cellspacing='0'> <tr> <td style='[r:btnformat]'> <span title='[r:toolTip]' style='text-decoration:none; color:[r:arg(2)]'> [r:macroLink(arg(3),arg(4)+libType,output,args)] </span> </td> </tr> </table> </td>''' , **params)) filename = addon.zipme() log.warning("Done generating 1 library token: %s", addon) poi = POI("POI") # fetch the monsters(token) and spells from dnd5Api or get them from the serialized file #tokens = itertools.chain((Token(m) for m in monsters), Token.load('build')) # dont use online api, use the fectched local database instead tokens = itertools.chain([poi], (Token(m) for m in itertools.chain(localMonsters))) # 5e-database is probably a link with open(r'../5e-database/5e-SRD-Spells.json', 'r') as mfile: localSpells = json.load(mfile) Spell.spellDB = [Spell(spell) for spell in localSpells] sTokens = [] # used for further serialization, because tokens is a generator and will be consumed cnt = 0 deliveryFilename = 'build/dnd5eTokens.zip' zfile = zipfile.ZipFile(deliveryFilename, "w", zipfile.ZIP_STORED) if args.delivery else None # add lib:addon5e to the zipfile if zfile: zfile.write(filename, os.path.relpath(filename, start='build')) for token in itertools.islice(tokens, args.max_token): log.info(token) log.debug(token.verbose()) filename = token.zipme() if zfile: zfile.write(filename, os.path.relpath(filename, start='build')) sTokens.append(token) if 'dft.png' in token.img.name: log.warning(str(token)) cnt += 1 log.warning("Done generating %s tokens"%cnt) log.warning("building campaign file") zone = Zone('Library') zone.build(sTokens + [addon]) cp = Campaign('demo5e') cp.build([zone], [PSet('Basic', [])], []) log.warning("Done building campaign file") if zfile: zfile.close() log.warning("Done writing delivery zip file '%s'" % deliveryFilename) Token.dump('build', sTokens) Spell.dump('build', Spell.spellDB)
def properties_xml(self): return jenv().get_template('properties.template').render()
def content_xml(self): return jenv().get_template('content.template').render(token=self) or u''
class Token(Dnd5ApiObject): sentinel = object() sfile_name = 'tokens.pickle' category = 'monsters' pngFiles = sentinel def __init__(self, js): self.js = js self._assets = None self.x, self.y = 0, 0 # for cached properties self._guid = self.sentinel self._macros = [] def __repr__(self): return 'Token<name=%s,attr=%s,hp=%s(%s),ac=%s,CR%s,img=%s>' % (self.name, [ self.strength, self.dexterity, self.constitution, self.intelligence, self.wisdom, self.charisma ], self.hit_points, self.roll_max_hp, self.armor_class, self.challenge_rating, self.img.name) # The 2 following methods are use by pickle to serialize a token def __setstate__(self, state): Dnd5ApiObject.__setstate__(self, state) self._guid = self.sentinel self._assets = None @property def assets(self): if self._assets is None: self._assets = {} # try to fetch an appropriate image from the imglib directory # using a stupid heuristic: the image / token.name match ratio # compute the diff ratio for the given name compared to the token name ratio = lambda name: difflib.SequenceMatcher(None, name.lower(), self.name.lower()).ratio() # morph "/abc/def/anyfile.png" into "anyfile" short_name = lambda full_path: os.path.splitext(os.path.basename(full_path))[0] bratio=0 if self.pngs: # generate the diff ratios ratios = ((f, ratio(short_name(f))) for f in self.pngs) # pickup the best match, it's a tuple (fpath, ratio) bfpath, bratio = max(itertools.chain(ratios, [('', 0)]), key = lambda i: i[1]) log.debug("Best match from the img lib is %s(%s)" % (bfpath, bratio)) # in delivery mode, do not add the tome of beast art, per author request if bratio > 0.8 and (self.js.get("ref", "")!="Tome of Beast" or not args.delivery): self._assets['null'] = Img(bfpath) else: self._assets['null'] = Img(imglib+'/dft.png') return self._assets @property def guid(self): if self._guid is self.sentinel: self._guid = guid() return self._guid @property def content_xml(self): return jenv().get_template('content.template').render(token=self) or u'' @property def properties_xml(self): return jenv().get_template('properties.template').render() def render(self): return self.content_xml @property def portrait(self): return None def abonus(self, attribute): return (getattr(self, attribute.lower())-10)/2 @property def bcon(self): return self.abonus('constitution') @property def bdex(self): return self.abonus('dexterity') @property def bwis(self): return self.abonus('wisdom') @property def roll_max_hp(self): dice, value = map(int, self.hit_dice.split('d')) return '%sd%s+%s' % (dice, value, dice*self.bcon) @property def max_hit_dice(self): dice, value = map(int, self.hit_dice.split('d')) hd = {'1d12':0, '1d10':0, '1d8':0, '1d6':0} hd.update({'1d%s'%value:dice}) return hd # spellcasting @property def sc(self): return next((spe for spe in self.specials if spe['name'] == 'Spellcasting'), None) # wisdom, charisma ot intelligence @property def scAttributes(self): # spellcasting attribute if self.sc is None: return None desc = self.sc['desc'].lower() if self.sc else '' attr = next((attr for attr in ['intelligence', 'charisma', 'wisdom'] if attr in desc), None) match = re.search(r'save dc (\d+)', desc, re.IGNORECASE) dc = match and int(match.group(1)) match = re.search(r'([+-]\d+) to hit with spell', desc) # extrack the spell hit bonus, otherwise use the spell castin attribute bonus. attack = match and match.group(1) if attack is None and attr: attack = self.abonus(attr) return (attr, dc, attack) if desc and attr and dc and attack else None @property def actions(self): return self.js.get('actions', []) @property def specials(self): return self.js.get('special_abilities', []) @property def legends(self): return self.js.get('legendary_actions', []) @property def lair_actions(self): return self.js.get('lair_actions', []) @property def regional_effects(self): return self.js.get('regional_effects', []) @property def passive_perception(self): return 10+self.js.get('perception', self.bwis) @property def vulnerabilities(self): return self.js.get('damage_vulnerabilities', "") @property def type(self): foo = self.js.get('type', 'unknown type') if foo[-1] == 's': print self return foo @property def skills(self): skills = self.js.get('skills', "") if skills=="": skills = ", ".join(["%s +%d" % (sk,self.js[sk]) for sk in all_skills().keys() if self.js.get(sk, None)]) return skills # saves can be specified in different ways: # either a field "saves": "Saving Throws Int +5, Wis +5, Cha +4" # or respective field like "wisdom_save": 5 @property def saves(self): saves = self.js.get('saves', "") if saves == "": saves = ", ".join([ '%s %+d'% (attr[:3], self.js["%s_save"%attr.lower()]) for attr in self.attributes if "%s_save" % attr.lower() in self.js]) return saves @property def note(self): return '' @property def immunities(self): return self.js.get('damage_immunities', '') @property def resistances(self): return self.js.get('damage_resistances','') @property def size_guid(self): # XXX may depend on the maptool version return { 'tiny': 'fwABAc5lFSoDAAAAKgABAA==', 'small': 'fwABAc5lFSoEAAAAKgABAA==', 'medium': 'fwABAc9lFSoFAAAAKgABAQ==', 'large': 'fwABAdBlFSoGAAAAKgABAA==', 'huge': 'fwABAdBlFSoHAAAAKgABAA==', 'gargantuan': 'fwABAdFlFSoIAAAAKgABAQ==', }[self.size.lower()] @property def macros(self): if self._macros: return self._macros # get optinal macros related to the token actions actions = (macros.ActionMacro(self, action) for action in self.actions if action["name"]) lairs = (macros.LairMacro(self, action) for action in self.lair_actions if action["name"]) reg = (macros.RegionalEffectMacro(self, action) for action in self.regional_effects if action["name"]) legends= (macros.LegendaryMacro(self, leg) for leg in self.legends if leg["name"]) attributes = self.scAttributes if self.sc and attributes is None: log.warning("Token %s has malformed spellcasting info: %s", self, self.sc) spellCast = [] if attributes: attr, dc, attack = attributes groupName = 'Spells(%s) save DC%s attack %s' % (attr[:3], dc, attack) spellCast = (macros.SpellCastingMacro(self, spe, groupName) for spe in self.specials if spe['name'].lower()=="spellcasting") specials = (macros.SpecialMacro(self, spe) for spe in self.specials if spe['name'] and spe['name'].lower()!="spellcasting") spells = (macros.SpellMacro(self, spell) for spell in self.spells) commons = [ macros.SheetMacro(self), macros.Macro(self, None, 'Init', '[macro("Init@Lib:Addon5e"):0]', **{'group': 'Rolls', 'colors': ('white', 'green'), 'tooltip': 'Roll and add to the init panel'}), macros.Macro(self, None, 'SaveMe', '[macro("SaveMe@Lib:Addon5e"):0]', **{'group': 'Rolls', 'colors': ('white', 'green'), 'tooltip': 'Roll Saving Throws'}), macros.Macro(self, None, 'CheckMe', '[macro("CheckMe@Lib:Addon5e"):0]', **{'group': 'Rolls', 'colors': ('white', 'green'), 'tooltip': 'Roll Skill Checks'}), ] if not args.delivery: commons.append(macros.Macro(self, None, 'Debug', '[r: a5e.debug()]', **{'group': 'zDebug', 'colors': ('white', 'black')})) self._macros = list(itertools.chain(actions, spellCast, specials, legends, lairs, reg, commons, spells)) return self._macros @property def slots(self): # current spendable slots slots = collections.OrderedDict() if self.sc is not None: sc = self.sc['desc'] match = re.search(r'1st level \((\d) slot', sc) slots['First'] = int(match.group(1)) if match else 0 match = re.search(r'2nd level \((\d) slot', sc) slots['Second'] = int(match.group(1)) if match else 0 match = re.search(r'3rd level \((\d) slot', sc) slots['Third'] = int(match.group(1)) if match else 0 match = re.search(r'4th level \((\d) slot', sc) slots['Fourth'] = int(match.group(1)) if match else 0 match = re.search(r'5th level \((\d) slot', sc) slots['Fifth'] = int(match.group(1)) if match else 0 match = re.search(r'6th level \((\d) slot', sc) slots['Sixth'] = int(match.group(1)) if match else 0 match = re.search(r'7th level \((\d) slot', sc) slots['Seventh'] = int(match.group(1)) if match else 0 match = re.search(r'8th level \((\d) slot', sc) slots['Eighth'] = int(match.group(1)) if match else 0 match = re.search(r'9th level \((\d) slot', sc) slots['Ninth'] = int(match.group(1)) if match else 0 return slots @property def spell_slots(self): #max slots available spells={} for i, (k,v) in enumerate(self.slots.iteritems()): spells["%s"%(i+1)] = v return spells @property def spells(self): spells = [] for ability in (a for a in self.specials if 'spellcasting' in a['name'].lower()): spells.extend([s for s in Spell.spellDB if s.name.lower() in ability['desc']]) return spells @property def attributes(self): return ['Strength', 'Dexterity', 'Constitution', 'Intelligence', 'Wisdom', 'Charisma'] @property def props(self): return (Prop(name, value) for name, value in [ ('mname', self.name), ('AC', self.armor_class), ('MaxHp', self.hit_points), ('Hp', self.hit_points), ('HitDice', self.hit_dice), ('Strength', self.strength), ('Dexterity', self.dexterity), ('Constitution', self.constitution), ('Wisdom', self.wisdom), ('Intelligence', self.intelligence), ('Charisma', self.charisma), ('Initiative', '[h,macro("getNPCInitBonus@Lib:Addon5e"):0][r: macro.return]'), ('Immunities', self.immunities), # XXX add condition immunities ? ('Resistances', self.resistances), ('CreatureType', self.type + ', CR ' + str(self.challenge_rating)), ('Alignment', self.alignment), ('Speed', self.speed), ('Saves', self.saves), ('Skills', self.skills), ('jSkills', '[h,macro("getNPCSkills@Lib:Addon5e"):0][r: macro.return]'), ('Senses', self.senses), ('Vulnerabilities', self.vulnerabilities), ('Resistances', self.resistances), ('Immunities', self.immunities), ('Languages', self.languages), ('passive perception', self.passive_perception), ('ImageName', self.img.name), ('SpellSlots', self.spell_slots), # do ('bstr', '{floor((getProperty("Strength")-10)/2)}') for all attributes ] + [('b%s' % a[:3].lower(), '{floor((getProperty("%s")-10)/2)}' % a) for a in self.attributes] + [(k, v) for k,v in self.slots.iteritems()] ) @property def pngs(self): if self.pngFiles is self.sentinel: Token.pngFiles = list(itertools.chain(*(glob.glob(os.path.join(os.path.expanduser(imglib), '*.png')) for imglib in imglibs))) return iter(self.pngFiles) if self.pngFiles else None @property def img(self): return self.assets.get('null', None) @property def states(self): return [s for s in [State('Concentrating', 'false')]] def zipme(self): """Zip the token into a rptok file.""" # directory hierarchy uses the monster type as subfolder root = os.path.join('build', self.type) try: os.makedirs(root) except OSError, e: pass filename = os.path.join(root, '%s.rptok'%(self.name.replace(":","_"))) # don't compress to avoid technical issue when sharing files # the gain is very small anyway with zipfile.ZipFile(filename, "w", zipfile.ZIP_STORED) as zipme: zipme.writestr('content.xml', self.content_xml.encode('utf-8')) zipme.writestr('properties.xml', self.properties_xml.encode('utf-8')) # default image for the token, right now it's a brown bear # zip the xml file named with the md5 containing the asset properties for name, asset in self.assets.iteritems(): zipme.writestr('assets/%s' % asset.md5, jenv().get_template('md5.template').render(name=name, extension='png', md5=asset.md5).encode("utf-8")) zipme.writestr('assets/%s.png' % asset.md5, asset.bytes) # build thumbnails zipme.writestr('thumbnail', self.img.thumbnail(50,50).getvalue()) #dont include the large thumbnail, it will double the token size for no benefit #zipme.writestr('thumbnail_large', self.img.thumbnail(500,500).getvalue()) return filename
def properties_xml(self): return jenv().get_template('cmpgn_properties.template').render( cmpgn=self) or u''
def properties_xml(self): return jenv().get_template('token_properties.template').render(token=self) or u''
def content_xml(self): if self._content is self.sentinel: self._content = jenv().get_template('token_content.template').render(token=self) return self._content or ''
def properties_xml(self): content = jenv().get_template('cmpgn_properties.template').render(cmpgn=self) return content or ''
def content_xml(self): content = jenv().get_template('cmpgn_content.template').render(cmpgn=self) return content or ''
def render(self): return jenv().get_template('table_content.template').render(table=self)
def content_xml(self): content = jenv().get_template('zone_content.template').render(zone=self) return content or ''
def template(self): if self._template is None: self._template = jenv().get_template(self.tmpl) return self._template
def content_xml(self): return jenv().get_template('cmpgn_content.template').render( cmpgn=self) or u''