Python Notes: Picture-of-the-Day

  1. The information presented here is intended for educational use.
  2. The information presented here is provided free of charge, as-is, with no warranty of any kind.
  3. Edit: 2024-07-14

Overview

file-1: HTML test page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta content="#ffffff" name="theme-color">
    <title>Test Pix</title>
    <link href="css/nsr-20170909.css" rel="stylesheet" type="text/css">
    <script src="js/picture-viewer72.js"></script>
</head>
<body lang="en-ca" >
    <h4>Test-Pix</h4>
    <table class="tbl-blue" style="width:100%">
	<tr><td>msg</td> <td id="msg"></td></tr>
	<tr><td>src</td> <td id="src"></td></tr>
	<tr><td style="width:50px">com</td>
<td id="comment" style="min-width:90%"></td>
</tr> <tr><td>txt</td>
<td id="text" style="text-align: center; font-weight: bold;height:280px"></td>
</tr> <tr><td>calc</td><td id="calc"></td></tr> <tr><td>mmdd</td><td id="mmdd"></td></tr> </table> <script> testYear(); </script> </body> </html>

file-2: HTML production page

<!DOCTYPE html>
<html lang="en-ca">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Hackerspace - STEMspace</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.slim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
<link href="css/nsr-20170909.css" rel="stylesheet" type="text/css"> <script src="js/picture-viewer74.js"></script> </head>
<body>

...snip...

<div class="col-xs-12 col-sm-5 col-md-4 col-lg-3" id="pixoftheday"
style="text-align: center; font-weight: bold;height:280px">
<img alt="400ky of atmospheric CO2" src="images/carbon_dioxide_400kyr.png" style="height:80%"> <div style="text-align:center;margin-top:5px;font-weight:bold">
CO2 levels cycle between<br>180 and 280 ppm every 120K years</div> </div>
</div>

