Browse Source

Merge branch 'webview' of gogsadmin/prayer-generator into master

gogsadmin 1 year ago
parent
commit
ef00d097da
8 changed files with 279 additions and 54 deletions
  1. 77 33
      create-views.sql
  2. 1 0
      license/WEB.txt
  3. 6 0
      rest/Dockerfile
  4. 141 12
      rest/pyapi.py
  5. 1 0
      rest/requirements.txt
  6. 0 1
      will.csv
  7. 51 7
      xml/pg_view_style.xsl
  8. 2 1
      z_kingdom.csv

+ 77 - 33
create-views.sql

@@ -1,28 +1,78 @@
+CREATE OR REPLACE FUNCTION public.getfallbacktext(p_translation character varying, p_fallback character varying, p_reference text) RETURNS TABLE(translation character varying, text character varying)
+    LANGUAGE SQL
+    AS $$
+SELECT translation, text FROM (SELECT
+    0 AS ordinal,
+    p_translation AS translation,
+    gettext(p_translation, p_reference) AS text
+UNION SELECT
+    1 AS ordinal,
+    p_fallback AS translation,
+    gettext(p_fallback, p_reference) AS text
+UNION SELECT
+    3 AS ordinal,
+    getdefaulttranslation() AS translation,
+    getdefaulttext(p_reference) AS text
+) AS subq
+WHERE text IS NOT NULL
+ORDER BY ordinal ASC LIMIT 1;
+$$;
+
+CREATE TABLE IF NOT EXISTS public.pg_categories(
+    tableid int NOT NULL,
+    name text NOT NULL,
+    tablename text NOT NULL,
+    PRIMARY KEY(name)
+);
+ALTER TABLE public.pg_categories OWNER TO pgdb;
+
+INSERT INTO public.pg_categories(tablename, name, tableid)
+VALUES
+    ('c_name', 'name', 10), ('c_z_kingdom', 'z_kingdom', 15), ('c_a_kingdom', 'a_kingdom', 20),
+    ('c_will', 'will', 25), ('c_bread', 'bread', 30), ('c_because', 'because', 35),
+    ('c_forgive', 'forgive', 40), ('c_debtors', 'debtors', 45), ('c_tempt', 'tempt', 50),
+    ('c_deliver', 'deliver', 60), ('c_glory', 'glory', 70)
+ON CONFLICT DO NOTHING;
+
+CREATE OR REPLACE FUNCTION public.getprayer(p_name text[], p_translation character varying [], p_reference text [])
+RETURNS TABLE(ordinal integer, category text, translation character varying, reference text, txt text)
+    LANGUAGE plpgsql
+    AS $$
+DECLARE
+  r record;
+BEGIN
+FOR r IN
+  SELECT tableid, c.name, p.translation AS translation, p.reference AS reference FROM pg_categories AS c JOIN UNNEST(p_name, p_translation, p_reference) AS p(name, translation, reference) ON c.name = p.name
+LOOP
+  SELECT t.translation, t.text INTO translation, txt FROM getfallbacktext(r.translation, getdefaulttranslation(), r.reference) AS t;
+  RETURN QUERY SELECT r.tableid, r.name, translation, normalizeverse(r.reference::character varying)::text, txt;
+END LOOP;
+RETURN;
+END
+$$;
+
+
 --
 -- Name: getrandomsample(text, bigint); Type: FUNCTION; Schema: public; Owner: pgdb
 --
 
-CREATE OR REPLACE FUNCTION public.getrandomsample(p_table text, p_count bigint) RETURNS TABLE(ordinal integer, category text, translation character varying, reference text)
+CREATE OR REPLACE FUNCTION public.getrandomsample(p_name text, p_count bigint) RETURNS TABLE(ordinal integer, category text, translation character varying, reference text)
     LANGUAGE plpgsql
     AS $$
 DECLARE
   v_estimate bigint;
   v_percentage double precision;
   v_table_id int;
+  v_table text;
 BEGIN
