import cgi
import os
import re
import uuid
import html
import zipfile
import imghdr

from http.server import SimpleHTTPRequestHandler
from http.server import HTTPServer
from socketserver import ThreadingMixIn
from urllib.parse import urlparse
from urllib.parse import parse_qsl
from datetime import datetime
from collections import OrderedDict
from zipfile import ZipFile
from io import BytesIO

from . import globs

from .worker import TaskModuleInit, TaskExit

from . import sdk
from .sdk import TaskSpeak, TaskSound, FastTask, ImageObject, HtmlPage, TaskModuleEvt, TaskSaveSettings

g_oHttpdWorker = None

# {<name> :
}
class StartPage(OrderedDict):

	def writeToPage(self,
		oHtmlPage,		# HTML-Seite, in welche zu schreiben ist
		bEdit=False,	# True, um die Startseite zu editieren
		secEdt="",		# Name der zu editierenden Sektion
		artEdt="",		# Name des zu editierenden Artikels
		btnEdt=""		# Name des zu editierenden Buttons
		):

		globs.dbg(
			"StartPage::writeToPage(bEdit=%r, secEdt=\"%s\", artEdt=\"%s\", btnEdt=\"%s\")" % (
			bEdit, secEdt, artEdt, btnEdt))

		if bEdit:
			self.editStartPage(oHtmlPage)
			return
		if self.editTarget(oHtmlPage, secEdt, artEdt, btnEdt):
			return
		bIsEmpty = True

		for (_, oSection) in self.items():
			oSection.writeToPage(oHtmlPage)
			bIsEmpty = False

		if bIsEmpty:
			oHtmlPage.append(
				"
🔧 Die Startseite ist noch leer. Jetzt bearbeiten.
" % (
				globs.s_strStartPageUrl))

		return

	def editStartPage(self,
		oHtmlPage
		):
		oHtmlPage.createBox("Startseite bearbeiten",
			"Ein Element aus der Strukturansicht auswählen und bearbeiten " +
			"oder ein neues Element hinzufügen.", bClose=False)
		oHtmlPage.extend([
			"
",
			"",
			"
Strukturansicht
"
		])
		oHtmlPage.append("
")
		for (strSecName, oSection) in self.items():
			strType = "Nebenaufgaben"
			if oSection.m_bPrimary:
				strType = "Hauptaufgaben"
			oHtmlPage.append(
				"
%s Abschnitt \"%s\"" % (
					globs.s_strStartPageUrl, strSecName, "✎", strType))
			oHtmlPage.append("
")
			for (strArtName, oArticle) in oSection.items():
				oHtmlPage.append(
					"
%s Aufgabe \"%s\"
" % (
						globs.s_strStartPageUrl, strSecName, strArtName, "✎", oArticle.m_strTitle))
			oHtmlPage.append(
				"
%s Neue Aufgabe erstellen
" % (
					globs.s_strStartPageUrl, strSecName, uuid.uuid1().hex, "✚"))
			oHtmlPage.append("
")
			oHtmlPage.append("
")
		oHtmlPage.append(
			"
%s Neuen Abschnitt anlegen
" % (
				globs.s_strStartPageUrl, uuid.uuid1().hex, "✚"))
		oHtmlPage.extend([
			"
",
			"",
			"
"
		])
		oHtmlPage.createButton("Fertig", strClass="ym-save")
		oHtmlPage.closeBox()
		return

	def editTarget(self,
		oHtmlPage,
		secEdt,		# Name der zu editierenden Sektion
		artEdt,		# Name des zu editierenden Artikels
		btnEdt		# Name des zu editierenden Buttons
		):
		# Targets vorbereiten
		dictTargets = {}
		dictQueries = {}
		dictButtons = {}
		oSection = None
		oArticle = None
		oButton = None
		oTarget = None
		# Bearbeitungshierarchie prüfen
		if secEdt and secEdt in self:
			oSection = self[secEdt]
			dictTargets.update({"sec" 	: secEdt})
		if artEdt and oSection and artEdt in oSection:
			oArticle = oSection[artEdt]
			dictTargets.update({"art" 	: artEdt})
		if btnEdt and oArticle and btnEdt in oArticle:
			oButton = oArticle[btnEdt]
			dictTargets.update({"btn" 	: btnEdt})
			dictQueries.update({
				"sec" 		: oSection.m_strName,
				"artEdt" 	: oArticle.m_strName
				})
		# Target identifizieren
		if oButton != None:
			oHtmlPage.createBox("Schaltfläche",
				"Schaltfläche bearbeiten", bClose=False)
			oTarget = oButton
			dictButtons.update({
				"del" : ["btn", "Schaltfläche löschen", "ym-delete ym-danger"]
			})
		elif oArticle != None:
			dictQueries.update({"edit" 	: "startpage"})
			oHtmlPage.createBox("Aufgabe",
				"Aufgabe bearbeiten", bClose=False)
			oTarget = oArticle
			dictButtons.update({
				"del" 		: ["art", "Aufgabe löschen", "ym-delete ym-danger"],
				"btnAdd"	: [uuid.uuid1().hex, "Neue Schaltfläche", "ym-add"]
			})
		elif oSection != None:
			dictQueries.update({"edit" 	: "startpage"})
			oHtmlPage.createBox("Abschnitt",
				"Abschnitt bearbeiten", bClose=False)
			oTarget = oSection
			dictButtons.update({
				"del" : ["sec", "Abschnitt löschen", "ym-delete ym-danger"]
			})

		globs.dbg(
			"StartPage::editTarget(secEdt=%r, artEdt=%r, btnEdt=%r): oTarget=%r, oSection=%r, oArticle=%r, oButton=%r, dictTargets=%r, dictQueries=%r, StartPage=%r" % (
			secEdt, artEdt, btnEdt, oTarget, oSection, oArticle, oButton, dictTargets, dictQueries, globs.s_oStartPage))

		# Target bearbeiten
		if oTarget != None:
			oHtmlPage.openForm(
				dictTargets=dictTargets,
				dictQueries=dictQueries)
			oTarget.writeToPage(oHtmlPage, bEdit=True)
			oHtmlPage.closeForm(dictButtons=dictButtons)
			oHtmlPage.closeBox()
			return True
		return False

# {<name> :
}
class Section(OrderedDict):

	def setSection(self,
		strName,
		bPrimary=True
		):
		self.m_strName = strName
		self.m_bPrimary = bPrimary
		return

	def writeToPage(self,
		oHtmlPage,		# HTML-Seite, in welche zu schreiben ist
		bEdit=False		# True, um den Aufgabentyp zu editieren
		):
		if bEdit:
			oHtmlPage.appendForm("secPrimary", strInput="primary",
				strTitle="Hauptaufgaben", bCheck=True,
				bSelected=self.m_bPrimary)
			return
		strClass = "" # Alternating "ym-gl" and "ym-gr"
		if self.m_bPrimary:
			oHtmlPage.append("
")
		else:
			oHtmlPage.append("
")
		for (_, oArticle) in self.items():
			if self.m_bPrimary:
				oHtmlPage.append(
					"
")
			else:
				if strClass == "ym-gl":
					strClass = "ym-gr"
				else:
					strClass = "ym-gl"
				oHtmlPage.append(
					"
" % (strClass))
			oHtmlPage.append("
")
			oArticle.writeToPage(oHtmlPage)
			oHtmlPage.extend([
				"
",
				"
"
			])
		oHtmlPage.append("
")
		return

# {<name> : <Button>}
class Article(OrderedDict):

	def setArticle(self,
		strName,
		strTitle="",
		strMsg="",
		strType=""
		):
		self.m_strName = strName
		self.m_strTitle = strTitle
		self.m_strMsg = strMsg
		self.m_strType = strType
		return

	def writeToPage(self,
		oHtmlPage,		# HTML-Seite, in welche zu schreiben ist
		bEdit=False		# True, um den Artikel zu editieren
		):
		if bEdit:
			oHtmlPage.appendForm("artTitle",
				strInput=self.m_strTitle, strTitle="Titelzeile")
			oHtmlPage.appendForm("artContent",
				strInput=self.m_strMsg, strTitle="Inhalt", nLines=3)
			oHtmlPage.appendForm("artType",
				strInput=self.m_strType, strTitle="Hintergrund", dictChoice = {
					"Grün" 		: "success",
					"Rot" 		: "error",
					"Gelb" 		: "warning",
					"Info" 		: "info",
					"Neutral"	: ""})
			oHtmlPage.append("
Schaltflächen bearbeiten
")
			for (btnName, oButton) in self.items():
				oHtmlPage.appendForm("btnEdt",
					strInput=btnName,
					strTitle=(oButton.m_strTitle),
					bButton=True,
					strClass=("%s %s" % (oButton.m_strIcon, oButton.m_strColor)))
		else:
			oHtmlPage.createBox(self.m_strTitle, self.m_strMsg,
				strType=self.m_strType, bClose = False)
			for (btnName, oButton) in self.items():
				oButton.writeToPage(oHtmlPage)
			oHtmlPage.closeBox()
		return

class Button:

	def setButton(self,
		strName,
		strTitle="Ohne Titel",
		strHRef="",
		strIcon="",
		strColor="",
		strRedirect="startpage"
		):
		self.m_strName = strName
		self.m_strTitle = strTitle
		self.m_strHRef = strHRef
		self.m_strIcon = strIcon
		self.m_strColor = strColor
		self.m_strRedirect = strRedirect
		return

	def writeToPage(self,
		oHtmlPage,		# HTML-Seite, in welche zu schreiben ist
		bEdit=False		# True, um die Schaltfläche zu editieren
		):
		if bEdit:
			oHtmlPage.appendForm("btnTitle",
				strInput=self.m_strTitle, strTitle="Titel der Schaltfläche")
			oHtmlPage.appendForm("btnHRef",
				strInput=self.m_strHRef, strTitle="URL mit Abfrage")
			oHtmlPage.appendForm("btnRedirect",
				strInput=self.m_strRedirect,
				strTitle="Zu dieser Seite navigieren",
				bUseKeyAsValue=True,
				bEscape=True,
				dictChoice = globs.s_dictSettings["Redirects"])
			oHtmlPage.appendForm("btnIcon",
				strInput=self.m_strIcon,
				strTitle="Icon",
				bEscape=False,
				dictChoice = {
					"" : "",
					"🚫 Verboten" 				: "ym-forbid",
					"🆗 OK" 						: "ym-ok",
					"⛔ Gesperrt" 				: "ym-noentry",
					"✅ Check" 					: "ym-check",
					"🔁 Aktualisieren" 			: "ym-refresh",
					"🔂 Aktualisieren einmal"	: "ym-refresh1",
					"🔀 Austauschen" 			: "ym-exchange",
					"🚮 Abfall" 					: "ym-trash",
					"🔊 Lautstärke laut" 		: "ym-volumemax",
					"🔉 Lautstärke mittel" 		: "ym-volumemid",
					"🔈 Lautstärke leise" 		: "ym-volumemin",
					"🔇 Lautstärke aus" 			: "ym-volumeoff",
					"🔔 Alarm" 					: "ym-bell",
					"🔕 Alarm aus" 				: "ym-belloff",
					"🔍 Details" 				: "ym-details",
					"🔆 Helligkeit max" 			: "ym-brightmax",
					"🔅 Helligkeit min" 			: "ym-brightmin",
					"🔧 Schraubenschlüssel" 		: "ym-wrench",
					"📥 Posteingang" 			: "ym-inbox",
					"📤 Postausgang" 			: "ym-outbox",
					"📄 Seite" 					: "ym-page",
					"📃 Seite gerollt" 			: "ym-curl",
					"📜 Schriftrolle" 			: "ym-scroll",
					"📅 Kalender" 				: "ym-calendar",
					"🍅 Tomate" 					: "ym-tomato",
					"🍌 Banane" 					: "ym-banana",
					"🍐 Birne" 					: "ym-pear",
					"🍑 Pfirsicht" 				: "ym-peach",
					"🍓 Erdbeere" 				: "ym-strawberry",
					"🍇 Grapefruit" 				: "ym-grapes",
					"🍒 Kirschen" 				: "ym-cherries",
					"🍋 Lemone" 					: "ym-lemon",
					"🍎 Apfel rot" 				: "ym-redapple",
					"🍏 Apfel grün" 				: "ym-greenapple",
					"💤 Schlafen" 				: "ym-sleep",
					"✚ Hinzufügen" 				: "ym-add",
					"✘ Entfernen" 				: "ym-delete",
					"✕ Schließen" 				: "ym-close",
					"✎ Ändern" 					: "ym-edit",
					"✉ E-Mail" 					: "ym-email",
					"❤ Herz" 					: "ym-like",
					"➜ Weiter" 					: "ym-next",
					"▶ Abspielen" 				: "ym-play",
					"➥ Antworten" 				: "ym-reply",
					"✔ Speichern" 				: "ym-save",
					"✍ Unterschreiben" 			: "ym-sign",
					"✷ Funken" 					: "ym-spark",
					"✆ Telefon" 				: "ym-support",
					"★ Stern" 					: "ym-star",
					"⚠ Warnzeichen" 			: "ym-warnsign",
					"✋ Hand (Stop)" 			: "ym-handstop"})
			oHtmlPage.appendForm("btnColor",
				strInput=self.m_strColor, strTitle="Farbe", dictChoice = {
					"Grün" 		: "ym-success",
					"Rot" 		: "ym-danger",
					"Gelb" 		: "ym-warning",
					"Blau" 		: "ym-primary",
					"Neutral"	: ""})
		else:
			strRedirect = ""
			if "?" in self.m_strHRef:
				strRedirect = "&redirect2="
			else:
				strRedirect = "?redirect2="
			strRedirect += self.m_strRedirect
			oHtmlPage.createButton(
				self.m_strTitle,
				"%s%s" % (self.m_strHRef, strRedirect),
				"%s %s" % (self.m_strIcon, self.m_strColor))
		return

class TaskModuleCmd(FastTask):

	def __init__(self,
		oWorker,
		strPath,
		oHtmlPage,
		dictForm=None,
		dictQuery=None
		):
		super(TaskModuleCmd, self).__init__(oWorker)
		self.m_strPath = strPath
		self.m_dictForm = dictForm
		self.m_dictQuery = dictQuery
		self.m_oHtmlPage = oHtmlPage
		return

	def __str__(self):
		strDesc = "Ausführen von Modulkommandos"
		return  strDesc

	def do(self):
		for (strName, (oInstance, _)) in self.m_oWorker.m_dictModules.items():
			if (oInstance and re.match("/modules/%s\\..+" % (strName), self.m_strPath)):
				self.m_oResult = oInstance.moduleExec(self.m_strPath,
					self.m_oHtmlPage, self.m_dictQuery, self.m_dictForm)
				return
		return

class TaskInstallModule(FastTask):

	def __init__(self, oWorker, strModule):
		super(TaskInstallModule, self).__init__(oWorker)
		self.m_strModule = strModule
		return

	def __str__(self):
		strDesc = "Installieren des Moduls %s" % (
			self.m_strModule)
		return  strDesc

	def do(self):
		if not self.m_strModule in globs.s_dictSettings["listModules"]:
			globs.s_dictSettings["listModules"].append(self.m_strModule)
		return

class TaskRemoveModule(FastTask):

	def __init__(self, oWorker, strModule):
		super(TaskRemoveModule, self).__init__(oWorker)
		self.m_strModule = strModule
		return

	def __str__(self):
		strDesc = "Entfernen des Moduls %s" % (self.m_strModule)
		return  strDesc

	def do(self):
		if self.m_strModule in globs.s_dictSettings["listInactiveModules"]:
			globs.s_dictSettings["listInactiveModules"].remove(self.m_strModule)
		if self.m_strModule in globs.s_dictSettings["listModules"]:
			globs.s_dictSettings["listModules"].remove(self.m_strModule)
		return

class TaskEnableModule(FastTask):

	def __init__(self, oWorker, strModule, bEnable):
		super(TaskEnableModule, self).__init__(oWorker)
		self.m_strModule = strModule
		self.m_bEnable = bEnable
		return

	def __str__(self):
		strDesc = ""
		if self.m_bEnable:
			strDesc += "Einschalten"
		else:
			strDesc += "Ausschalten"
		strDesc += " des Moduls %s" % (self.m_strModule)
		return  strDesc

	def do(self):
		if self.m_bEnable:
			if self.m_strModule in globs.s_dictSettings["listInactiveModules"]:
				globs.s_dictSettings["listInactiveModules"].remove(self.m_strModule)
				globs.saveSettings()
		elif self.m_strModule not in globs.s_dictSettings["listInactiveModules"]:
				globs.s_dictSettings["listInactiveModules"].append(self.m_strModule)
				globs.saveSettings()
		return

class TaskDisplayModules(FastTask):

	def __init__(self, oWorker, oHtmlPage, dictQuery=None):
		super(TaskDisplayModules, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		self.m_dictQuery = dictQuery
		self.m_oTargetEdit = None
		return

	def __str__(self):
		strDesc = ""
		if self.m_oTargetEdit:
			strDesc += "Ändern"
		else:
			strDesc += "Darstellen"
		strDesc += " der Modulkonfiguration"
		return  strDesc

	def do(self):
		self.displayModules()
		return

	def displayModules(self):
		self.m_oHtmlPage.openTableForm(
			"Installierte Module",
			["Modul", "Status"],
			strChk = "Auswahl")
		for strModule in sorted(globs.s_dictSettings["listModules"]):
			strStatus = "info"
			strContent = "N/A"
			if strModule in globs.s_dictSettings["listInactiveModules"]:
				strStatus = "warning"
				strContent = "Ausgeschaltet"
			elif strModule not in self.m_oWorker.m_dictModules.keys():
				strStatus = "error"
				strContent = "Fehlerhaft"
			else:
				strStatus = "success"
				strContent = "Eingeschaltet"
			self.m_oHtmlPage.appendTableForm(
				strModule,
				["%s" % (strModule), "
%s
" % (strStatus, strContent)],
				bChk = False, bEscape=False)
		self.m_oHtmlPage.closeTableForm(
			dictAct = {
				"disable" : {
					"type" 		: "forbid ym-warning",
					"content" 	: "Ausschalten",
					"title" 	: "Alle ausgewählten Module ausschalten",
				},
				"enable" : {
					"type" 		: "play ym-success",
					"content" 	: "Einschalten",
					"title" 	: "Alle ausgewählten Module einschalten",
				},
				"delete" : {
					"type" 		: "close ym-danger",
					"content" 	: "Löschen",
					"title" 	: "Alle ausgewählten Module entfernen",
				}
			})
		return

class TaskDisplaySystem(FastTask):

	def __init__(self, oWorker, oHtmlPage, dictForm=None, dictQuery=None):
		super(TaskDisplaySystem, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		self.m_dictForm = dictForm
		self.m_dictQuery = dictQuery
		self.m_strFxn = ""
		self.m_strArg = ""
		return

	def __str__(self):
		strDesc = "Darstellen der Systemwerte"
		return  strDesc

	def do(self):
		self.m_strFxn = ""
		self.m_strArg = ""

		self.m_oHtmlPage.setTitle("Systemwerte")

		if self.m_dictQuery:
			for strVal in ("edit", "date", "time"):
				if strVal in self.m_dictQuery:
					self.m_strFxn = "%s" % (strVal)
					self.m_strArg = self.m_dictQuery[self.m_strFxn][0]
					break
			if (self.m_strFxn
				and self.m_strFxn == "edit"
				and self.m_strArg):
				if self.createForm():
					return

		if self.m_dictForm:
			if "target" in self.m_dictForm:
				self.m_strFxn = self.m_dictForm["target"][0]
			if self.m_strFxn and self.m_strFxn in self.m_dictForm:
				self.m_strArg = self.m_dictForm[self.m_strFxn][0]

		if (self.m_strFxn and self.m_strArg):
			if not self.updateValue():
				return
			TaskModuleEvt(g_oHttpdWorker, "/int/evt.src",
				dictQuery={
					"system" : [self.m_strFxn]
				}
			).start()

		self.displayValues()
		return

	def displayValues(self):
		dt = datetime.today()
		# Tabelle öffnen und Systeminformationen eintragen
		self.m_oHtmlPage.openTable(
			"Aktuelle Systemwerte",
			["System", ""], True, True)
		self.m_oHtmlPage.appendTable([
			"Version", "V%s" % (globs.getVersion())],
				bFirstIsHead=True, bEscape=False)
		self.m_oHtmlPage.appendTable([
			"Datum", "✎ %s" % (
				"/system/values.html?edit=date",
				dt.strftime("%d.%m.%Y"))],
				bFirstIsHead=True, bEscape=False)
		self.m_oHtmlPage.appendTable([
			"Uhrzeit", "✎ %s" % (
				"/system/values.html?edit=time",
				dt.strftime("%H:%M:%S"))],
				bFirstIsHead=True, bEscape=False)
		# Alle Sektionen durchgehen
		for strSection in sorted(globs.s_dictSystemValues.keys()):
			# Tabelle mit dem nächsten Tabellenkopf fortsetzen
			self.m_oHtmlPage.appendHeader([strSection, ""])
			# Alle Werte durchgehen
			for strProperty in sorted(globs.s_dictSystemValues[strSection].keys()):
				self.m_oHtmlPage.appendTable(
					[
						strProperty,
						globs.s_dictSystemValues[strSection][strProperty]
					],
					bFirstIsHead=True)
		# Tabelle schließen
		self.m_oHtmlPage.closeTable()
		self.m_oHtmlPage.setAutoRefresh(10)
		return

	def createForm(self):
		dt = datetime.today()
		varVal = None
		strTitle = self.m_strArg
		if self.m_strArg == "date":
			varVal = "%s" % (dt.strftime("%Y-%m-%d"))
			strTitle = "Datum"
			self.m_oHtmlPage.createBox(
				strTitle,
				"Das Datum kann unabhängig von der Uhrzeit eingestellt werden, wobei " +
				"die Angabe im Format YYYY.MM.DD (Jahr, Monat, Tag, z.B. %s) erwartet wird." % (varVal),
				bClose=False)
		elif self.m_strArg == "time":
			varVal = "%s" % (dt.strftime("%H:%M:%S"))
			strTitle = "Uhrzeit"
			self.m_oHtmlPage.createBox(
				strTitle,
				"Die Uhrzeit kann unabhängig vom Datum eingestellt werden, wobei " +
				"die Angabe im Format HH:MM:SS (Stunde, Minute, Sekunde, z.B. %s) erwartet wird." % (varVal),
				bClose=False)
		else:
			return False

		self.m_oHtmlPage.openForm(dictTargets={"target" : self.m_strArg})
		self.m_oHtmlPage.appendForm(self.m_strArg,
			strInput="%s" % (varVal),
			strTitle=strTitle, strTextType=self.m_strArg)
		self.m_oHtmlPage.closeForm()
		self.m_oHtmlPage.closeBox()
		return True

	def updateValue(self):
		strResult = ""

		if (self.m_strFxn == "date"):
			try:
				strResult = sdk.setDate(self.m_strArg, "%d.%m.%Y")
				globs.log("Datum einstellen: %s" % (strResult))
			except Exception as ex:
				strResult = " %s" % (ex)
				globs.wrn("Datum einstellen: %s" % (strResult))
			else:
				return True
			try:
				strResult = sdk.setDate(self.m_strArg, "%Y-%m-%d")
				globs.log("Datum einstellen: %s" % (strResult))
			except Exception as ex:
				globs.exc("Datum einstellen")
				strResult = " %s" % (ex)
			else:
				return True
		elif (self.m_strFxn == "time"):
			try:
				strResult = sdk.setTime(self.m_strArg, "%H:%M:%S")
				globs.log("Uhrzeit einstellen: %s" % (strResult))
			except Exception as ex:
				globs.exc("Uhrzeit einstellen")
				strResult = " %s" % (ex)
			else:
				return True

		self.m_oHtmlPage.createBox(self.m_strFxn,
			"Die Einstellung \"%s\" konnte nicht auf den Wert \"%s\" geändert werden.%s" % (
			self.m_strFxn, self.m_strArg, strResult),
			strType="warning")

		return False

class TaskDisplaySettings(FastTask):

	def __init__(self, oWorker, oHtmlPage, dictForm=None, dictQuery=None):
		super(TaskDisplaySettings, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		self.m_dictForm = dictForm
		self.m_dictQuery = dictQuery
		self.m_strFxn = ""
		self.m_strVal = ""
		self.m_strKey = ""
		return

	def __str__(self):
		strDesc = "Darstellen der Konfiguration"
		return  strDesc

	def do(self):
		self.m_strFxn = ""
		self.m_strVal = ""
		self.m_strKey = ""

		self.m_oHtmlPage.setTitle("Konfigurationseinstellungen")

		if (self.m_dictQuery
			and "edit" in self.m_dictQuery
			and "key" in self.m_dictQuery):
			self.m_strFxn = "edit"
			self.m_strVal = self.m_dictQuery[self.m_strFxn][0]
			self.m_strKey = self.m_dictQuery["key"][0]
			if self.createForm():
				return

		if (self.m_dictForm
			and "target" in self.m_dictForm
			and "key" in self.m_dictForm):
			self.m_strFxn = self.m_dictForm["target"][0]
			self.m_strKey = self.m_dictForm["key"][0]
			self.m_strVal = ""
			if self.m_strFxn in self.m_dictForm:
				self.m_strVal = self.m_dictForm[self.m_strFxn][0]
				if not self.updateValue():
					return

		self.displayValues()
		return

	def displayValues(self):
		# >>> Critical Section
		with globs.s_oSettingsLock:
			try:
				bOpened = False
				for strSubsystem in sorted(globs.s_dictUserSettings.keys()):

					if (strSubsystem not in globs.s_dictSettings
						or not globs.s_dictSettings[strSubsystem]
						or not globs.s_dictUserSettings[strSubsystem]):
						continue
					if (strSubsystem not in ("System", "PIP") and strSubsystem not in globs.s_dictSettings["listModules"]):
						continue
					if (strSubsystem in globs.s_dictSettings["listInactiveModules"]):
						continue

					if bOpened:
						self.m_oHtmlPage.appendHeader([strSubsystem, ""])
					else:
						bOpened = True
						self.m_oHtmlPage.openTable("Konfigurationseinstellungen",
							[strSubsystem, ""], True, True)

					for strProperty in (
						globs.s_dictUserSettings[strSubsystem]["properties"]
						if "properties" in globs.s_dictUserSettings[strSubsystem] else
						globs.s_dictUserSettings[strSubsystem].keys()):
						if strProperty in globs.s_dictSettings[strSubsystem]:
							strTitle = strProperty
							dictProperties = globs.s_dictUserSettings[strSubsystem][strProperty]
							if ("hidden" in dictProperties and dictProperties["hidden"]):
								continue
							if ("title" in dictProperties):
								strTitle = dictProperties["title"]
							if ("readonly" in dictProperties and dictProperties["readonly"]):
								self.m_oHtmlPage.appendTable([
									strTitle,
									"%s" % (globs.s_dictSettings[strSubsystem][strProperty])
									], bFirstIsHead=True, bEscape=False)
							elif ("showlink" in dictProperties and dictProperties["showlink"]
								and "description" in dictProperties
								and dictProperties["description"]):
								self.m_oHtmlPage.appendTable([
									strTitle,
									"➥ %s" % (
										dictProperties["default"],
										dictProperties["description"])
									], bFirstIsHead=True, bEscape=False)
							else:
								self.m_oHtmlPage.appendTable([
									strTitle,
									"✎ %s" % (
										"/system/settings.html", strProperty, strSubsystem,
										globs.s_dictSettings[strSubsystem][strProperty])
									], bFirstIsHead=True, bEscape=False)
				if bOpened:
					self.m_oHtmlPage.closeTable()
			except:
				globs.exc("Darstellen der Konfiguration")
		# <<< Critical Section
		return

	def createForm(self):
		dictProperties = None
		dictChoices = {}
		strTitle = self.m_strVal
		strDesc = ""
		strDefault = ""
		strType = ""
		strPattern = ""
		varVal = None
		bUseKeyAsValue = False
		# >>> Critical Section
		with globs.s_oSettingsLock:
			try:
				if (self.m_strKey in globs.s_dictUserSettings
					and self.m_strKey in globs.s_dictSettings
					and self.m_strVal in globs.s_dictSettings[self.m_strKey]
					and self.m_strKey not in (
					"listModules", "listInactiveModules", "Redirects")):
					dictValues = globs.s_dictUserSettings[self.m_strKey]
					varVal = globs.s_dictSettings[self.m_strKey][self.m_strVal]
					strDefault = "%s" % (varVal)
				if dictValues and self.m_strVal in dictValues:
					dictProperties = dictValues[self.m_strVal]
				if dictProperties:
					if "title" in dictProperties:
						strTitle = dictProperties["title"]
					if "description" in dictProperties:
						strDesc = dictProperties["description"]
					if "default" in dictProperties:
						strDefault = dictProperties["default"]
					if "choices" in dictProperties:
						dictChoices = dictProperties["choices"]
					if "keyIsValue" in dictProperties:
						bUseKeyAsValue = dictProperties["keyIsValue"]
					if "type" in dictProperties:
						strType = dictProperties["type"]
					if "pattern" in dictProperties:
						strPattern = dictProperties["pattern"]
			except:
				globs.exc("Ändern der Konfiguration")
				varVal = None
		# <<< Critical Section
		if varVal == None:
			return False
		if strDesc:
			strDesc += " "
		strDesc += "Der Standardwert für die Einstellung ist %s." % (strDefault)
		self.m_oHtmlPage.createBox(
			strTitle, strDesc, bClose=False)
		self.m_oHtmlPage.openForm(dictTargets={
			"target" 	: self.m_strVal,
			"key" 		: self.m_strKey
		})
		if isinstance(varVal, bool):
			bCheck = True if strType == "check" else False
			bRadio = True if strType == "radio" else False
			if (bRadio or not bCheck) and not dictChoices:
				dictChoices = {
					"Ein"	: "True",
					"Aus"	: "False"
				}
			self.m_oHtmlPage.appendForm(self.m_strVal,
				strInput="%s" % (varVal),
				strTitle=strTitle,
				bCheck=bCheck,
				bRadio=bRadio,
				dictChoice = dictChoices)
		else:
			bCheck = True if strType == "check" else False
			bButton = True if strType == "button" else False
			self.m_oHtmlPage.appendForm(self.m_strVal,
				strInput="%s" % (varVal),
				strTitle=strTitle,
				bCheck=bCheck,
				bButton=bButton,
				strTextType=strType,
				strTypePattern=strPattern,
				dictChoice=dictChoices,
				bUseKeyAsValue=bUseKeyAsValue)
		self.m_oHtmlPage.closeForm()
		self.m_oHtmlPage.closeBox()
		return True

	def updateValue(self):
		if (self.m_strKey == "PIP"):
			if (globs.getSetting(self.m_strKey, self.m_strFxn) != None):
				if (self.m_strVal == "install"):
					if (os.system("pip3 install %s" %(self.m_strFxn)) == 0
						and globs.setSetting(self.m_strKey, self.m_strFxn, "uninstall")):
						globs.saveSettings()
						return True
				elif (self.m_strVal == "uninstall"):
					if (os.system("pip3 uninstall -y %s" %(self.m_strFxn)) == 0):
						globs.unregisterPipPackage(self.m_strFxn)
						globs.saveSettings()
						return True
			self.m_oHtmlPage.createBox(self.m_strFxn,
				"Das Paket \"%s\" konnte nicht installiert/deinstalliert werden." % (
				self.m_strFxn),
				strType="warning")
			return False
		elif globs.setSetting(self.m_strKey, self.m_strFxn, self.m_strVal):
			globs.saveSettings()
			TaskModuleEvt(g_oHttpdWorker, "/int/evt.src",
				dictQuery={
					"settings" : [self.m_strKey]
				},
				dictForm={
					self.m_strFxn : [self.m_strVal]
				}
			).start()
			return True
		self.m_oHtmlPage.createBox(self.m_strFxn,
			"Die Einstellung \"%s\" konnte nicht auf den Wert \"%s\" geändert werden." % (
			self.m_strFxn, self.m_strVal),
			strType="warning")
		return False

class TaskDisplayLogMem(FastTask):

	def __init__(self, oWorker, oHtmlPage, dictQuery=None, dictForm=None):
		super(TaskDisplayLogMem, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		self.m_dictQuery = dictQuery
		self.m_dictForm = dictForm
		self.m_strMode = ""
		return

	def __str__(self):
		strDesc = "Darstellen der Protokollierung"
		return  strDesc

	def do(self):
		self.m_strMode = ""

		self.m_oHtmlPage.setTitle("Protokollierung")

		if self.m_dictQuery and "mode" in self.m_dictQuery:
			self.m_strMode = self.m_dictQuery["mode"][0]
		elif self.m_dictForm and "mode" in self.m_dictForm:
			self.m_strMode = self.m_dictForm["mode"][0]

		bUpdate = (self.m_strMode and (self.m_strMode == "update"))
		lstLogMem = globs.getLogMem(bUpdate)
		if self.m_strMode and self.m_strMode == "edit":
			self.createForm()
		elif self.m_strMode and self.m_strMode in ("EXC", "ERR", "WRN", "INF", "DBG"):
			globs.setLogLvl(self.m_strMode)
			self.m_oHtmlPage.createBox("Protokollierung",
				"Die Detail-Tiefe der Protokollierung ist jetzt auf \"%s\" festgelegt." % (
					globs.getLogLvl()))
		elif self.m_strMode and self.m_strMode.isdigit():
			self.showDetail(lstLogMem)
		else:
			self.showLogging(lstLogMem)
		return

	def createForm(self):
		self.m_oHtmlPage.createBox("Protokollierung",
			"Die Detail-Tiefe der Protokollierung kann geändert werden. "+
			"Niedrigere Detail-Stufen schließen höhere implizit mit ein.",
			bClose = False)
		self.m_oHtmlPage.openForm()
		self.m_oHtmlPage.appendForm(
			"mode", globs.getLogLvl(), "Detail-Tiefe",
			dictChoice = {
				"Ausnahmen"	: "EXC",
				"Fehler"	: "ERR",
				"Warnungen"	: "WRN",
				"Hinweise"	: "INF",
				"Debugging" : "DBG"
			})
		self.m_oHtmlPage.closeForm()
		self.m_oHtmlPage.closeBox()
		return

	def showDetail(self, lstLogMem):
		nIndex = int(self.m_strMode)
		if (nIndex >= 0 and nIndex < len(lstLogMem)):
			oLogEntry = lstLogMem[nIndex]
			self.m_oHtmlPage.createBox(
				"%s - %s" % (oLogEntry.m_strType, oLogEntry.m_strDate),
				"%s" % (oLogEntry.m_strText), bClose=False)
			if (oLogEntry.m_lstTB):
				self.m_oHtmlPage.append("
")
				for (filename, line, function, text) in oLogEntry.m_lstTB:
					self.m_oHtmlPage.append("
File \"%s\", line %s, in %s %s
" % (
						html.escape("%s" % filename),
						line,
						html.escape("%s" % function),
						html.escape("%s" % text)))
				self.m_oHtmlPage.append("
")
			self.m_oHtmlPage.createButton("OK")
			self.m_oHtmlPage.closeBox()
		else:
			self.m_oHtmlPage.createBox(
				"Protokollierung",
				"Der angeforderte Eintrag existiert nicht.",
				strType="warning")
		return

	def showLogging(self, lstLogMem):
		# Tabelle öffnen
		self.m_oHtmlPage.openTable(
			"Protokollierung",
			["Nr", "Typ", "Zeit", "Meldung"], True, True)
		nIndex = 0
		for oLogEntry in lstLogMem:
			strType = ""
			if (oLogEntry.m_strType == "EXC"):
				strType = "ym-danger"
			elif (oLogEntry.m_strType == "ERR"):
				strType = "ym-warning"
			elif (oLogEntry.m_strType == "WRN"):
				strType = "ym-primary"
			elif (oLogEntry.m_strType == "INF"):
				strType = "ym-success"
			self.m_oHtmlPage.appendTable([
				"%s" % (nIndex),
				"
%s
" % (
					"ym-button ym-xsmall", strType,
					self.m_oHtmlPage.m_strPath, nIndex,
					html.escape("%s" % oLogEntry.m_strType)),
				"%s" % (html.escape("%s" % oLogEntry.m_strDate)),
				"%s" % (html.escape("%s" % oLogEntry.m_strText))],
				bFirstIsHead=True, bEscape=False)
			nIndex += 1
		# Tabelle schließen
		self.m_oHtmlPage.closeTable()
		return

class TaskDisplaySounds(FastTask):

	def __init__(self,
		oWorker,
		oHtmlPage,
		dictForm=None,
		dictQuery=None
		):
		super(TaskDisplaySounds, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		self.m_dictForm = dictForm
		self.m_dictQuery = dictQuery
		return

	def __str__(self):
		strDesc = "Darstellen der installierten Klänge"
		return  strDesc

	def do(self):
		self.displaySounds()
		return

	def displaySounds(self):
		self.m_oHtmlPage.setTitle("Klänge")
		self.m_oHtmlPage.extend([
			"
",
			"",
			"
Installierte Klänge
"
		])

		strSound = ""

		if self.m_dictQuery and "cache" in self.m_dictQuery:
			if self.m_dictQuery["cache"][0] == "clear":
				globs.scanSoundFiles(bClear=True)
			if self.m_dictQuery["cache"][0] == "rescan":
				globs.scanSoundFiles(bRescan=True)
		if self.m_dictQuery and "sound" in self.m_dictQuery:
			strSound = self.m_dictQuery["sound"][0]

		self.m_oHtmlPage.append("
")
		if "Sounds" in globs.s_dictSettings:
			dictSounds = globs.s_dictSettings["Sounds"]
			# Standardklänge darstellen
			if "Default" in dictSounds:
				strAnchor = uuid.uuid1().hex
				self.m_oHtmlPage.append("
Standard")
				self.m_oHtmlPage.append("
")
				for (strName, strFile) in sorted(dictSounds["Default"].items()):
					if strName == strSound:
						strActive = "♫"
					else:
						strActive = "▶"

					_, strTail = os.path.split(strFile)
					_, strExt = os.path.splitext(strTail)

					self.m_oHtmlPage.append(
						"
%s %s [%s]
" % (
							strName, "/sound/values.html", strName, strAnchor, strName,
							strActive, strName, strExt.strip(".").upper()))
				self.m_oHtmlPage.append("
")
				self.m_oHtmlPage.append("
")
			# Alle übrigen Klänge darstellen
			for (strCategory, dictSounds) in sorted(globs.s_dictSettings["Sounds"].items()):
				if strCategory == "Default":
					continue
				strAnchor = uuid.uuid1().hex
				self.m_oHtmlPage.append("
%s" % (strCategory))
				self.m_oHtmlPage.append("
")
				for (strName, strFile) in sorted(dictSounds.items()):
					if strName == strSound:
						strActive = "♫"
					else:
						strActive = "▶"

					_, strTail = os.path.split(strFile)
					_, strExt = os.path.splitext(strTail)

					self.m_oHtmlPage.append(
						"
%s %s [%s]
" % (
							strName, "/sound/values.html", strName, strAnchor, strName,
							strActive, strName, strExt.strip(".").upper()))
				self.m_oHtmlPage.append("
")
				self.m_oHtmlPage.append("
")
		self.m_oHtmlPage.extend([
			"
",
			"",
			"
"
		])
		return

class TaskDisplayImages(FastTask):

	def __init__(self,
		oWorker,
		oHtmlPage,
		dictForm=None,
		dictQuery=None
		):
		super(TaskDisplayImages, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		self.m_dictForm = dictForm
		self.m_dictQuery = dictQuery
		return

	def __str__(self):
		strDesc = "Darstellen der installierten Bilder"
		return  strDesc

	def do(self):
		self.displayImages()
		return

	def displayImages(self):
		self.m_oHtmlPage.setTitle("Bilder")
		self.m_oHtmlPage.extend([
			"
",
			"",
			"
Installierte Bilder
"
		])

		strImage = ""

		if self.m_dictQuery and "cache" in self.m_dictQuery:
			if self.m_dictQuery["cache"][0] == "clear":
				globs.scanImageFiles(bClear=True)
			if self.m_dictQuery["cache"][0] == "rescan":
				globs.scanImageFiles(bRescan=True)
		if self.m_dictQuery and "image" in self.m_dictQuery:
			strImage = self.m_dictQuery["image"][0]

		self.m_oHtmlPage.append("
")
		if "Images" in globs.s_dictSettings:
			dictImages = globs.s_dictSettings["Images"]
			# Standard-Bilder darstellen
			if "Default" in dictImages:
				strAnchor = uuid.uuid1().hex
				self.m_oHtmlPage.append("
Standard")
				self.m_oHtmlPage.append("
")
				for (strName, strFile) in sorted(dictImages["Default"].items()):

					_, strTail = os.path.split(strFile)
					_, strExt = os.path.splitext(strTail)

					if strName == strImage:
						#strActive = "👀"
						strActive = "

" % (strTail, strFile)
						TaskModuleEvt(g_oHttpdWorker, "/int/evt.src",
							dictQuery={
								"picture" : [strImage]
							}
						).start()
					else:
						strActive = "👁"

					self.m_oHtmlPage.append(
						"
%s %s [%s]
" % (
							strName, "/image/values.html", strName, strAnchor, strName,
							strActive, strName, strExt.strip(".").upper()))
				self.m_oHtmlPage.append("
")
				self.m_oHtmlPage.append("
")
			# Alle übrigen Bilder darstellen
			for (strCategory, dictImages) in sorted(globs.s_dictSettings["Images"].items()):
				if strCategory == "Default":
					continue
				strAnchor = uuid.uuid1().hex
				self.m_oHtmlPage.append("
%s" % (strCategory))
				self.m_oHtmlPage.append("
")
				for (strName, strFile) in sorted(dictImages.items()):

					_, strTail = os.path.split(strFile)
					_, strExt = os.path.splitext(strTail)

					if strName == strImage:
						#strActive = "👀"
						strActive = "

" % (strTail, strFile)
						TaskModuleEvt(g_oHttpdWorker, "/int/evt.src",
							dictQuery={
								"picture" : [strImage]
							}
						).start()
					else:
						strActive = "👁"

					self.m_oHtmlPage.append(
						"
%s %s [%s]
" % (
							strName, "/image/values.html", strName, strAnchor, strName,
							strActive, strName, strExt.strip(".").upper()))
				self.m_oHtmlPage.append("
")
				self.m_oHtmlPage.append("
")
		self.m_oHtmlPage.extend([
			"
",
			"",
			"
"
		])
		return

class TaskConfigSound(FastTask):

	def __init__(self,
		oWorker,
		oHtmlPage
		):
		super(TaskConfigSound, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		return

	def __str__(self):
		strDesc = "Konfiguration der Klangeinstellungen"
		return  strDesc

	def do(self):
		self.createForm()
		return

	def createForm(self):

		strOut = sdk.getAlsaControlValue("PCM Playback Route")
		strVol = sdk.getAlsaControlValue("PCM Playback Volume")

		#
		# (x% / 100%) = (y + 10239) / 10639
		#
		# x% = 100% * (y + 10239) / 10639
		# ===============================
		#
		# (y + 10239) = 10639 * (x% / 100%)
		#
		# y = (10639 * (x% / 100%)) - 10239
		# =================================
		#

		nVol = int(100 * (int(strVol) + 10239) / 10639)

		self.m_oHtmlPage.createBox(
			"Konfiguration Klangausgabe",
			"Hier können Schnittstelle und Lautstärke für die Klangausgabe eingestellt werden.",
			bClose = False)
		self.m_oHtmlPage.openForm()
		self.m_oHtmlPage.appendForm(
			"SoundOutput",
			strInput = strOut,
			strTitle = "Ausgang",
			dictChoice={
				"Automatisch"	: "0",
				"Klinke analog"	: "1",
				"HDMI"			: "2"})
		self.m_oHtmlPage.appendForm(
			"SoundVolume",
			strInput = str(nVol),
			strTitle = "Lautstärke",
			strTextType = "range",
			strTypePattern = "min=\"0\" max=\"100\"")
		self.m_oHtmlPage.closeForm()
		self.m_oHtmlPage.closeBox()
		return

class TaskStartPage(FastTask):

	def __init__(self,
		oWorker,
		oHtmlPage,
		dictForm=None,
		dictQuery=None
		):
		super(TaskStartPage, self).__init__(oWorker)
		self.m_oHtmlPage = oHtmlPage
		self.m_dictForm = dictForm
		self.m_dictQuery = dictQuery
		return

	def __str__(self):
		strDesc = "Darstellen der Startseite"
		return  strDesc

	def do(self):
		secEdt = ""
		artEdt = ""
		btnEdt = ""

		self.m_oHtmlPage.setTitle("Startseite")
		if (not globs.s_oStartPage):
			globs.s_oStartPage = StartPage()

		bEdit = ("edit" in self.m_dictQuery and self.m_dictQuery["edit"][0] == "startpage")

		if self.m_dictForm:
			if "del" in self.m_dictForm:
				self.doDelete(self.m_dictForm["del"][0])
			elif "submit" in self.m_dictForm:
				self.doSubmit()
			elif ("btnAdd" in self.m_dictForm
				and "sec" in self.m_dictForm
				and "art" in self.m_dictForm):
				btnEdt = self.m_dictForm["btnAdd"][0]
				artEdt = self.m_dictForm["art"][0]
				secEdt = self.m_dictForm["sec"][0]
				self.doSubmit()
				self.doAdd(addBtn=btnEdt)
				bEdit = False
			elif ("btnEdt" in self.m_dictForm
				and "sec" in self.m_dictForm
				and "art" in self.m_dictForm):
				btnEdt = self.m_dictForm["btnEdt"][0]
				artEdt = self.m_dictForm["art"][0]
				secEdt = self.m_dictForm["sec"][0]
				self.doSubmit()
				bEdit = False
		if self.m_dictQuery:
			if "secEdt" in self.m_dictQuery:
				secEdt = self.m_dictQuery["secEdt"][0]
			if ("artEdt" in self.m_dictQuery
				and "sec" in self.m_dictQuery):
				secEdt = self.m_dictQuery["sec"][0]
				artEdt = self.m_dictQuery["artEdt"][0]
			if "secAdd" in self.m_dictQuery:
				secEdt = self.m_dictQuery["secAdd"][0]
				self.doAdd(addSec=secEdt)
			if ("artAdd" in self.m_dictQuery
				and "sec" in self.m_dictQuery):
				secEdt = self.m_dictQuery["sec"][0]
				artEdt = self.m_dictQuery["artAdd"][0]
				self.doAdd(addArt=artEdt)

		globs.s_oStartPage.writeToPage(self.m_oHtmlPage, bEdit, secEdt, artEdt, btnEdt)
		return

	def doAdd(self, addSec="", addArt="", addBtn=""):

		strSec = ""
		strArt = ""

		globs.dbg(
			"doAdd(addSec=\"%s\", addArt=\"%s\", addBtn=\"%s\")" % (
			addSec, addArt, addBtn))

		if addBtn and self.m_dictForm:
			if "sec" in self.m_dictForm:
				strSec = self.m_dictForm["sec"][0]
				if "art" in self.m_dictForm:
					strArt = self.m_dictForm["art"][0]
		elif addSec:
			oSection = Section()
			oSection.setSection(addSec, True)
			globs.s_oStartPage.update({addSec : oSection})
			return
		elif (addArt and self.m_dictQuery
			and "sec" in self.m_dictQuery):
			strSec = self.m_dictQuery["sec"][0]
		else:
			return

		for (strName, oSection) in globs.s_oStartPage.items():
			if strName == strSec:
				if addArt:
					oArticle = Article()
					oArticle.setArticle(addArt)
					oSection.update({addArt : oArticle})
					return
				for (strName, oArticle) in oSection.items():
					if strName == strArt:
						if addBtn:
							oButton = Button()
							oButton.setButton(addBtn)
							oArticle.update({addBtn : oButton})
						return
				return
		return

	def doDelete(self, strType):

		strSec = ""
		strArt = ""
		strBtn = ""

		if "sec" in self.m_dictForm:
			strSec = self.m_dictForm["sec"][0]
			if "art" in self.m_dictForm:
				strArt = self.m_dictForm["art"][0]
				if "btn" in self.m_dictForm:
					strBtn = self.m_dictForm["btn"][0]
		else:
			return False

		globs.dbg(
			"doDelete(strType=\"%s\"): strSec=\"%s\", strArt=\"%s\", strBtn=\"%s\"" % (
			strType, strSec, strArt, strBtn))

		for (strName, oSection) in globs.s_oStartPage.items():
			if strName == strSec:
				if strType == "sec":
					globs.s_oStartPage.pop(strSec)
					return True
				elif strArt:
					for (strName, oArticle) in oSection.items():
						if strName == strArt:
							if strType == "art":
								oSection.pop(strArt)
								return True
							elif strBtn:
								for (strName, _) in oArticle.items():
									if strName == strBtn:
										if strType == "btn":
											oArticle.pop(strBtn)
											return True
										return False
							return False
				return False
		return False

	def doSubmit(self):

		strSec = ""
		strArt = ""
		strBtn = ""

		if "sec" in self.m_dictForm:
			strSec = self.m_dictForm["sec"][0]
			strType = "sec"
			if "art" in self.m_dictForm:
				strArt = self.m_dictForm["art"][0]
				strType = "art"
				if "btn" in self.m_dictForm:
					strBtn = self.m_dictForm["btn"][0]
					strType = "btn"
		else:
			return

		for (strName, oSection) in globs.s_oStartPage.items():
			if strName == strSec:
				if strType == "sec":
					self.doSubmitSection(oSection)
				elif strArt:
					for (strName, oArticle) in oSection.items():
						if strName == strArt:
							if strType == "art":
								self.doSubmitArticle(oArticle)
							elif strBtn:
								for (strName, oButton) in oArticle.items():
									if strName == strBtn:
										if strType == "btn":
											self.doSubmitButton(oButton)
										return
							return
				return
		return

	def doSubmitSection(self, oSection):
		if ("secPrimary" in self.m_dictForm
			and self.m_dictForm["secPrimary"][0] == "primary"):
			oSection.m_bPrimary = True
		else:
			oSection.m_bPrimary = False
		return

	def doSubmitArticle(self, oArticle):
		if "artTitle" in self.m_dictForm:
			oArticle.m_strTitle = self.m_dictForm["artTitle"][0]
		if "artContent" in self.m_dictForm:
			oArticle.m_strMsg = self.m_dictForm["artContent"][0]
		if "artType" in self.m_dictForm:
			oArticle.m_strType = self.m_dictForm["artType"][0]
		return

	def doSubmitButton(self, oButton):
		if "btnTitle" in self.m_dictForm:
			oButton.m_strTitle = self.m_dictForm["btnTitle"][0]
		if "btnHRef" in self.m_dictForm:
			oButton.m_strHRef = self.m_dictForm["btnHRef"][0]
		if "btnIcon" in self.m_dictForm:
			oButton.m_strIcon = self.m_dictForm["btnIcon"][0]
		if "btnColor" in self.m_dictForm:
			oButton.m_strColor = self.m_dictForm["btnColor"][0]
		if "btnRedirect" in self.m_dictForm:
			oButton.m_strRedirect = self.m_dictForm["btnRedirect"][0]
		return

class Httpd:

	def __init__(self, oWorker):
		global g_oHttpdWorker

		globs.signalCriticalActivity()
		g_oHttpdWorker = oWorker
		return

	##
	#  @brief
	#  Startet den Web-Server und kehrt erst nach dessen Terminierung zurück.
	#
	#  @param [in] self
	#  				die Httpd-Instanz
	#
	#  @details
	#  Bevor der Web-Server gestartet wird, wechselt die Routine das Arbeitsverzeichnis in das
	#  Wurzelverzeichnis des Web-Servers. Das Wurzelverzeichnis des Web-Servers setzt sich
	#  zusammen aus `[@ref installdir]/www`.
	#
	#
	def run(self):
		strHttpDir = os.path.join(globs.s_strBasePath, "www")
		if (os.path.isdir(strHttpDir)):
			# Das Arbeitsverzeichnis muss das "www"-Verzeichnis sein
			os.chdir(strHttpDir)
			# Web-Server instanziieren
			globs.s_oHttpd = BerryHttpServer((
				globs.getSetting("System", "strHttpIp",
					"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}",
					"0.0.0.0"),
				globs.getSetting("System", "nHttpPort",
					"\\d{1,5}",
					8081)),
				BerryHttpHandler)
			# Web-Server starten
			print("Running HTTP Server on %s at %s ..." % (
				globs.s_oHttpd.server_name, globs.s_oHttpd.server_address))
			globs.s_oHttpd.serve_forever()
			# Ressourcen des Web-Servers freigeben
			globs.s_oHttpd.server_close()
		else:
			globs.err("Arbeitsverzeichnis des Web-Servers nicht gefunden: " +
				strHttpDir)
		return

class BerryHttpHandler(SimpleHTTPRequestHandler):

	def installModule(self, strPath, dictForm):
		if ("ModuleFile" not in dictForm or
			not dictForm["ModuleFile"][0].filename or
			not dictForm["ModuleFile"][0].file):
			oHtmlPage = HtmlPage(strPath, strTitle = "Modulinstallation")
			oHtmlPage.createBox(
				"Unvollständige Eingabe",
				"Bitte geben Sie die zu installierende Python3 Moduldatei an.",
				strType="warning")
			return oHtmlPage
		oHtmlPage = HtmlPage("/system/modules.html", strTitle = "Modulkonfiguration")
		oModuleFile = dictForm["ModuleFile"][0]
		strFilename = oModuleFile.filename
		strFilename = strFilename.replace("\\", "/")
		_, strFilename = os.path.split(strFilename)
		strModule, strExt = os.path.splitext(strFilename)
		if not re.match("\\.[Pp][Yy]", strExt):
			oHtmlPage = HtmlPage(strPath, strTitle = "Modulinstallation")
			oHtmlPage.createBox(
				"Unzulässige Eingabe",
				"Die Datei '%s' ist keine unterstützte Python3 Datei." % (strFilename) +
				"Bitte geben Sie eine Python3 Moduldatei (*.py) an.",
				strType="warning")
			return oHtmlPage
		try:
			foFile = open("%s/%s" % (
				globs.s_strModulePath, strFilename), "w")
			oData = oModuleFile.file.read()
			foFile.write(oData.decode())
			foFile.close()
			del oData
		except:
			globs.exc("Schreiben der Moduldatei %s/%s" % (
				globs.s_strModulePath, strFilename))
			oHtmlPage.createBox(
				"Fehler",
				"Die hochgeladene Moduldatei '%s'" % (strFilename) +
				"konnte nicht in '%s' gespeichert werden." % (globs.s_strModulePath),
				strType="error")
			return oHtmlPage
		# Tasks ausführen
		TaskInstallModule(g_oHttpdWorker, strModule).start()
		TaskModuleInit(g_oHttpdWorker, strModule).start()
		TaskSaveSettings(g_oHttpdWorker).start()
		oTask = TaskDisplayModules(g_oHttpdWorker, oHtmlPage)
		if oTask.start():
			oTask.wait()
		return oHtmlPage

	def installSound(self, strPath, dictForm):
		if ("SoundFile" not in dictForm):
			oHtmlPage = HtmlPage(strPath, strTitle = "Klanginstallation")
			oHtmlPage.createBox(
				"Unvollständige Eingabe",
				"Es muss mindestens eine Klang- oder Zip-Datei angegeben werden.",
				strType="warning")
			return oHtmlPage
		oHtmlPage = HtmlPage("/sound/values.html", strTitle = "Installierte Klänge")

		try:
			for oSoundFile in dictForm["SoundFile"]:
				if (not oSoundFile.filename or
					not oSoundFile.file):
					oHtmlPage = HtmlPage(strPath, strTitle = "Klanginstallation")
					oHtmlPage.createBox(
						"Unvollständige Eingabe",
						"Es konnte keine gültige Klang- oder Zip-Datei empfangen werden.",
						strType="warning")
					return oHtmlPage
				if not self.processSoundFile(oSoundFile):
					oHtmlPage = HtmlPage(strPath, strTitle = "Klanginstallation")
					oHtmlPage.createBox(
						"Unzulässige Eingabe",
						"Bitte verwenden Sie nur zulässige Klangdateien (*.wav, *.mp3).",
						strType="warning")
					return oHtmlPage
		except Exception as ex:
			globs.exc("Verarbeiten der Klangdatei %s" % (
				oSoundFile.filename))
			oHtmlPage.createBox(
				"Fehler",
				"Die empfangene Datei '%s' " % (oSoundFile.filename) +
				"konnte nicht verarbeitet werden (%s)." % (ex),
				strType="error")
			return oHtmlPage
		# Tasks ausführen
		globs.scanSoundFiles(bRescan=True)
		oTask = TaskDisplaySounds(g_oHttpdWorker, oHtmlPage)
		if oTask.start():
			oTask.wait()
		return oHtmlPage

	def installImage(self, strPath, dictForm):
		if ("ImageFile" not in dictForm):
			oHtmlPage = HtmlPage(strPath, strTitle = "Bildinstallation")
			oHtmlPage.createBox(
				"Unvollständige Eingabe",
				"Es muss mindestens eine Bild- oder Zip-Datei angegeben werden.",
				strType="warning")
			return oHtmlPage
		oHtmlPage = HtmlPage("/image/values.html", strTitle = "Installierte Bilder")

		try:
			for oImageFile in dictForm["ImageFile"]:
				if (not oImageFile.filename or
					not oImageFile.file):
					oHtmlPage = HtmlPage(strPath, strTitle = "Bildinstallation")
					oHtmlPage.createBox(
						"Unvollständige Eingabe",
						"Es konnte keine gültige Bild- oder Zip-Datei empfangen werden.",
						strType="warning")
					return oHtmlPage
				if not self.processImageFile(oImageFile):
					oHtmlPage = HtmlPage(strPath, strTitle = "Bildinstallation")
					oHtmlPage.createBox(
						"Unzulässige Eingabe",
						"Bitte verwenden Sie nur zulässige Bilddateien (*.gif, *.jpeg, *.bmp, *.png).",
						strType="warning")
					return oHtmlPage
		except Exception as ex:
			globs.exc("Verarbeiten der Bilddatei %s" % (
				oImageFile.filename))
			oHtmlPage.createBox(
				"Fehler",
				"Die empfangene Datei '%s' " % (oImageFile.filename) +
				"konnte nicht verarbeitet werden (%s)." % (ex),
				strType="error")
			return oHtmlPage
		# Tasks ausführen
		globs.scanImageFiles(bRescan=True)
		oTask = TaskDisplayImages(g_oHttpdWorker, oHtmlPage)
		if oTask.start():
			oTask.wait()
		return oHtmlPage

	def configSound(self, strPath, dictForm):
		oHtmlPage = HtmlPage(strPath, strTitle = "Klangeinstellungen")
		if ("submit" not in dictForm):
			oHtmlPage.createBox(
				"Fehler",
				"Im Formular fehlt die Angabe der auszuführenden Aktion (Parameter \"submit\").",
				strType="error")
			return oHtmlPage
		strAction = dictForm["submit"][0]
		if strAction == "save":
			if (("SoundOutput" not in dictForm)
				or (int(dictForm["SoundOutput"][0]) not in [0, 1, 2])
				or ("SoundVolume" not in dictForm)
				or not (0 <= int(dictForm["SoundVolume"][0]) <= 100)):
				oHtmlPage.createBox(
					"Achtung",
					"Bei der Konfiguration der Klangeinstellungen wurden ungültige Werte übermittelt.",
					strType="error")
				return oHtmlPage

			strOut = str(dictForm["SoundOutput"][0])
			strVol = str(dictForm["SoundVolume"][0])

			#nVol = int((10639 * (int(strVol) / 100)) - 10239)

			print("Out = " + strOut + ", Vol = " + strVol + "%")

			bOkOut = sdk.setAlsaControlValue("PCM Playback Route", strOut)
			bOkVol = sdk.setAlsaControlValue("PCM Playback Volume", strVol + "%")

			if (not bOkOut):
				oHtmlPage.createBox(
					"Achtung",
					"Die Einstellung ('"+strOut+"') für den Audio-Ausgang konnte nicht übernommen werden.",
					strType="error")
				return oHtmlPage

			if (not bOkVol):
				oHtmlPage.createBox(
					"Achtung",
					"Die Einstellung ('"+strVol+"') für die Lautstärke konnte nicht übernommen werden.",
					strType="error")
				return oHtmlPage

			print("Output="+strOut+", Volume="+strVol)

		oTask = TaskConfigSound(g_oHttpdWorker, oHtmlPage)
		if oTask.start():
			oTask.wait()
		return oHtmlPage

	def processSoundFile(self, oSoundFile):
		strSoundPath = globs.getSetting("System", "strUsrSoundLocation",
			varDefault=globs.s_strSoundPath)
		strFilename = oSoundFile.filename
		strFilename = strFilename.replace("\\", "/")
		_, strFilename = os.path.split(strFilename)
		strName, strExt = os.path.splitext(strFilename)
		bResult = False
		oData = oSoundFile.file.read()
		globs.log("Data: %r" % (oData))
		if (zipfile.is_zipfile(BytesIO(oData))):
			strDirname = os.path.join(strSoundPath, strName)
			if not (os.path.isdir(strDirname) or os.path.islink(strDirname)):
				os.mkdir(strDirname)
			with ZipFile(BytesIO(oData), "r") as oZipFile:
				for oZipInfo in (oZipFile.infolist()):
					foFile = oZipFile.open(oZipInfo)
					_, strFilename = os.path.split(oZipInfo.filename)
					if not re.match("\\.[Ww][Aa][Vv]|\\.[Mm][Pp]3", strExt):
						continue
					self.installSoundFile(foFile, os.path.join(strDirname, strFilename))
					bResult = True
		elif not re.match("\\.[Ww][Aa][Vv]|\\.[Mm][Pp]3", strExt):
			del oData
			return False
		else:
			self.installSoundFile(BytesIO(oData), os.path.join(strSoundPath, strFilename))
			bResult = True
		del oData
		return bResult

	def installSoundFile(self, foSource, strFilename):
		globs.log("Klangdatei installieren %r, %r" % (foSource, strFilename))
		foFile = open(strFilename, "wb")
		oData = foSource.read()
		globs.log("Inhalt Klangdatei: %r, Original: %r" % (oData, foSource))
		foFile.write(oData)
		foFile.close()
		del oData
		return

	def processImageFile(self, oImageFile):
		strImagePath = globs.getSetting("System", "strUsrImageLocation",
			varDefault=globs.s_strImagePath)
		strFilename = oImageFile.filename
		strFilename = strFilename.replace("\\", "/")
		_, strFilename = os.path.split(strFilename)
		strName, strExt = os.path.splitext(strFilename)
		bResult = False
		oData = oImageFile.file.read()
		globs.log("Data: %r" % (oData))
		if (zipfile.is_zipfile(BytesIO(oData))):
			strDirname = os.path.join(strImagePath, strName)
			if not (os.path.isdir(strDirname) or os.path.islink(strDirname)):
				os.mkdir(strDirname)
			with ZipFile(BytesIO(oData), "r") as oZipFile:
				for oZipInfo in (oZipFile.infolist()):
					foFile = oZipFile.open(oZipInfo)
					_, strFilename = os.path.split(oZipInfo.filename)
					if (not re.match(r"\.([Gg][Ii][Ff]|[Jj][Pp][Ee][Gg]|[Bb][Mm][Pp]|[Pp][Nn][Gg])", strExt)
						or not re.match("gif|jpeg|bmp|png", imghdr.what(foFile))):
						continue
					foFile = oZipFile.open(oZipInfo)
					self.installImageFile(foFile, os.path.join(strDirname, strFilename))
					bResult = True
		elif (not re.match(r"\.([Gg][Ii][Ff]|[Jj][Pp][Ee][Gg]|[Bb][Mm][Pp]|[Pp][Nn][Gg])", strExt)
			or not re.match("gif|jpeg|bmp|png", imghdr.what(BytesIO(oData)))):
			del oData
			return False
		else:
			self.installImageFile(BytesIO(oData), os.path.join(strImagePath, strFilename))
			bResult = True
		del oData
		return bResult

	def installImageFile(self, foSource, strFilename):
		globs.log("Bilddatei installieren %r, %r" % (foSource, strFilename))
		foFile = open(strFilename, "wb")
		oData = foSource.read()
		globs.log("Inhalt Bilddatei: %r, Original: %r" % (oData, foSource))
		foFile.write(oData)
		foFile.close()
		del oData
		return

	def changeModules(self, strPath, dictForm):
		oHtmlPage = HtmlPage(strPath, strTitle = "Modulkonfiguration")
		if ("submit" not in dictForm):
			oHtmlPage.createBox(
				"Fehler",
				"Im Formular fehlt die Angabe der auszuführenden Aktion (Parameter \"submit\").",
				strType="error")
			return oHtmlPage
		if ("target" not in dictForm):
			oHtmlPage.createBox(
				"Achtung",
				"Für den auszuführenden Vorgang müssen zunächst Objekte ausgewählt werden.",
				strType="warning")
			return oHtmlPage
		strAction = dictForm["submit"][0]
		if strAction == "save":
			if ("ModuleClass" not in dictForm):
				oHtmlPage.createBox(
					"Achtung",
					"Es muss ein gültiger Bezeichner für die Hauptklasse des Moduls angegeben werden.",
					strType="error")
				return oHtmlPage
			strTarget = dictForm["target"][0]
			# Modul installieren
			TaskInstallModule(g_oHttpdWorker, strTarget).start()
			TaskModuleInit(g_oHttpdWorker, strTarget).start()
			TaskSaveSettings(g_oHttpdWorker).start()
		elif strAction == "enable" or strAction == "disable":
			lstTarget = dictForm["target"]
			bEnable = True
			if strAction == "disable":
				bEnable = False
			# Module aktivieren oder deaktivieren
			for strTarget in lstTarget:
				TaskEnableModule(g_oHttpdWorker, strTarget, bEnable).start()
				TaskModuleInit(g_oHttpdWorker, strTarget).start()
			TaskSaveSettings(g_oHttpdWorker).start()
		elif strAction == "delete":
			lstTarget = dictForm["target"]
			for strTarget in lstTarget:
				TaskRemoveModule(g_oHttpdWorker, strTarget).start()
				TaskModuleInit(g_oHttpdWorker, strTarget).start()
			TaskSaveSettings(g_oHttpdWorker).start()
		else:
			oHtmlPage.createBox(
				"Achtung",
				"Der angeforderte Vorgang wird (noch) nicht unterstützt.",
				strType="warning")
			return oHtmlPage
		oTask = TaskDisplayModules(g_oHttpdWorker, oHtmlPage)
		if oTask.start():
			oTask.wait()
		return oHtmlPage

	def serveGet(self,
		strPath,
		strRedirect=None,
		dictForm=None,
		dictQuery=None
		):
		try:
			oFutureTask = None
			oHtmlPage = None

			oHtmlPage = self.doCommand(strPath,
				dictForm=dictForm,
				dictQuery=dictQuery)
			if strRedirect:
				strPath = strRedirect
				oHtmlPage = None

			globs.log("serveGet: '%s', redirect='%s' -> %s" % (strPath, strRedirect, oHtmlPage))

			if (not oHtmlPage):
				if strPath == "/system/settings.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskDisplaySettings(g_oHttpdWorker,
						oHtmlPage,
						dictQuery=dictQuery,
						dictForm=dictForm)
				elif strPath == "/system/values.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskDisplaySystem(g_oHttpdWorker,
						oHtmlPage,
						dictQuery=dictQuery,
						dictForm=dictForm)
				elif strPath == "/system/modules.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskDisplayModules(g_oHttpdWorker,
						oHtmlPage,
						dictQuery=dictQuery)
				elif strPath == "/system/logging.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskDisplayLogMem(g_oHttpdWorker,
						oHtmlPage,
						dictQuery=dictQuery,
						dictForm=dictForm)
				elif strPath == "/system/startpage.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskStartPage(g_oHttpdWorker,
						oHtmlPage,
						dictQuery=dictQuery,
						dictForm=dictForm)
				elif strPath == "/sound/values.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskDisplaySounds(g_oHttpdWorker,
						oHtmlPage,
						dictQuery=dictQuery,
						dictForm=dictForm)
				elif strPath == "/sound/config.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskConfigSound(g_oHttpdWorker,
						oHtmlPage)
				elif strPath == "/image/values.html":
					oHtmlPage = HtmlPage(strPath)
					oFutureTask = TaskDisplayImages(g_oHttpdWorker,
						oHtmlPage,
						dictQuery=dictQuery,
						dictForm=dictForm)
				elif re.match(r"^/image/.*\.([Gg][Ii][Ff]|[Jj][Pp][Ee][Gg]|[Bb][Mm][Pp]|[Pp][Nn][Gg])$", strPath):
					oHtmlPage = ImageObject(strPath)
					print("ImageObject: %r" % (oHtmlPage))
					pass

			if oFutureTask:
				if oFutureTask.start():
					oFutureTask.wait()
				else:
					oHtmlPage = None

			if not oHtmlPage:
				SimpleHTTPRequestHandler.do_GET(self)
				return
		except:
			globs.exc("HTTP GET Version %s: Client '%s' (%s), Path '%s'" % (
				self.request_version, self.client_address, self.address_string(),
				strPath))
			oHtmlPage = HtmlPage(strPath, strTitle = "Fehler")
			oHtmlPage.createBox(
				"Fehler",
				"Die Anforderung (GET) wurde wegen Fehlern abgebrochen.",
				strType="error")
		# HTML-Seite absenden
		self.send_response(200)
		self.end_headers()
		self.wfile.write(oHtmlPage.getContent())
		return

	def servePost(self, strPath, oForm, dictQuery = None):
		oHtmlPage = None
		try:
			# Formulardaten aufbereiten
			dictForm = {}
			for oKey in oForm.keys():
				oItem = oForm[oKey]
				self.buildParams(oForm, oKey, oItem, dictForm)

			if strPath == "/system/install.html":
				oHtmlPage = self.installModule(strPath, dictForm)
			elif strPath == "/sound/install.html":
				oHtmlPage = self.installSound(strPath, dictForm)
			elif strPath == "/sound/config.html":
				oHtmlPage = self.configSound(strPath, dictForm)
			elif strPath == "/image/install.html":
				oHtmlPage = self.installImage(strPath, dictForm)
			elif strPath == "/system/modules.html":
				oHtmlPage = self.changeModules(strPath, dictForm)

			if not oHtmlPage:
				self.serveGet(strPath, dictForm=dictForm, dictQuery=dictQuery)
				return
		except:
			globs.exc("HTTP POST Version %s: Client '%s' (%s), Path '%s'" % (
				self.request_version, self.client_address, self.address_string(),
				strPath))
			oHtmlPage = HtmlPage(strPath, strTitle = "Fehler")
			oHtmlPage.createBox(
				"Fehler",
				"Die Anforderung (POST) wurde wegen Fehlern abgebrochen.",
				strType="error")
		# HTML-Seite absenden
		self.send_response(200)
		self.end_headers()
		self.wfile.write(oHtmlPage.getContent())
		return

	def buildParams(self,
		oForm,
		oKey,
		oItem,
		dictForm):

		if (oKey not in dictForm):
			dictForm.update({oKey : []})

		try:
			if oItem.file and oItem.filename:
				dictForm[oKey].append(oItem)
				return
		except:
			pass

		if (type(oItem) is list):
			for oSubItem in oItem:
				self.buildParams(oForm, oKey, oSubItem, dictForm)
			return

		dictForm[oKey].extend(oForm.getlist(oKey))
		return

	def doCommand(self,
		strPath,
		dictForm=None,
		dictQuery=None
		):
		globs.dbg("doCommand(strPath=%s, dictForm=%r, dictQuery=%r)" % (
			strPath, dictForm, dictQuery))
		# Built-in sound and speak command processing for GET and POST
		if (dictQuery and "sound" in dictQuery):
			for strArg in dictQuery["sound"]:
				TaskSound(g_oHttpdWorker, strArg).start()
		if (dictForm and "sound" in dictForm):
			for strArg in dictForm["sound"]:
				TaskSound(g_oHttpdWorker, strArg).start()
		if (dictQuery and "speak" in dictQuery):
			for strArg in dictQuery["speak"]:
				TaskSpeak(g_oHttpdWorker, strArg).start()
		if (dictForm and "speak" in dictForm):
			for strArg in dictForm["speak"]:
				TaskSpeak(g_oHttpdWorker, strArg).start()
		# External timer event from cron-job
		if (re.match("/ext/evt\\.src", strPath)
			and dictQuery
			and "timer" in dictQuery
			and dictQuery["timer"]
			and dictQuery["timer"][0] == "cron"):
			TaskModuleEvt(g_oHttpdWorker, strPath, dictQuery=dictQuery).start()
			return HtmlPage(strPath)
		# System or program termination
		if (re.match("/exit/(exit|halt|boot)\\.html", strPath)
			and dictQuery
			and "exit" in dictQuery
			and dictQuery["exit"]):
			TaskExit(g_oHttpdWorker, dictQuery["exit"][0]).start()
			return None
		globs.log("doCommand: '%s'" % strPath)
		# Commands being passed to installed modules
		if (re.match("/modules/.+\\.(cmd|htm|html)", strPath)):
			globs.log("Modulkommando: '%s'" % strPath)
			oHtmlPage = HtmlPage(strPath)
			oFutureTask = TaskModuleCmd(g_oHttpdWorker,
				strPath,
				oHtmlPage,
				dictForm=dictForm,
				dictQuery=dictQuery)
			if oFutureTask.start():
				oFutureTask.wait()
			else:
				oHtmlPage = None
			return oHtmlPage
		return None

	def do_HEAD(self):
		self.send_response(200)
		self.send_header('Content-type', 'text/html')
		self.end_headers()
		return

	def do_AUTHHEAD(self):
		self.send_response(401)
		self.send_header('WWW-Authenticate', 'Basic realm=\"RasPyWeb\"')
		self.send_header('Content-type', 'text/html')
		self.end_headers()
		return

	def do_GET(self):
		# Webserver-Aktivität signalisieren
		globs.signalCriticalActivity()
		# Die angeforderten Informationen auswerten
		oParsedPath = urlparse(self.path)
		strPath = oParsedPath.path
		dictQuery = {}
		for (p, v) in parse_qsl(oParsedPath.query):
			p = p.strip()
			if p not in dictQuery:
				dictQuery.update({p : []})
			dictQuery[p].append(v.strip())
		strRedirect = None
		# HTTP-Server redirect commands
		if ("redirect2" in dictQuery):
			strRedirect = globs.getRedirect(dictQuery["redirect2"][0], strPath)
		# Angeforderten Inhalt liefern
		self.serveGet(strPath,
			strRedirect=strRedirect,
			dictQuery = dictQuery)
		return

	def do_POST(self):
		# Webserver-Aktivität signalisieren
		globs.signalCriticalActivity()
		# URL analysieren
		oParsedPath = urlparse(self.path)
		strPath = oParsedPath.path
		# Die angeforderten Informationen (Query) auswerten
		dictQuery = {}
		for (p, v) in parse_qsl(oParsedPath.query):
			p = p.strip()
			if p not in dictQuery:
				dictQuery.update({p : []})
			dictQuery[p].append(v.strip())
		# Die angeforderten Informationen (Formular) auswerten
		oForm = cgi.FieldStorage(
			fp=self.rfile,
			headers=self.headers,
			environ={'REQUEST_METHOD':'POST',
				'CONTENT_TYPE':self.headers['Content-Type'],
				})
		# Das abgesendete Formular verarbeiten
		self.servePost(strPath, oForm, dictQuery=dictQuery)
		return

class BerryHttpServer(ThreadingMixIn, HTTPServer):
	"""Handle requests in a separate thread."""
	pass


n,c = input().split()
n = int(n)
c = int(c)
quantosTemDentro = 0
sobrepeso = False
for i in range(0,n):
	s,e= input().split()
	s = int(s)
	e = int(e)
	quantosTemDentro -= s
	quantosTemDentro += e
	if quantosTemDentro > c:
		sobrepeso = True

if sobrepeso == True:
	print('S')
else:
	print('N')


from waflib.Configure import conf

def options(opt):
  opt.add_option('--simple_nn', type='string', help='path to simple_nn', dest='simple_nn')

@conf
def check_simple_nn(conf, *k, **kw):
    def fail(msg, required):
        if required:
            conf.fatal(msg)
        conf.end_msg(msg, 'RED')
    def get_directory(filename, dirs):
        res = conf.find_file(filename, dirs)
        return res[:-len(filename)-1]

    required = kw.get('required', False)
    includes_check = ['/usr/local/include', '/usr/include']

    if conf.options.simple_nn:
        includes_check = [conf.options.simple_nn + '/include']
    try:
        conf.start_msg('Checking for simple_nn includes')
        incl = get_directory('simple_nn/neural_net.hpp', includes_check)
        incl = get_directory('simple_nn/layer.hpp', includes_check)
        conf.end_msg(incl)
        conf.env.INCLUDES_SIMPLE_NN = [incl]
    except:
        fail('Not found', required)
        return
    return 1


import pytz, datetime
import pandas as pd
import data

class bro:
    def __init__(self, timeslots, timezone):
        self.timeslots = timeslots
        self.timezone = pytz.timezone(timezone)
        self.gmt_offset = self.timezone.utcoffset(self.timeslots[0][0])
        self.gmt_timedelta = self.convert_to_GMT()
        self.nativezonetime = []
        print("User Created successful.")

    def set(self, timeslots, timezone):
        self.timeslots = timeslots
        self.timezone = pytz.timezone(timezone)
        self.gmt_offset = self.timezone.utcoffset(self.timeslots[0][0])
        self.gmt_timedelta = self.convert_to_GMT()
        self.nativezonetime = []

    def convert_to_GMT(self):
        gmt_list = []
        for timeslot in self.timeslots:
            #print(timeslot)
            list_of_datetime = []
            for n in timeslot:
                list_of_datetime.append(
                    (n - self.gmt_offset) - datetime.datetime(2019, 12, 2, 13, 00)
                    )
            gmt_list.append(list_of_datetime)
        return pd.DataFrame(gmt_list, columns=['Lowerlimit', 'Upperlimit']).sort_values(["Lowerlimit"], ascending=True)

    def convert_from_GMT(self, lower, upper):
            self.nativezonetime.append(
                [
                    (datetime.datetime(2019, 12, 2, 13, 00) + lower + self.gmt_offset ),
                    (datetime.datetime(2019, 12, 2, 13, 00) + upper + self.gmt_offset )
                ]
            )
            print("In GMT Convert")


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jun  4 23:35:11 2022
This is a supplementary script that visualizes a 3D cubic lattice, using data from main.py
@author: mrahm32
"""

import itertools
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import color as color

def generate_fig_parameters(cluster_num):
    colors = ["#b6174b","#cbf3d2","#ff0000", "#4e937a","#ffcc00", "#7b287d","#37c871", "#b7c0ee","#0066ff", "#957DAD"]
    colors = ['#A5be00', '#d81e5b','#331832', '#b892ff', '#29bf12','#08bdbd', '#ff9914', '#17bebb', '#003049', '#2a0800', '#314cb6', "#b6174b","#cbf3d2","#ff0000", "#4e937a","#ffcc00", "#7b287d","#37c871", "#b7c0ee","#0066ff", "#957DAD"]
    color_gradient = color.polylinear_gradient(colors,n=10)
    color_gradient = color_gradient['hex']
    markers = ['o', 's', 'v', '*', 'x']
    return colors, markers

def make_vis(networks, L, path):
    '''parameters: networks = dictionary of networks with their labels and 3D coordinates of the members.
    L = dimension of cube of sized L^3
    path = all the points visited in order to determine that there is a spanning network. this is for just
    visual confirmation that there is indeed a path.'''

    path = np.array(path)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    network_num = len(list(networks.keys()))
    colors, markers = generate_fig_parameters(network_num)
    for label in networks.keys():
        if label != 0:
            indx = list(networks.keys()).index(label)-1
            triplets = networks[label]
            triplets = np.array(triplets)
            col = colors[indx]
            ax.scatter(triplets[:,0], triplets[:,1], triplets[:,2], marker = 's', s=150,c = col)
            #ax.scatter(triplets[:,0], triplets[:,1], triplets[:,2], marker = 's', s=150,c ='black')
            ax.scatter(path.T[0],path.T[1],path.T[2], marker = 'o', c='black', s= 100)
            ax.set_xlim3d(0,L-1)
            ax.set_ylim3d(0,L-1)
            ax.set_zlim3d(0,L-1)
    plt.show()
    fig.savefig(f'percolation_network.pdf')


import requests
from datetime import datetime
from os.path import join
from pandas import read_html, read_csv, to_numeric, concat
from glob import glob

class WorldFootball(object):

    def __init__(self, base_path) -> None:
        self.URL = 'https://www.worldfootball.net'
        self.header={'User-Agent': 'Mozilla/5.0'}
        self.base_path = base_path
        self.datasets_raw = []
        self.datasets_processed = []
        self.dataset_curated = None
        self.ultima_temporada = 1
        self.temporada = datetime.now().year

    def run(self, ultima_temporada) -> None:
        print('Rodando ETL')
        self.extract(ultima_temporada=ultima_temporada)
        self.transform(input = 'env')
        self.load(input = 'env')

    def extract(self, ultima_temporada:int = 1, save=True) -> None:
        print('Extracting...', end = ' ')
        self.ultima_temporada=ultima_temporada
        for temporada in range(self.temporada, self.temporada-self.ultima_temporada, -1):

            for rodada in range(1, 39):
                # get raw data
                url = f'{self.URL}/schedule/bra-serie-a-{temporada}-spieltag/{rodada}/'
                r = requests.get(url, headers=self.header)

                dfs = read_html(r.text,header=0)
                file_save = f"df_{temporada}_{rodada}.csv"
                if len(dfs[3]) == 0: # se temporada regular, para o loop na rodada atual
                    break

                # Adicionando variavel para distincao
                df_rw = dfs[3].copy()
                df_rw['temporada'] = temporada

                # Save raw data
                self.datasets_raw.append(df_rw)
                if save:
                    df_rw.to_csv(join(self.base_path, 'data', 'raw', file_save), index = False)
        print('Done!')

    def transform(self, input:str = 'csv') -> None:
        print('Transforming...', end = ' ')
        if input == 'csv' or len(self.datasets_raw) == 0:
            datasets = []
            for csv in glob(join(self.base_path, 'data', 'raw', '*.csv')):
                datasets.append(read_csv(csv))
        else:
            datasets = self.datasets_raw

        for df in datasets:
            df = df.copy()
            df.drop(['Team'], inplace=True, axis=1) # null values, escuto
            df.rename(index=str, columns={'#': 'posicao', 'Team.1': 'time', 'M.': 'rodada', 'W': 'vitoria',
                                            'D': 'empate', 'L': 'derrota', 'goals': 'gols',
                                            'Dif.': 'diferenca_gols', 'Pt.': 'pontos'}, inplace=True)

            df[['gols', 'gols_sofridos']] = df['gols'].str.split(':', n = 1, expand = True)

            df = df.apply(to_numeric, errors='ignore')
            df['posicao'] =  to_numeric(df.index) + 1

            df = df.reindex(columns=[
                'posicao',
                'time',
                'rodada',
                'vitoria',
                'empate',
                'derrota',
                'gols',
                'gols_sofridos',
                'diferenca_gols',
                'pontos',
                'temporada'])
            self.datasets_processed.append(df)
            file_name = f"df_processed_{df['temporada'].iloc[0]}_{df['rodada'].iloc[0]}.csv"
            df.to_csv(join(self.base_path, 'data', 'processed', file_name), index = False)
        print('Done!')

    def load(self, input:str = 'csv') -> None:
        print('loading...', end = ' ')
        if input == 'csv' or len(self.datasets_processed) == 0:
            datasets = []
            for csv in glob(join(self.base_path, 'data', 'processed', '*.csv')):
                datasets.append(read_csv(csv))
        else:
            datasets = self.datasets_processed

        self.dataset_curated = concat(datasets)
        file_name = f'df_curated.csv'
        self.dataset_curated.to_csv(join(self.base_path, 'data', 'curated', file_name), index = False)
        print('Done!')


from pygame import Vector2
from content.space_objs.space_obj import SpaceObj
from content.online.obj_msg import ObjMsg

class Bullet(SpaceObj):
    """子弹"""
    COLOR = (0, 0, 0)
    next_id = 0

    def __init__(self, settings,
                 loc0: Vector2 = Vector2(0, 0), spd0: Vector2 = Vector2(0, 0),
                 bullet_id=-1):
        super().__init__(settings, 1*loc0, 1*spd0)
        self.radius = settings.bullet_radius  # 子弹半径
        self.damage = settings.bullet_damage  # 子弹伤害
        self.id = Bullet.next_id
        if bullet_id >= 0:
            self.id = bullet_id
        Bullet.next_id = self.id + 1

    @staticmethod
    def init(settings):
        Bullet.COLOR = settings.bullet_color
        Bullet.next_id = 0

    def __get_image__(self, settings):
        return settings.bullet_image

    def display(self, camera):
        super().display(camera)
        camera.draw_dot(Vector2(self.rect.center), Bullet.COLOR)

    def get_e_d_m(self, planets, center_v: Vector2):
        """获取机械能和质量的比值"""
        ekdm = self.get_ek_d_m(center_v)
        epdm = self.get_ep_d_m(planets)
        return ekdm + epdm

    def check_del(self, planets, ships, center_v: Vector2, max_dis: float) -> bool:
        """检查是否需要删除"""
        min_dis = self.loc.length()  # 所有距离中最小的
        for objs in planets, ships:
            for obj in objs:
                dis = (self.loc-obj.loc).length()  # 这个距离
                if (self.spd-obj.spd)*(obj.loc-self.loc) > 0:  # 如果还在向星球/飞船飞则不删除
                    return False
                if dis < min_dis:
                    min_dis = dis
        edm = self.get_e_d_m(planets, center_v)
        if edm > 0 and min_dis > max_dis\
                or edm > -1e-5 and min_dis > max_dis*2\
                or edm > -1e-2 and min_dis > max_dis*3\
                or min_dis > max_dis*4:
            return True
        return False

    def update_by_msg(self, msg: list, planets):
        super().update_by_msg(msg, planets)
        msg = ObjMsg(msg=msg)
        self.id = msg.id


__all__ = ("Deployments", "Deployment")

from .logger import getLogger
from .ce_adapter import Interface, ContainerState, CEAdapterError, NotFound
import falcon
import json

logger = getLogger(__name__.split(".", 1)[-1])

def reqDebugLog(req):
    logger.debug("method='{}' path='{}' content_type='{}'".format(req.method, req.path, req.content_type))

def reqErrorLog(req, ex):
    logger.error("method='{}' path='{}' - {}".format(req.method, req.path, ex))

class Deployments:
    def __init__(self, ce_adapter: Interface):
        self.__ce_adapter = ce_adapter

    def on_get(self, req: falcon.request.Request, resp: falcon.response.Response):
        reqDebugLog(req)
        try:
            items = self.__ce_adapter.listContainers()
            resp.content_type = falcon.MEDIA_JSON
            resp.body = json.dumps(items)
            resp.status = falcon.HTTP_200
        except NotFound as ex:
            resp.status = falcon.HTTP_404
            reqErrorLog(req, ex)
        except Exception as ex:
            resp.status = falcon.HTTP_500
            reqErrorLog(req, ex)

    def on_post(self, req: falcon.request.Request, resp: falcon.response.Response):
        reqDebugLog(req)
        if not req.content_type == falcon.MEDIA_JSON:
            resp.status = falcon.HTTP_415
        else:
            try:
                data = json.load(req.bounded_stream)
                if data["name"] in self.__ce_adapter.listContainers():
                    raise RuntimeError("container '{}' already exists".format(data["name"]))
                self.__ce_adapter.createContainer(data["name"], data["deployment_configs"], data.get("service_configs"), data.get("runtime_vars"))
                resp.status = falcon.HTTP_200
            except KeyError as ex:
                resp.status = falcon.HTTP_400
                reqErrorLog(req, ex)
            except RuntimeError as ex:
                resp.status = falcon.HTTP_409
                reqErrorLog(req, ex)
            except Exception as ex:
                resp.status = falcon.HTTP_500
                reqErrorLog(req, ex)

class Deployment:
    def __init__(self, ce_adapter: Interface):
        self.__ce_adapter = ce_adapter

    def on_patch(self, req: falcon.request.Request, resp: falcon.response.Response, deployment):
        reqDebugLog(req)
        if not req.content_type == falcon.MEDIA_JSON:
            resp.status = falcon.HTTP_415
        else:
            try:
                data = json.load(req.bounded_stream)
                if data["state"] == ContainerState.running:
                    self.__ce_adapter.startContainer(deployment)
                elif data["state"] == ContainerState.stopped:
                    self.__ce_adapter.stopContainer(deployment)
                else:
                    raise ValueError("unknown state '{}'".format(data["state"]))
                resp.status = falcon.HTTP_200
            except KeyError as ex:
                resp.status = falcon.HTTP_400
                reqErrorLog(req, ex)
            except NotFound as ex:
                resp.status = falcon.HTTP_404
                reqErrorLog(req, ex)
            except Exception as ex:
                resp.status = falcon.HTTP_500
                reqErrorLog(req, ex)

    def on_delete(self, req: falcon.request.Request, resp: falcon.response.Response, deployment):
        reqDebugLog(req)
        try:
            if req.params and "option" in req.params:
                if req.params["option"] == "purge":
                    self.__ce_adapter.removeContainer(deployment, purge=True)
                else:
                    raise RuntimeError("unknown option '{}'".format(req.params["option"]))
            else:
                self.__ce_adapter.removeContainer(deployment, purge=False)
            resp.status = falcon.HTTP_200
        except NotFound as ex:
            resp.status = falcon.HTTP_404
            reqErrorLog(req, ex)
        except RuntimeError as ex:
            resp.status = falcon.HTTP_400
            reqErrorLog(req, ex)
        except Exception as ex:
            resp.status = falcon.HTTP_500
            reqErrorLog(req, ex)


# -*- coding: utf-8 -*

from time import sleep, time
from listpage_crawler import ptt_listpage_crawler
from postpage_crawler import post_crawler
import json

# 取得指定天數的 post list 資料，存成陣列
def get_post_list(day_bias = 7):
    # Input day_bias: 要查詢的天數(正整數)
    # return msg_r : 可傳給使用者看的查詢結果(string)
    # return df_r : 查詢結果組成的sorted DataFrame
    # PTT表特版首頁
    url = "https://www.ptt.cc/bbs/Beauty/index.html"

    day_bias = -day_bias + 1

    start_time = time()

    post_list = []
    delay_sec = 0.1
    count = 0
    if day_bias > 0:
        print("Check day_bias!!!")
    # while url != None:
    while url is not None:
        url, next_post_list = ptt_listpage_crawler(url, day_bias)
        print(url,'\n')
        post_list = post_list + next_post_list[::-1]
        count += 1
        sleep(delay_sec)

    end_time = time()
    duration = end_time - start_time
    print("花費時間: ",duration)
    return post_list

post_list = get_post_list(2)

print("\n\n post_list:", post_list)

# 從陣列裡面把每個 url 丟進 post_crawler 爬取 po 文內容，回傳 post_time, author, comments, img_name_list, img_url_list
for index, post in enumerate(post_list):
    post_time, author, comments, img_name_list, img_url_list = post_crawler(post['url'], index)
    # print(post_time, author, img_name_list, img_url_list)
    post_list[index]['post_time'] = post_time
    post_list[index]['imgs'] = img_url_list
    post_list[index]['author'] = author
    post_list[index]['comments'] = comments

    # 顯示每一頁的資料
    print(post_list[index]['url'])
    print(post_list[index],'\n')

print("\n\n Done \n\n",post_list, "\n\n Done \n\n")

# 存成字典
with open('data.json', 'w') as file:
    json.dump(post_list, file, ensure_ascii=False)


"""
1156. Swap For Longest Repeated Character Substring
https://leetcode.com/problems/swap-for-longest-repeated-character-substring/

Given a string text, we are allowed to swap two of the characters in the string. Find the length of the longest substring with repeated characters.

Example 1:

Input: text = "ababa"
Output: 3
Explanation: We can swap the first 'b' with the last 'a', or the last 'b' with the first 'a'. Then, the longest repeated character substring is "aaa", which its length is 3.
Example 2:

Input: text = "aaabaaa"
Output: 6
Explanation: Swap 'b' with the last 'a' (or the first 'a'), and we get longest repeated character substring "aaaaaa", which its length is 6.
Example 3:

Input: text = "aaabbaaa"
Output: 4
Example 4:

Input: text = "aaaaa"
Output: 5
Explanation: No need to swap, longest repeated character substring is "aaaaa", length is 5.
Example 5:

Input: text = "abcdef"
Output: 1

Constraints:

1 <= text.length <= 20000
text consist of lowercase English characters only.

"""
from collections import defaultdict

class Solution:
    def maxRepOpt1(self, text: str) -> int:
        length = len(text)
        cnt = defaultdict(int)
        for c in text:
            cnt[c] += 1

        longest = 0
        for i, chr in enumerate(text):
            first_gap = True
            for j in range(i, length):
                if text[j] != text[i]:
                    if not first_gap:
                        # if cnt[chr] > j - i, there must have chr outside text[i:j]
                        # we can swap one to first gap
                        longest = max(longest, min(cnt[chr], j-i))
                        break
                    first_gap = False
                elif j == length - 1:
                    # try to swap a chr to text[i-1]
                    longest = max(longest, min(cnt[chr], j-i+1))

        return longest

def main():
    solu = Solution()
    print(solu.maxRepOpt1("aaabbaaa"))
    print(solu.maxRepOpt1("aaaaaa"))

if __name__ == "__main__":
    main()


from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework_simplejwt.tokens import RefreshToken

from education.models import Course, Lesson
from users.models import User, UserRoles

class LessonTestCase(APITestCase):

    def setUp(self):
        """Заполнение первичных данных"""

        self.user = User.objects.create(
            email='test@test.ru',
            is_staff=True,
            is_superuser=True,
            is_active=True,
            role=UserRoles.MEMBER,
        )
        self.user.set_password('324214Kross!')
        self.user.save()

        token = RefreshToken.for_user(self.user)
        self.access_token = str(token.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')

        self.course = Course.objects.create(
            title='course test',
            description='course test'
        )

        self.lesson = Lesson.objects.create(
            title='lesson test',
            description='lesson test',
            course=self.course,
            author=self.user
        )

    def test_lesson_list(self):
        """ Тест получения списка уроков"""

        response = self.client.get(
            reverse('education:lesson_list'),
        )

        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK
        )

        # print(response.json())

        self.assertEqual(
            response.json(),
            {'count': 1, 'next': None, 'previous': None, 'results': [
                {'id': self.lesson.id, 'title': 'lesson test', 'description': 'lesson test', 'preview': None,
                 'link': None,
                 'course': self.course.id, 'author': self.user.id}]}
        )

        self.assertTrue(
            Lesson.objects.all().exists()
        )

    def test_create_lesson(self):
        """ Тест создания уроков"""

        data = {
            'title': 'test create',
            'description': 'test create'
        }
        self.client.force_authenticate(user=self.user)
        response = self.client.post(
            reverse('education:lesson_create'),
            data=data
        )

        self.assertEqual(
            response.status_code,
            status.HTTP_201_CREATED
        )

        self.assertTrue(
            Lesson.objects.all().exists()
        )
    def test_lesson_update(self):
        """ Тест обновления урока """
        self.client.force_authenticate(user=self.user)

        data = {
            'title': 'update lesson',
            'description': 'update lesson',
        }

        response = self.client.put(
            path=f'/lesson/update/{self.lesson.id}/',
            data=data,
        )

        self.assertEqual(
            response.status_code, status.HTTP_200_OK,
        )
        response = response.json()

        self.assertTrue(
            Lesson.objects.all().exists()
        )
    def test_lesson_delete(self):
        self.client.force_authenticate(user=self.user)

        response = self.client.delete(
            f'/lesson/delete/{self.lesson.id}/',
        )

        self.assertEqual(
            response.status_code, status.HTTP_204_NO_CONTENT,
        )
        self.assertFalse(
            Lesson.objects.all().exists(),
        )

    def tearDown(self) -> None:
        self.user.delete()
        self.course.delete()
        self.lesson.delete()

class SubscriptionTest(APITestCase):
    def setUp(self) -> None:
        """Заполнение первичных данных"""
        self.course = Course.objects.create(
            title='test_course',
            description='test_course'
        )

        self.user = User.objects.create(
            email='test@test.com',
            is_staff=False,
            is_superuser=True,
            is_active=True,
            role=UserRoles.MEMBER,
        )
        self.user.set_password('324214Kross!')
        self.user.save()

        token = RefreshToken.for_user(self.user)
        self.access_token = str(token.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')

    def test_create_subscription(self):
        response = self.client.post('/subscription/create/',
                                    {'course': self.course.id, 'user': self.user.id, 'status': False})

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)


# coding:utf-8
# Created by PyCharm.
# Author  : nick（飞虎队工作室）
# Wechat  : cai9503
# Shop    : https://shop.zbj.com/6463852/
# Date    : 2021/10/6
# Time    : 20:57
#路由文件
from tornado.web import Application
from views.index import IndexHandler, DetailHandler, DictinfoHandler, WenjianHandler, StudentHandler, UserHandler, BaseHandler
from config import settings

# 配置路由
class App(Application):
    def __init__(self):
        url = [
            (r'/', IndexHandler),  # 路由匹配试图函数
            (r'/index/', IndexHandler),  # 路由匹配试图函数
            (r'/no/([0-9]+)/', DetailHandler),  # 路由无名分组
            (r'/detail/(?P<id>[0-9]+)/', DetailHandler),  #  路由有名分组
            (r'/dictinfo/', DictinfoHandler),
            (r'/wenjian/', WenjianHandler),
            (r'/stu/', StudentHandler),
            (r'/user/', UserHandler),
            (r'/user/(?P<id>[0-9]+)', UserHandler),
            (r".*", BaseHandler),
        ]

        super(App, self).__init__(url, **settings)


from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
import logging

from .models import TwofactorInfo
from . import util
from project.util.success_url import get_success_url

logger = logging.getLogger(__name__)

@login_required
def verify(request):
    """
    Authenticate the currently logged-in user via a Time-based One-time Password (TOTP),
    which classifies them as "verified", then redirect them to a success URL passed-in
    through the 'next' querystring argument.

    If the user has never set up their 2FA, their TOTP provisioning URI and QR code
    will be presented to them so that they can set everything up.

    If the user is already verified, this view will automatically redirect them to
    the success URL.
    """

    success_url = get_success_url(request)
    if util.is_request_user_verified(request):
        return HttpResponseRedirect(success_url)

    twofactor, _ = TwofactorInfo.objects.get_or_create(user=request.user)
    error = None
    if request.method == "POST":
        otp: str = request.POST.get("otp", "").replace(" ", "")
        if twofactor.totp.verify(otp):
            util.verify_request_user(request)
            twofactor.has_user_seen_secret_yet = True
            twofactor.save()
            return HttpResponseRedirect(success_url)
        else:
            error = "Alas, your one-time password is invalid."
            logger.warning(f"The user {request.user} submitted an invalid 2FA OTP.")

    return render(
        request,
        "twofactor/verify.html",
        {"error": error, "next": success_url, "twofactor": twofactor},
    )


from __future__ import print_function
from time import sleep
from tweet_sentiment import calculate_tweet_sentiment, create_sentiment_dictionary
import twitterstream
import sys
import json
import datetime

def print_average_sentiment(tweet_file, sentiment_dictionary):
	date = datetime.datetime.now().date()
	time = datetime.datetime.now().time()
	average_sentiment = calculate_average_sentiment(tweet_file, sentiment_dictionary)
	print(date, time, average_sentiment, sep='\t')

def calculate_average_sentiment(tweet_file, sentiment_dictionary):
	total_sentiment = 0
	tweet_counter = 0
	for line in tweet_file:
		try:
			tweet = json.loads(line)
			total_sentiment += calculate_tweet_sentiment(tweet["text"], sentiment_dictionary)
			tweet_counter += 1
		except:
			pass  # Tweet file empty or tweet doesn't have a text value
	try:
		average_sentiment = float(total_sentiment) / tweet_counter
	except:
		if (tweet_counter == 0):
			average_sentiment = "Error: No Tweets Detected"
		else:
			average_sentiment = "Error: Unknown"
	return average_sentiment

def main():
	try:
		sentiment_file = open(sys.argv[1])
		output_file_path = sys.argv[2]
		number_of_tweets = int(sys.argv[3])
	except:
		sys.exit(
			"Please enter the path to the sentiment file, " +
			"desired path for the Twitter Stream output, " +
			"and the number of tweets to collect for each sample " +
			"as arguments in that order.")

	headers = "Date\tTime\tAverage Sentiment"

	# Create sentiment dictionary
	sentiment_dictionary = create_sentiment_dictionary(sentiment_file)

	print("This script runs indefinitely. Press Ctrl+C to quit.")
	print(headers)

	while True:
		# Run twitter stream to generate json file
		twitterstream.main(output_file_path, number_of_tweets)

		# Open json tweet file and calculate average sentiment
		tweet_file = open(output_file_path)
		print_average_sentiment(tweet_file, sentiment_dictionary)

		# Wait five minutes
		sleep(300)

if __name__ == '__main__':
    main()


from flask import request, make_response, json
import app.action.model as model

def explore():
    page = request.args.get('page', 1)
    resp = model.explore(page)
    return make_response(json.jsonify(data=resp), 200)

def write():
    name = request.form['name']
    action_type = request.form['action_type']
    objective = request.form['objective']
    amount = request.form['amount']

    resp = model.translate(name, action_type, objective, amount)
    return make_response(json.jsonify(data="ok"), 200)


class Solution(object):
    def maximalSquare(self, matrix):
        n = len(matrix)
        if n==0: return 0
        m = len(matrix[0])
        s = [[0]*(m+1) for i in xrange(n+1)]
        for i in xrange(1,n+1):
            for j in xrange(1,m+1):
                s[i][j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+int(matrix[i-1][j-1])
        def square(i,j,L):
            return s[i+L-1][j+L-1]-s[i-1][j+L-1]-s[i+L-1][j-1]+s[i-1][j-1] == L*L
        def check(L):
            for i in xrange(1,n+1):
                for j in xrange(1,m+1):
                    if i+L-1<=n and j+L-1<=m and square(i,j,L): return True
            return False
        l,r = 0,max(n,m)+1
        while l+1<r:
            mid = (l+r)/2
            if check(mid): l=mid
            else: r = mid
        return l*l


import numpy as np
import matplotlib as mplt
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import time

'''
в этом файле содержатся функции для отрисовки графиков на интерфейсе приложения
'''

'''функция для обновления графика'''
def fig_maker(window, RATE, data_one_channel, time_start=8.6, time_finish=9.6):  # this should be called as a thread, then time.sleep() here would not freeze the GUI
   t = np.arange(time_start/RATE, time_finish/RATE, 1 / RATE)
   fig = mplt.figure.Figure(figsize=(8, 4), dpi=100)
   ax = fig.add_subplot(1, 1, 1)
   ax.plot(t, data_one_channel)
   ax.set_xlabel("Время, сек")
   ax.set_ylabel("Амплитуда, миллиВольты")
   window.write_event_value('-THREAD-', 'done.')
   time.sleep(1)
   return fig

def fig_maker_multi(window, RATE, data, time_start=8, time_finish=9):  # this should be called as a thread, then time.sleep() here would not freeze the GUI
   t = np.arange(time_start / RATE, time_finish / RATE, 1 / RATE)

   fig = mplt.figure.Figure(figsize=(8, 4), dpi=100)
   gs = fig.add_gridspec(8, hspace=0)
   axs = gs.subplots(sharex=True, sharey=True)
   i = 0
   print(data)
   for v in data.values():
       print(type(v[-1]), v[-1], time_start, time_finish)
       axs[i].plot(t, v[-1][int(time_start):int(time_finish)])
       axs[i].set_ylabel(v[0],  rotation=0, fontweight='bold', color='orange')
       axs[i].label_outer()    # Hide x labels and tick labels for all but bottom plot
       i += 1

   window.write_event_value('-THREAD-', 'done.')
   time.sleep(1)
   return fig

def fig_maker_multi_ica(window, RATE, data, time_start=8, time_finish=9):  # this should be called as a thread, then time.sleep() here would not freeze the GUI
   '''
   функция, для отрисовки сразу двух графиков: самого сигнала и сигнала после ica
   :param window:
   :param RATE: 500, количество считываний в секунду
   :param data: dict, в нём -1 значение - сгнал после ica, -2 - сам сигнал
   :param time_start: int - время начала временной шкалы
   :param time_finish: int - точка окнчания временной шкалы
   :return: None
   '''
   t = np.arange(time_start / RATE, time_finish / RATE, 1 / RATE)

   fig = mplt.figure.Figure(figsize=(8, 4), dpi=100)
   gs = fig.add_gridspec(8, hspace=0)
   axs = gs.subplots(sharex=True, sharey=True)
   i = 0
   for k, v in data.items():
       axs[i].plot(t, v[-2][int(time_start):int(time_finish)])
       axs[i].plot(t, v[-1][:-1], color='darkorange')
       axs[i].set_ylabel(k[4:],  rotation=0, fontweight='bold', color='orange')
       axs[i].label_outer()    # Hide x labels and tick labels for all but bottom plot
       i += 1

   window.write_event_value('-THREAD-', 'done.')
   time.sleep(1)
   return fig

'''функция для отрисовки графика, создание виджета'''
def draw_figure(canvas, figure):
   tkcanvas = FigureCanvasTkAgg(figure, canvas)
   tkcanvas.draw()
   tkcanvas.get_tk_widget().pack(side='top', fill='both', expand=1)
   return tkcanvas

'''функция для удаления старого графика'''
def delete_fig_agg(fig_agg):
   fig_agg.get_tk_widget().forget()
   plt.close('all')