...snip...
<script> //
// use a namespace technique for declaring functions
// p.s. my implementation is way too complicated for most sites
// // create an object called nsNSR1 // var nsNSR1 = function(){ // // show/hide the xmas alert // function xmas_support(){ var snap = new Date(); var ccyy = snap.getFullYear(); var mm = snap.getMonth(); //var dow = snap.getDay(); var dd = snap.getDate(); if (mm == 0){ ccyy--; } // display for Western Christmas document.getElementById("xmasyear").outerHTML = (ccyy); if ((mm == 11) || ((mm == 0) && (dd <= 2))){ document.getElementById("xmasmsg").hidden = false; }else{ document.getElementById("xmasmsg").hidden = true; } } // // enable a hyperlink (old site only) // function moving(){ var this_host = window.location.hostname; var caveat = this_host.indexOf('neilrieck.net'); console.log('moving-caveat:' + this_host); console.log('moving-caveat:' + caveat); if (caveat<0){ // enable HTML warning on the page document.getElementById("moving9").hidden = false; } } // // pod (picture-of-day) notes: // 1) Rotate between 4 images (3 environmental and 1 scientist) // 2) this routine calls things in namespace "nsNSR7" defined in picture-viewer75.js // 3) image "co2-400kyr" is already on the screen in case of failures here // 4) steps: // 1: load "local co2-400kyr" (into slot 0) // load "NOAA co2-data" (into slot 1) // load "NOAA co2-trend" (into slot 2) // load "local Scientist" (into slot 3) // 2-5: kill 3 seconds // 6: test if NOAA images loaded // load "local co2-data" (into slot 1 only if it is empty) // load "local co2-trend" (into slot 2 only if it is empty) // 7-10: kill 3 seconds (only if necessary) // 11: stop timer9 then check if we have four images; // if all is well then start timer8 to rotate between four prefetched images // function pod(){ // pod = picture-of-day var pix9 = 0; // init console.log("starting pod()"); function init(){ console.log("pod-init()"); try{ console.log("init1: ", JSON.stringify( Object.assign ( {}, nsNSR7.myTxt ))); nsNSR7.initArrays(10); console.log("init2: ", JSON.stringify( Object.assign ( {}, nsNSR7.myTxt ))); for (i=1;i<10;i++){ console.log("hack"+i, JSON.stringify( Object.assign ( {}, nsNSR7.myTxt[i] ))); } }catch(e){ console.log("init-error:",e); } } // init(); // start timer-9 var intervalId9 = setInterval( function(){ pix9++; console.log("pod-pix9: "+pix9); if (pix9==1){ nsNSR7.fetchDefault(0,8000); // co2 400kyr (local) nsNSR7.fetchDefault(1,8002); // co2-data (NOAS) nsNSR7.fetchDefault(2,8004); // co2-trend (NOAS) nsNSR7.fetchToday(3); // scientist picture-of-the-day return; } if (pix9<6){ // kill some time return; } if (pix9==6){ // test if NOAA images loaded // image1: if no 8002 then load 8001 var work = 0; var tmp; try{ tmp=nsNSR7.imageArray[1].width; }catch(e){ console.log("-e-image1: ",e); tmp=0; } if (tmp==0){ console.log("pod: 8002 fubar so loading 8001"); nsNSR7.fetchDefault(1,8001); // co2-data (local) work += 1; // did some work } // image2: if no 8004 then load 8003 var tmp; try{ tmp=nsNSR7.imageArray[2].width; }catch(e){ console.log("-e-image2: ",e); tmp=0; } if (tmp==0){ console.log("pod: 8004 fubar so loading 8003"); nsNSR7.fetchDefault(2,8003); // co2-trend-local work += 1; // did some work } if (work==0){ // if no need to wait pix9=11; console.log("pod-pix9 jumps to: "+pix9); }else{ return; } } if (pix9<11){ // killing time return; } if (pix9==11){ console.log("pod: stopping timer #9"); clearInterval(intervalId9); // stop this timer var recipe = 0; // init try{ // make sure we have four good images for (var i=0;i<4;i++){ console.log("image: "+i+" size:",nsNSR7.imageArray[i].width); if (nsNSR7.imageArray[i].width==0){ console.log("oops: image "+i+" is zero length"); return; }else{ recipe++; } } }catch(e){ console.log("recipe error: ",e); recipe = 99; } console.log("recipe: "+recipe); if (recipe != 4){ console.log("pod: recipe error so will not rotate any images"); }else{ console.log("pod: arming timer #8"); var pix8=0; // init var intervalId8 = setInterval( function (){ pix8++; // pix = pix8%4; // only 4 pictures: 0-3 console.log("pod-pix8: " +pix8+" index: "+pix); if (pix8 >= 24){ console.log("pod: stopping timer #8"); clearInterval(intervalId8); }else{ console.log("pod-inject:" + nsNSR7.myTxt[pix]); document.getElementById("pixoftheday").innerHTML = nsNSR7.myTxt[pix]; } }, 6000); // 6 seconds } } }, 1000); // 1 second } // // expose anything we want externally referenced // return{ xmas_support:xmas_support, moving:moving, pod:pod } }(); // now execute these tasks console.log("-i-local javascript tasks begin"); function show_answer(element) { element.parentNode.children[2].style.visibility = "visible"; } function hide_answer(element) { element.parentNode.children[2].style.visibility = "hidden"; } // nsNSR1.xmas_support(); // nsNSR1.moving(); // odyssey_viewer(); console.log("-i-queueing a 2-second delay"); setTimeout(function() { console.log("-i-timer expired"); nsNSR1.pod(); }, 2000);
// ############################## nsNSR1.pod(); // execute my function </script>
</body> </html>

file-3: JavaScript

