gstaff commited on
Commit
af7014e
1 Parent(s): 1a5a832

Improve card formatting, render html into png for return

Browse files
Files changed (3) hide show
  1. app.py +71 -11
  2. monsterMakerTemplate.html +30 -0
  3. requirements.txt +0 -0
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import base64
2
  import pathlib
3
  import re
 
4
  from io import BytesIO
5
 
6
  import gradio as gr
@@ -10,6 +11,7 @@ from PIL import Image
10
  from fastai.callback.core import Callback
11
  from fastai.learner import *
12
  from fastai.torch_core import TitledStr
 
13
  from min_dalle import MinDalle
14
  from torch import tensor, Tensor, float16, float32
15
  from torch.distributions import Transform
@@ -113,6 +115,13 @@ def extract_text_for_header(text, header):
113
  return match.group(1)
114
 
115
 
 
 
 
 
 
 
 
116
  def format_monster_card(monster_text, image_data):
117
  print('FORMATTING MONSTER TEXT')
118
  # see giffyglyph's monster maker https://giffyglyph.com/monstermaker/app/
@@ -146,14 +155,40 @@ def format_monster_card(monster_text, image_data):
146
  card = card.replace('{cha_stat}', cha_stat)
147
  saving_throws = extract_text_for_header(monster_text, 'Saving Throws')
148
  card = card.replace('{saving_throws}', saving_throws)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  senses = extract_text_for_header(monster_text, 'Senses')
150
  card = card.replace('{senses}', senses)
 
 
151
  languages = extract_text_for_header(monster_text, 'Languages')
152
  card = card.replace('{languages}', languages)
 
 
153
  challenge = extract_text_for_header(monster_text, 'Challenge')
154
  card = card.replace('{challenge}', challenge)
155
- # TODO: Skills, immunities etc., remove unavailable
156
- # include Melee or Ranged Weapon Attack:
157
 
158
  description = extract_text_for_header(monster_text, 'Description')
159
  card = card.replace('{description}', description)
@@ -180,22 +215,30 @@ def format_monster_card(monster_text, image_data):
180
  else:
181
  card = card.replace('{passives}', f'<div class="monster-trait"><p>{passives}</p></div>')
182
 
183
- # TODO: multiple lines
184
- match = re.search(r"Actions:\n(.*)", monster_text)
185
  if match is None:
186
  actions = ''
187
  else:
188
  actions = match.group(1)
189
  a = actions.split(':')
190
  if len(a) > 1:
191
- action = a[0]
192
- detail = ":".join(a[1:])
193
- card = card.replace('{actions}',
194
- f'<div class="monster-action"><p><span class="name">{action}</span> <span class="detail">{detail}</span></p></div>')
 
 
 
 
 
 
 
 
195
  else:
196
  card = card.replace('{actions}', f'<div class="monster-action"><p>{actions}</p></div>')
197
 
198
  card = card.replace('Melee Weapon Attack:', '<i>Melee Weapon Attack:</i>')
 
199
  card = card.replace('Hit:', '<i>Hit:</i>')
200
 
201
  print('FORMATTING MONSTER TEXT COMPLETE')
@@ -211,14 +254,31 @@ def pil_to_base64(image):
211
  return img_str
212
 
213
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  def run(name):
 
 
215
  text = gen_monster_text(name)
216
  description = parse_monster_description(text)
217
  pil = gen_image(description)
218
  image_data = pil_to_base64(pil)
219
- card = format_monster_card(text, image_data)
220
- return text, pil, card
 
 
 
221
 
222
 
223
- iface = gr.Interface(fn=run, inputs="text", outputs=["text", "pil", 'html'])
224
  iface.launch()
 
1
  import base64
2
  import pathlib
3
  import re
4
+ import time
5
  from io import BytesIO
6
 
7
  import gradio as gr
 
11
  from fastai.callback.core import Callback
12
  from fastai.learner import *
13
  from fastai.torch_core import TitledStr
14
+ from html2image import Html2Image
15
  from min_dalle import MinDalle
16
  from torch import tensor, Tensor, float16, float32
17
  from torch.distributions import Transform
 
115
  return match.group(1)
116
 
117
 
118
+ def remove_section(html, html_class):
119
+ match = re.search(f'<li class="{html_class}"([\w\W])*?li>', html)
120
+ if match is not None:
121
+ html = html.replace(match.group(0), '')
122
+ return html
123
+
124
+
125
  def format_monster_card(monster_text, image_data):
126
  print('FORMATTING MONSTER TEXT')
127
  # see giffyglyph's monster maker https://giffyglyph.com/monstermaker/app/
 
155
  card = card.replace('{cha_stat}', cha_stat)
156
  saving_throws = extract_text_for_header(monster_text, 'Saving Throws')
157
  card = card.replace('{saving_throws}', saving_throws)
