#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Maps see
# http://www.opensnowmap.org/?zoom=12&lat=51.68192&lon=11.00727&layers=snowbase&marker=false

from html.parser import HTMLParser
import urllib.request, urllib.error, urllib.parse
# https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/31915123#31915123
import ssl
import sys
import re
from bs4 import BeautifulSoup
import time
from datetime import datetime
#import locale
#locale.setlocale(locale.LC_NUMERIC, 'de_DE.utf8')
import sqlite3 as lite

harzinfo="https://wintersport.harzinfo.de/langlauf"

re_url = re.compile(r'^(([a-zA-Z_-]+)://([^/]+))(/.*)?$')
re_town = re.compile(r'^http://wintersport\.harzinfo\.de/orte/ort/(.+)\.html')
re_townlink = re.compile(r'<option value="(/orte/ort/.*\.html)[^"]+">(.+)</option>')
re_km = re.compile(r'^([.,\d]+) *km$')
re_m = re.compile(r'^(\d+) *m$')

SPACE='   '
SNOWMAX=0
KMGOOD=0
KMMEDI=0
KMBAD=0
NGOOD=0
NMEDI=0
NBAD=0

debug=1

DB='/var/lib/brocken/data/loipe/loipe.db'

global con
global cur

gcontext = ssl.SSLContext()  # https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/31915123#31915123
# Deprecated - aber mit gcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) gibt es Serverfehler

def dict_factory(cursor, row):
    # See https://docs.python.org/2/library/sqlite3.html#accessing-columns-by-name-instead-of-by-index
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

try:
    con = lite.connect(DB)
    con.row_factory = dict_factory
    cur = con.cursor()
except lite.Error as e:
    print("Error %s:" % e.args[0])
    exit(1)

def resolve_link(link, url):
    # see https://gist.github.com/remram44/6540454
    m = re_url.match(link)
    if m is not None:
        if not m.group(4):
            # http://domain -> http://domain/
            return link + '/'
        else:
            return link
    elif link[0] == '/':
        # /some/path
        murl = re_url.match(url)
        return murl.group(1) + link
    else:
        # relative/path
        if url[-1] == '/':
            return url + link
        else:
            return url + '/' + link

def execute_query(query):
    if debug > 0:
        print(query)
    try:
        cur.execute(query)
    except:
         print("Query failed:", query)
         exit(1)

def read_float_with_comma(num):
    return float(num.replace(",", "."))

def string_to_km(value):
    if value.endswith('km'):
        return(read_float_with_comma(re.sub(' *km','',value)))
    if value.endswith('m'):
        return(read_float_with_comma(re.sub(' *m','',value))/1000.)
    print("Unknown unit of %s" % value)
    return None

def string_to_m(value):
    if value.endswith('m'):
        return(int(re.sub(' *m','',value)))
    print("Unknown unit of %s" % value)
    return None

def string_to_cm(value):
    if value.endswith('cm'):
        return(int(re.sub(' *cm','',value)))
    print("Unknown unit of %s" % value)
    return None

def string_to_time(value):
    if value.endswith('Stunden'):
        return(re.sub(' *Stunden',':00',value))
    elif value.endswith('Minuten'):
        return('0:'+re.sub(' *Minuten',':00',value))
    print("Unknown unit of %s" % value)
    return None

class ListingParser(HTMLParser):
    """Parses an harzinfo main page and build a list of links to subpages
    Links are stored into the 'links' set. They are resolved into absolute
    links.
    """
    def __init__(self, url):
        HTMLParser.__init__(self)

        if url[-1] != '/':
            url += '/'
        self.__url = url
        self.links = set()

    def handle_starttag(self, tag, attrs):
        if tag == 'a':
            for key, value in attrs:
                if key == 'href':
                    if not value:
                        continue
                    value = resolve_link(value, self.__url)
                    self.links.add(value)
                    break

SNOWTYPES = { 'Pulverschnee':   20,
              'Neuschnee':      10,
              'verharscht':      6,
              'vereist':         5,
              'beschneit':       4,
              'Altschnee':       3,
              'pappig':          2,
              'nass':            1,
              'kein Schnee':    -1,
            }
LOIPETYPES = { 'gespurt':        2,
               'alt gespurt':    1,
               'nicht gespurt':  0,
                u'Langlauf nicht möglich': -1,
               'gesperrt':                 -2,
             }