// =======================================================================
// title : picture-viewer75.js
// author: Neil Rieck (Waterloo, Ontario, Canada)
// notes :
// 1) In order to avoid name clashes with other javascript routines, this
//    code block declares functions and variables inside a namespace. So 
//    any labels you wish published must be added to the return statment
//    at the end of this file
// 2) this file currently supports default date codes: 8000-8004
//
// ver when       what 
// --- ---------- --------------------------------------------------------
//   4 2019-11-28 modified to work with my Python-based cgi
//   5 2020-04-04 code cleanup
//   6 2020-10-29 moved everything into a namespace
//  71 2021-10-30 better overlapping AJAX (pulling from slow sites)
//  72 2021-10-31 XmlDoc is no longer global (what was I thinking?)
//  73 2021-12-01 removed protocol from url
//  74 2022-08-20 changes to allow 3-5 default pictures
//  75 2024-07-06 experiments with async fetches. See: myAsync[]
// =======================================================================
//
//	start of name-space
//
var nsNSR7 = (function() {
//
'use strict';		// no kid-stuff here
//
//	some private variables
//
var debug=0;		//
var slot=0;		//
var mySrc=[];		// image source
var myTxt=[];		// image text (from payload)
var imageArray=[];	// image data
var myAsync=[]; // array of objects // // store data as well as preload the image // function preloadImage(url,txt,slot){ if (typeof slot == "undefined"){ var slot=9; } console.log("preload url: "+url+" slot: "+slot); try{ mySrc[slot] = url; myTxt[slot] = txt; imageArray[slot] = new Image(); imageArray[slot].src = url; }catch(e){ console.error("preload url: "+url+" slot: "+slot+" error: ",e); } } // // start_ajax (start an AJAX transaction) // Note: use new to create copies of this object
//
function start_ajax(msg,slot){ console.log("start ajax slot: "+slot);
// still some Microsoft browsers out there if (typeof XMLHttpRequest == "undefined") XMLHttpRequest=function(){ try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch (e) {} try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch (e) {} try { return new ActiveXObject("Msxml2.XMLHTTP") } catch (e) {} try { return new ActiveXObject("Microsoft.XMLHTTP") } catch (e) {} throw new Error("This browser does not support XMLHttpRequest.") } if (msg != ""){ var xhr=new XMLHttpRequest(); if (xhr != null){ xhr.open("GET", msg, true); // async=true xhr.timeout=3000; // 3 seconds max xhr.ontimeout = function(){ console.log("Timeout:",slot); } xhr.onreadystatechange=function(){ if (xhr.readyState == 4 && xhr.status == 200){ var resp$=xhr.responseText; ajax_xml_extract(resp$); } } console.log("sending ajax request for slot: "+slot) xhr.send(null); // not null for POST } } } // // ajax_xml_extract // function ajax_xml_extract(resp$){ // console.log("ajax-xml-extract raw:",resp$); try{ if (window.DOMParser) { var parser=new DOMParser(); var xml=parser.parseFromString(resp$,"text/xml"); }else{ var xml=new ActiveXObject("Microsoft.XMLDOM"); xml.async=false; xml.loadXML(resp$); } var src = get_xml_data("src",xml); var txt = get_xml_data("text",xml); var slot= get_xml_data("slot",xml); console.log("calling preloadImage for slot: "+slot) preloadImage(src,txt,slot); }catch(e){ console.error("ajax_xml_extract:",e) } } // // does string 'x' represent a positive integer? // function isStrInt(x){ x=x.replace(/\s/g,''); if (x=="") return false; try{ y = parseInt(x); }catch(e){ y = -1; } if(y>0){ return true; }else{ return false; } } function htmlEncode(str) { return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } function htmlDecode(str) { return String(str).replace(/</g, '<').replace(/>>/g, '>').replace(/"/g, '"').replace(/&/g, '&'); } // // get_xml_data (look for one item) // function get_xml_data(tag,xml){ var x; try{ x = xml.getElementsByTagName(tag)[0].childNodes[0].nodeValue; }catch(e){ console.error("get-xml-data: ",e); x = null; } return(x); } // // get number of days per month // function monthSize(x){ switch(x) { case 2: return(28); case 9: case 4: case 6: case 11: return(30); default: return(31); } } function pad(num, size) { var s = num+""; while (s.length < size) s = "0" + s; return s; } // // test picture-of-the-day logic for the whole year // caveat: only used in testing // function testYear(){ debug=1; // change global var mm=0; // month var dd=0; // day var mmdd=""; var intervalId = setInterval(function(){ if (mm==0){ mm=1; dd=1; }else{ dd++; limit=monthSize(mm); if (dd>limit){ mm++; dd=1; } } if (mm>12){ clearInterval(intervalId); }else{ mmdd=junk=pad(mm,2)+pad(dd,2); console.log("mmdd:"+mmdd);
document.getElementById("mmdd").innerHTML="mmdd: "+mmdd; msg = "https://neilrieck.net/cgi-bin/daily_pix?xml=1&slot=0&mmdd="+mmdd; start_ajax(msg,0); } }, 1000); } // // get one picture for today // function fetchToday(slot){ var d = new Date(); var mm = d.getMonth(); // 0..11 var dd = d.getDate(); // 1..31 var mmdd= pad(mm+1,2)+pad(dd,2); console.log("fetchToday-mmdd: "+mmdd); var msg = "https://neilrieck.net/cgi-bin/daily_pix?xml=1&slot="+slot+"&mmdd="+mmdd; // start_ajax(msg,slot);
myAsync.push(new start_ajax(msg,slot)); // create one-time use object
} // // get default picture // 8000: co2_400kyr // 8001: co2_levels (static-local) // 8002: co2_levels (dynamic-remote) // function fetchDefault(slot,mmdd){ if (typeof mmdd == "undefined"){ var slot=8000; } if ((mmdd<8000)||(mmdd>8002)){ slot=8000; } console.log("fetchDefault-mmdd: "+mmdd+" slot: "+slot); var msg = "https://neilrieck.net/cgi-bin/daily_pix?xml=1&slot="+slot+"&mmdd="+mmdd; // start_ajax(msg,slot);
myAsync.push(new start_ajax(msg,slot)); // create one-time use object } // // end-of-name-space logic // now publish the names of anything referenced externally // return{ preloadImage:preloadImage, fetchToday:fetchToday, fetchDefault:fetchDefault, mySrc:mySrc, myTxt:myTxt, imageArray:imageArray } // end of name-space }()); // end of file