158
+ if not saving_throws:
159
+ card = remove_section(card, 'monster-saves')
160
+ skills = extract_text_for_header(monster_text, 'Skills')
161
+ card = card.replace('{skills}', skills)
162
+ if not skills:
163
+ card = remove_section(card, 'monster-skills')
164
+ damage_vulnerabilities = extract_text_for_header(monster_text, 'Damage Vulnerabilities')
165
+ card = card.replace('{damage_vulnerabilities}', damage_vulnerabilities)
166
+ if not damage_vulnerabilities:
167
+ card = remove_section(card, 'monster-vulnerabilities')
168
+ damage_resistances = extract_text_for_header(monster_text, 'Damage Resistances')
169
+ card = card.replace('{damage_resistances}', damage_resistances)
170
+ if not damage_resistances:
171
+ card = remove_section(card, 'monster-resistances')
172
+ damage_immunities = extract_text_for_header(monster_text, 'Damage Immunities')
173
+ card = card.replace('{damage_immunities}', damage_immunities)
174
+ if not damage_immunities:
175
+ card = remove_section(card, 'monster-immunities')
176
+ condition_immunities = extract_text_for_header(monster_text, 'Condition Immunities')
177
+ card = card.replace('{condition_immunities}', condition_immunities)
178
+ if not condition_immunities:
179
+ card = remove_section(card, 'monster-conditions')
180
  senses = extract_text_for_header(monster_text, 'Senses')
181
  card = card.replace('{senses}', senses)
182
+ if not senses:
183
+ card = remove_section(card, 'monster-senses')
184
  languages = extract_text_for_header(monster_text, 'Languages')
185
  card = card.replace('{languages}', languages)
186
+ if not languages:
187
+ card = remove_section(card, 'monster-languages')
188
  challenge = extract_text_for_header(monster_text, 'Challenge')
189
  card = card.replace('{challenge}', challenge)
190
+ if not challenge:
191
+ card = remove_section(card, 'monster-challenge')
192
 
193
  description = extract_text_for_header(monster_text, 'Description')
194
  card = card.replace('{description}', description)
 
215
  else:
216
  card = card.replace('{passives}', f'<div class="monster-trait"><p>{passives}</p></div>')
217
 
218
+ match = re.search(r"Actions:\n([\w\W]*)", monster_text)
 
219
  if match is None:
220
  actions = ''
221
  else:
222
  actions = match.group(1)
223
  a = actions.split(':')
224
  if len(a) > 1:
225
+ a = ":".join(a)
226
+ a = a.split('\n')
227
+ actions_data = ''
228
+ for x in a:
229
+ x = x.split(':')
230
+ if len(x) > 1:
231
+ action = x[0]
232
+ if 'Passive' in action:
233
+ break
234
+ detail = ":".join(x[1:])
235
+ actions_data += f'<div class="monster-action"><p><span class="name">{action}</span> <span class="detail">{detail}</span></p></div>'
236
+ card = card.replace('{actions}', actions_data)
237
  else:
238
  card = card.replace('{actions}', f'<div class="monster-action"><p>{actions}</p></div>')
239
 
240
  card = card.replace('Melee Weapon Attack:', '<i>Melee Weapon Attack:</i>')
241
+ card = card.replace('Ranged Weapon Attack:', '<i>Ranged Weapon Attack:</i>')
242
  card = card.replace('Hit:', '<i>Hit:</i>')
243
 
244
  print('FORMATTING MONSTER TEXT COMPLETE')
 
254
  return img_str
255
 
256
 
257
+ hti = Html2Image(output_path='rendered_cards')
258
+
259
+
260
+ def html_to_png(html):
261
+ print('CONVERTING HTML CARD TO PNG IMAGE')
262
+ paths = hti.screenshot(html_str=html, css_file="monstermaker.css", save_as="test.png")
263
+ path = paths[0]
264
+ img = Image.open(path).convert("RGB")
265
+ print('CONVERTING HTML CARD TO PNG IMAGE COMPLETE')
266
+ return img
267
+
268
+
269
  def run(name):
270
+ start = time.time()
271
+ print(f'BEGINNING RUN FOR {name}')
272
  text = gen_monster_text(name)
273
  description = parse_monster_description(text)
274
  pil = gen_image(description)
275
  image_data = pil_to_base64(pil)
276
+ card_html = format_monster_card(text, image_data)
277
+ card_image = html_to_png(card_html)
278
+ end = time.time()
279
+ print(f'RUN COMPLETED IN {end - start} seconds')
280
+ return text, pil, card_image, card_html
281
 
282
 
283
+ iface = gr.Interface(fn=run, inputs="text", outputs=["text", "pil", "pil", 'html'])
284
  iface.launch()
monsterMakerTemplate.html CHANGED
@@ -90,6 +90,36 @@
90
  <span>{saving_throws}</span>
91
  </p>
92
  </li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  <li class="monster-senses">
94
  <p>
95
  <span class="label">Senses</span>
 
90
  <span>{saving_throws}</span>
91
  </p>
92
  </li>
93
+ <li class="monster-skills">
94
+ <p>
95
+ <span class="label">Skills</span>
96
+ <span>{skills}</span>
97
+ </p>
98
+ </li>
99
+ <li class="monster-vulnerabilities">
100
+ <p>
101
+ <span class="label">Damage Vulnerabilities</span>
102
+ <span>{damage_vulnerabilities}</span>
103
+ </p>
104
+ </li>
105
+ <li class="monster-resistances">
106
+ <p>
107
+ <span class="label">Damage Resistances</span>
108
+ <span>{damage_resistances}</span>
109
+ </p>
110
+ </li>
111
+ <li class="monster-immunities">
112
+ <p>
113
+ <span class="label">Damage Immunities</span>
114
+ <span>{damage_immunities}</span>
115
+ </p>
116
+ </li>
117
+ <li class="monster-conditions">
118
+ <p>
119
+ <span class="label">Condition Immunities</span>
120
+ <span>{condition_immunities}</span>
121
+ </p>
122
+ </li>
123
  <li class="monster-senses">
124
  <p>
125
  <span class="label">Senses</span>
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