WEATHERTYPES = { 'sonnig':      20,
                 'wolkenlos':   18,
                 'bewölkt':      9,
                 'bedeckt':      8,
                 'Schneefall':   5,
                 'Nebel':        3,
                 'Sturm':        1,
                 'unbekannt':    0,
                 'Regen':      -10,
               }

KNOWNKEYS     = { 'Loipe'                        : 'loipe',
                   u'Schneehöhe min.'            : 'snowmin',
                   u'Schneehöhe max.'            : 'snowmax',
                   'Schneebeschaffenheit'        : 'snowtype',
                   u'Temperatur (in °C)'         : 'temp',
                   'Wetterlage'                  : 'wetter',
                   'Dauer'                       : 'dauer',
                   u'Länge'                      : 'length',
                   'Schwierigkeit'               : 'difficulty',
                   'Aufstieg'                    : 'aufstieg',
                   'Abstieg'                     : 'abstieg',
                   'Startpunkt'                  : 'start',
                   'Zielpunkt'                   : 'ziel',
                   'Technik'                     : 'technik',
                   'Kondition'                   : 'kondition',
                   'Niedrigster Punkt'           : 'lowest',
                   u'Höchster Punkt'             : 'highest',
                   'Flutlicht'                   : 'light',
                   'PDF'                         : 'pdf',
                   'GPX'                         : 'gpx',
                   'date'                        : 'date',
                   'Zuletzt gespurt'             : 'prevdate',  # Neu
                   'Langlauftechnik'             : 'style',     # Neu
                   'Letzter Schneefall'          : 'lastsnow',  # ganz Neu
                   'Neuschnee (in cm, letzte 24h)' : 'newsnow', # ganz Neu
                   'Zuletzt geändert'            : 'lastchange',
                }

