From 48e07eb4558d34cb780497fae0ab5e00535136fc Mon Sep 17 00:00:00 2001 From: Mark Zeman Date: Fri, 21 Oct 2016 08:14:55 +0200 Subject: [PATCH 1/6] Enabled PDF download from web GUI Fixes #23 --- gurpsspace/starsystem.py | 9 +++++++++ server.py | 17 +++++++++++++++++ webgui/templates/overview.html | 1 + 3 files changed, 27 insertions(+) diff --git a/gurpsspace/starsystem.py b/gurpsspace/starsystem.py index 54985b5..14a3968 100644 --- a/gurpsspace/starsystem.py +++ b/gurpsspace/starsystem.py @@ -364,6 +364,15 @@ def write_latex(self, filename='starsystem.tex') -> None: writer = LW(self, filename) writer.write() + def make_latex(self): + """ + Create a string containing all the information about the starsystem in LaTeX form. + :return: str + """ + + writer = LW(self, "") + return writer.make_latex() + def get_age(self) -> int: """Return star system age in billion years""" return self.age diff --git a/server.py b/server.py index 1deacfe..a1b156f 100644 --- a/server.py +++ b/server.py @@ -5,12 +5,16 @@ import operator +import pypandoc + from gurpsspace import starsystem as starsys from namegenerator import namegenerator from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader('webgui/templates')) +from cherrypy.lib.static import serve_file + class WebServer(object): @@ -140,6 +144,19 @@ def satellites(self, planet_id=""): tmpl = env.get_template('moons.html') return tmpl.render(moons=moons, planet_name=planet.get_name()) + @cherrypy.expose + def pdf(self): + try: + starsystem = cherrypy.session['starsystem'] + except KeyError: + raise cherrypy.HTTPError(404) + print("Creating PDF!") + starsystem.write_latex() + pypandoc.convert_file("starsystem.tex", 'pdf', outputfile="starsystem.pdf", format="latex") + abs_path = os.path.abspath("starsystem.pdf") + + return serve_file(abs_path, "application/x-download", "attachment") + def translate_row(self, planet, row): """ It is difficult in HTML and Jinja to make a table where each column is a single item, rather than each row. diff --git a/webgui/templates/overview.html b/webgui/templates/overview.html index e0abbf1..c7839fd 100644 --- a/webgui/templates/overview.html +++ b/webgui/templates/overview.html @@ -14,6 +14,7 @@

Star System Generator for GURPS 4th Edition

Click here if you want to change the settings.

Seed: {{seed}}

+

Download PDF here.

You have successfully generated the following star system:

Star system properties

