Monday 30 December 2019

QR Codes using simple HTML and the Google Chart API

A nice simple way of generating QR codes using HTML and the Google Chart API.

<form method="get" action='https://chart.googleapis.com/chart'>
<input type="text" name="chs" value="150x150" />
<input type="text" name="cht" value="qr" />
<input type="text" name="chl" value="https://technologyisnotdull.blogspot.com" />
<input type="text" name="choe" value="UTF-8" />
<input type="submit" />
</form>


Sunday 29 December 2019

YouTube Videos part 2

Having now set up an IIS/ASP server, now it is time to use it to set up your YouTube videos.
As noted in the previous post, ASP files comprise conventional HTML combined with server side Javascript code.

The JavaScript functions can be in separate included files. This allows the code to be easily reused.
The calls to the functions can then be made within the main file as required:

<% videolist="I68GG96bSYQ:Christmas lights,I68GG96bSYQ:More Chrismas,I68GG96bSYQ:Not Christmas";
Response.Write(makebuttons(videolist));
Response.Write(makeplaylist(videolist));

Server Side Functions

Create a file called makebuttons.asp.
There are three functions.
The first one assembles a button similar to that in the previous post.
The second takes a list of Youtube video IDs and their titles and assembles a button for each of the pairs.
The third function assembles the same list into YouTube playlists.
<%
function makebutton(index,buttonsource,playsource,buttonID,videotitle){
   result="";
   result=result +"<div class='videobuttoncontainer'>";
   result=result +"<img src='" +buttonsource+"' alt='Title' class='image' width='100%' onclick='setPlayList("+index.toString()+")' />";
   result=result +"<div class='videobuttonoverlay'>";
   result=result +"<img class='videobuttonicon' src='"+playsource+"' id='"+buttonID+"' onclick='setPlayList("+index.toString()+")' alt='"+videotitle+"' title='Play video: "+videotitle+"' oncontextmenu='copytoClipboard("+index.toString()+")' />";
   result=result +"</div>";
   result=result +"<span id='buttontext' class='buttontext'>"+videotitle+"</span>";
   result=result +"</div>";
   return result;
}
function makebuttons(videolist){
   result="";
   videos=videolist.split(",");
   for(ix =0;ix<videos.length;ix++){
  videoitem=videos[ix].split(":");
  result=result+makebutton(ix,"assets/button.png","assets/play.png", videoitem[0],videoitem[1]);
   }
   return result;

}
function makeplaylist(videolist){
   result="";
   videos=videolist.split(",");
   videoitem=videos[0].split(":");
   result=result+"'" +  videoitem[0]+"?playlist=";
   for(ix =1;ix<videos.length;ix++){
  videoitem2=videos[ix].split(":");
  result=result+','+videoitem2[0]
   }
   result=result+"'";
   for(ix =1;ix<videos.length;ix++){
  videoitem=videos[ix].split(":");
  result=result+",'" +  videoitem[0]+"?playlist=";
  for(iy =ix;iy<videos.length;iy++){
 videoitem2=videos[iy].split(":");
 result=result+','+videoitem2[0]
  }
  for(iy =0;iy<ix;iy++){
 videoitem2=videos[iy].split(":");
 result=result+','+videoitem2[0]
  }
  result=result+"'";
   }
   result="<script>playlists=["+result+"]</script>";
   return result;
}
%>

New Client Side functions

There are two pieces of code that will be used client side. The first is effectively the same as that used in the client side example. The second piece sets up a capability to copy the path to the video within the page to the clipboard.
<script>

function setPlayList(aPlaylist){
//alert(playlists[aPlaylist]);
youtubePlayer = document.getElementById("youtube_video");
youtubePlayer.src="https://www.youtube.com/embed/"+playlists[aPlaylist] + "&rel=0&autoplay=1";
}