class Loipe():

    def __init__(self, name, link, state, idstate):
        global execute_query
        self.data = {}
        self.name = name
        self.data['Name'] = "-".join([x.capitalize() for x in name.split('-')])
        self.data['Town'] = ''
        self.data['link'] = 'https://wintersport.harzinfo.de/'+link
        for key in KNOWNKEYS:
            self.data[KNOWNKEYS[key]] = ''
        self.show = None
        self.isnowtype = None
        self.iweathertype = None
        self.length = 0
        self.data['length_km'] = 0.0
        self.data['dauer_h'] = '0:00:00'
        self.data['abstieg_m'] = 0
        self.data['aufstieg_m'] = 0
        self.data['lowest_m'] = 0
        self.data['highest_m'] = 0
        self.data['light'] = 0
        self.data['technik'] = 0
        self.data['kondition'] = 0
        self.data['snowmin_m'] = -1
        self.data['snowmax_m'] = -1
        self.data['temp_c'] = -100
        self.data['newsnow_m'] = -1
        self.data['state'] = state
        self.data['idstate'] = idstate
        execute_query("SELECT id, town FROM loipe WHERE name = '%(Name)s'" % self.data)
        try:
            result=cur.fetchone()
            self.data['idloipe'] = result['id']
            if 'town' in result:
                self.data['Town'] = result['town']
            else:
                print("No town found for Loipe %s (%s)" % (name, str(result)))
            #print(self.data['Town'])
            execute_query("SELECT idloipe, MAX(lastchange) as lastchange FROM data WHERE idloipe = %(idloipe)s GROUP BY idloipe" % self.data)
            try:
                self.lastchange = lastchanged = datetime.strptime(cur.fetchone()['lastchange'], '%Y-%m-%d %H:%M:%S')
                if debug > 0:
                    print("Eintrag für Loipe %s von %s gefunden." % (self.data['Name'], self.lastchange))
            except:
                self.lastchange = None
                if debug > 0:
                    print("No data for loipe %s" % name)
            self.dbnew = False
        except:
            self.data['idloipe'] = None
            self.lastchange = None
            self.dbnew = True
        self.dbdatanew = True
        if debug > 0:
            print("self.data['idloipe']=", self.data['idloipe'])
        self.data['datum'] = datetime.now()

    def add_value(self, key, value):
        global SNOWMAX

        if key == 'date':
            self.data['date'] = datetime.strptime(value, '%d.%m.%Y - %H:%M Uhr').strftime("%d.%m %H:%M")
            return
        if key in KNOWNKEYS:
            self.data[KNOWNKEYS[key]] = value
        else:
            print("Unknown key "+key)
            return
        if key == 'Loipe':
            if value in LOIPETYPES:
                self.show = LOIPETYPES[value]
            else:
                print("Unknown Loipe status " + value)
            return
        if key == u'Schneehöhe max.':
            sh=re.sub(' *cm', '', value)
            try:
                if int(sh) > SNOWMAX:
                    SNOWMAX=int(sh)
            except:
                pass
            return
        if key == 'Schneebeschaffenheit':
            if value in SNOWTYPES:
                self.isnowtype = SNOWTYPES[value]
            else:
                print("Unknown snowtype " + value)
                self.isnowtype = -100
            return
        if key == 'Wetterlage':
            if value in WEATHERTYPES:
                self.iweathertype = WEATHERTYPES[value]
            else:
                print("Unbekannte Wetterlage " + value)
                self.iweathertype = -100
            return
        if key == u'Länge':
            self.data['length_km'] = string_to_km(value)
            km = re_km.match(value)
            if km is not None:
               try:
                   self.length=int(1000*float(re.sub('[.,]','.',km.group(1))))
               except:
                   print("No number found in km value %s" % value)
            else:
               m = re_m.match(value)
               if m is not None:
                   try:
                       self.length=int(m.group(1))
                   except:
                       print("No number found in m value %s" % value)
        if key == 'Dauer':
            self.data['dauer_h'] = string_to_time(value)
        if key == 'Abstieg':
            self.data['abstieg_m'] = string_to_m(value)
        if key == 'Aufstieg':
            self.data['aufstieg_m'] = string_to_m(value)
        if key == 'Niedrigster Punkt':
            self.data['lowest_m'] = string_to_m(value)
        if key == u'Höchster Punkt' :
            self.data['highest_m'] = string_to_m(value)
        if key == u'Schneehöhe min.':
            self.data['snowmin_m'] = string_to_cm(value)
        if key == u'Schneehöhe max.' :
            self.data['snowmax_m'] = string_to_cm(value)
        if key == u'Temperatur (in °C)' :
            self.data['temp_c'] = int(value)
        if key == 'Neuschnee (in cm, letzte 24h)' :
            self.data['newsnow_m'] = string_to_cm(value)
        if key == 'Letzter Schneefall' :
            try:
                self.data['lastsnow'] = datetime.strptime(value, '%d.%m.%Y')
            except ValueError:
                self.data['lastsnow'] = None
        if key == 'Zuletzt gespurt' :
            try:
                self.data['prevdate'] = datetime.strptime(value, '%d.%m.%Y')
            except ValueError:
                self.data['prevdate'] = None

    def html(self):
        global KMGOOD
        global KMMEDI
        global KMBAD
        global NGOOD
        global NMEDI
        global NBAD

        if self.length < 100 and self.length > 0: # Wenn die Längenangabe fehlt, soll die Loipe gedruckt werden
            if debug > 0:
                print("Loipe %s ist zu kurz (%im) und wird daher ignoriert" % (self.data['Name'], self.length))
            return ''
        if self.data['loipe'] == 'gespurt':
            min=0
            try:
                min=int(self.data['snowmin'])
            except:
                pass
            temp=0
            try:
                temp=int(self.data['temp'])
            except:
                pass
            if min >= 30:
                if temp <= -5:
                    css='good'
                    KMGOOD=KMGOOD+self.length
                    NGOOD+=1
                elif self.data['snowtype'] != 'pappig' and self.data['snowtype'] != 'nass':
                    css='good'
                    KMGOOD=KMGOOD+self.length
                    NGOOD+=1
                else:
                    if self.data['wetter'] == 'Regen':
                        css='bad'
                        KMBAD=KMBAD+self.length
                        NBAD+=1
                    else:
                        css='medi'
                        KMMEDI=KMMEDI+self.length
                        NMEDI+=1
            else:
                if self.data['snowtype'] == 'kein Schnee' or self.data['wetter'] == 'Regen':
                    css='bad'
                    KMBAD=KMBAD+self.length
                    NBAD+=1
                else:
                    css='medi'
                    KMMEDI=KMMEDI+self.length
                    NMEDI+=1
        else:
            css='bad'
            KMBAD=KMBAD+self.length
            NBAD+=1
        if self.data['gpx'] != '':
            maplink='<a href="http://www.gpswandern.de/gpxviewer/tvanzeige.shtml?url=%s" alt="map">Karte</a> / ' % self.data['gpx']
        else:
            maplink=''
        if self.data['pdf'] != '':
            pdflink='<a href="%s" alt="pdf">PDF</a>' % self.data['pdf']
        else:
            pdflink=''
        ret='<tr class="%s">\n' % css
        ret+="  <td>{Town}</td><td>{Name}</td><td>{loipe}</td><td>{snowmin}/{snowmax}</td><td>{snowtype}</td><td>{temp}°C / {wetter}</td><td>{length}</td><td>{technik}/{kondition}</td><td>{lastsnow}</td><td>{newsnow}</td>".format(**self.data)
        ret+='<td>%s%s</td><td>%s</td><td><a href="%s" type="application/pdf" alt="web">web</a></td>\n'%(maplink, pdflink, self.data['date'], self.data['link'])
        ret+='</tr>\n'
        return ret

    def __str__(self):
        ret="{Name}: {loipe} {snowmin}/{snowmax} {snowtype} {wetter} {length} {technik}/{kondition} {date} {state}".format(**self.data)
        return ret