From c61dde1e7bdf780d7c5335ccc1f3fbe752d57050 Mon Sep 17 00:00:00 2001 From: Mark Zeman Date: Fri, 21 Oct 2016 08:26:32 +0200 Subject: [PATCH 2/6] Refactored PDF generation out from server.py --- gurpsspace/output/latexout.py | 10 ++++++++++ gurpsspace/starsystem.py | 10 +++++----- server.py | 9 +++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/gurpsspace/output/latexout.py b/gurpsspace/output/latexout.py index 8be3daf..e74728a 100644 --- a/gurpsspace/output/latexout.py +++ b/gurpsspace/output/latexout.py @@ -4,6 +4,10 @@ processing towards a nicely formatted PDF file. """ +import pypandoc +import typing +import os + # from ..starsystem import StarSystem from ..tables import AtmCompAbbr @@ -13,6 +17,12 @@ def __init__(self, starsystem, filename='starsystem.tex'): self.starsystem = starsystem self.filename = filename + def make_pdf(self) -> str: + self.write() + pdf_name = os.path.splitext(self.filename)[0] + ".pdf" + pypandoc.convert_file(self.filename, 'pdf', outputfile=pdf_name, format="latex") + return os.path.abspath(pdf_name) + def write(self): # Open the file file = open(self.filename, 'w') diff --git a/gurpsspace/starsystem.py b/gurpsspace/starsystem.py index 14a3968..8058c06 100644 --- a/gurpsspace/starsystem.py +++ b/gurpsspace/starsystem.py @@ -364,14 +364,14 @@ def write_latex(self, filename='starsystem.tex') -> None: writer = LW(self, filename) writer.write() - def make_latex(self): + def make_pdf(self, filename='starsystem.tex'): """ - Create a string containing all the information about the starsystem in LaTeX form. - :return: str + Create a PDF document with all the information about the starsystem. + :return: Path to the PDF. """ - writer = LW(self, "") - return writer.make_latex() + writer = LW(self, filename) + return writer.make_pdf() def get_age(self) -> int: """Return star system age in billion years""" diff --git a/server.py b/server.py index a1b156f..71cc818 100644 --- a/server.py +++ b/server.py @@ -150,12 +150,9 @@ def pdf(self): starsystem = cherrypy.session['starsystem'] except KeyError: raise cherrypy.HTTPError(404) - print("Creating PDF!") - starsystem.write_latex() - pypandoc.convert_file("starsystem.tex", 'pdf', outputfile="starsystem.pdf", format="latex") - abs_path = os.path.abspath("starsystem.pdf") - - return serve_file(abs_path, "application/x-download", "attachment") + + pdf_path = starsystem.make_pdf() + return serve_file(pdf_path, "application/x-download", "attachment") def translate_row(self, planet, row): """ From 5632a288aefd8fe5420cea1c3a56f133c8543521 Mon Sep 17 00:00:00 2001 From: Mark Zeman Date: Fri, 21 Oct 2016 10:13:42 +0200 Subject: [PATCH 3/6] Switched PDF generation from pandoc to pdflatex --- gurpsspace/output/latexout.py | 22 ++++++++++++++++++---- server.py | 2 -- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/gurpsspace/output/latexout.py b/gurpsspace/output/latexout.py index e74728a..86b2510 100644 --- a/gurpsspace/output/latexout.py +++ b/gurpsspace/output/latexout.py @@ -4,8 +4,7 @@ processing towards a nicely formatted PDF file. """ -import pypandoc -import typing +import subprocess import os # from ..starsystem import StarSystem @@ -19,8 +18,23 @@ def __init__(self, starsystem, filename='starsystem.tex'): def make_pdf(self) -> str: self.write() - pdf_name = os.path.splitext(self.filename)[0] + ".pdf" - pypandoc.convert_file(self.filename, 'pdf', outputfile=pdf_name, format="latex") + base_name = os.path.splitext(self.filename)[0] + pdf_name = base_name + ".pdf" + + cmd = ['pdflatex', '-interaction', 'nonstopmode', self.filename] + proc = subprocess.Popen(cmd) + proc.communicate() + + retcode = proc.returncode + if not retcode == 0: + os.unlink(pdf_name) + raise ValueError('Error {} executing command: {}'.format(retcode, ' '.join(cmd))) + + os.unlink(base_name + '.log') + os.unlink(base_name + '.aux') + os.unlink(base_name + '.toc') + os.unlink(base_name + '.out') + return os.path.abspath(pdf_name) def write(self): diff --git a/server.py b/server.py index 71cc818..4f3a1c2 100644 --- a/server.py +++ b/server.py @@ -5,8 +5,6 @@ import operator -import pypandoc - from gurpsspace import starsystem as starsys from namegenerator import namegenerator From 556c9af5df1712810ff226ad84fc452194385196 Mon Sep 17 00:00:00 2001 From: Mark Zeman Date: Fri, 21 Oct 2016 10:19:08 +0200 Subject: [PATCH 4/6] PDF is now generated correctly --- gurpsspace/output/latexout.py | 9 +++++++++ server.py | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/gurpsspace/output/latexout.py b/gurpsspace/output/latexout.py index 86b2510..f8a9fa3 100644 --- a/gurpsspace/output/latexout.py +++ b/gurpsspace/output/latexout.py @@ -25,6 +25,15 @@ def make_pdf(self) -> str: proc = subprocess.Popen(cmd) proc.communicate() + retcode = proc.returncode + if not retcode == 0: + os.unlink(pdf_name) + raise ValueError('Error {} executing command: {}'.format(retcode, ' '.join(cmd))) + # PDFLaTeX has to be run twice to properly generate the ToC + cmd = ['pdflatex', '-interaction', 'nonstopmode', self.filename] + proc = subprocess.Popen(cmd) + proc.communicate() + retcode = proc.returncode if not retcode == 0: os.unlink(pdf_name) diff --git a/server.py b/server.py index 4f3a1c2..53d689a 100644 --- a/server.py +++ b/server.py @@ -7,12 +7,11 @@ from gurpsspace import starsystem as starsys from namegenerator import namegenerator +from cherrypy.lib.static import serve_file from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader('webgui/templates')) -from cherrypy.lib.static import serve_file - class WebServer(object): From bd53d2814100ddd313ca1953d8f13217f35c419f Mon Sep 17 00:00:00 2001 From: Mark Zeman Date: Sun, 30 Oct 2016 22:21:30 +0100 Subject: [PATCH 5/6] Naming now happens immediately after starsystem generation, instead of lazily --- server.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server.py b/server.py index 53d689a..4c74dfe 100644 --- a/server.py +++ b/server.py @@ -69,6 +69,22 @@ def starsystem(self, must_have_garden="False", open_cluster=None, num_stars=0, a else: mysys = starsys.StarSystem(**arguments) + for star in mysys.stars: + for key, v in star.planetsystem.get_orbitcontents().items(): + if namegen is not None: + simple_name = v.get_name().replace("-", "") + if cherrypy.session.get('name_of_' + simple_name) is None: + name = namegen.get_random_name() + star.planetsystem.get_orbitcontents()[key].set_name(name) + # For some reason, using simple_name here leads to storing stuff improperly and + # generating new names every time. No idea why. + cherrypy.session['name_of_' + v.get_name().replace("-", "")] = name + else: + name = cherrypy.session.get('name_of_' + simple_name) + star.planetsystem.get_orbitcontents()[key].set_name(name) + + cherrypy.session.save() + tmpl = env.get_template('overview.html') cherrypy.session['starsystem'] = mysys cherrypy.response.cookie['names'] = {} From ceec34da8b461d306d7173d647075eabe5a8d627 Mon Sep 17 00:00:00 2001 From: Mark Zeman Date: Sun, 30 Oct 2016 22:35:45 +0100 Subject: [PATCH 6/6] Set up a one-page overview for printability --- server.py | 36 ++--- webgui/templates/overview.html | 2 +- webgui/templates/printable.html | 264 ++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 19 deletions(-) create mode 100644 webgui/templates/printable.html diff --git a/server.py b/server.py index 4c74dfe..ffb865b 100644 --- a/server.py +++ b/server.py @@ -100,34 +100,20 @@ def planetsystem(self, star_id=""): else: star_id = int(star_id) - namegen = cherrypy.session.get('namegen') - tmpl = env.get_template('planetsystem.html') env.globals['translate_row'] = self.translate_row t_count = 0 a_count = 0 g_count = 0 - for key, v in starsystem.stars[star_id].planetsystem.get_orbitcontents().items(): + for key, _ in starsystem.stars[star_id].planetsystem.get_orbitcontents().items(): if starsystem.stars[star_id].planetsystem.get_orbitcontents()[key].type() == 'Terrestrial': t_count += 1 if starsystem.stars[star_id].planetsystem.get_orbitcontents()[key].type() == 'Ast. Belt': a_count += 1 if starsystem.stars[star_id].planetsystem.get_orbitcontents()[key].type() == 'Gas Giant': g_count += 1 - if namegen is not None: - simple_name = v.get_name().replace("-", "") - if cherrypy.session.get('name_of_' + simple_name) is None: - name = namegen.get_random_name() - starsystem.stars[star_id].planetsystem.get_orbitcontents()[key].set_name(name) - # For some reason, using simple_name here leads to storing stuff improperly and - # generating new names every time. No idea why. - cherrypy.session['name_of_' + v.get_name().replace("-", "")] = name - else: - name = cherrypy.session.get('name_of_' + simple_name) - starsystem.stars[star_id].planetsystem.get_orbitcontents()[key].set_name(name) - cherrypy.session.save() cherrypy.session['planetsystem'] = starsystem.stars[star_id].planetsystem return tmpl.render(planetsystem=starsystem.stars[star_id].planetsystem, terrestrial_count=t_count, asteroid_count=a_count, gas_giant_count=g_count) @@ -158,14 +144,28 @@ def satellites(self, planet_id=""): return tmpl.render(moons=moons, planet_name=planet.get_name()) @cherrypy.expose - def pdf(self): + def printable(self): try: starsystem = cherrypy.session['starsystem'] except KeyError: raise cherrypy.HTTPError(404) - pdf_path = starsystem.make_pdf() - return serve_file(pdf_path, "application/x-download", "attachment") + t_count = 0 + a_count = 0 + g_count = 0 + for star in starsystem.stars: + for key, _ in star.planetsystem.get_orbitcontents().items(): + if star.planetsystem.get_orbitcontents()[key].type() == 'Terrestrial': + t_count += 1 + if star.planetsystem.get_orbitcontents()[key].type() == 'Ast. Belt': + a_count += 1 + if star.planetsystem.get_orbitcontents()[key].type() == 'Gas Giant': + g_count += 1 + + tmpl = env.get_template('printable.html') + env.globals['translate_row'] = self.translate_row + + return tmpl.render(starsystem=starsystem, seed=self.random_seed, terrestrial_count=t_count, asteroid_count=a_count, gas_giant_count=g_count) def translate_row(self, planet, row): """ diff --git a/webgui/templates/overview.html b/webgui/templates/overview.html index ffdf934..caa04e3 100644 --- a/webgui/templates/overview.html +++ b/webgui/templates/overview.html @@ -14,7 +14,7 @@