function copytoClipboard(videonum){
result=window.location.href;
hashbit="";
querybit="";
newquery="";
starthash = result.indexOf("#");
startquery=result.indexOf("?");
if(starthash>0){
hashbit=result.slice(starthash);
result=result.substring(0,starthash)
 }
 else{
 hashbit="#demo";
 }
if(startquery>0){
querybit=result.slice(startquery).slice(1);
querybits=querybit.split("&");
result=result.substring(0,startquery)
foundVideo=false;
for(ix=0;ix<querybits.length;ix++){
endname=querybits[ix].indexOf("=");
if(querybits[ix].substring(0,endname)=="video"){
newquery=newquery+"video="+videonum.toString() +"&";
foundVideo=true;
}
else{
newquery=newquery+querybits[ix] +"&";
}
}
if (!foundVideo){
newquery=newquery+"video="+videonum.toString() +"&";
}
if(newquery.length>0){
newquery=newquery.slice(0,newquery.length-1);
}
result=result+"?"+newquery;

}
else{
result=result+"?video="+videonum.toString();
}
result=result+hashbit;
var copything=document.getElementById("copything")
copything.value=result;
copything.select();
copything.setSelectionRange(0, 99999);
}
</script>

The Main ASP file

The main ASP file is modified from the original:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style>
.videoWrapper {
position: relative;
padding-bottom: 56.25%; /* 16:9 */
padding-top: 25px;
height: 0;
}
.videoWrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.videoDisplay {
float: left;
width: 85%;
}
.videoList {
float: left;
width: 15%;
opacity:1;
}

.videobuttoncontainer {
position: relative;
width: 100%;
max-width: 400px;
}
.videobuttonoverlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
opacity: 0.25;
transition: .3s ease;
}

.videobuttoncontainer:hover .videobuttonoverlay:hover {
background-color: red;
opacity: 0.8;
color: white;
}
.videobuttonicon {
color: white;
font-size: 100px;
position: absolute;
top: 30%;
left: 40%;
width:25%;
height:auto;
text-align: center;
}
.buttontext{
opacity:1.0;
position:absolute;
bottom:4px;
left: 4px;
font-size: small;
color:white;
}
.buttontext:hover{
color:white;
}

</style>
<!-- #include file="makebuttons.asp" -->
<!-- #include file="makebuttons_clientside.asp"-->
</head>
<body>
<table width="100%">
<tr>
<th border="1" width="5%"></th>
<th border="1">
Test stuff
</th>
<th  width="5%">

</th>
</tr>
<tr>
<td>Ignore this</td>
<td border="1">
<div class="row">
<div class="videoList">
<% videolist="I68GG96bSYQ:Christmas lights,I68GG96bSYQ:More Chrismas,I68GG96bSYQ:Not Christmas";
Response.Write(makebuttons(videolist));
Response.Write(makeplaylist(videolist));
%>
<input type="text" value="Hello" id="copything">
</div>
 </div>
   <div class="videoDisplay">
 <div class="videoWrapper">
<iframe  id="youtube_video"
src="https://www.youtube.com/embed/I68GG96bSYQ"
frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>
</div>
</div>
</td>
<td>Ignore this</td>
</tr>
</table>
</body>
</html>

When browsed to, the result is as follows:

Friday 27 December 2019

Drinking from the Flask part 3

A Brief Interlude

So far the source has been Python and the recipient has been .Net.

Python has a number of modules to handle HTTP requests, and one of the simplest to use is the Requests module.

import requests
req = requests.get('https://<your-user-name>.pythonanywhere.com/subscribers')
print(req.text)

This will return the same data as the browser example earlier:

>>> %Run firstrequest.py
{"Subscribers":[{"subscriber": {"emailaddress": "email_0@tec.com", "subscriptionid": 0}}, {"subscriber": {"emailaddress": "email_1@tec.com", "subscriptionid": 1}}, {"subscriber": {"emailaddress": "email_2@tec.com", "subscriptionid": 2}}, {"subscriber": {"emailaddress": "email_3@tec.com", "subscriptionid": 3}}, {"subscriber": {"emailaddress": "email_4@tec.com", "subscriptionid": 4}}]}
>>>
Now once you have the data, it needs to be converted into something usable.

JSON is based on the structure of Javascript, but the Python Dictionary is easily convertible to and from JSON.

The data has a dictionary (Subscribers), within which is a list. Each item (subscriber) in the list has two name-value pairs (subscriptionid and emailaddress).

import json
import requests