-SELECT num INTO v_table_id FROM (VALUES 
-    ('c_name', 10), ('c_z_kingdom', 15), ('c_a_kingdom', 20),
-    ('c_will', 25), ('c_bread', 30), ('c_because', 35),
-    ('c_forgive',40), ('c_debtors', 45), ('c_tempt', 50),
-    ('c_deliver', 60), ('c_glory', 70)
-    
-) AS q(name, num)
-WHERE name = p_table;
-SELECT pg_class.reltuples INTO v_estimate FROM pg_class WHERE pg_class.relname = p_table;
+SELECT tableid, tablename, reltuples INTO v_table_id, v_table, v_estimate
+FROM pg_categories
+JOIN pg_class ON tablename = relname
+WHERE p_name = name;
 SELECT 1000::double precision / v_estimate INTO v_percentage;
 RETURN QUERY EXECUTE 'SELECT '|| v_table_id ||' AS ordinal,
-  '''|| REPLACE(p_table, 'c_', '') ||''' AS category, translation, reference
-FROM ' || p_table || ' TABLESAMPLE bernoulli ('|| v_percentage ||')
+  '''|| p_name ||''' AS category, translation, reference
+FROM ' || v_table || ' TABLESAMPLE bernoulli ('|| v_percentage ||')
 LIMIT ' || p_count;
 END
 $$;