Star System Generator for GURPS 4th Edition

Click here if you want to change the settings.

Seed: {{seed}}

-

Download PDF here.

+

Printable view here.

You have successfully generated the following star system:

Star system properties

diff --git a/webgui/templates/printable.html b/webgui/templates/printable.html new file mode 100644 index 0000000..861b4cb --- /dev/null +++ b/webgui/templates/printable.html @@ -0,0 +1,264 @@ + + + + + Starsystem Generator for GURPS + + + + + +

Star System Generator for GURPS 4th Edition

+

Seed: {{seed}}

+

Star system properties

+ + {% if starsystem.is_open_cluster() %} +

The star system is located in an open cluster.

+ {% endif %} + {% if starsystem.stars|count == 1 %} +
+ There is a single star in the system.
+ {% else %} +
+ There are {{ starsystem.stars|count }} stars in the system.
+ {% endif %} + {% if starsystem.get_age() < 1 %} + The stars in this system are {{starsystem.get_age()|round(2)}} billion years old.
+ {% else %} + The stars in this system are {{starsystem.get_age() * 1000}} million years old.
+ {% endif %} +
+ + + + {% if starsystem.stars|count > 1 %} + + + {% endif %} + + + + + + + + + {% if starsystem.stars|count > 1 %} + + {% endif %} + + + {% for star in starsystem.stars %} + + + {% if star.get_letter() == 'A' and starsystem.stars|count > 1 %} + + + {% elif star.get_letter() != 'A' %} + + + {% endif %} + + + + + + + + + {% if star.get_letter() == 'A' and starsystem.stars|count > 1 %} + + {% elif star.get_letter() != 'A' %} + + {% endif %} + {% if star.planetsystem.get_orbitcontents()|count > 0 %} + + {% else %} + + {% endif %} + + {% endfor %} +
NameOrbital RadiusOrbital PeriodSequenceMass*Avg. Temp.Luminosity*Radius*Inner LimitOuter LimitSnow LineForbidden ZonePlanetary System
{{star.get_letter()}}--{{starsystem.get_orbits()[loop.index0 - 1][0]|round(2)}} AU{{starsystem.get_period()[loop.index0 - 1]|round(1, 'common')}} days{{star.get_sequence()}}{{star.get_mass()|round(2)}}{{star.get_temp()|round(2)}} K{{star.get_luminosity()|round(4)}}{{(star.get_radius() * 214.93) | round(2)}}{{star.get_orbit_limits()[0]|round(2)}} AU{{star.get_orbit_limits()[1]|round(2)}} AU{{star.get_snowline()|round(2)}} AU-{{star.get_forbidden_zone()[0]|round(2)}} AU - {{star.get_forbidden_zone()[1]|round(2)}} AU + {{star.planetsystem.get_orbitcontents()|count }} Planets in System + No Planetary System
+