req = requests.get('https://<your-user-name>.pythonanywhere.com/subscribers'

aDictionary = json.loads(req.text)
#print(y['Subscribers'])

for x in aDictionary['Subscribers']:
    print(x['subscriber']['subscriptionid'],x['subscriber']['emailaddress'])

The output:

0 email_0@tec.com
1 email_1@tec.com
2 email_2@tec.com
3 email_3@tec.com
4 email_4@tec.com

References



My First ASP Page

ASP or Active Server Pages was a Microsoft web service in the early days of dynamic web pages. Though generally no longer a cutting edge technology, it is still used in a surprisingly large number of web sites.

Setup

Most Windows computers can host Internet Information Services (IIS). It is not installed by default but can be activated using the "Turn Windows features on or off" dialogue box. You can get to that either through the Control Panel application or by searching for it.


You will need to select down to the optional features to activate the ASP option.

By default ASP expects VBScript to be the default server side language, this can be changed when you start IIS.

ECMAScript is a synonym for Javascript or Jscript.
To make life easier for debugging, you can set "Send Errors To Browser" to true. This sends informative error messages rather than "This is broke". You do not want to have this set in a live environment as it makes your site extremely vulnerable to attack.

Hello World

This is a simple ASP page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
     <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
     <title>ASP Test page</title>
    </head>
    <body>
     <h1>Hello World</h1>
    </body>
</html>

So far, so HTML.

What you can do is run code at the server rather than just serve up a fixed page.

Hello World .ASP

The difference between a static HTML page and an ASP page is the Active part.
To define code that will run on the server, you need to put it between <% %>.
You can also include other files within your page. This is very useful as you can define standard parts of your web site in one location, and then reuse them on different pages.
For this you use the #include command inside HTML comments:

<!-- #include file="include-filename.asp" -->

The timeline of inclusion is that the included files are added to the file before any code is executed, so it is not possible to conditionally include file content - this does not work:

<% if(condition){%>
<!-- #include file="this-file.asp" -->
<% } %>

Remember this, otherwise you will get annoyed with it.

The first "active" ASP page will extend the previous example to print out the time.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
        <title>ASP Hello World page</title>
    </head>
    <body>
        <h1>Hello World</h1>
        <p>It is <%t=Date();
            Response.Write(t);
            %></p>
    </body>
</html>

When you first go to this page, it will display the current time. When you refresh the page, it will have the new (current) time.

The Response.Write  adds the parameter content to the page content (similar to a print or console.write command). It is possible to shorten a Response.Write(x) to <%=x%>.

Hello You

Now the previous example did make the page more dynamic, but does not really make it interactive.

For that we need two new ideas:

The Form.

A form is a block of HTML. If there is a Submit button, any selected, entered or clicked values are sent back to the web address identified by the action attribute. If no address is provided, the values are sent back to the address of the page. 

The page with the form does not need to be an ASP page, for that matter neither does the target page - however the target page does need to be one that can make use of the information being sent.

This form is simple and made up of four parts:
  • An enclosing form tag, it has an empty action attribute as it will be sending the data back to itself.
  • A text label asking you to enter your name.
  • An input of type text box for you to enter your name. It has a name attribute to identify it when it is sent. It also has a value attribute. It might seem counter-intuitive to give it a value as it is an input, but this will also put your entered name in the box after you send it (just in case you made a mistake). 
  • An input of type Submit. This has a value that will be displayed on the button. This sends the information to the address in the form's action attribute.

The Request object. 

The Request object contains all the assorted stuff that is being sent to the web page (such as the information being entered on the form).

In this case we will be asking it for the value of the myname parameter, which is supplied by the tag with the name attribute "myname". If no parameter is supplied, the value of Request("myname") is an empty string.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
        <title>ASP Form test page</title>
    </head>
    <body>
        <h1>Hello 
            <%
            personName = Request("myname");
            if(personName!=""){
                Response.Write(personName)
            }
            %>
        </h1>
        <form action="">
            Please enter your name: <input type="text" name="myname" value="<%=personName%>" />
            <input type="submit" value="Enter" name="go" />
        </form>
    </body>
</html>

When you first visit the page it will say Hello, and ask for your name.

When you enter your name and click on Enter, the display will change to say Hello <your name>, it will also add your name into the input text box.

You will also notice the URL will have changed to include the name and values of the parameters (the elements within the form):
http://localhost/form.asp?myname=Fred&go=Enter
You can actually tailor the parameters in the address bar of the browser, try changing Fred to Ned.
This can be useful, but it does rather expose the workings of your cunningly crafted web site.

This is because the default behaviour of the form tag is to use the method "GET". This does have some advantages, you can see that it is working, and you can edit the input in the address bar (great while testing). The disadvantages are that it can look messy, and that the parameters are on display and are limited by length.

The alternative is the method "POST". This packages up the parameters' names and values and sends it separately from the address.

Change the form tag's attributes to include a method = "POST" attribute:
<form action="" method="POST">
Save and refresh your page in your browser.
The problem is that you can enter the new name, but it continues to display the old one. This is because the address includes the existing (GET) parameters, and it overrides POST parameters.
Clear the existing information from the address, and it will behave as you expect.

It is important to be aware that a value sent via GET will override a value sent by POST.

There is a way round that, but that is for a second post.



Wednesday 25 December 2019

Saturday 21 December 2019

Drinking from the Flask part 2

Drinking from the Flask with .Net

Background

This small project will integrate Python, Flask, JSON and .Net.

Task 1: Set up a JSON end point using Python and Flask
Task 2:  .Net application to read data from the end point
Task 3: Expand end points on the Python server for details
Task 4: Select and search for details via JSON calls.

A first sip from the Flask

This assumes you have a suitable version of Visual Studio installed, and an internet connection (plus you have a working JSON source from the previous post).

Ordering the drink

For simplicity we shall start with a Console application.

Create a new .Net Framework Console application (is it me or does Microsoft continue to add steps that add no value to their dialogues?). I called mine JSONTestProgram.

Leaving the Console program until later, I then created another project (a .Net Framework Class) in the solution called JSONClient. I renamed the class to JSONGetData. I then added an additional class file to the project called JSONSubscribers.cs.

The JSONGetData.cs file contains the following code (remember to use the URL of your Flask project).

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net.Http;
//using System

namespace JSONClient
{
    /// <summary>
    /// This class is used to get JSON data from an endpoint
    /// </summary>
    public class JSONGetData
    {
        /// <summary>
        /// URL for getting the sunscribers information from the JSON source
        /// </summary>
        public static readonly string URLGetSubscribers = "https:// <yourusername>.pythonanywhere.com/subscribers";
        
        static HttpClient client = new HttpClient();
        /// <summary>
        /// Asynchronous function that obtains data from the URLSubscribers endpoint 
        /// and returns a dictionary of email addresses and subscription ids
        /// </summary>
        /// <returns>A dictionary with key and value of type string</returns>
        public static async Task<Dictionary<string,string>> GetSubscribers()
        {
            string content;
            Dictionary<string, string> dictSubscribers=null;
            using (HttpResponseMessage response = await client.GetAsync(URLGetSubscribers, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
            {
                if (response.IsSuccessStatusCode)
                {
                    dictSubscribers = new Dictionary<string, string>();
                    content = await response.Content.ReadAsStringAsync();
                    Rootobject x = Newtonsoft.Json.JsonConvert.DeserializeObject<Rootobject>(content);
                    foreach (JSONClient.Subscriber subscriber in x.Subscribers)
                    {
                        dictSubscribers.Add(subscriber.subscriber.emailaddress, subscriber.subscriber.subscriptionid.ToString());
                    }
                }
            }  
            return dictSubscribers;
        }
    }
}
You need to add a reference to the Newtonsoft.Json package via Edit/Manage NuGet Packages.

A static instance of the HTTPClient is used in this case for simplicity (only one URL is in use).

The asynchronous task GetSubscribers will return a Dictionary of email and subscriber ids.
The GetSubscribers code first creates the dictionary to be returned.
An HTTPResponseMessage is created and initialised by an asynchronous call to the URLGetSubscribers URL.
If the response contains a status success code, the dictionary to be returned is initialised.
The content of the response is read into a string and then deserialized using the NewtonSoft component and a target class (more of which anon).
For each subscriber in the object, the email address and subscription ID is extracted and added to the dictionary.
If the response does not contain a status success code, the dictionary is returned uninitialized.

JSON data cannot be read in the same way as XML data (there is no Document Object Model equivalent), so you generally need to know the format or schema of the JSON.

This is where the JSONSubscribers.cs file comes in.
Visual Studio has added a feature which allows you to use an Edit/Paste Special command to paste text as either XML or JSON, converting the text being pasted into the required classes. When you do this, it is generally a good idea to paste it into a dedicated file - then when (if) you change the JSON file, you can just scrub and re-paste it.
This is the result in the current JSONSubscribers.cs file.

namespace JSONClient
{

    public class Rootobject
    {
        public Subscriber[] Subscribers { get; set; }
    }

    public class Subscriber
    {
        public Subscriber1 subscriber { get; set; }
    }

    public class Subscriber1
    {
        public string emailaddress { get; set; }
        public int subscriptionid { get; set; }
    }

}

The root object is used as the overall class for the conversion. The element names within the classes may not match up with the recommended naming scheme in C#, it is possible to tell it to use one name in the source and generate another in the object but that is beyond this simple example.
So, how do we call this code?

The Console program

The most complicated part of the Main method is the RunAsync().GetAwaiter().GetResult() call.
It is a lazy (and liable to deadlocks) way of calling an asynchronous call synchronously. Not really for production code.
You will need to add references to the Newtonsoft.Json package via Manage NuGet packages menu item. You also need to reference the JSONClient project.

class Program
    {
        static void Main(string[] args)
        {
            RunAsync().GetAwaiter().GetResult();
            Console.WriteLine("Press any key");
            Console.ReadKey();
        }
        static async Task RunAsync()
        {
            Dictionary<string, string> subscribers = new Dictionary<string, string>();
            subscribers = await JSONClient.JSONGetData.GetSubscribers();
            foreach (string key in subscribers.Keys)
            {
                Console.WriteLine($"{key}: {subscribers[key]}");
            }

        }
    }

The RunAysnc task calls the GetSubscribers code to obtain the Dictionary and then writes the email address and subscriber id (key - value) to the Console.
When the Console program runs, the result is as follows:
email_0@tec.com: 0
email_1@tec.com: 1
email_2@tec.com: 2
email_3@tec.com: 3
email_4@tec.com: 4
Press any key

Building a Windows Form program

This is slightly more complicated, as there are a lot of threads that can get tangled (for more details see the references).
Add a new Windows Forms project to your solution - in my case I called it JSONTestProgram.

Construct your form similar to this one (it has a label, a text box and a second button used ina later example).


The form comprises a status bar at the bottom and a tool bar at the top. There is a Docked panel in the middle with a Docked SplitContainer inside.
One of the split panels has a ListBox called lbSubscribers (which again is Docked).
The Toolbar has a number of buttons, but only the one marked Load is currently active.
The project needs to have references to the project  JSONClient and a NuGet reference to NewtonSoft.JSON.

Code wise, click on the Load Subscribers button and note the control's name.
Add:

static Dictionary<string, string> subscribers =new Dictionary<string, string>();
inside the Program class (we will be using it later).
You also need to replace the existing method generated when you clicked on the button with:

        private async void toolStripButton2_Click(object sender, EventArgs e)
        {
            subscribers = await JSONClient.JSONGetData.GetSubscribers();
            foreach (string key in subscribers.Keys)
            {
                lbSubscribers.Items.Add(key);
            }
        }

Change the tool strip button name to match the button on your form. Visual Studio will grumble about the method name, which is annoying as it created it.

That is the first part completed. Now change this to be the Start Up project for the solution and run it.
Click on the Load Subscribers button and the list of subscribers generated by your Flask project will be displayed.




References


Drinking from the Flask part 1

Drinking from the Flask

This small project will integrate Python, Flask, JSON and .Net.

Task 1: Set up a JSON end point using Python and Flask
Task 2:  .Net application to read data from the end point
Task 3: Expand end points on the Python server for details
Task 4: Select and search for details via JSON calls.

Background

PythonAnywhere is an online (Platform as a Service) integrated Development Environment and web hosting service. One of its great advantages is that there is a free version - the free version has limitations on available libraries, calls etc, but is very useful if you have a need for somewhere to develop and test Python based web content.

Flask is a lightweight web framework written in Python. It has no database abstraction layer, and is suitable when you want a simple way of generating web content from lower power hardware.

It is available on the PythonAnywhere cloud based Python environment (there is a very helpful tutorial on the site which is worth having a look at. The tutorial is so good, I will just put a link to it rather than run through it again).

Filling the Flask

Python has numerous options for developing web sites and web content delivery. Two of the most popular are Flask and Django. For this case, I will be using Flask on PythonAnywhere.
At this point I will assume that you have gone through the setting up your first database and web based Flask site tutorial from the PythonAnywhere blog: https://blog.pythonanywhere.com/121/.

One of the great advantages of Python over a number of other popular languages is that you can really write once and reuse, so first we will write some test code in your Python IDE of choice (you could use the PythonAnywhere IDE, but I am using Thonny on my trusty Raspberry Pi).

The requirement is to generate a JSON document that contains a list of email addresses and subscriber IDs.

Start off by creating a Python file called subscriptions.py.
This will contain a class to hold a subscriber called Subscriber. Because we will be using this for testing, I have added a testFill method that returns a List of subscribers (with made up names).
The Subscriber class has a method called toJSON that packages up the contents of an instance in a JSON compatible form.

class Subscriber:
    def __init__(self,email_address,subscriptionid):
        self.email_address = email_address
        self.subscriptionid = subscriptionid

    def toJSON(self):
        return {"subscriber": {'emailaddress': self.email_address,
                     'subscriptionid': self.subscriptionid}}

    def testFill():
        subscriptionsList = []
        for ix in range(0,5):
            subscriber = Subscriber('email_'+str(ix)+'@tec.com',ix)
            subscriptionsList.append(subscriber)
        return subscriptionsList

To test this, create another file called testjson.py.
This imports the Subscriber class from the file subscriptions.py and the json class from flask.
It then prints the test data out. The sort keys and indent "pretty print" the text.

from subscriptions import Subscriber
from flask import json
print( json.dumps([e.toJSON() for e in Subscriber.testFill()], sort_keys=True, indent=4))

The results look like this:
[
    {
        "subscriber": {
            "emailaddress": "email_0@tec.com",
            "subscriptionid": 0
        }
    },
    {
        "subscriber": {
            "emailaddress": "email_1@tec.com",
            "subscriptionid": 1
        }
    },
    {
        "subscriber": {
            "emailaddress": "email_2@tec.com",
            "subscriptionid": 2
        }
    },
    {
        "subscriber": {
            "emailaddress": "email_3@tec.com",
            "subscriptionid": 3
        }
    },
    {
        "subscriber": {
            "emailaddress": "email_4@tec.com",
            "subscriptionid": 4
        }
    }
]
So having successfully generated the body of the JSON document, it is now on to the web delivery.

Pouring the Flask

If you have followed the tutorial from the PythonAnywhere blog, you will have a working web site.
Now you need to add some additional pieces to deliver the JSON document.

Log into PythonAnywhere.

Select the Web tab.

Scroll down to Code and on the row Source Code click on Go to directory.

I created a new subscriptions.py file and copied the contents (you could upload it, but it was short enough that that was not an issue). I have mentioned the write once, use many times joy of Python.

Now down to the business of delivering the JSON to your eager customers.

Right click on flask_app.py and open in a new tab.

This will contain the various pieces you have added as part of the tutorial, or may be empty if you have written it yourself.

To use the Subscriber class, you need to add an import at the top of the file (you can copy and paste the one from the testjson.py file.

You will need to create a suitable endpoint.

Using Flask, you add a decorator of the form @app.route('/somewhere').
This is followed by the function that will be called when the user goes to the page somewhere on your site.

The function creates the output content from the testFill method of the Subscriber class (as previously) and then wraps it in a Subscribers class.
It then sets up the response object and returns it.

...
@app.route('/subscribers')
def testJson():
    content=json.dumps([e.toJSON() for e in Subscriber.testFill()])
    content="{\"Subscribers\":"+content+"}"
    response = app.response_class(
        response=content,
        status=200,
        mimetype='application/json'
    )
    return response
...

So, to view your work you just need to go to http://<yourusername>.pythonanywhere.com/subscribers

Your browser should display something like:
{"Subscribers":[{"subscriber": {"emailaddress": "email_0@tec.com", "subscriptionid": 0}}, {"subscriber": {"emailaddress": "email_1@tec.com", "subscriptionid": 1}}, {"subscriber": {"emailaddress": "email_2@tec.com", "subscriptionid": 2}}, {"subscriber": {"emailaddress": "email_3@tec.com", "subscriptionid": 3}}, {"subscriber": {"emailaddress": "email_4@tec.com", "subscriptionid": 4}}]}

Next: Drinking from the Flask with .Net

References


Tuesday 15 October 2019

YouTube Videos

Programmed YouTube Video viewer

Youtube takes a lot of the effort out of hosting and displaying videos. If you are happy for Google to have access to the information about who is watching your videos, then it makes sense to use them to host them.

One problem is making it easy to control how the videos are displayed and allow the viewer to select what they want to watch.

Server Side

For later.

Client Side

It is a matter of Style

Getting the video to display properly, especially if you are using a responsive style web site is a bit more complicated than Google makes out, as the default height is about 150 pixels, which is a bit embarrassing.

The solution from CSS-Tricks based on work by Thierry Koblentz sets the height as a percentage of the width. When the page resizes, the height is recalculated. Any IFRAME inside it is scaled to 100% to fill the container.

.videoWrapper {
position: relative;
padding-bottom: 56.25%; /* 16:9 */
padding-top: 25px;
height: 0;
}
.videoWrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
The next bit is getting the play button to hover over the images.
  .container {
  position: relative;
  width: 100%;
  max-width: 400px;
  }
  .overlay {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100%;
  width: 100%;
  opacity: 0.25;
  transition: .3s ease;
  }

  .container:hover .overlay {
  background-color: red;
  opacity: 0.8;
  }
  .icon {
  color: white;
  font-size: 100px;
  position: absolute;
  top: 30%;
  left: 40%;
  width:25%;
  height:auto;
  text-align: center;
}

The Buttons

The buttons are contained in two nested div objects.
The first (of class 'container') holds the YouTube thumbnail image.
The second (of class 'overlay') holds the play button image and has the onclick event handler described below.
<div class="container">
   <img src=http://i1.ytimg.com/vi/xxxxxxxxxxx/mqdefault.jpg 
     alt="Title" class="image" width='100%' />
   <div class="overlay">
     <img class="icon" src="play.png" onclick='setPlayList(1)'/ >
   </div>
</div>
The overlay has a normal opacity of 0.25, so the button image displays faintly. When the mouse hovers over the image, the background changes to red and the opacity increases to 0.8.

Video Selection

The video choice requires an array containing the video to play plus a rotating set of the other videos. That is a coding exercise for server side and another time.
<script>
playlists =['xxxxxx?playlist=yyyyy,zzzzz', 'yyyyy?playlist=zzzzz,xxxxx'];
 </script>

Each play button has an onclick event that calls the setPlayList function with a number which selects the element from the array described above.
<script>
function setPlayList(aPlaylist){
youtubePlayer = document.getElementById("youtube_video");
youtubePlayer.src="https://www.youtube.com/embed/"+playlists[aPlaylist] + "&rel=0&autoplay=1";
}
</script>

The autoplay parameter starts the video playing, the rel = 0 prevents videos from other channels from being displayed,

References

https://support.google.com/youtube/answer/171780?hl=en
https://css-tricks.com/NetMag/FluidWidthVideo/Article-FluidWidthVideo.php
https://alistapart.com/article/creating-intrinsic-ratios-for-video/

Saturday 28 September 2019

Haskell on the Raspberry Pi

Haskell is one of the Functional Programming languages and follows a rather different paradigm to the more commonly taught languages.

There are browser based options, such as https://www.haskellmooc.co.uk/

Installer

There is information about installing the Haskell Platform here.

For a Raspberry Pi, it is in the repository:

sudo apt-get install haskell-platform

References

https://www.haskellmooc.co.uk/