HTMLSTART="""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xml:lang="de_DE" lang="de_DE" xmlns="http://www.w3.org/1999/xhtml">
<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Loipen im Harz</title>
<link rel="stylesheet" type="text/css" href="loipen.css" media="all" />
</head>
<body>
"""
TABLESTART='<table border="1" cellpadding="0" cellspacing="1">\n<tbody>\n'
HEADER="<tr>\n<th>Ort</th><th>Loipe</th><th>Zustand</th><th>Schneehöhe<br />(min/max)</th><th>Qualität</th><th>Wetter</th><th>Länge</th><th>Techn./Kond.</th><th>Letzter Schneefall</th><th>Neuschnee<br />(in cm, letzte 24h)</th><th>Geoinfo</th><th>Stand</th><th>Link<th>\n</tr>\n"
TABLEEND='</tbody>\n</table>'
TABLELINE='<tr class="linie">'
HTMLEND="</body>\n</html>"

def main():
    try:
        response = urllib.request.urlopen(harzinfo, context=gcontext)
    except urllib.error.HTTPError as err:
        print(err)
        exit(1)
    htmloutput=''
    if response:
#        contents = response.read()
        parser = ListingParser(harzinfo)
        resp = urllib.request.urlopen(harzinfo, context=gcontext)
        cont = resp.read()
        soup = BeautifulSoup(cont.decode('utf-8'), "lxml")

        loipelinks = soup.find_all("div", attrs={"class":"wintersport-detail__section-item"})
        for ll in loipelinks:
            link=ll.find('a')
            lstate=ll.find('div', attrs={'class':"wintersport-detail__state"})
            loipe = Loipe(link.text, link.get('href'), lstate.text, lstate.get('data-state'))
            print(loipe)
            resp = urllib.request.urlopen(loipe.data['link'], context=gcontext)
            if resp:
                cont = resp.read()
                print(cont)
                print("Read link ", loipe.data['link'])
                pars = ListingParser(loipe.data['link'])
                pars.feed(cont.decode('utf-8'))
                exit(1)
                # Irgendwie erfindet ListingParser poi/detail/<town.name> zum echten Link hinzu
                # FIXME: simply hack around this ...
                # re_loipe = re.compile(r'^%s/poi/loipe/(.+)\.html' % town.link) ## Stadt ist nicht mehr Teil des Links
                re_loipe = re.compile(r'^http://wintersport.harzinfo.de/orte/loipe/(.+)\.html' )
                for li in sorted(pars.links):
                    l = re_loipe.match(li)
                    print(l)
                    exit(1)
                    if l:
                        if debug > 0:
                            print("%s -> %s (%s)" % (town.Name, l.group(1), l.group(0)))
                        link = re.sub('/poi/detail/'+town.name+'.html', '', l.group(0)) # FIXME: part 2 of the hack to remove the wrong injection
                        loipe = Loipe(town.Name, l.group(1), link)
                        town.add_loipe(loipe)
                        try:
                            if debug > 0:
                                print("Opening link " + link)
                            resloipe=urllib.request.urlopen(link, context=gcontext)
                        except urllib.error.HTTPError as err:
                            print(err)
                            print("Can't open link " + link)
                            exit(1)

                        # print("%s - %s (%s)" % (town.Name, l.group(1), link))
                        if resloipe:
                            contloipe = resloipe.read()
                            soup = BeautifulSoup(contloipe.decode('utf-8'), "lxml")
                            lc1 = soup.find("section", attrs={"class":"section-wrap"}) #  <div class="section__inner"> <div> <small>Zuletzt geändert am 13.02.2020 um 08:38 Uhr </small>  <hr class="mt-- mb-" />
                            lastchanged_str = re.sub(r"Zuletzt geändert am ([.0-9]+) um ([:0-9]+) Uhr","\\1 \\2", lc1.find("small").getText().strip())
                            lastchanged = datetime.strptime(lastchanged_str, '%d.%m.%Y %H:%M')
                            if lastchanged == loipe.lastchange:
                                loipe.dbdatanew = False
                                print("Loipe %s wurde am %s geändert.  Es gibt keinen neuen Eintrag." % (loipe.data['Name'], lastchanged))
                            else:
                                if debug > 0:
                                    print("Loipe %s hat neue daten vom %s.  Der letzte Eintrag war am %s." % (loipe.data['Name'], lastchanged, loipe.lastchange))