file-4: Python3 (optional launcher)

note: this file is usually located in the cgi-bin folder of your web server
#!/usr/bin/python3
# title : daily_pix
# author: Neil Rieck (Waterloo, Ontario, Canada)
# edit  : 2022-09-30
# notes : forces Python3 to do a one-time compile of whatever is imported so
#         'file_100.py' is compiled to '__pycache__/file_100.cpython-36.pyc'
import daily_pix_133
daily_pix_133.main()

file-5: Python3 (program)

note: in this example, this file is found under cgi-bin (but you could move it anywhere else provided Apache can access it)
#!/usr/bin/python3
'''
===================================================================================
title  : daily_pix_133.py
author : Neil Rieck
history:
ver when       what
100 2019-11-23 calls daily_pix_mysql.py
101 2019-12-15 cleanup
102 2020-04-18 added JSON support
130 2020-05-09 moved db into here; changed the connector; cursor now returns a dict
    2020-07-11 tiny fix in anniversary/season calc (see: 'calc' vs 'calc1')
131 2020-07-11 replaced format strings with f strings
    2021-12-01 removed 'http:' from urls (to properly support CORS)
132 2022-08-22 now also encode 'comment' in output_xml
133 2022-09-30 added support for pattern: +ccyy+
purpose:
1) for a given month-day (format: mmdd) return a picture along with associated text
2) optionally, return the data in XML or JSON format
3) these PEP8 rules are disabled: E305,E501 so I can code in a traditional way
4) I used vim8 along with the pymode plugin
===================================================================================
'''
import cgi
import datetime
import json
import sys
import html
import mysql.connector as db
#
#   constants
#
HOST = "neilrieck.net"
#
#   common function to BAIL (will be visible in the browser's console)
#
def errorExit(msg):
    print("content-type: text/plain; charset=utf-8")
    print("")
    print(msg)
    sys.exit()
#
#   logIt (a simple file-based logger)
#   caveat: if SELinux is enabled then write to /var/log/daily_pix_msg_log.txt
#
def logIt(msg):
    try:
        fn = "daily_pix_msg_log.txt"
        f = open(fn, "a")
        f.write(f"msg: {msg}\r\n")
        f.close()
    except Exception:
        pass
#
#   do that database thing
#
def get_record(parm):
    try:
        con = db.connect(host='127.0.0.1', user='read123', passwd='read123', database='hack1')
        cur = con.cursor(dictionary=True)                           # force return of dict
        cmd = ("select * from pixofday "
               f"where (mmdd1 <= {parm}) and (mmdd2 >= {parm}) "
               "order by concat(mmdd2,mmdd1) limit 1")
        cur.execute(cmd)
        # row = cur.fetchall()                                      # returns a list of tuples (or dicts)
        row = cur.fetchone()                                        # returns a tuple (or dict)
        return row
    except Exception as e:
        msg = f"Exception: {e} while opening database"
        logIt(msg)
        errorExit(msg)
