Added placeholder files for the Chapter H notes.

master
Pacman Ghost 5 years ago
parent ed14c063c1
commit 65b6eca087
  1. 5
      chapter-h/README.md
  2. BIN
      chapter-h/chapter-h-placeholders.zip
  3. 120
      vasl_templates/tools/make_chapter_h_placeholders.py
  4. 8
      vasl_templates/webapp/files.py
  5. BIN
      vasl_templates/webapp/static/help/images/chapter-h/ma-notes-snippet.png
  6. BIN
      vasl_templates/webapp/static/help/images/chapter-h/ma-notes-snippet.small.png
  7. BIN
      vasl_templates/webapp/static/help/images/chapter-h/notes-snippet.png
  8. BIN
      vasl_templates/webapp/static/help/images/chapter-h/notes-snippet.small.png
  9. BIN
      vasl_templates/webapp/static/help/images/chapter-h/psw-234.1-note74.png
  10. BIN
      vasl_templates/webapp/static/help/images/chapter-h/psw-234.1-note74.small.png
  11. BIN
      vasl_templates/webapp/static/help/images/chapter-h/ui.png
  12. BIN
      vasl_templates/webapp/static/help/images/chapter-h/ui.small.png
  13. 41
      vasl_templates/webapp/static/help/index.html
  14. 4
      vasl_templates/webapp/utils.py
  15. 13
      vasl_templates/webapp/vo_notes.py