#                            print(lastchanged)
#                            exit(1)
                            loipe.add_value('Zuletzt geändert', lastchanged)
                            div = soup.find("div", attrs={"class":"mt bg-alternating--quartary"})
                            if not div:
                                print("div class=loipetable not found in link %s" % link)
                                # FIXME: better handling than exit needed
                                print(soup)
                                exit(1)
                            datasets = []
                            for row in div.findAll("div", attrs={"class":"flex palm-flex--wrap"}):
                                key = re.sub(':$', '', row.find("div",   attrs={"class":"palm-1/1 1/2 p- pv-- palm-pb0"}).getText())
                                value = row.find("div", attrs={"class":"palm-1/1 1/2 p- pv-- palm-pt0"}).getText().strip()
                                if key == 'Technik' or key == 'Kondition':
                                    v = None
                                    ivalue=0
                                    while ( v == None ):
                                        ivalue += 1
                                        if ivalue > 6: # Ach, 6/5 existiert auch!
                                            # FIXME: better handling than exit needed
                                            print("no ivalue found in row: ", row)
                                            exit(1)
                                        v = row.find("div", attrs={"class":"rating rating--%i/5" % ivalue})
                                    value = str(ivalue)
                                if debug > 0:
                                    print("  %s: '%s'" % (key, value))
                                loipe.add_value(key, value)

                            teaser = soup.find("div", attrs={"class":"teaser downloads"})
                            if teaser:
                                pdf = teaser.find("a", attrs={"class":"pdflink"})
                                if pdf.has_attr('href'):
                                    loipe.add_value('PDF', pdf['href'])
                                gpx = teaser.find("a", attrs={"class":"pgxlink"})
                                if gpx.has_attr('href'):
                                    loipe.add_value('GPX', gpx['href'])
                            # FIXME: skidate
                            # date = soup.find("p", attrs={"class":"bodytext skidate"}).getText()
                            date = None
                            if date:
                                loipe.add_value('date', date)
                            if loipe.dbnew:
                               execute_query("""INSERT INTO loipe (town, name, length, dauer, difficulty, abstieg, aufstieg,
                                                start, ziel, style, technik, kondition, lowest, highest, light, link)
                                   VALUES ('%(Town)s', '%(Name)s', %(length_km)s, '%(dauer_h)s', '%(difficulty)s', %(abstieg_m)s, %(aufstieg_m)s,
                                           '%(start)s', '%(ziel)s', '%(style)s', %(technik)s, %(kondition)s, %(lowest_m)s, %(highest_m)s, %(light)s, '%(link)s')"""
                                   % loipe.data)
                               execute_query("SELECT id FROM loipe WHERE town = '%(Town)s' AND name = '%(Name)s'" % loipe.data)
                               try:
                                   loipe.data['idloipe'] = cur.fetchone()['id']
                               except:
                                   print("Failed reading id of just inserted loipe %(Name)s in %(Town)s" % loipe.data)
                                   exit(1)
                               if debug > 0:
                                   print("Loipe %(Name)s in %(Town)s mit Id %(idloipe)s eingefügt." % loipe.data)
                            if loipe.dbdatanew:
                                query="""INSERT INTO data (idloipe, datum, snowmin, snowmax, snowtype, temp, wetter, lastsnow, newsnow, prevdate, lastchange)
                                    VALUES (%(idloipe)i, '%(datum)s', %(snowmin_m)i, %(snowmax_m)i, '%(snowtype)s', %(temp_c)i, '%(wetter)s',
                                    '%(lastsnow)s', %(newsnow_m)i, '%(prevdate)s', '%(lastchange)s')""" % loipe.data
                                try:
                                    execute_query(query)
                                except TypeError as err:
                                    print(err)
                                    print(loipe.data)
                                except:
                                    print("Error in query:\n", query)
                                print("*** Neue Daten für Loipe %(Name)s in %(Town)s vom %(lastchange)s (ID=%(idloipe)s). ***" % loipe.data)
                            # print(loipe)
                            htmloutput+=loipe.html()
                    #else:
                    #    print("Keine Loipe für %s unter %s gefunden" % (townkey, li))

            if town.has_loipe():
                htmloutput+=TABLELINE
            else:
                # print("Keine Loipe in " + town.Name)
                pass
        print("Now writing HTML file: maximale Schneehöhe %d, %i - %ikm / %i - %ikm / %i - %ikm" % (SNOWMAX, NGOOD, KMGOOD/1000, NMEDI, KMMEDI/1000, NBAD, KMBAD/1000))
        fh = open("loipen.html", "w")
        fh.write(HTMLSTART)
        fh.write('<h3>Loipenzustand im Harz (Daten von <a href="http://wintersport.harzinfo.de/">http://wintersport.harzinfo.de/</a> mit Stand %s, maximale Schneehöhe %d cm)</h3>' % \
                 (time.strftime("%d.%m.%Y %H:%M"), SNOWMAX))
        fh.write("""
<table>
 <tr><th>Legende:</th></tr>
 <tr class="good"><td>gespurt und minimale Schneehöhe &gt;= 30cm und nicht pappig (wenn Angabe "pappig" aber T&lt;-5°C)</td><td>%i Loipen / %i km</td></tr>
 <tr class="medi"><td>gespurt aber minimale Schneehöhe &lt; 30cm oder pappig - aber nicht wenn "kein Schnee" oder Regen</td><td>%i Loipen / %i km</td></tr>
 <tr class="bad"><td>nicht gespurt oder keine Angaben oder Regen</td><td>%i Loipen / %i km</td></tr>
</table>
""" % (NGOOD, KMGOOD/1000, NMEDI, KMMEDI/1000, NBAD, KMBAD/1000))
        fh.write(TABLESTART+HEADER)
        fh.write(htmloutput)
        fh.write(TABLEEND)
        fh.write("""
<h3>Geheimtips</h3>
<table border="1" cellpadding="0" cellspacing="1">
<tbody>
<tr>
<th>Ort</th><th>Loipe</th><th>Zustand</th><th>Schneehöhe / Qualität / Wetter</th><th>Länge</th><th>Techn./Kond.</th><th>Geoinfo</th><th>Stand</th>
</tr>
<tr>
  <td>Allrode</td><td>Allrode-Friedrichsbrunn-Loipe</td><td>geheim</td><td>siehe Friedrichsbrunn</td><td>9,3 km</td><td>2/2</td><td><a href="http://www.gpswandern.de/gpxviewer/tvanzeige.shtml?url=http://fam-tille.de/gps/loipen/allrode-friedrichsbrunn.gpx" alt="map">Karte</a></td><td>28.01.2017</td>
</tr>
<tr>
  <td>Allrode</td><td>Allrode-Loipe</td><td>geheim</td><td>siehe Friedrichsbrunn</td><td>10 km (unvollständig!)</td><td>3/3</td><td><a href="http://www.gpswandern.de/gpxviewer/tvanzeige.shtml?url=http://fam-tille.de/gps/loipen/allrode.gpx" alt="map">Karte</a></td><td>29.01.2017 unvollständig!</td>
</tr>
</tbody>
</table>
""")
        fh.write(HTMLEND)
        fh.close()
        con.commit()

if __name__ == '__main__':
    main()