'''
===============================================================

 <<< general description >>>

 1) database field definitions
   0: mmdd1   - start date
   1: mmdd2   - end date
   2: comment -
   3: calc    - used to do an anniversary (or season) calculation
   4: src     - image source
   5: text1   - usually a title associated with the photo
   6: text2
   7: text3
 2) data:
   1: each record will indicate that a certain photo be shown between mmmdd1 and mmdd2
   2: one special record has mmdd1="0000" and mmdd2="9999" so something will always be returned
 3) logic:
   1: the 'src' field contains an image location like these two examples:
      1) local:  /images/Edmond_Halley.jpg
      2) remote: https://www.esrl.noaa.gov/gmd/webdata/ccgg/trends/co2_data_mlo.png
   2: any entry not containing "http" will need a local prefix
   3: text1 to text3 contain HTML so merge them into one string then test for the following patterns:
   4: if we detect the string "+xxx+" then replace it with the resultant 'src' string seen at the end of step 2
   5: if we detect the string "+host+" then replace it with the default HOST of this site
   6: if we detect the string "+calc+" then do an season calculation (eg. 45th season of Quirks+Quarks)
   7: if we detect the string "+calc1+" then do an offset+1 calculation (eg. 50th Anniversary of Oktoberfest)
   8: if we detect the string "+ccyy+" then we will replace it with the current year
   9: return data to caller via XML or JSON (this means the data must be properly escaped)
 4) Cross-Origin Resource Sharing (CORS)
    For AJAX use you must add a few Cross-Origin Resource Sharing (CORS) directives to Apache config like so:
    <Directory "/var/www/html/cgi-bin">
      AllowOverride None
      Options None
      Require all granted
      # 1) CORS for initial AJAX testing.
      #Header set Access-Control-Allow-Origin "*"
      # 2) CORS for single second site support (this Apache instance runs on "https://neilrieck.net"
      # but we want to allow AJAX access from "http://www3.sympatico.ca/n.rieck/")
      Header set Access-Control-Allow-Origin http://www3.sympatico.ca
      Header set Access-Control-Allow-Headers "Content-Type,Accept"
      Header merge Vary Origin
      Header set MyHeader "Neil's Hack"
     </Directory>
===============================================================
'''
#
#   raw data in :: cooked data out
#
def process_data(row):
    imgSrc = row['src']                                             # LOGIC-1
    if (imgSrc.find('http') == -1):                                 # LOGIC-2 (local or remote)
        imgSrc = ".." + imgSrc                                      # would '/images' be better?
    allText = row['text1'] + row['text2'] + row['text3']            # LOGIC-3
    temp = allText.find('+xxx+')                                    # LOGIC-4
    if (temp != -1):                                                # if +xxx+ was found...
        allText = allText[:temp] + imgSrc + allText[temp+5:]        #
    temp = allText.find('+host+')                                   # LOGIC-5
    if (temp != -1):                                                # if +host+ was found...
        allText = allText[:temp] + HOST + allText[temp+6:]          #
    temp = allText.find('+calc+')                                   # LOGIC-6 (season number)
    if (temp != -1):                                                # if +calc+ was found...
        year = datetime.datetime.now().strftime('%Y')               #
        try:                                                        #
            season = int(row['calc'])                               # eg. ccyy
        except Exception as e:                                      #
            season = 1900                                           #
            msg = f"error: {e}"                                     #
            logIt(msg)                                              #
        diff = int(year) - season                                   #
        allText = allText[:temp] + str(diff) + allText[temp+6:]     #
    temp = allText.find('+calc1+')                                  # LOGIC-7 (anniversary)
    if (temp != -1):                                                # if +calc1+ was found...
        year = datetime.datetime.now().strftime('%Y')               #
        try:                                                        #
            anniv = int(row['calc'])                                # eg. ccyy
        except Exception as e:                                      #
            anniv = 1900                                            #
            msg = f"error: {e}"                                     #
            logIt(msg)                                              #
        diff1 = int(year) - anniv + 1                               #
        allText = allText[:temp] + str(diff1) + allText[temp+7:]    #
    temp = allText.find('+ccyy+')                                   # LOGIC-8
    if (temp != -1):                                                # if +ccyy+ was found...
        year = datetime.datetime.now().strftime('%Y')               #
        allText = allText[:temp] + str(year) + allText[temp+6:]     #
    return allText, imgSrc                                          #

