diff --git a/run-container.sh b/run-container.sh
index 251542b..6aaaea8 100755
--- a/run-container.sh
+++ b/run-container.sh
@@ -143,9 +143,10 @@ if [ -n "$VASSAL" ]; then
echo "Can't find the VASSAL directory: $VASSAL"
exit 1
fi
- target=/data/vassal/
- VASSAL_VOLUME="--volume `readlink -f "$VASSAL"`:$target"
- VASSAL_ENV="--env VASSAL_DIR=$target"
+ mpoint=/data/vassal/
+ target=$( readlink -f "$VASSAL" )
+ VASSAL_VOLUME="--volume $target:$mpoint"
+ VASSAL_ENV="--env VASSAL_DIR=$mpoint --env VASSAL_DIR_TARGET=$target"
fi
# check if a VASL module file has been specified
@@ -154,9 +155,10 @@ if [ -n "$VASL_MOD" ]; then
echo "Can't find the VASL .vmod file: $VASL_MOD"
exit 1
fi
- target=/data/vasl.vmod
- VASL_MOD_VOLUME="--volume `readlink -f "$VASL_MOD"`:$target"
- VASL_MOD_ENV="--env VASL_MOD=$target"
+ mpoint=/data/vasl.vmod
+ target=$( readlink -f "$VASL_MOD" )
+ VASL_MOD_VOLUME="--volume $target:$mpoint"
+ VASL_MOD_ENV="--env VASL_MOD=$mpoint --env VASL_MOD_TARGET"
fi
# check if a VASL extensions directory has been specified
@@ -165,9 +167,10 @@ if [ -n "$VASL_EXTNS" ]; then
echo "Can't find the VASL extensions directory: $VASL_EXTNS"
exit 1
fi
- target=/data/vasl-extensions/
- VASL_EXTNS_VOLUME="--volume `readlink -f "$VASL_EXTNS"`:$target"
- VASL_EXTNS_ENV="--env VASL_EXTNS_DIR=$target"
+ mpoint=/data/vasl-extensions/
+ target=$( readlink -f "$VASL_EXTNS" )
+ VASL_EXTNS_VOLUME="--volume $target:$mpoint"
+ VASL_EXTNS_ENV="--env VASL_EXTNS_DIR=$mpoint --env VASL_EXTNS_DIR_TARGET=$target"
fi
# check if a VASL boards directory has been specified
@@ -176,9 +179,10 @@ if [ -n "$VASL_BOARDS" ]; then
echo "Can't find the VASL boards directory: $VASL_BOARDS"
exit 1
fi
- target=/data/boards/
- VASL_BOARDS_VOLUME="--volume `readlink -f "$VASL_BOARDS"`:$target"
- VASL_BOARDS_ENV="--env BOARDS_DIR=$target"
+ mpoint=/data/boards/
+ target=$( readlink -f "$VASL_BOARDS" )
+ VASL_BOARDS_VOLUME="--volume $target:$mpoint"
+ VASL_BOARDS_ENV="--env BOARDS_DIR=$mpoint --env BOARDS_DIR_TARGET=$target"
fi
# check if a Chapter H notes directory has been specified
@@ -187,9 +191,10 @@ if [ -n "$CHAPTER_H_NOTES" ]; then
echo "Can't find the Chapter H notes directory: $CHAPTER_H_NOTES"
exit 1
fi
- target=/data/chapter-h-notes/
- CHAPTER_H_NOTES_VOLUME="--volume `readlink -f "$CHAPTER_H_NOTES"`:$target"
- CHAPTER_H_NOTES_ENV="--env CHAPTER_H_NOTES_DIR=$target"
+ mpoint=/data/chapter-h-notes/
+ target=$( readlink -f "$CHAPTER_H_NOTES" )
+ CHAPTER_H_NOTES_VOLUME="--volume $target:$mpoint"
+ CHAPTER_H_NOTES_ENV="--env CHAPTER_H_NOTES_DIR=$mpoint --env CHAPTER_H_NOTES_DIR_TARGET=$target"
fi
# check if a user files directory has been specified
@@ -198,9 +203,10 @@ if [ -n "$USER_FILES" ]; then
echo "Can't find the user files directory: $USER_FILES"
exit 1
fi
- target=/data/user-files/
- USER_FILES_VOLUME="--volume `readlink -f "$USER_FILES"`:$target"
- USER_FILES_ENV="--env USER_FILES_DIR=$target"
+ mpoint=/data/user-files/
+ target=$( readlink -f "$USER_FILES" )
+ USER_FILES_VOLUME="--volume $target:$mpoint"
+ USER_FILES_ENV="--env USER_FILES_DIR=$mpoint --env USER_FILES_DIR_TARGET=$target"
fi
# check if a template pack has been specified
@@ -210,9 +216,10 @@ if [ -n "$TEMPLATE_PACK" ]; then
echo "Can't find the template pack: $TEMPLATE_PACK"
exit 1
fi
- target=/data/template-pack
- TEMPLATE_PACK_VOLUME="--volume `readlink -f "$TEMPLATE_PACK"`:$target"
- TEMPLATE_PACK_ENV="--env DEFAULT_TEMPLATE_PACK=$target"
+ mpoint=/data/template-pack
+ target=$( readlink -f "$TEMPLATE_PACK" )
+ TEMPLATE_PACK_VOLUME="--volume $target:$mpoint"
+ TEMPLATE_PACK_ENV="--env DEFAULT_TEMPLATE_PACK=$mpoint --env DEFAULT_TEMPLATE_PACK_TARGET"
fi
# check if testing has been enabled
@@ -239,6 +246,9 @@ echo Launching the \"$IMAGE_TAG\" image as \"$CONTAINER_NAME\"...
docker run \
--name $CONTAINER_NAME \
--publish $PORT:5010 \
+ --env DOCKER_IMAGE_NAME="vasl-templates:$IMAGE_TAG" \
+ --env DOCKER_IMAGE_TIMESTAMP="$(date --utc +"%Y-%m-%d %H:%M:%S %:z")" \
+ --env DOCKER_CONTAINER_NAME="$CONTAINER_NAME" \
$CONTROL_TESTS_PORT_RUN \
$VASSAL_VOLUME $VASL_MOD_VOLUME $VASL_EXTNS_VOLUME $VASL_BOARDS_VOLUME $CHAPTER_H_NOTES_VOLUME $TEMPLATE_PACK_VOLUME $USER_FILES_VOLUME \
$VASSAL_ENV $VASL_MOD_ENV $VASL_EXTNS_ENV $VASL_BOARDS_ENV $CHAPTER_H_NOTES_ENV $TEMPLATE_PACK_ENV $USER_FILES_ENV \
diff --git a/vasl_templates/webapp/main.py b/vasl_templates/webapp/main.py
index 15deefe..03da3a1 100644
--- a/vasl_templates/webapp/main.py
+++ b/vasl_templates/webapp/main.py
@@ -5,11 +5,14 @@ import threading
import concurrent
import json
import uuid
+from datetime import datetime, timedelta
+import re
import logging
from flask import request, render_template, jsonify, send_file, redirect, url_for, abort
from vasl_templates.webapp import app, shutdown_event
+from vasl_templates.webapp.vassal import VassalShim
from vasl_templates.webapp.utils import MsgStore, parse_int
import vasl_templates.webapp.config.constants
from vasl_templates.webapp.config.constants import BASE_DIR, DATA_DIR
@@ -39,7 +42,6 @@ def get_startup_msgs():
if _check_versions:
_check_versions = False
# check the VASSAL version
- from vasl_templates.webapp.vassal import VassalShim
try:
VassalShim.check_vassal_version( startup_msg_store )
except Exception as ex: #pylint: disable=broad-except
@@ -95,7 +97,7 @@ def get_app_config():
}
if isinstance( vals["THEATERS"], str ):
vals["THEATERS"] = vals["THEATERS"].split()
- for key in ["APP_NAME","APP_VERSION","APP_DESCRIPTION","APP_HOME_URL"]:
+ for key in [ "APP_NAME", "APP_VERSION", "APP_DESCRIPTION", "APP_HOME_URL" ]:
vals[ key ] = getattr( vasl_templates.webapp.config.constants, key )
# include the ASL Scenario Archive config
@@ -120,7 +122,6 @@ def get_app_config():
vals[ "ALTERNATE_WEBAPP_BASE_URL" ] = alt_webapp_base_url
# include information about VASSAL and VASL
- from vasl_templates.webapp.vassal import VassalShim
try:
vals[ "VASSAL_VERSION" ] = VassalShim.get_version()
except Exception as ex: #pylint: disable=broad-except
@@ -157,6 +158,83 @@ def get_app_config():
# ---------------------------------------------------------------------
+@app.route( "/program-info" )
+def get_program_info():
+ """Get the program info."""
+
+ # NOTE: We can't convert to local time, since the time zone inside a Docker container
+ # may not be the same as on the host (or where the client is). It's possible to make it so,
+ # but messy, so to keep things simple, we get the client to pass in the timezone offset.
+ tz_offset = parse_int( request.args.get( "tz_offset", 0 ) )
+ def to_localtime( tstamp ):
+ """Convert a timestamp to local time."""
+ return tstamp + timedelta( minutes=tz_offset )
+
+ # set the basic details
+ params = {
+ "APP_VERSION": vasl_templates.webapp.config.constants.APP_VERSION,
+ "VASSAL_VERSION": VassalShim.get_version()
+ }
+ if globvars.vasl_mod:
+ params[ "VASL_VERSION" ] = globvars.vasl_mod.vasl_version
+ for key in [ "VASSAL_DIR", "VASL_MOD", "VASL_EXTNS_DIR", "BOARDS_DIR",
+ "JAVA_PATH", "WEBDRIVER_PATH", "CHAPTER_H_NOTES_DIR", "USER_FILES_DIR" ]:
+ params[ key ] = app.config.get( key )
+
+ def parse_timestamp( val ):
+ """Parse a timestamp."""
+ if not val:
+ return None
+ # FUDGE! Adjust the timezone offset from "HH:MM" to "HHMM".
+ val = re.sub( r"(\d{2}):(\d{2})$", r"\1\2", val )
+ try:
+ val = datetime.strptime( val, "%Y-%m-%d %H:%M:%S %z" )
+ except ValueError:
+ return None
+ return to_localtime( val )
+
+ def replace_mountpoint( key ):
+ """Replace a mount point with its corresponding target (on the host)."""
+ params[ key ] = os.environ.get( "{}_TARGET".format( key ) )
+
+ # check if we are running inside a Docker container
+ if app.config.get( "IS_CONTAINER" ):
+ # yup - return related information
+ params[ "DOCKER_IMAGE_NAME" ] = os.environ.get( "DOCKER_IMAGE_NAME" )
+ params[ "DOCKER_IMAGE_TIMESTAMP" ] = datetime.strftime(
+ parse_timestamp( os.environ.get( "DOCKER_IMAGE_TIMESTAMP" ) ),
+ "%H:%M %d %b %Y"
+ )
+ params[ "DOCKER_CONTAINER_NAME" ] = os.environ.get( "DOCKER_CONTAINER_NAME" )
+ with open( "/proc/self/cgroup", "r" ) as fp:
+ buf = fp.read()
+ mo = re.search( r"^\d+:name=.+/docker/([0-9a-f]{12})", buf, re.MULTILINE )
+ params[ "DOCKER_CONTAINER_ID" ] = mo.group(1) if mo else "???"
+ # replace Docker mount points with their targets on the host
+ for key in [ "VASSAL_DIR", "VASL_MOD", "VASL_EXTNS_DIR", "BOARDS_DIR",
+ "CHAPTER_H_NOTES_DIR", "USER_FILES_DIR" ]:
+ replace_mountpoint( key )
+
+ # check the scenario index downloads
+ def check_df( df ): #pylint: disable=missing-docstring
+ with df:
+ if not os.path.isfile( df.cache_fname ):
+ return
+ mtime = datetime.utcfromtimestamp( os.path.getmtime( df.cache_fname ) )
+ key = "LAST_{}_SCENARIO_INDEX_DOWNLOAD_TIME".format( df.key )
+ params[ key ] = datetime.strftime(to_localtime(mtime), "%H:%M (%d %b %Y)" )
+ generated_at = parse_timestamp( getattr( df, "generated_at", None ) )
+ if generated_at:
+ key = "LAST_{}_SCENARIO_INDEX_GENERATED_AT".format( df.key )
+ params[ key ] = datetime.strftime( generated_at, "%H:%M %d %b %Y" )
+ from vasl_templates.webapp.scenarios import _asa_scenarios, _roar_scenarios
+ check_df( _asa_scenarios )
+ check_df( _roar_scenarios )
+
+ return render_template( "program-info-content.html", **params )
+
+# ---------------------------------------------------------------------
+
@app.route( "/help" )
def show_help():
"""Show the help page."""
diff --git a/vasl_templates/webapp/scenarios.py b/vasl_templates/webapp/scenarios.py
index 2a97ad1..233f4ef 100644
--- a/vasl_templates/webapp/scenarios.py
+++ b/vasl_templates/webapp/scenarios.py
@@ -34,6 +34,7 @@ def _build_asa_scenario_index( df, new_data, logger ):
}
# install the results
df.index = index
+ df.generated_at = new_data.get( "_generatedAt_" )
if logger:
logger.debug( "Loaded the ASL Secenario Archive index: #scenarios=%d", len(df.index) )
logger.debug( "- Generated at: %s", new_data.get( "_generatedAt_", "n/a" ) )
@@ -60,6 +61,7 @@ def _build_roar_scenario_index( df, new_data, logger ):
_update_roar_matching_index( id_matching, scenario.get("scenario_id"), roar_id )
# install the results
df.index, df.title_matching, df.id_matching = index, title_matching, id_matching
+ df.generated_at = new_data.get( "_generatedAt_" )
if logger:
logger.debug( "Loaded the ROAR scenario index: #scenarios=%d", len(df.index) )
logger.debug( "- Generated at: %s", new_data.get( "_generatedAt_", "n/a" ) )
diff --git a/vasl_templates/webapp/static/css/program-info.css b/vasl_templates/webapp/static/css/program-info.css
new file mode 100644
index 0000000..e845556
--- /dev/null
+++ b/vasl_templates/webapp/static/css/program-info.css
@@ -0,0 +1,12 @@
+.ui-dialog.program-info .ui-dialog-titlebar { background: #80d0ff ; }
+
+#program-info table { margin-bottom: 0.5em ; }
+#program-info td { text-align: bottom ; }
+#program-info td.key { width: 8.5em ; font-weight: bold ; white-space: nowrap ; }
+#program-info td.val { border: none ; }
+#program-info td ul { margin-top: 0 ; }
+
+#program-info .na { font-style: italic ; color: #444 ; }
+#program-info .path { font-family: monospace ; font-size: 90% ; }
+#program-info .extra { padding-left: 0.75em ; }
+#program-info .info { font-size: 80% ; font-style: italic ; color: #666 ; }
diff --git a/vasl_templates/webapp/static/images/menu/info.png b/vasl_templates/webapp/static/images/menu/info.png
new file mode 100644
index 0000000..6e88ea6
Binary files /dev/null and b/vasl_templates/webapp/static/images/menu/info.png differ
diff --git a/vasl_templates/webapp/static/main.js b/vasl_templates/webapp/static/main.js
index 49dde1f..93721ad 100644
--- a/vasl_templates/webapp/static/main.js
+++ b/vasl_templates/webapp/static/main.js
@@ -52,7 +52,7 @@ $(document).ready( function () {
// initialize the menu
var $menu = $("#menu input") ;
var imagesDir = gImagesBaseUrl + "/menu" ;
- $menu.popmenu( {
+ var menuItems = {
new_scenario: { label: "New scenario", icon: imagesDir+"/new.png", action: on_new_scenario },
load_scenario: { label: "Load scenario", icon: imagesDir+"/open.png", action: on_load_scenario },
save_scenario: { label: "Save scenario", icon: imagesDir+"/save.png", action: on_save_scenario },
@@ -66,8 +66,12 @@ $(document).ready( function () {
user_settings( null, null ) ;
} },
separator3: { type: "separator" },
+ program_info: { label: "Program info", icon: imagesDir+"/info.png", action: show_program_info },
show_help: { label: "Help", icon: imagesDir+"/help.png", action: show_help },
- } ) ;
+ } ;
+ if ( getUrlParam( "pyqt" ) )
+ delete menuItems.program_info ;
+ $menu.popmenu( menuItems ) ;
// nb: we only show the popmenu on left click (not the normal right-click)
$menu.off( "contextmenu" ) ;
$menu.click( function() {
@@ -910,6 +914,40 @@ function handle_escape( evt )
// --------------------------------------------------------------------
+function show_program_info()
+{
+ // show the PROGRAM INFO dialog
+ $( "#program-info" ).dialog( {
+ title: gAppConfig.APP_NAME + " (" + gAppConfig.APP_VERSION + ")",
+ dialogClass: "program-info",
+ modal: true,
+ width: $(window).width() * 0.8,
+ minWidth: 750,
+ height: $(window).height() * 0.8,
+ minHeight: 400,
+ open: function() {
+ var $dlg = $(this) ;
+ $dlg.find( ".content" ).hide() ;
+ $dlg.find( ".loader" ).show() ;
+ var url = gGetProgramInfoUrl + "?tz_offset=" + -new Date().getTimezoneOffset() ;
+ $.get( url, function( resp ) {
+ $dlg.find( ".loader" ).hide() ;
+ $dlg.find( ".content" ).html( resp ).fadeIn( 250 ) ;
+ } ).fail( function( xhr, status, errorMsg ) {
+ showErrorMsg( "Can't get the program info:
" + escapeHTML(errorMsg) + "
" ) ;
+ $dlg.dialog( "close" ) ;
+ } ) ;
+ },
+ buttons: {
+ OK: function() {
+ $(this).dialog( "close" ) ;
+ }
+ },
+ } ) ;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
function show_help()
{
// check if we need to load the HELP tab
diff --git a/vasl_templates/webapp/templates/index.html b/vasl_templates/webapp/templates/index.html
index 2bd25fc..306e115 100644
--- a/vasl_templates/webapp/templates/index.html
+++ b/vasl_templates/webapp/templates/index.html
@@ -34,6 +34,7 @@
+
@@ -90,6 +91,7 @@
{%include "lfa-upload.html"%}
{%include "user-settings-dialog.html"%}
+{%include "program-info-dialog.html"%}
{%include "ask-dialog.html"%}
{%include "please-wait.html"%}
@@ -140,6 +142,7 @@ gUpdateVsavUrl = "{{url_for('update_vsav')}}" ;
gPrepareAsaUploadUrl = "{{url_for('prepare_asa_upload')}}" ;
gOnSuccessfulAsaUploadUrl = "{{url_for('on_successful_asa_upload',scenario_id='ID')}}" ;
gMakeSnippetImageUrl = "{{url_for('make_snippet_image')}}" ;
+gGetProgramInfoUrl = "{{url_for('get_program_info')}}" ;
gHelpUrl = "{{url_for('show_help')}}" ;
diff --git a/vasl_templates/webapp/templates/program-info-content.html b/vasl_templates/webapp/templates/program-info-content.html
new file mode 100644
index 0000000..5584965
--- /dev/null
+++ b/vasl_templates/webapp/templates/program-info-content.html
@@ -0,0 +1,49 @@
+{%if DOCKER_CONTAINER_ID%}
+
+ Docker container:
+ | {{DOCKER_CONTAINER_NAME}} ({{DOCKER_CONTAINER_ID}})
+ |
Docker image:
+ | {{DOCKER_IMAGE_NAME}} (built {{DOCKER_IMAGE_TIMESTAMP}})
+ |
+{%endif%}
+
+
+ VASSAL:
+ | {%if VASSAL_VERSION%} {{VASSAL_VERSION}} {%else%} n/a {%endif%}
+ {%if VASSAL_DIR%} {%endif%}
+ |
VASL:
+ | {%if VASL_VERSION%} {{VASL_VERSION}} {%else%} n/a {%endif%}
+ {%if VASL_MOD%} {%endif%}
+ |
VASL extensions:
+ | {%if VASL_EXTNS_DIR%} {{VASL_EXTNS_DIR}} {%else%} - {%endif%}
+ |
VASL boards:
+ | {%if BOARDS_DIR%} {{BOARDS_DIR}} {%else%} - {%endif%}
+ |
+
+
+ Java:
+ | {%if JAVA_PATH%} {{JAVA_PATH}} {%else%} - {%endif%}
+ |
Web driver:
+ | {%if WEBDRIVER_PATH%} {{WEBDRIVER_PATH}} {%else%} - {%endif%}
+ |
+
+
+ Chapter H:
+ | {%if CHAPTER_H_NOTES_DIR%} {{CHAPTER_H_NOTES_DIR}} {%else%} - {%endif%}
+ |
User files:
+ | {%if USER_FILES_DIR%} {{USER_FILES_DIR}} {%else%} - {%endif%}
+ |
+
+
+ Scenario downloads:
+ |
+ | {%if LAST_ASA_SCENARIO_INDEX_DOWNLOAD_TIME%}
+ {{LAST_ASA_SCENARIO_INDEX_DOWNLOAD_TIME}}
+ {%if LAST_ASA_SCENARIO_INDEX_GENERATED_AT%} (generated {{LAST_ASA_SCENARIO_INDEX_GENERATED_AT}}) {%endif%}
+ {%else%} - {%endif%}
+ |
+ | {%if LAST_ROAR_SCENARIO_INDEX_DOWNLOAD_TIME%}
+ {{LAST_ROAR_SCENARIO_INDEX_DOWNLOAD_TIME}}
+ {%if LAST_ROAR_SCENARIO_INDEX_GENERATED_AT%} (generated {{LAST_ROAR_SCENARIO_INDEX_GENERATED_AT}}) {%endif%}
+ {%else%} - {%endif%}
+ |
diff --git a/vasl_templates/webapp/templates/program-info-dialog.html b/vasl_templates/webapp/templates/program-info-dialog.html
new file mode 100644
index 0000000..a8b43f5
--- /dev/null
+++ b/vasl_templates/webapp/templates/program-info-dialog.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+