@ -0,0 +1,5 @@
# Chapter H Vehicle/Ordnance notes
*VASL Templates* supports including Chapter H notes in your VASL scenarios, but since this is copyrighted material, it is not included in releases, and you will need to set up the data yourself.
The ZIP file in this directory contains placeholder files for the Chapter H notes, refer to the [documentation](https://rawgit.com/pacman-ghost/vasl-templates/master/vasl_templates/webapp/static/help/index.html?tab=chapterh) for instructions on how to set things up.

@ -0,0 +1,120 @@
#!/usr/bin/env python3
""" Create placeholder files for the Chapter H notes. """
import os
import zipfile
import json
import re
import click
# ---------------------------------------------------------------------
@click.command()
@click.option( "--output","-o", "output_fname", help="Output ZIP file to generate." )
def main( output_fname ): # pylint: disable=too-many-locals,too-many-branches
"""Create a ZIP file with placeholder files for each Chapter H note and multi-applicable note."""
# initialize
if not output_fname:
raise RuntimeError( "Output ZIP file not specified." )
if os.path.isfile( output_fname ):
raise RuntimeError( "Output ZIP file exists." )
results = {}
# load the vehicle/ordnance data files
base_dir = os.path.join( os.path.split(__file__)[0], "../webapp/data/" )
for vo_type in ("vehicles","ordnance"):
dname = os.path.join( base_dir, vo_type )
for root,_,fnames in os.walk( dname ):
for fname in fnames:
fname = os.path.join( root, fname )
if os.path.splitext( fname )[1] != ".json":
continue
dname2, fname2 = os.path.split( fname )
nat = os.path.splitext( fname2 )[0]
if nat == "common":
nat = os.path.split( dname2 )[1]
if nat in ("british-commonwealth-forces-korea","cvpa","kpa","us-rok-ounc","un-forces"):
continue
notes, ma_notes = load_vo_data( fname )
if nat not in results:
results[ nat ] = {}
if nat == "landing-craft":
results[ nat ][ vo_type ] = { "notes": notes, "ma_notes": ma_notes }
else:
results[ nat ][ vo_type ] = { "notes": notes, "ma_notes": ma_notes }
# generate the placeholder files
with zipfile.ZipFile( output_fname, "w" ) as zip_file:
nats = sorted( results.keys() )
for nat in nats: #pylint: disable=too-many-nested-blocks
for vo_type in ("vehicles","ordnance"):
print( "Generating {} {}...".format( nat, vo_type ) )
for note_type in ("notes","ma_notes"):
# get the next set of note ID's
vals = results[nat].get( vo_type, {} ).get( note_type )
if not vals:
continue
print( "- {}: {}".format( note_type, ", ".join( str(v) for v in vals ) ) )
for val in vals:
# generate the filename for the next note placeholder
if isinstance(val, str):
# NOTE: Filenames are always lower-case, unless the note ID itself is lower-case,
# in which case we indicate this with a trailing underscore
if re.search( r"^[A-Z][A-Za-z]?$", val ):
val = val.lower()
elif re.search( r"^[a-z]{1,2}?$", val ):
val += "_"
if nat == "landing-craft":
fname = "{}/{}.{}".format( nat, val, "png" if note_type == "notes" else "html" )
else:
fname = "{}/{}/{}.{}".format( nat, vo_type, val, "png" if note_type == "notes" else "html" )
# add the placeholder file to the ZIP
zip_file.writestr( fname, b"" )
print()
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MA_NOTE_REGEXES = [
re.compile( r"^([A-Z]{1,2})$" ),
re.compile( r"^([A-Z]{1,2})\u2020" ),
re.compile( r"^([a-z])$" ),
re.compile( r"^([a-z])\u2020" ),
re.compile( r"^([A-Z][a-z])$" ),
re.compile( r"^([A-Za-z])<sup>" ),
re.compile( r"^<s>([A-Za-z])</s>$" ),
]
def load_vo_data( fname ):
"""Load a vehicle/ordnance data file."""
# initialize
notes, ma_notes = set(), set()
# load the file
vo_data = json.load( open( fname, "r" ) )
for vo_entry in vo_data:
# load the vehicle/ordnance's note number
mo = re.search( r"^\d+", vo_entry["note_number"] )
notes.add( int( mo.group() ) )
# load the multi-applicable note ID's
for ma_note in vo_entry.get("notes",[]):
matches = [ regex.search(ma_note) for regex in MA_NOTE_REGEXES ]
matches = [ mo.group(1) for mo in matches if mo ]
assert len(matches) == 1
ma_notes.add( matches[0] )
return sorted(notes), sorted(ma_notes)
# ---------------------------------------------------------------------
if __name__ == "__main__":
main() #pylint: disable=no-value-for-parameter

@ -10,7 +10,7 @@ from flask import send_file, send_from_directory, jsonify, redirect, url_for, ab
from vasl_templates.webapp import app
from vasl_templates.webapp.file_server.vasl_mod import get_vasl_mod
from vasl_templates.webapp.utils import resize_image_response
from vasl_templates.webapp.utils import resize_image_response, is_empty_file
# ---------------------------------------------------------------------
@ -23,7 +23,7 @@ class FileServer:
else:
self.base_dir = os.path.abspath( base_dir )
def serve_file( self, path ):
def serve_file( self, path, ignore_empty=False ):
"""Serve a file."""
# NOTE: We return a Flask Response object, instead of the file data, so that (1) we can use
# send_from_directory() and (2) if we have to download a file from a URL, we can include
@ -44,6 +44,8 @@ class FileServer:
return send_file( buf, mimetype=mime_type )
else:
path = path.replace( "\\", "/" ) # nb: for Windows :-/
if ignore_empty and is_empty_file( os.path.join( self.base_dir, path ) ):
return None
return send_from_directory( self.base_dir, path )
@staticmethod
@ -60,6 +62,8 @@ def get_user_file( path ):
if not dname:
abort( 404 )
resp = FileServer( dname ).serve_file( path )
if not resp:
abort( 404 )
return resize_image_response( resp )
# ---------------------------------------------------------------------

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -26,6 +26,7 @@
<ul>
<li> <a href="#helptabs-installation">Installation</a>
<li> <a href="#helptabs-userguide">User Guide</a>
<li> <a href="#helptabs-chapterh">Chapter H</a>
<li> <a href="#helptabs-templatepacks">Template packs</a>
<li> <a href="#helptabs-fordevelopers">For developers</a>
<li> <a href="#helptabs-license">License</a>
@ -176,6 +177,46 @@ Finally, if special support weapons such as PF and BAZ are in play, snippets are
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<div id="helptabs-chapterh">
<p> <em>VASL Templates</em> supports including Chapter H notes in your VASL scenarios, but since this is copyrighted material, the data files are not included in releases, and you will need to set them up yourself.
<h2> Setting up the Chapter H data files </h2>
<p> Unpack this <a href="https://github.com/pacman-ghost/vasl-templates/tree/master/chapter-h/chapter-h-placeholders.zip">ZIP file</a> somewhere, and configure the location in the <em>Server Settings</em> dialog (or the <tt>CHAPTER_H_NOTES_DIR</tt> setting in <tt>site.cfg</tt>, if you are running from source).
<p> The ZIP file contains placeholder files for the Chapter H notes and multi-applicable notes, so all you have to do is replace these files with the real content.
<p> Multi-applicable notes are stored as HTML, so, for example, for German Multi-Applicable Vehicle Note A, change the file <tt>german/vehicles/a.html</tt> as follows:
<div class="code">
&lt;p&gt; MA and CMG (if so equipped) have AA capability - signified by "MA:AA" being printed on the counter.
</div>
<div class="info">NOTE: Because Windows has case-insensitive filenames, the convention is that Multi-Applicable Note "A" is stored in a file called <tt>a.html</tt>, while Multi-Applicable Note "a" is stored in <tt>a_.html</tt> (with a trailing underscore).</div>
<p> <img src="images/chapter-h/psw-234.1-note74.png" style="float:left;" class="preview"> The vehicle and ordnance notes themselves are stored as image files, so you need to scan your Chapter H pages, and crop each individual note. For example, an image for the German PSW 234/1 can be seen to the left. Right-click on it, download it, and save it on top of <tt>german/vehicles/74.png</tt> (because it's note #74).
<br clear="all">
<h2> Adding Chapter H notes to your VASL scenario </h2>
<p> <img src="images/chapter-h/ui.png" style="float:right;" class="preview"> Restart the <em>VASL Templates</em> program, and add a PSW 234/1 to the German OB. You will see some changes in the UI (see screenshot).
<br clear="all">
<p> <img src="images/chapter-h/ma-notes-snippet.png" style="float:left;" class="preview"> In the bottom-right, there are new controls for generating an HTML snippet for the multi-applicable notes. Click on the <em>Snippet</em> button, create a label in a VASL scenario, and you see the multi-applicable notes for the vehicles in the OB (the program will only include those that are needed). This content was taken from the <tt>a.html</tt> file you set up earlier.
<br clear="all">
<p> <img src="images/chapter-h/notes-snippet.png" style="float:right;" class="preview">Each individual vehicle and ordnance can also now have its own snippet button. Click on the one for the PSW 234/1, transfer the snippet to your VASL scenario, and you will see the image for Note 74 you set up earlier.
<br clear="all">
<div class="hint">NOTE: Depending on how you scanned the Chapter H pages, the image may not be the right size, so you can set a scaling factor for these images in the Server Settings dialog. As a guide, I scanned my ASLRB at 300dpi, and a scaling factor of 40% produced images that fit in with the rest of the scenario. Note, however, that if you have a lot of these images, there will be a noticeable delay when loading the scenario in VASSAL (because the program has to resize all the images), so you will be better off shrinking the images yourself, then setting the scaling factor to 100, so that no resizing needs to be done.</div>
<h2> Creating a scenario reference sheet </h2>
<p> If you shift-click on the Snippet buttons, an <em>image</em> of the snippet will be copied to the clipboard, instead of the raw HTML snippet. You can then copy these into Microsoft Word (or any other editor that can accept images), and print it out, to get a reference sheet with all the Chapter H notes for a scenario. Very handy, even if you're not using VASL!
</div>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<div id="helptabs-templatepacks">
<h2 style="margin-top:0;"> How template files work </h2>

@ -145,6 +145,10 @@ def is_image_file( fname ):
extn = os.path.splitext( fname )[0]
return extn.lower() in (".png",".jpg",".jpeg",".gif")
def is_empty_file( fname ):
"""Check if a file is empty."""
return os.stat( fname ).st_size == 0
# ---------------------------------------------------------------------
class SimpleError( Exception ):

@ -10,7 +10,7 @@ from flask import render_template, jsonify, abort
from vasl_templates.webapp import app
from vasl_templates.webapp.files import FileServer
from vasl_templates.webapp.utils import is_image_file, resize_image_response
from vasl_templates.webapp.utils import resize_image_response, is_image_file, is_empty_file
_vo_notes_lock = threading.RLock() # nb: this controls the cached V/O notes and the FileServer
_cached_vo_notes = None
@ -90,6 +90,8 @@ def _do_get_vo_notes( vo_type ): #pylint: disable=too-many-locals,too-many-branc
if not all( ch.isdigit() or ch in (".") for ch in key ):
logging.warning( "Unexpected vehicle/ordnance note key: %s", key )
fname = os.path.join( root, fname )
if is_empty_file( fname ):
continue # nb: ignore placeholder files
prefix = os.path.commonpath( [ dname, fname ] )
if prefix:
vo_notes[vo_type2][nat2][key] = fname[len(prefix)+1:]
@ -99,6 +101,8 @@ def _do_get_vo_notes( vo_type ): #pylint: disable=too-many-locals,too-many-branc
key = get_ma_note_key( nat2, fname )
with open( os.path.join(root,fname), "r" ) as fp:
buf = fp.read().strip()
if not buf:
continue # nb: ignore placeholder files
if buf.startswith( "<p>" ):
buf = buf[3:].strip()
ma_notes[key] = buf
@ -123,7 +127,12 @@ def get_vo_note( vo_type, nat, key ):
abort( 404 )
vo_notes = _do_get_vo_notes( vo_type )
fname = vo_notes.get( nat, {} ).get( key )
resp = _vo_notes_file_server.serve_file( fname )
if not fname:
abort( 404 )
# nb: we ignore placeholder files (return 404 for empty files)
resp = _vo_notes_file_server.serve_file( fname, ignore_empty=True )
if not resp:
abort( 404 )
default_scaling = app.config.get( "CHAPTER_H_IMAGE_SCALING", 100 )
return resize_image_response( resp, default_scaling=default_scaling )

Loading…
Cancel
Save