#
#   output xml
#
def output_xml(row, mmdd, slot):
    allText, imgSrc = process_data(row)                             #
    escText = html.escape(allText)                                  # LOGIC-9
    comment = row['comment']                                        #
    escComm = html.escape(comment)                                  #
    calc = row['calc']                                              #
    print('Content-Type: text/xml; charset=utf-8')                  # we're returning an XML response
    print('')                                                       # end of HTTP header
    print('<?xml version="1.0" encoding="utf-8"?>')                 # start of XML content
    print('<result>')                                               # user-defined by me (see JavaScript)
    print('<status>1</status>')                                     #
    print(f'<debug>{mmdd}</debug>')                                 # the parameter passed in
    print(f'<comment>{escComm}</comment>')                          #
    print(f'<calc>{calc}</calc>')                                   #
    print(f'<src>{imgSrc}</src>')                                   #
    print(f'<text>{escText}</text>')                                #
    print(f'<slot>{slot}</slot>')                                   #
    print('</result>')                                              #
#
#   output json
#
def output_json(row, mmdd, slot):
    allText, imgSrc = process_data(row)                             # cook the data
    print('Content-Type: text/json; charset=utf-8')                 # we're returning an XML response
    print('')                                                       # end of HTTP header
    del row['text3']                                                # remove raw
    del row['text2']
    del row['text1']
    row['imgSrc'] = imgSrc                                          # append cooked
    row['text'] = allText                                           # append cooked
    print(json.dumps(row, indent=1))                                # magic happens here
#
def output_plain(row, mmdd, slot):                                  #
    allText, imgSrc = process_data(row)                             #
    print('Content-Type: text/plain; charset=utf-8')                #
    print('')                                                       #
    print("debug  : " + mmdd)                                       #
    print("comment: " + row['comment'])                             # output: comment field
    print("calc   : " + row['calc'])                                #
    print("src    : " + imgSrc)                                     # output: IMG src field
    print("text   : " + allText)                                    # output: text fields

# ==============================================================
# main block
#
# example URLs:
# Dec-25) https://neilrieck.net/cgi-bin/daily_pix?mmdd=1225&xml=1
# Jan-01) https://neilrieck.net/cgi-bin/daily_pix?mmdd=0101&xml=1
# CO2   ) https://neilrieck.net/cgi-bin/daily_pix?mmdd=8000&xml=1
# ==============================================================
#
#   locate named data in cgi field storage
#
def getFld(cfs, label, dflt):
    if label not in cfs:
        return dflt
    else:
        return cfs.getvalue(label)

def main():
    cfs = cgi.FieldStorage()                                        # convert CGI data into a Python data
    xml = getFld(cfs, 'xml', '0')                                   # default to "0" if missing
    json = getFld(cfs, 'json', '0')                                 #
    slot = getFld(cfs, 'slot', '0')                                 # slot may be useful when caching an image
    mmdd = getFld(cfs, 'mmdd', '0212')                              # default to Charles Darwin's birthday
    try:
        x = int(mmdd)                                               #
        if (x < 0):                                                 #
            mmdd = "0000"                                           #
        if (x > 9999):                                              #
            mmdd = "9999"                                           #
    except Exception:                                               #
        mmdd = "0212"                                               #
    #
    #   start by logging this event (default privs may not allow this)
    #   caveat: if SELinux is enabled then we may need to write to /var/log
    #
    try:
        snap = datetime.datetime.now()                              # snapshot of the current time
        date = snap.strftime('%Y%m%d')                              #
        fn = f"dp-log/dp_{date}.txt"                                # all logs go into a daily file
        f = open(fn, "a")                                           #
        dt = snap.strftime('%Y%m%d%H%M%S')                          #
        f.write(f"time: {dt}\r\n")                                  #
        f.write(f"mmdd: {mmdd} xml: {xml} json: {json})\r\n")       #
        f.close()                                                   #
    except Exception as e:                                          #
        msg = f"error: {e} in logger"                               #
        logIt(msg)                                                  #
        pass                                                        #
    #
    row = get_record(mmdd)                                          # fetch one record from our database
    #
    # return data in the requested format
    #
    if (json == "1"):
        output_json(row, mmdd, slot)                                # output JSON data
    elif (xml == "1"):
        output_xml(row, mmdd, slot)                                 # output xml data
    else:
        output_plain(row, mmdd, slot)                               # output plain data

#
#   catch-all
#
if __name__ == '__main__':
    main()
#
# === end-of-file

External Links


Back to Home
 Neil Rieck
 Waterloo, Ontario, Canada.