*: Mass, Luminosity and Radius are given relative to the Sun: 1.0 is equal to the Sun.

+
+
+ {% for star in starsystem.stars %} + {% set planetsystem = star.planetsystem %} + +

Properties of Planet System {{planetsystem.parentstar.get_letter()}}

+ +

+ This planetary system orbits star {{planetsystem.parentstar.get_letter()}} of the star system. +

+

+ +
+

Overview

+

This planetary system contains the following planets and other astronomical objects:

+ + + + + + + + + + + + + + + {% for key, planet in planetsystem.get_orbitcontents().items()|sort %} + {% set astro_body = planetsystem.get_orbitcontents()[key] %} + + + + + + + + + + + + + + {% endfor %} +
NameTypeSizeWorldOrbital RadiusOrbital PeriodOrbital EccentricityMinimum RadiusMaximum RadiusMoonsMoonlets
{{astro_body.get_name()|e}}{{astro_body.type()}}{% if astro_body.get_size() != '' %}{{astro_body.get_size()}}{% else %} N/A {% endif %}{% if astro_body.get_type() != '' %}{{astro_body.get_type()}}{% else %} N/A {% endif %}{{astro_body.get_orbit()|round(2)}} AU{{(astro_body.get_period() * 365)|round(2)}} days{{astro_body.get_eccentricity()}}{{astro_body.get_min_max()[0]|round(2)}} AU{{astro_body.get_min_max()[1]|round(2)}} AU + {% if astro_body.num_moons() != '' %} + {{astro_body.num_moons()|round(2)}} + {% else %} + N/A + {% endif %} + {% if astro_body.num_moonlets() != '' %}{{astro_body.num_moonlets()|round(2)}}{% else %} N/A {% endif %}
+
+ {% for key, planet in planetsystem.get_orbitcontents().items()|sort %} + {% if planetsystem.get_orbitcontents()[key].num_moons()|int > 0 %} + {% set planet_name = planet.get_name() %} + {% if planet.type() == 'Terrestrial' %} + {% set moons = planet.get_satellites() %} + {% else %} + {% set moons = planet.get_moons() %} + {% endif %} +