@@ -41,70 +91,70 @@ CREATE OR REPLACE VIEW public.pg_random_view AS
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_name'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('name'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_bread'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('bread'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_debtors'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('debtors'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_because'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('because'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_tempt'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('tempt'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_deliver'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('deliver'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_glory'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('glory'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_will'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('will'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_a_kingdom'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('a_kingdom'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
 UNION
  SELECT getrandomsample.ordinal,
     getrandomsample.category,
     getrandomsample.translation,
     getrandomsample.reference,
     public.gettext(getrandomsample.translation, (getrandomsample.reference)::character varying) AS txt
-   FROM public.getrandomsample('c_z_kingdom'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
+   FROM public.getrandomsample('z_kingdom'::text, (1)::bigint) getrandomsample(ordinal, category, translation, reference)
   ORDER BY 1;
 
 ALTER TABLE public.pg_random_view OWNER TO pgdb;
@@ -116,17 +166,11 @@ ALTER TABLE public.pg_random_view OWNER TO pgdb;
 
 -- DROP VIEW IF EXISTS public.pg_random_view_default_if_null CASCADE;
 CREATE OR REPLACE VIEW public.pg_random_view_default_if_null AS
- SELECT subq.category,
-        CASE
-            WHEN (subq.txt IS NULL) THEN public.getdefaulttranslation()
-            ELSE subq.translation
-        END AS translation,
-    subq.reference,
-        CASE
-            WHEN (subq.txt IS NULL) THEN public.getdefaulttext((subq.reference)::character varying)
-            ELSE subq.txt
-        END AS txt
-   FROM public.pg_random_view subq;
+ SELECT category, (rec).translation, reference, (rec).text AS txt FROM (
+        SELECT subq.category,
+	    subq.reference,
+            public.getfallbacktext(subq.translation, getdefaulttranslation(), subq.reference) AS rec
+        FROM public.pg_random_view subq) AS q;
 
 ALTER TABLE public.pg_random_view_default_if_null OWNER TO pgdb;
 

+ 1 - 0
license/WEB.txt

@@ -0,0 +1 @@
+The World English Bible is a 1997 revision of the American Standard Version of the Holy Bible, first published in 1901. It is in the Public Domain. Please feel free to copy and distribute it freely.

+ 6 - 0
rest/Dockerfile

@@ -0,0 +1,6 @@
+FROM python:3-slim
+WORKDIR /usr/src/app
+COPY  requirements.txt ./
+RUN pip install --no-cache-dir -r requirements.txt
+COPY pyapi.py .
+CMD [ "python", "./pyapi.py" ]

+ 141 - 12
rest/pyapi.py

@@ -1,28 +1,157 @@
 # Copyright (c) Daniel Sheffield 2022
 # All rights reserved.
-from bottle import route, run, template, response
+from bottle import route, request, run, template, response, abort
 import psycopg
-from psycopg import Cursor
+from psycopg import Cursor, sql
 import os
-user = os.getenv('USER')
-password = os.getenv('PASSWORD')
-host = 'host=localhost'
-conn: Cursor = psycopg.connect(f"{host} dbname=pgdb user={user} {password}")
-statement = """SELECT
-  table_to_xml_and_xmlschema('pg_random_view_default_if_null'::regclass,
+host = f"host={os.getenv('HOST')}"
+db = f"dbname={os.getenv('DB', 'pgdb')}"
+user = f"user={os.getenv('USER', 'pgdb')}"
+password = f"password={os.getenv('PASSWORD','')}"
+if not password.split('=',1)[1]:
+    password = ''
+conn: Cursor = psycopg.connect(f"{host} {db} {user} {password}")
+random_statement = """SELECT
+  query_to_xml('SELECT category, translation, reference, txt
+    FROM pg_random_view_default_if_null
+    JOIN pg_categories ON category = name
+    ORDER BY tableid ASC',
   false, false, ''::text);"""
+specified_statement = sql.SQL("""SELECT query_to_xml($$SELECT category, translation, reference, txt
+  FROM getprayer(
+    {categories}::text[],
+    {translations}::text[],
+    {references}::text[]
+  ) ORDER BY ordinal ASC$$, false, false, ''::text);""")
 heading = """<?xml version="1.0" encoding="UTF-8"?>
 <?xml-stylesheet type="text/xsl" href="https://shandan.one/xml/pg_view_style.xsl"?>
 """
+random_schema = """
+<xsd:schema
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+<xsd:simpleType name="UDT.pgdb.pg_catalog.text">
+  <xsd:restriction base="xsd:string">
+  </xsd:restriction>
+</xsd:simpleType>
+
+<xsd:simpleType name="VARCHAR">
+  <xsd:restriction base="xsd:string">
+  </xsd:restriction>
+</xsd:simpleType>
+
+<xsd:complexType name="RowType.pgdb.public.pg_random_view_default_if_null">
+  <xsd:sequence>
+    <xsd:element name="category" type="UDT.pgdb.pg_catalog.text" minOccurs="0"></xsd:element>
+    <xsd:element name="translation" type="VARCHAR" minOccurs="0"></xsd:element>
+    <xsd:element name="reference" type="UDT.pgdb.pg_catalog.text" minOccurs="0"></xsd:element>
+    <xsd:element name="txt" type="VARCHAR" minOccurs="0"></xsd:element>
+  </xsd:sequence>
+</xsd:complexType>
+
+<xsd:complexType name="TableType.pgdb.public.pg_random_view_default_if_null">
+  <xsd:sequence>
+    <xsd:element name="row" type="RowType.pgdb.public.pg_random_view_default_if_null" minOccurs="0" maxOccurs="unbounded"/>
+  </xsd:sequence>
+</xsd:complexType>
+
+<xsd:element name="table" type="TableType.pgdb.public.pg_random_view_default_if_null"/>
+
+</xsd:schema>
+"""
+specified_heading = heading + """
+<xsd:element name="table" type="TableType.pgdb.public.pg_view"/>
+
+</xsd:schema>
+"""
+specified_schema = """
+<xsd:schema
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+<xsd:simpleType name="UDT.pgdb.pg_catalog.text">
+  <xsd:restriction base="xsd:string">
+  </xsd:restriction>
+</xsd:simpleType>
+
+<xsd:simpleType name="VARCHAR">
+  <xsd:restriction base="xsd:string">
+  </xsd:restriction>
+</xsd:simpleType>
+
+<xsd:complexType name="RowType.pgdb.public.pg_view">
+  <xsd:sequence>
+    <xsd:element name="category" type="UDT.pgdb.pg_catalog.text" minOccurs="0"></xsd:element>
+    <xsd:element name="translation" type="VARCHAR" minOccurs="0"></xsd:element>
+    <xsd:element name="reference" type="UDT.pgdb.pg_catalog.text" minOccurs="0"></xsd:element>
+    <xsd:element name="txt" type="VARCHAR" minOccurs="0"></xsd:element>
+  </xsd:sequence>
+</xsd:complexType>
+
+<xsd:complexType name="TableType.pgdb.public.pg_view">
+  <xsd:sequence>
+    <xsd:element name="row" type="RowType.pgdb.public.pg_view" minOccurs="0" maxOccurs="unbounded"/>
+  </xsd:sequence>
+</xsd:complexType>
+
+<xsd:element name="table" type="TableType.pgdb.public.pg_view"/>
+
+</xsd:schema>
+"""
+
 
 @route('/pyapi/random')
-def index():
+def random():
+    try:
+        with conn.cursor() as cur:
+            xml = cur.execute(random_statement).fetchone()[0].splitlines()
+    finally:
+        conn.commit()
+    response.content_type = 'application/xhtml+xml; charset=utf-8'
+    return f"{heading}{xml[0]}{random_schema}" + '\n'.join(xml[1:])
+
+
+@route('/pyapi/pg')
+def specified():
+    kingdom = { 'a_kingdom', 'z_kingdom' }
+    categories = [ k for k in request.query.keys() ]
+    translations = []
+    references = []
+    for c in categories:
+        try:
+            r, t = request.query[c].split(' ', 1)
+        except ValueError:
+            raise abort(
+                400,
+                f"Could not parse value for {c}."
+                f" Given: {request.query[c]}"
+            )
+
+        if None in (r or None, t or None,):
+            raise abort(
+                400,
+                f"Both reference and translation must be provided for {c}."
+                f" Given: {request.query[c]}"
+            )
+        references.append(r)
+        translations.append(t)
+
+    if len(kingdom - set(categories)) not in (0, 2,):
+        raise abort(
+            400,
+            f"Both of {kingdom} must be specified or neither specified."
+            f" Missing: {kingdom - set(categories)}"
+        )
+
     try:
         with conn.cursor() as cur:
-            xml = cur.execute(statement).fetchone()[0]
+            xml = cur.execute(specified_statement.format(
+                categories=sql.Literal(categories),
+                translations=sql.Literal(translations),
+                references=sql.Literal(references)
+            )).fetchone()[0].splitlines()
     finally:
         conn.commit()
     response.content_type = 'application/xhtml+xml; charset=utf-8'
-    return f"{heading}{xml}"
+    return f"{heading}{xml[0]}{specified_schema}" + '\n'.join(xml[1:])
 
-run(host='192.168.0.20', port=11888)
+run(host='0.0.0.0', port=11888)

+ 1 - 0
rest/requirements.txt

@@ -1 +1,2 @@
+psycopg[binary]
 bottle

+ 0 - 1
will.csv

@@ -1,7 +1,6 @@
 reference	translation
 Isaiah 43:7	NKJV
 John 11:40	NKJV
-Acts 16:1-25:27	ESV
 John 4:23	ESV
 Colossians 3:2	ESV
 2 Corinthians 3:18	NKJV

+ 51 - 7
xml/pg_view_style.xsl

@@ -23,16 +23,57 @@ All rights reserved.
 
     <html>
       <head>
-	      <meta name="viewport" content="width=device-width, initial-scale=1"/>
-	      <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/purecss@2.1.0/build/pure-min.css" integrity="sha384-yHIFVG6ClnONEA5yB5DJXfW2/KC173DIQrYoZMEtBvGzmf0PKiGyNEqe9N6BNDBH" crossorigin="anonymous"/>
-	      <link rel="stylesheet" href="https://shandan.one/css/grids-responsive-min.css"/>
-	      <link rel="stylesheet" href="https://shandan.one/css/responsive-visibility-collapse.css"/>
-        <title><xsl:value-of select="name(current())"/></title>
+        <title>
+        <xsl:choose>
+          <xsl:when test="$tabletypename = 'TableType.pgdb.public.pg_random_view_default_if_null'">
+          Random Prayer Generator
+          </xsl:when>
+          <xsl:when test="$tabletypename = 'TableType.pgdb.public.pg_view'">
+          Prayer Generator
+          </xsl:when>
+          <xsl:otherwise>
+          <xsl:value-of select="$tabletypename"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        </title>
+        <meta>
+        <xsl:attribute name="name">title</xsl:attribute>
+        <xsl:attribute name="content">
+        <xsl:choose>
+          <xsl:when test="$tabletypename = 'TableType.pgdb.public.pg_random_view_default_if_null'">Random Prayer Generator</xsl:when>
+          <xsl:when test="$tabletypename = 'TableType.pgdb.public.pg_view'">Prayer Generator</xsl:when>
+          <xsl:otherwise>
+          <xsl:value-of select="$tabletypename"/>
+          </xsl:otherwise>
+        </xsl:choose>
+        </xsl:attribute>
+        </meta>
+	<meta name="description" content="Generate a template prayer following the form of the model prayer outlined in Matthew 6:9‑13 and Luke 11:2‑4"/>
+	<meta name="keywords" content="Bible, Prayer, Generator, SWORD"/>
+	<meta name="author" content="Daniel Sheffield"/>
+	<meta name="viewport" content="width=device-width, initial-scale=1"/>
+	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/purecss@2.1.0/build/pure-min.css" integrity="sha384-yHIFVG6ClnONEA5yB5DJXfW2/KC173DIQrYoZMEtBvGzmf0PKiGyNEqe9N6BNDBH" crossorigin="anonymous"/>
+	<link rel="stylesheet" href="https://shandan.one/css/grids-responsive-min.css"/>
+	<link rel="stylesheet" href="https://shandan.one/css/responsive-visibility-collapse.css"/>
+        <style>
+span.engraved {
+    color: #A8A8A8;
+    font-size: 0.8em;
+    text-shadow: 0px 0.1em 0px rgba(168,168,168,.1), 0px -0.1em 0px rgba(0,0,0,.7);
+    float: right;
+}
+	</style>
       </head>
       <body>
         <div style="background: darkgray; position: sticky; top: 0; margin: 0; padding: 0; width: 100%; z-index: 1; text-align: center;">
         <form action="random">
           <button type="submit" style="margin: 0.2em">Generate</button>
+          <span style="position: absolute; right: 0; padding: 0.4em">
+          <a>
+          <xsl:attribute name="href">./pg?<xsl:for-each select="//row"><xsl:value-of select="category"/>=<xsl:value-of select="translate(reference,' ','')"/>+<xsl:value-of select="translation"/><xsl:if test="position() != last()">&#038;</xsl:if></xsl:for-each></xsl:attribute>
+          Permalink
+          </a>
+          </span>
 	</form>
         </div>
         <div style="position: relative; max-width: 68em; min-width: min(100%, 68em); left: 50%; transform: translate(max(-50%, -34em),0)">
@@ -64,7 +105,10 @@ All rights reserved.
                               select="name(current())"/>
                 <xsl:choose>
                 <xsl:when test="$elementtype = 'txt'">
-                <td class="opt-all"><xsl:value-of select="."/></td>
+                <td class="opt-all" style="text-align: justify"><xsl:value-of select="."/>&#160;<span class="engraved">
+                <xsl:value-of select="../translation"/>
+                </span>
+                </td>
                 </xsl:when>
                 <xsl:when test="$elementtype = 'category'">
                   <xsl:choose>
@@ -94,7 +138,7 @@ All rights reserved.
               </xsl:for-each>
             </tr>
           </xsl:for-each>
-        <tfoot>
+        <tfoot style="line-height: 1.4">
         <tr>
           <td colspan="3">
           Copyright (c) Daniel Sheffield 2022. All right reserved.

+ 2 - 1
z_kingdom.csv

@@ -637,7 +637,8 @@ Dan 9:9,KJV
 1 Sam 15:23,KJV
 1 Tim 5:1,KJV
 2 Thes 2:10-11,KJV
-1 Jn 1:8,10;
+1 Jn 1:8,KJV
+1 Jn 1:10,KJV
 Acts 16:6,KJV
 1 Jn 3:16,KJV
 Lk 19:14,KJV