Satellites of Planet {{planet_name}}

+ +
+ + {% set list_of_rows = ["", "World Size", "World Type", "Atm. Mass*", "Atm. Composition", "Hydr. Coverage", "Avg. Surface Temperature", "Climate Type", "Density*", "Diameter*", "Surface Gravity", "Mass*", "Atm. Pressure", "Pressure Category", "Volcanics", "Tectonics", "Resource Value Modifier", "Habitability", "Affinity", "Rotational Period*"] %} + {% for row in list_of_rows %} + + {% if row == "" %} + {% else %} + {% endif %} + + {% for astro_body in moons%} + {% if row == "" %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} +
{{row}}{{translate_row(astro_body, row)}}{{translate_row(astro_body, row)}}
+

*: Atmospheric mass, density, diameter, and mass are all relative to Earth dimensions; 1.0 is what you'd expect on Earth. The rotation of the body is prograde by default, negative rotational period values indicate retrograde rotation.

+
+ {% endif %} + {% endfor %} + {% if asteroid_count > 0 %} +
+ {% if asteroid_count == 1 %} +

Asteroid Belt Details

+ {% else %} +

Details of the Asteroid Belts

+ {% endif %} + + + + + + + + + {% for key, _ in planetsystem.get_orbitcontents().items()|sort if planetsystem.get_orbitcontents()[key].type() == 'Ast. Belt'%} + {% set astro_body = planetsystem.get_orbitcontents()[key] %} + + + + + + + + {% endfor %} +
NameAvg. Surface TemperatureClimate TypeResource Value ModifierAffinity
{{astro_body.get_name()|e}}{{astro_body.get_average_surface_temp()|round(2)}} K / {{(astro_body.get_average_surface_temp()-273.15)|round(2)}}°C{{astro_body.get_climate()}}{{astro_body.get_rvm()}}{{astro_body.get_affinity()}}
+
+ {% endif %} + {% if terrestrial_count > 0 %} +
+ {% if terrestrial_count == 1 %} +

Planet Details

+ {% else %} +

Details of the Planets

+ {% endif %} + + {% set list_of_rows = ["", "World Size", "World Type", "Atm. Mass*", "Atm. Composition", "Hydr. Coverage", "Avg. Surface Temperature", "Climate Type", "Axial Tilt", "Density*", "Diameter*", "Surface Gravity", "Mass*", "Atm. Pressure", "Pressure Category", "Volcanics", "Tectonics", "Resource Value Modifier", "Habitability", "Affinity", "Rotational Period*"] %} + {% for row in list_of_rows %} + + {% if row == "" %} + {% else %} + {% endif %} + + {% for key, _ in planetsystem.get_orbitcontents().items()|sort if planetsystem.get_orbitcontents()[key].type() == 'Terrestrial'%} + {% set astro_body = planetsystem.get_orbitcontents()[key] %} + + {% if row == "" %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} +
{{row}}{{translate_row(astro_body, row)}}{{translate_row(astro_body, row)}}
+ +

*: Atmospheric mass, density, diameter, and mass are all relative to Earth dimensions; 1.0 is what you'd expect on Earth. The rotation of the body is prograde by default, negative rotational period values indicate retrograde rotation.

+
+ {% endif %} + {% if gas_giant_count > 0 %} +
+ {% if gas_giant_count == 1%} +

Gas Giant Details

+ {% else %} +

Details of the Gas Giants

+ {% endif %} + + {% set list_of_rows = ["", "World Size", "Density*", "Diameter*", "Mass*", "Cloudtop Gravity", "Blackbody Temperature"] %} + {% for row in list_of_rows %} + + {% if row == "" %} + {% else %} + {% endif %} + + {% for key, _ in planetsystem.get_orbitcontents().items()|sort if planetsystem.get_orbitcontents()[key].type() == 'Gas Giant'%} + {% set astro_body = planetsystem.get_orbitcontents()[key] %} + + {% if row == "" %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} +
{{row}}{{translate_row(astro_body, row)}}{{translate_row(astro_body, row)}}
+ +

*: Atmospheric mass, density, diameter, and mass are all relative to Earth dimensions; 1.0 is what you'd expect on Earth.

+
+ {% endif %} + {% endfor %} +
+ +